mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-05-08 04:20:14 -05:00
Combining satellite & sbdb renderables under a common base class
This commit is contained in:
@@ -28,6 +28,7 @@ set(HEADER_FILES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.h
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablerings.h
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablerings.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableorbitalkepler.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesatellites.h
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesatellites.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesmallbody.h
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesmallbody.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.h
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.h
|
||||||
@@ -44,6 +45,7 @@ set(SOURCE_FILES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablerings.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablerings.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableorbitalkepler.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesatellites.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesatellites.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesmallbody.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablesmallbody.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.cpp
|
||||||
|
|||||||
@@ -0,0 +1,525 @@
|
|||||||
|
/*****************************************************************************************
|
||||||
|
* *
|
||||||
|
* OpenSpace *
|
||||||
|
* *
|
||||||
|
* Copyright (c) 2014-2020 *
|
||||||
|
* *
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||||
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||||
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||||
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||||
|
* conditions: *
|
||||||
|
* *
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies *
|
||||||
|
* or substantial portions of the Software. *
|
||||||
|
* *
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||||
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||||
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||||
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||||
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#include <modules/space/rendering/renderableorbitalkepler.h>
|
||||||
|
|
||||||
|
#include <modules/space/translation/keplertranslation.h>
|
||||||
|
#include <modules/space/translation/tletranslation.h>
|
||||||
|
#include <modules/space/spacemodule.h>
|
||||||
|
#include <openspace/engine/openspaceengine.h>
|
||||||
|
#include <openspace/rendering/renderengine.h>
|
||||||
|
#include <openspace/engine/globals.h>
|
||||||
|
#include <openspace/documentation/documentation.h>
|
||||||
|
#include <openspace/documentation/verifier.h>
|
||||||
|
#include <openspace/util/time.h>
|
||||||
|
#include <openspace/util/updatestructures.h>
|
||||||
|
#include <ghoul/filesystem/filesystem.h>
|
||||||
|
#include <ghoul/filesystem/file.h>
|
||||||
|
#include <ghoul/misc/csvreader.h>
|
||||||
|
#include <ghoul/opengl/programobject.h>
|
||||||
|
#include <ghoul/logging/logmanager.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <math.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr const char* ProgramName = "OrbitalKepler";
|
||||||
|
constexpr const char* _loggerCat = "OrbitalKepler";
|
||||||
|
|
||||||
|
static const openspace::properties::Property::PropertyInfo PathInfo = {
|
||||||
|
"Path",
|
||||||
|
"Path",
|
||||||
|
"The file path to the data file to read"
|
||||||
|
};
|
||||||
|
static const openspace::properties::Property::PropertyInfo SegmentsInfo = {
|
||||||
|
"Segments",
|
||||||
|
"Segments",
|
||||||
|
"The number of segments to use for each orbit ellipse"
|
||||||
|
};
|
||||||
|
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
||||||
|
"LineWidth",
|
||||||
|
"Line Width",
|
||||||
|
"This value specifies the line width of the trail if the selected rendering "
|
||||||
|
"method includes lines. If the rendering mode is set to Points, this value is "
|
||||||
|
"ignored."
|
||||||
|
};
|
||||||
|
constexpr openspace::properties::Property::PropertyInfo LineColorInfo = {
|
||||||
|
"Color",
|
||||||
|
"Color",
|
||||||
|
"This value determines the RGB main color for the lines and points of the trail."
|
||||||
|
};
|
||||||
|
constexpr openspace::properties::Property::PropertyInfo TrailFadeInfo = {
|
||||||
|
"TrailFade",
|
||||||
|
"Trail Fade",
|
||||||
|
"This value determines how fast the trail fades and is an appearance property. "
|
||||||
|
};
|
||||||
|
static const openspace::properties::Property::PropertyInfo UpperLimitInfo = {
|
||||||
|
"UpperLimit",
|
||||||
|
"Upper Limit",
|
||||||
|
"Upper limit on the number of objects for this renderable, regardless of "
|
||||||
|
"how many objects are contained in the data file"
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const char* KeyFile = "Path";
|
||||||
|
constexpr const char* KeyLineNum = "LineNumber";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace openspace {
|
||||||
|
|
||||||
|
// Count the number of full days since the beginning of 2000 to the beginning of
|
||||||
|
// the parameter 'year'
|
||||||
|
int countDays(int year) {
|
||||||
|
// Find the position of the current year in the vector, the difference
|
||||||
|
// between its position and the position of 2000 (for J2000) gives the
|
||||||
|
// number of leap years
|
||||||
|
constexpr const int Epoch = 2000;
|
||||||
|
constexpr const int DaysRegularYear = 365;
|
||||||
|
constexpr const int DaysLeapYear = 366;
|
||||||
|
|
||||||
|
if (year == Epoch) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the position of the most recent leap year
|
||||||
|
const auto lb = std::lower_bound(LeapYears.begin(), LeapYears.end(), year);
|
||||||
|
|
||||||
|
// Get the position of the epoch
|
||||||
|
const auto y2000 = std::find(LeapYears.begin(), LeapYears.end(), Epoch);
|
||||||
|
|
||||||
|
// The distance between the two iterators gives us the number of leap years
|
||||||
|
const int nLeapYears = static_cast<int>(std::abs(std::distance(y2000, lb)));
|
||||||
|
|
||||||
|
const int nYears = std::abs(year - Epoch);
|
||||||
|
const int nRegularYears = nYears - nLeapYears;
|
||||||
|
|
||||||
|
// Get the total number of days as the sum of leap years + non leap years
|
||||||
|
const int result = nRegularYears * DaysRegularYear + nLeapYears * DaysLeapYear;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of leap seconds that lie between the {year, dayOfYear}
|
||||||
|
// time point and { 2000, 1 }
|
||||||
|
int countLeapSeconds(int year, int dayOfYear) {
|
||||||
|
// Find the position of the current year in the vector; its position in
|
||||||
|
// the vector gives the number of leap seconds
|
||||||
|
struct LeapSecond {
|
||||||
|
int year;
|
||||||
|
int dayOfYear;
|
||||||
|
bool operator<(const LeapSecond& rhs) const {
|
||||||
|
return std::tie(year, dayOfYear) < std::tie(rhs.year, rhs.dayOfYear);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeapSecond Epoch = { 2000, 1 };
|
||||||
|
|
||||||
|
// List taken from: https://www.ietf.org/timezones/data/leap-seconds.list
|
||||||
|
static const std::vector<LeapSecond> LeapSeconds = {
|
||||||
|
{ 1972, 1 },
|
||||||
|
{ 1972, 183 },
|
||||||
|
{ 1973, 1 },
|
||||||
|
{ 1974, 1 },
|
||||||
|
{ 1975, 1 },
|
||||||
|
{ 1976, 1 },
|
||||||
|
{ 1977, 1 },
|
||||||
|
{ 1978, 1 },
|
||||||
|
{ 1979, 1 },
|
||||||
|
{ 1980, 1 },
|
||||||
|
{ 1981, 182 },
|
||||||
|
{ 1982, 182 },
|
||||||
|
{ 1983, 182 },
|
||||||
|
{ 1985, 182 },
|
||||||
|
{ 1988, 1 },
|
||||||
|
{ 1990, 1 },
|
||||||
|
{ 1991, 1 },
|
||||||
|
{ 1992, 183 },
|
||||||
|
{ 1993, 182 },
|
||||||
|
{ 1994, 182 },
|
||||||
|
{ 1996, 1 },
|
||||||
|
{ 1997, 182 },
|
||||||
|
{ 1999, 1 },
|
||||||
|
{ 2006, 1 },
|
||||||
|
{ 2009, 1 },
|
||||||
|
{ 2012, 183 },
|
||||||
|
{ 2015, 182 },
|
||||||
|
{ 2017, 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the position of the last leap second before the desired date
|
||||||
|
LeapSecond date { year, dayOfYear };
|
||||||
|
const auto it = std::lower_bound(LeapSeconds.begin(), LeapSeconds.end(), date);
|
||||||
|
|
||||||
|
// Get the position of the Epoch
|
||||||
|
const auto y2000 = std::lower_bound(
|
||||||
|
LeapSeconds.begin(),
|
||||||
|
LeapSeconds.end(),
|
||||||
|
Epoch
|
||||||
|
);
|
||||||
|
|
||||||
|
// The distance between the two iterators gives us the number of leap years
|
||||||
|
const int nLeapSeconds = static_cast<int>(std::abs(std::distance(y2000, it)));
|
||||||
|
return nLeapSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
double calculateSemiMajorAxis(double meanMotion) {
|
||||||
|
constexpr const double GravitationalConstant = 6.6740831e-11;
|
||||||
|
constexpr const double MassEarth = 5.9721986e24;
|
||||||
|
constexpr const double muEarth = GravitationalConstant * MassEarth;
|
||||||
|
|
||||||
|
// Use Kepler's 3rd law to calculate semimajor axis
|
||||||
|
// a^3 / P^2 = mu / (2pi)^2
|
||||||
|
// <=> a = ((mu * P^2) / (2pi^2))^(1/3)
|
||||||
|
// with a = semimajor axis
|
||||||
|
// P = period in seconds
|
||||||
|
// mu = G*M_earth
|
||||||
|
double period = std::chrono::seconds(std::chrono::hours(24)).count() / meanMotion;
|
||||||
|
|
||||||
|
const double pisq = glm::pi<double>() * glm::pi<double>();
|
||||||
|
double semiMajorAxis = pow((muEarth * period*period) / (4 * pisq), 1.0 / 3.0);
|
||||||
|
|
||||||
|
// We need the semi major axis in km instead of m
|
||||||
|
return semiMajorAxis / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double epochFromSubstring(const std::string& epochString) {
|
||||||
|
// The epochString is in the form:
|
||||||
|
// YYDDD.DDDDDDDD
|
||||||
|
// With YY being the last two years of the launch epoch, the first DDD the day
|
||||||
|
// of the year and the remaning a fractional part of the day
|
||||||
|
|
||||||
|
// The main overview of this function:
|
||||||
|
// 1. Reconstruct the full year from the YY part
|
||||||
|
// 2. Calculate the number of seconds since the beginning of the year
|
||||||
|
// 2.a Get the number of full days since the beginning of the year
|
||||||
|
// 2.b If the year is a leap year, modify the number of days
|
||||||
|
// 3. Convert the number of days to a number of seconds
|
||||||
|
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
|
||||||
|
// 5. Adjust for the fact the epoch starts on 1st Januaray at 12:00:00, not
|
||||||
|
// midnight
|
||||||
|
|
||||||
|
// According to https://celestrak.com/columns/v04n03/
|
||||||
|
// Apparently, US Space Command sees no need to change the two-line element
|
||||||
|
// set format yet since no artificial earth satellites existed prior to 1957.
|
||||||
|
// By their reasoning, two-digit years from 57-99 correspond to 1957-1999 and
|
||||||
|
// those from 00-56 correspond to 2000-2056. We'll see each other again in 2057!
|
||||||
|
|
||||||
|
// 1. Get the full year
|
||||||
|
std::string yearPrefix = [y = epochString.substr(0, 2)](){
|
||||||
|
int year = std::atoi(y.c_str());
|
||||||
|
return year >= 57 ? "19" : "20";
|
||||||
|
}();
|
||||||
|
const int year = std::atoi((yearPrefix + epochString.substr(0, 2)).c_str());
|
||||||
|
const int daysSince2000 = countDays(year);
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
// 2.a
|
||||||
|
double daysInYear = std::atof(epochString.substr(2).c_str());
|
||||||
|
|
||||||
|
// 2.b
|
||||||
|
const bool isInLeapYear = std::find(
|
||||||
|
LeapYears.begin(),
|
||||||
|
LeapYears.end(),
|
||||||
|
year
|
||||||
|
) != LeapYears.end();
|
||||||
|
if (isInLeapYear && daysInYear >= 60) {
|
||||||
|
// We are in a leap year, so we have an effective day more if we are
|
||||||
|
// beyond the end of february (= 31+29 days)
|
||||||
|
--daysInYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3
|
||||||
|
using namespace std::chrono;
|
||||||
|
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
|
||||||
|
//Need to subtract 1 from daysInYear since it is not a zero-based count
|
||||||
|
const double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay;
|
||||||
|
|
||||||
|
// 4
|
||||||
|
// We need to remove additional leap seconds past 2000 and add them prior to
|
||||||
|
// 2000 to sync up the time zones
|
||||||
|
const double nLeapSecondsOffset = -countLeapSeconds(
|
||||||
|
year,
|
||||||
|
static_cast<int>(std::floor(daysInYear))
|
||||||
|
);
|
||||||
|
|
||||||
|
// 5
|
||||||
|
const double nSecondsEpochOffset = static_cast<double>(
|
||||||
|
seconds(hours(12)).count()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Combine all of the values
|
||||||
|
const double epoch = nSecondsSince2000 + nLeapSecondsOffset - nSecondsEpochOffset;
|
||||||
|
return epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
documentation::Documentation RenderableOrbitalKepler::Documentation() {
|
||||||
|
using namespace documentation;
|
||||||
|
return {
|
||||||
|
"RenderableOrbitalKepler",
|
||||||
|
"space_renderable_keplerian_orbitals",
|
||||||
|
{
|
||||||
|
{
|
||||||
|
SegmentsInfo.identifier,
|
||||||
|
new DoubleVerifier,
|
||||||
|
Optional::No,
|
||||||
|
SegmentsInfo.description
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PathInfo.identifier,
|
||||||
|
new StringVerifier,
|
||||||
|
Optional::No,
|
||||||
|
PathInfo.description
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LineWidthInfo.identifier,
|
||||||
|
new DoubleVerifier,
|
||||||
|
Optional::Yes,
|
||||||
|
LineWidthInfo.description
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LineColorInfo.identifier,
|
||||||
|
new DoubleVector3Verifier,
|
||||||
|
Optional::No,
|
||||||
|
LineColorInfo.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderableOrbitalKepler::RenderableOrbitalKepler(const ghoul::Dictionary& dictionary)
|
||||||
|
: Renderable(dictionary)
|
||||||
|
, _path(PathInfo)
|
||||||
|
, _nSegments(SegmentsInfo, 120, 4, 1024)
|
||||||
|
{
|
||||||
|
documentation::testSpecificationAndThrow(
|
||||||
|
Documentation(),
|
||||||
|
dictionary,
|
||||||
|
"RenderableOrbitalKepler"
|
||||||
|
);
|
||||||
|
|
||||||
|
_path = dictionary.value<std::string>(PathInfo.identifier);
|
||||||
|
_nSegments = static_cast<int>(dictionary.value<double>(SegmentsInfo.identifier));
|
||||||
|
|
||||||
|
if (dictionary.hasKeyAndValue<glm::vec3>(LineColorInfo.identifier)) {
|
||||||
|
_appearance.lineColor = dictionary.value<glm::vec3>(LineColorInfo.identifier);
|
||||||
|
}
|
||||||
|
if (dictionary.hasKeyAndValue<double>(TrailFadeInfo.identifier)) {
|
||||||
|
_appearance.lineFade = static_cast<float>(
|
||||||
|
dictionary.value<double>(TrailFadeInfo.identifier)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_appearance.lineFade = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dictionary.hasKeyAndValue<double>(UpperLimitInfo.identifier)) {
|
||||||
|
_upperLimit = static_cast<unsigned int>(
|
||||||
|
dictionary.value<double>(UpperLimitInfo.identifier)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_upperLimit = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dictionary.hasKeyAndValue<double>(LineWidthInfo.identifier)) {
|
||||||
|
_appearance.lineWidth = static_cast<float>(
|
||||||
|
dictionary.value<double>(LineWidthInfo.identifier)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_appearance.lineWidth = 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reinitializeTrailBuffers = [this]() {
|
||||||
|
initializeGL();
|
||||||
|
};
|
||||||
|
|
||||||
|
_path.onChange(reinitializeTrailBuffers);
|
||||||
|
_nSegments.onChange(reinitializeTrailBuffers);
|
||||||
|
_upperLimit.onChange(reinitializeTrailBuffers);
|
||||||
|
|
||||||
|
addPropertySubOwner(_appearance);
|
||||||
|
addProperty(_path);
|
||||||
|
addProperty(_nSegments);
|
||||||
|
addProperty(_opacity);
|
||||||
|
addProperty(_upperLimit);
|
||||||
|
|
||||||
|
setRenderBin(Renderable::RenderBin::Overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableOrbitalKepler::initializeGL() {
|
||||||
|
glGenVertexArrays(1, &_vertexArray);
|
||||||
|
glGenBuffers(1, &_vertexBuffer);
|
||||||
|
|
||||||
|
_programObject = SpaceModule::ProgramObjectManager.request(
|
||||||
|
ProgramName,
|
||||||
|
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
|
||||||
|
return global::renderEngine.buildRenderProgram(
|
||||||
|
ProgramName,
|
||||||
|
absPath("${MODULE_SPACE}/shaders/debrisViz_vs.glsl"),
|
||||||
|
absPath("${MODULE_SPACE}/shaders/debrisViz_fs.glsl")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
_uniformCache.modelView = _programObject->uniformLocation("modelViewTransform");
|
||||||
|
_uniformCache.projection = _programObject->uniformLocation("projectionTransform");
|
||||||
|
_uniformCache.lineFade = _programObject->uniformLocation("lineFade");
|
||||||
|
_uniformCache.inGameTime = _programObject->uniformLocation("inGameTime");
|
||||||
|
_uniformCache.color = _programObject->uniformLocation("color");
|
||||||
|
_uniformCache.opacity = _programObject->uniformLocation("opacity");
|
||||||
|
|
||||||
|
updateBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableOrbitalKepler::deinitializeGL() {
|
||||||
|
glDeleteBuffers(1, &_vertexBuffer);
|
||||||
|
glDeleteVertexArrays(1, &_vertexArray);
|
||||||
|
|
||||||
|
SpaceModule::ProgramObjectManager.release(
|
||||||
|
ProgramName,
|
||||||
|
[](ghoul::opengl::ProgramObject* p) {
|
||||||
|
global::renderEngine.removeRenderProgram(p);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
_programObject = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderableOrbitalKepler::isReady() const {
|
||||||
|
return _programObject != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableOrbitalKepler::render(const RenderData& data, RendererTasks&) {
|
||||||
|
if (_data.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_programObject->activate();
|
||||||
|
_programObject->setUniform(_uniformCache.opacity, _opacity);
|
||||||
|
_programObject->setUniform(_uniformCache.inGameTime, data.time.j2000Seconds());
|
||||||
|
|
||||||
|
|
||||||
|
glm::dmat4 modelTransform =
|
||||||
|
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
||||||
|
glm::dmat4(data.modelTransform.rotation) *
|
||||||
|
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
|
||||||
|
|
||||||
|
_programObject->setUniform(
|
||||||
|
_uniformCache.modelView,
|
||||||
|
data.camera.combinedViewMatrix() * modelTransform
|
||||||
|
);
|
||||||
|
|
||||||
|
// Because we want the property to work similar to the planet trails
|
||||||
|
float fade = static_cast<float>(pow(_appearance.lineFade.maxValue() - _appearance.lineFade, 2.0));
|
||||||
|
|
||||||
|
_programObject->setUniform(_uniformCache.projection, data.camera.projectionMatrix());
|
||||||
|
_programObject->setUniform(_uniformCache.color, _appearance.lineColor);
|
||||||
|
_programObject->setUniform(_uniformCache.lineFade, fade);
|
||||||
|
|
||||||
|
glLineWidth(_appearance.lineWidth);
|
||||||
|
|
||||||
|
const size_t nrOrbits = _data.size();
|
||||||
|
gl::GLint vertices = 0;
|
||||||
|
|
||||||
|
//glDepthMask(false);
|
||||||
|
//glBlendFunc(GL_SRC_ALPHA, GL_ONE)
|
||||||
|
|
||||||
|
glBindVertexArray(_vertexArray);
|
||||||
|
for (size_t i = 0; i < nrOrbits; ++i) {
|
||||||
|
glDrawArrays(GL_LINE_STRIP, vertices, _nSegments + 1);
|
||||||
|
vertices = vertices + _nSegments + 1;
|
||||||
|
}
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
_programObject->deactivate();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableOrbitalKepler::updateBuffers() {
|
||||||
|
readDataFile(_path);
|
||||||
|
|
||||||
|
const size_t nVerticesPerOrbit = _nSegments + 1;
|
||||||
|
_vertexBufferData.resize(_data.size() * nVerticesPerOrbit);
|
||||||
|
size_t orbitindex = 0;
|
||||||
|
|
||||||
|
for (const auto& orbit : _data) {
|
||||||
|
_keplerTranslator.setKeplerElements(
|
||||||
|
orbit.eccentricity,
|
||||||
|
orbit.semiMajorAxis,
|
||||||
|
orbit.inclination,
|
||||||
|
orbit.ascendingNode,
|
||||||
|
orbit.argumentOfPeriapsis,
|
||||||
|
orbit.meanAnomaly,
|
||||||
|
orbit.period,
|
||||||
|
orbit.epoch
|
||||||
|
);
|
||||||
|
|
||||||
|
for (size_t i=0 ; i < nVerticesPerOrbit; ++i) {
|
||||||
|
size_t index = orbitindex * nVerticesPerOrbit + i;
|
||||||
|
|
||||||
|
double timeOffset = orbit.period *
|
||||||
|
static_cast<double>(i)/ static_cast<double>(_nSegments);
|
||||||
|
|
||||||
|
glm::dvec3 position = _keplerTranslator.position({
|
||||||
|
{},
|
||||||
|
Time(timeOffset + orbit.epoch),
|
||||||
|
Time(0.0),
|
||||||
|
false
|
||||||
|
});
|
||||||
|
|
||||||
|
double positionX = position.x;
|
||||||
|
double positionY = position.y;
|
||||||
|
double positionZ = position.z;
|
||||||
|
|
||||||
|
_vertexBufferData[index].x = static_cast<float>(positionX);
|
||||||
|
_vertexBufferData[index].y = static_cast<float>(positionY);
|
||||||
|
_vertexBufferData[index].z = static_cast<float>(positionZ);
|
||||||
|
_vertexBufferData[index].time = static_cast<float>(timeOffset);
|
||||||
|
_vertexBufferData[index].epoch = orbit.epoch;
|
||||||
|
_vertexBufferData[index].period = orbit.period;
|
||||||
|
}
|
||||||
|
|
||||||
|
++orbitindex;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(_vertexArray);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
||||||
|
glBufferData(
|
||||||
|
GL_ARRAY_BUFFER,
|
||||||
|
_vertexBufferData.size() * sizeof(TrailVBOLayout),
|
||||||
|
_vertexBufferData.data(),
|
||||||
|
GL_STATIC_DRAW
|
||||||
|
);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(TrailVBOLayout), nullptr);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(1, 2, GL_DOUBLE, GL_FALSE, sizeof(TrailVBOLayout), (GLvoid*)(4 * sizeof(GL_FLOAT)));
|
||||||
|
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*****************************************************************************************
|
||||||
|
* *
|
||||||
|
* OpenSpace *
|
||||||
|
* *
|
||||||
|
* Copyright (c) 2014-2020 *
|
||||||
|
* *
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||||
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||||
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||||
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||||
|
* conditions: *
|
||||||
|
* *
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies *
|
||||||
|
* or substantial portions of the Software. *
|
||||||
|
* *
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||||
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||||
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||||
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||||
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLEORBITALKEPLER___H__
|
||||||
|
#define __OPENSPACE_MODULE_SPACE___RENDERABLEORBITALKEPLER___H__
|
||||||
|
|
||||||
|
#include <openspace/rendering/renderable.h>
|
||||||
|
|
||||||
|
#include <modules/base/rendering/renderabletrail.h>
|
||||||
|
#include <modules/space/translation/keplertranslation.h>
|
||||||
|
#include <openspace/properties/stringproperty.h>
|
||||||
|
#include <openspace/properties/scalar/uintproperty.h>
|
||||||
|
#include <ghoul/glm.h>
|
||||||
|
#include <ghoul/misc/objectmanager.h>
|
||||||
|
#include <ghoul/opengl/programobject.h>
|
||||||
|
|
||||||
|
namespace openspace {
|
||||||
|
|
||||||
|
const std::vector<int> LeapYears = {
|
||||||
|
1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996,
|
||||||
|
2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040,
|
||||||
|
2044, 2048, 2052, 2056
|
||||||
|
};
|
||||||
|
int countDays(int year);
|
||||||
|
int countLeapSeconds(int year, int dayOfYear);
|
||||||
|
double calculateSemiMajorAxis(double meanMotion);
|
||||||
|
double epochFromSubstring(const std::string& epochString);
|
||||||
|
|
||||||
|
class RenderableOrbitalKepler : public Renderable {
|
||||||
|
public:
|
||||||
|
RenderableOrbitalKepler(const ghoul::Dictionary& dictionary);
|
||||||
|
|
||||||
|
void initializeGL() override;
|
||||||
|
void deinitializeGL() override;
|
||||||
|
|
||||||
|
bool isReady() const override;
|
||||||
|
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
||||||
|
|
||||||
|
static documentation::Documentation Documentation();
|
||||||
|
/**
|
||||||
|
* Reads the provided data file and calls the KeplerTranslation::setKeplerElments
|
||||||
|
* method with the correct values. If \p filename is a valid data file but contains
|
||||||
|
* disallowed values (see KeplerTranslation::setKeplerElements), a
|
||||||
|
* KeplerTranslation::RangeError is thrown.
|
||||||
|
*
|
||||||
|
* \param filename The path to the file that contains the data file.
|
||||||
|
*
|
||||||
|
* \throw ghoul::RuntimeError if the data file does not exist or there is a
|
||||||
|
* problem with its format.
|
||||||
|
* \pre The \p filename must exist
|
||||||
|
*/
|
||||||
|
virtual void readDataFile(const std::string& filename) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Vertex {
|
||||||
|
glm::vec3 position = glm::vec3(0.f);
|
||||||
|
glm::vec3 color = glm::vec3(0.f);
|
||||||
|
glm::vec2 texcoord = glm::vec2(0.f);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KeplerParameters {
|
||||||
|
double inclination = 0.0;
|
||||||
|
double semiMajorAxis = 0.0;
|
||||||
|
double ascendingNode = 0.0;
|
||||||
|
double eccentricity = 0.0;
|
||||||
|
double argumentOfPeriapsis = 0.0;
|
||||||
|
double meanAnomaly = 0.0;
|
||||||
|
double meanMotion = 0.0;
|
||||||
|
double epoch = 0.0;
|
||||||
|
double period = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The layout of the VBOs
|
||||||
|
struct TrailVBOLayout {
|
||||||
|
float x = 0.f;
|
||||||
|
float y = 0.f;
|
||||||
|
float z = 0.f;
|
||||||
|
float time = 0.f;
|
||||||
|
double epoch = 0.0;
|
||||||
|
double period = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
KeplerTranslation _keplerTranslator;
|
||||||
|
std::vector<KeplerParameters> _data;
|
||||||
|
|
||||||
|
/// The backend storage for the vertex buffer object containing all points for this
|
||||||
|
/// trail.
|
||||||
|
std::vector<TrailVBOLayout> _vertexBufferData;
|
||||||
|
|
||||||
|
GLuint _vertexArray;
|
||||||
|
GLuint _vertexBuffer;
|
||||||
|
GLuint _indexBuffer;
|
||||||
|
|
||||||
|
//GLuint _vaoTest; // vertexArrayObject
|
||||||
|
//GLuint _vboTest; // vertextBufferObject
|
||||||
|
//GLuint _eboTest; // elementBufferObject/ indexBufferObject
|
||||||
|
|
||||||
|
void updateBuffers();
|
||||||
|
|
||||||
|
ghoul::opengl::ProgramObject* _programObject;
|
||||||
|
|
||||||
|
properties::StringProperty _path;
|
||||||
|
properties::UIntProperty _nSegments;
|
||||||
|
properties::UIntProperty _upperLimit;
|
||||||
|
|
||||||
|
RenderableTrail::Appearance _appearance;
|
||||||
|
|
||||||
|
glm::vec3 _position = glm::vec3(0.f);
|
||||||
|
|
||||||
|
UniformCache(modelView, projection, lineFade, inGameTime, color, opacity,
|
||||||
|
numberOfSegments) _uniformCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace openspace
|
||||||
|
|
||||||
|
#endif // __OPENSPACE_MODULE_SPACE___RENDERABLEORBITALKEPLER___H__
|
||||||
|
|
||||||
@@ -53,220 +53,10 @@ namespace {
|
|||||||
"Path",
|
"Path",
|
||||||
"The file path to the TLE file to read"
|
"The file path to the TLE file to read"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const openspace::properties::Property::PropertyInfo SegmentsInfo = {
|
|
||||||
"Segments",
|
|
||||||
"Segments",
|
|
||||||
"The number of segments to use for each orbit ellipse"
|
|
||||||
};
|
|
||||||
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
|
||||||
"LineWidth",
|
|
||||||
"Line Width",
|
|
||||||
"This value specifies the line width of the trail if the selected rendering "
|
|
||||||
"method includes lines. If the rendering mode is set to Points, this value is "
|
|
||||||
"ignored."
|
|
||||||
};
|
|
||||||
constexpr openspace::properties::Property::PropertyInfo LineColorInfo = {
|
|
||||||
"Color",
|
|
||||||
"Color",
|
|
||||||
"This value determines the RGB main color for the lines and points of the trail."
|
|
||||||
};
|
|
||||||
constexpr openspace::properties::Property::PropertyInfo TrailFadeInfo = {
|
|
||||||
"TrailFade",
|
|
||||||
"Trail Fade",
|
|
||||||
"This value determines how fast the trail fades and is an appearance property. "
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr const char* KeyFile = "Path";
|
|
||||||
constexpr const char* KeyLineNum = "LineNumber";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace openspace {
|
namespace openspace {
|
||||||
|
|
||||||
// Count the number of full days since the beginning of 2000 to the beginning of
|
|
||||||
// the parameter 'year'
|
|
||||||
int countDays(int year) {
|
|
||||||
// Find the position of the current year in the vector, the difference
|
|
||||||
// between its position and the position of 2000 (for J2000) gives the
|
|
||||||
// number of leap years
|
|
||||||
constexpr const int Epoch = 2000;
|
|
||||||
constexpr const int DaysRegularYear = 365;
|
|
||||||
constexpr const int DaysLeapYear = 366;
|
|
||||||
|
|
||||||
if (year == Epoch) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the position of the most recent leap year
|
|
||||||
const auto lb = std::lower_bound(LeapYears.begin(), LeapYears.end(), year);
|
|
||||||
|
|
||||||
// Get the position of the epoch
|
|
||||||
const auto y2000 = std::find(LeapYears.begin(), LeapYears.end(), Epoch);
|
|
||||||
|
|
||||||
// The distance between the two iterators gives us the number of leap years
|
|
||||||
const int nLeapYears = static_cast<int>(std::abs(std::distance(y2000, lb)));
|
|
||||||
|
|
||||||
const int nYears = std::abs(year - Epoch);
|
|
||||||
const int nRegularYears = nYears - nLeapYears;
|
|
||||||
|
|
||||||
// Get the total number of days as the sum of leap years + non leap years
|
|
||||||
const int result = nRegularYears * DaysRegularYear + nLeapYears * DaysLeapYear;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of leap seconds that lie between the {year, dayOfYear}
|
|
||||||
// time point and { 2000, 1 }
|
|
||||||
int countLeapSeconds(int year, int dayOfYear) {
|
|
||||||
// Find the position of the current year in the vector; its position in
|
|
||||||
// the vector gives the number of leap seconds
|
|
||||||
struct LeapSecond {
|
|
||||||
int year;
|
|
||||||
int dayOfYear;
|
|
||||||
bool operator<(const LeapSecond& rhs) const {
|
|
||||||
return std::tie(year, dayOfYear) < std::tie(rhs.year, rhs.dayOfYear);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const LeapSecond Epoch = { 2000, 1 };
|
|
||||||
|
|
||||||
// List taken from: https://www.ietf.org/timezones/data/leap-seconds.list
|
|
||||||
static const std::vector<LeapSecond> LeapSeconds = {
|
|
||||||
{ 1972, 1 },
|
|
||||||
{ 1972, 183 },
|
|
||||||
{ 1973, 1 },
|
|
||||||
{ 1974, 1 },
|
|
||||||
{ 1975, 1 },
|
|
||||||
{ 1976, 1 },
|
|
||||||
{ 1977, 1 },
|
|
||||||
{ 1978, 1 },
|
|
||||||
{ 1979, 1 },
|
|
||||||
{ 1980, 1 },
|
|
||||||
{ 1981, 182 },
|
|
||||||
{ 1982, 182 },
|
|
||||||
{ 1983, 182 },
|
|
||||||
{ 1985, 182 },
|
|
||||||
{ 1988, 1 },
|
|
||||||
{ 1990, 1 },
|
|
||||||
{ 1991, 1 },
|
|
||||||
{ 1992, 183 },
|
|
||||||
{ 1993, 182 },
|
|
||||||
{ 1994, 182 },
|
|
||||||
{ 1996, 1 },
|
|
||||||
{ 1997, 182 },
|
|
||||||
{ 1999, 1 },
|
|
||||||
{ 2006, 1 },
|
|
||||||
{ 2009, 1 },
|
|
||||||
{ 2012, 183 },
|
|
||||||
{ 2015, 182 },
|
|
||||||
{ 2017, 1 }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the position of the last leap second before the desired date
|
|
||||||
LeapSecond date { year, dayOfYear };
|
|
||||||
const auto it = std::lower_bound(LeapSeconds.begin(), LeapSeconds.end(), date);
|
|
||||||
|
|
||||||
// Get the position of the Epoch
|
|
||||||
const auto y2000 = std::lower_bound(
|
|
||||||
LeapSeconds.begin(),
|
|
||||||
LeapSeconds.end(),
|
|
||||||
Epoch
|
|
||||||
);
|
|
||||||
|
|
||||||
// The distance between the two iterators gives us the number of leap years
|
|
||||||
const int nLeapSeconds = static_cast<int>(std::abs(std::distance(y2000, it)));
|
|
||||||
return nLeapSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
double calculateSemiMajorAxis(double meanMotion) {
|
|
||||||
constexpr const double GravitationalConstant = 6.6740831e-11;
|
|
||||||
constexpr const double MassEarth = 5.9721986e24;
|
|
||||||
constexpr const double muEarth = GravitationalConstant * MassEarth;
|
|
||||||
|
|
||||||
// Use Kepler's 3rd law to calculate semimajor axis
|
|
||||||
// a^3 / P^2 = mu / (2pi)^2
|
|
||||||
// <=> a = ((mu * P^2) / (2pi^2))^(1/3)
|
|
||||||
// with a = semimajor axis
|
|
||||||
// P = period in seconds
|
|
||||||
// mu = G*M_earth
|
|
||||||
double period = std::chrono::seconds(std::chrono::hours(24)).count() / meanMotion;
|
|
||||||
|
|
||||||
const double pisq = glm::pi<double>() * glm::pi<double>();
|
|
||||||
double semiMajorAxis = pow((muEarth * period*period) / (4 * pisq), 1.0 / 3.0);
|
|
||||||
|
|
||||||
// We need the semi major axis in km instead of m
|
|
||||||
return semiMajorAxis / 1000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double epochFromSubstring(const std::string& epochString) {
|
|
||||||
// The epochString is in the form:
|
|
||||||
// YYDDD.DDDDDDDD
|
|
||||||
// With YY being the last two years of the launch epoch, the first DDD the day
|
|
||||||
// of the year and the remaning a fractional part of the day
|
|
||||||
|
|
||||||
// The main overview of this function:
|
|
||||||
// 1. Reconstruct the full year from the YY part
|
|
||||||
// 2. Calculate the number of seconds since the beginning of the year
|
|
||||||
// 2.a Get the number of full days since the beginning of the year
|
|
||||||
// 2.b If the year is a leap year, modify the number of days
|
|
||||||
// 3. Convert the number of days to a number of seconds
|
|
||||||
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
|
|
||||||
// 5. Adjust for the fact the epoch starts on 1st Januaray at 12:00:00, not
|
|
||||||
// midnight
|
|
||||||
|
|
||||||
// According to https://celestrak.com/columns/v04n03/
|
|
||||||
// Apparently, US Space Command sees no need to change the two-line element
|
|
||||||
// set format yet since no artificial earth satellites existed prior to 1957.
|
|
||||||
// By their reasoning, two-digit years from 57-99 correspond to 1957-1999 and
|
|
||||||
// those from 00-56 correspond to 2000-2056. We'll see each other again in 2057!
|
|
||||||
|
|
||||||
// 1. Get the full year
|
|
||||||
std::string yearPrefix = [y = epochString.substr(0, 2)](){
|
|
||||||
int year = std::atoi(y.c_str());
|
|
||||||
return year >= 57 ? "19" : "20";
|
|
||||||
}();
|
|
||||||
const int year = std::atoi((yearPrefix + epochString.substr(0, 2)).c_str());
|
|
||||||
const int daysSince2000 = countDays(year);
|
|
||||||
|
|
||||||
// 2.
|
|
||||||
// 2.a
|
|
||||||
double daysInYear = std::atof(epochString.substr(2).c_str());
|
|
||||||
|
|
||||||
// 2.b
|
|
||||||
const bool isInLeapYear = std::find(
|
|
||||||
LeapYears.begin(),
|
|
||||||
LeapYears.end(),
|
|
||||||
year
|
|
||||||
) != LeapYears.end();
|
|
||||||
if (isInLeapYear && daysInYear >= 60) {
|
|
||||||
// We are in a leap year, so we have an effective day more if we are
|
|
||||||
// beyond the end of february (= 31+29 days)
|
|
||||||
--daysInYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3
|
|
||||||
using namespace std::chrono;
|
|
||||||
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
|
|
||||||
//Need to subtract 1 from daysInYear since it is not a zero-based count
|
|
||||||
const double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay;
|
|
||||||
|
|
||||||
// 4
|
|
||||||
// We need to remove additional leap seconds past 2000 and add them prior to
|
|
||||||
// 2000 to sync up the time zones
|
|
||||||
const double nLeapSecondsOffset = -countLeapSeconds(
|
|
||||||
year,
|
|
||||||
static_cast<int>(std::floor(daysInYear))
|
|
||||||
);
|
|
||||||
|
|
||||||
// 5
|
|
||||||
const double nSecondsEpochOffset = static_cast<double>(
|
|
||||||
seconds(hours(12)).count()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Combine all of the values
|
|
||||||
const double epoch = nSecondsSince2000 + nLeapSecondsOffset - nSecondsEpochOffset;
|
|
||||||
return epoch;
|
|
||||||
}
|
|
||||||
|
|
||||||
documentation::Documentation RenderableSatellites::Documentation() {
|
documentation::Documentation RenderableSatellites::Documentation() {
|
||||||
using namespace documentation;
|
using namespace documentation;
|
||||||
return {
|
return {
|
||||||
@@ -302,57 +92,17 @@ documentation::Documentation RenderableSatellites::Documentation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RenderableSatellites::RenderableSatellites(const ghoul::Dictionary& dictionary)
|
RenderableSatellites::RenderableSatellites(const ghoul::Dictionary& dictionary)
|
||||||
: Renderable(dictionary)
|
: RenderableOrbitalKepler(dictionary)
|
||||||
, _path(PathInfo)
|
|
||||||
, _nSegments(SegmentsInfo, 120, 4, 1024)
|
|
||||||
{
|
{
|
||||||
documentation::testSpecificationAndThrow(
|
documentation::testSpecificationAndThrow(
|
||||||
Documentation(),
|
Documentation(),
|
||||||
dictionary,
|
dictionary,
|
||||||
"RenderableSatellites"
|
"RenderableSatellites"
|
||||||
);
|
);
|
||||||
|
|
||||||
_path = dictionary.value<std::string>(PathInfo.identifier);
|
|
||||||
_nSegments = static_cast<int>(dictionary.value<double>(SegmentsInfo.identifier));
|
|
||||||
|
|
||||||
if (dictionary.hasKeyAndValue<glm::vec3>(LineColorInfo.identifier)) {
|
|
||||||
_appearance.lineColor = dictionary.value<glm::vec3>(LineColorInfo.identifier);
|
|
||||||
}
|
|
||||||
if (dictionary.hasKeyAndValue<double>(TrailFadeInfo.identifier)) {
|
|
||||||
_appearance.lineFade = static_cast<float>(
|
|
||||||
dictionary.value<double>(TrailFadeInfo.identifier)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_appearance.lineFade = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dictionary.hasKeyAndValue<double>(LineWidthInfo.identifier)) {
|
|
||||||
_appearance.lineWidth = static_cast<float>(
|
|
||||||
dictionary.value<double>(LineWidthInfo.identifier)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_appearance.lineWidth = 2.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reinitializeTrailBuffers = [this]() {
|
|
||||||
initializeGL();
|
|
||||||
};
|
|
||||||
|
|
||||||
_path.onChange(reinitializeTrailBuffers);
|
|
||||||
_nSegments.onChange(reinitializeTrailBuffers);
|
|
||||||
|
|
||||||
addPropertySubOwner(_appearance);
|
|
||||||
addProperty(_path);
|
|
||||||
addProperty(_nSegments);
|
|
||||||
addProperty(_opacity);
|
|
||||||
|
|
||||||
setRenderBin(Renderable::RenderBin::Overlay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RenderableSatellites::readTLEFile(const std::string& filename) {
|
void RenderableSatellites::readDataFile(const std::string& filename) {
|
||||||
if (!FileSys.fileExists(filename)) {
|
if (!FileSys.fileExists(filename)) {
|
||||||
throw ghoul::RuntimeError(fmt::format(
|
throw ghoul::RuntimeError(fmt::format(
|
||||||
"Satellite TLE file {} does not exist.", filename
|
"Satellite TLE file {} does not exist.", filename
|
||||||
@@ -462,165 +212,10 @@ void RenderableSatellites::readTLEFile(const std::string& filename) {
|
|||||||
double period = seconds(hours(24)).count() / keplerElements.meanMotion;
|
double period = seconds(hours(24)).count() / keplerElements.meanMotion;
|
||||||
keplerElements.period = period;
|
keplerElements.period = period;
|
||||||
|
|
||||||
_TLEData.push_back(keplerElements);
|
_data.push_back(keplerElements);
|
||||||
|
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableSatellites::initializeGL() {
|
|
||||||
glGenVertexArrays(1, &_vertexArray);
|
|
||||||
glGenBuffers(1, &_vertexBuffer);
|
|
||||||
|
|
||||||
_programObject = SpaceModule::ProgramObjectManager.request(
|
|
||||||
ProgramName,
|
|
||||||
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
|
|
||||||
return global::renderEngine.buildRenderProgram(
|
|
||||||
ProgramName,
|
|
||||||
absPath("${MODULE_SPACE}/shaders/debrisViz_vs.glsl"),
|
|
||||||
absPath("${MODULE_SPACE}/shaders/debrisViz_fs.glsl")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
_uniformCache.modelView = _programObject->uniformLocation("modelViewTransform");
|
|
||||||
_uniformCache.projection = _programObject->uniformLocation("projectionTransform");
|
|
||||||
_uniformCache.lineFade = _programObject->uniformLocation("lineFade");
|
|
||||||
_uniformCache.inGameTime = _programObject->uniformLocation("inGameTime");
|
|
||||||
_uniformCache.color = _programObject->uniformLocation("color");
|
|
||||||
_uniformCache.opacity = _programObject->uniformLocation("opacity");
|
|
||||||
|
|
||||||
updateBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableSatellites::deinitializeGL() {
|
|
||||||
glDeleteBuffers(1, &_vertexBuffer);
|
|
||||||
glDeleteVertexArrays(1, &_vertexArray);
|
|
||||||
|
|
||||||
SpaceModule::ProgramObjectManager.release(
|
|
||||||
ProgramName,
|
|
||||||
[](ghoul::opengl::ProgramObject* p) {
|
|
||||||
global::renderEngine.removeRenderProgram(p);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
_programObject = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RenderableSatellites::isReady() const {
|
|
||||||
return _programObject != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableSatellites::render(const RenderData& data, RendererTasks&) {
|
|
||||||
if (_TLEData.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
_programObject->activate();
|
|
||||||
_programObject->setUniform(_uniformCache.opacity, _opacity);
|
|
||||||
_programObject->setUniform(_uniformCache.inGameTime, data.time.j2000Seconds());
|
|
||||||
|
|
||||||
|
|
||||||
glm::dmat4 modelTransform =
|
|
||||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
|
||||||
glm::dmat4(data.modelTransform.rotation) *
|
|
||||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
|
|
||||||
|
|
||||||
_programObject->setUniform(
|
|
||||||
_uniformCache.modelView,
|
|
||||||
data.camera.combinedViewMatrix() * modelTransform
|
|
||||||
);
|
|
||||||
|
|
||||||
// Because we want the property to work similar to the planet trails
|
|
||||||
float fade = static_cast<float>(pow(_appearance.lineFade.maxValue() - _appearance.lineFade, 2.0));
|
|
||||||
|
|
||||||
_programObject->setUniform(_uniformCache.projection, data.camera.projectionMatrix());
|
|
||||||
_programObject->setUniform(_uniformCache.color, _appearance.lineColor);
|
|
||||||
_programObject->setUniform(_uniformCache.lineFade, fade);
|
|
||||||
|
|
||||||
glLineWidth(_appearance.lineWidth);
|
|
||||||
|
|
||||||
const size_t nrOrbits = _TLEData.size();
|
|
||||||
gl::GLint vertices = 0;
|
|
||||||
|
|
||||||
//glDepthMask(false);
|
|
||||||
//glBlendFunc(GL_SRC_ALPHA, GL_ONE)
|
|
||||||
|
|
||||||
glBindVertexArray(_vertexArray);
|
|
||||||
for (size_t i = 0; i < nrOrbits; ++i) {
|
|
||||||
glDrawArrays(GL_LINE_STRIP, vertices, _nSegments + 1);
|
|
||||||
vertices = vertices + _nSegments + 1;
|
|
||||||
}
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
_programObject->deactivate();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableSatellites::updateBuffers() {
|
|
||||||
readTLEFile(_path);
|
|
||||||
|
|
||||||
const size_t nVerticesPerOrbit = _nSegments + 1;
|
|
||||||
_vertexBufferData.resize(_TLEData.size() * nVerticesPerOrbit);
|
|
||||||
size_t orbitindex = 0;
|
|
||||||
|
|
||||||
for (const auto& orbit : _TLEData) {
|
|
||||||
_keplerTranslator.setKeplerElements(
|
|
||||||
orbit.eccentricity,
|
|
||||||
orbit.semiMajorAxis,
|
|
||||||
orbit.inclination,
|
|
||||||
orbit.ascendingNode,
|
|
||||||
orbit.argumentOfPeriapsis,
|
|
||||||
orbit.meanAnomaly,
|
|
||||||
orbit.period,
|
|
||||||
orbit.epoch
|
|
||||||
);
|
|
||||||
|
|
||||||
for (size_t i=0 ; i < nVerticesPerOrbit; ++i) {
|
|
||||||
size_t index = orbitindex * nVerticesPerOrbit + i;
|
|
||||||
|
|
||||||
double timeOffset = orbit.period *
|
|
||||||
static_cast<double>(i)/ static_cast<double>(_nSegments);
|
|
||||||
|
|
||||||
glm::dvec3 position = _keplerTranslator.position({
|
|
||||||
{},
|
|
||||||
Time(timeOffset + orbit.epoch),
|
|
||||||
Time(0.0),
|
|
||||||
false
|
|
||||||
});
|
|
||||||
|
|
||||||
double positionX = position.x;
|
|
||||||
double positionY = position.y;
|
|
||||||
double positionZ = position.z;
|
|
||||||
|
|
||||||
_vertexBufferData[index].x = static_cast<float>(positionX);
|
|
||||||
_vertexBufferData[index].y = static_cast<float>(positionY);
|
|
||||||
_vertexBufferData[index].z = static_cast<float>(positionZ);
|
|
||||||
_vertexBufferData[index].time = static_cast<float>(timeOffset);
|
|
||||||
_vertexBufferData[index].epoch = orbit.epoch;
|
|
||||||
_vertexBufferData[index].period = orbit.period;
|
|
||||||
}
|
|
||||||
|
|
||||||
++orbitindex;
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray(_vertexArray);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
|
||||||
glBufferData(
|
|
||||||
GL_ARRAY_BUFFER,
|
|
||||||
_vertexBufferData.size() * sizeof(TrailVBOLayout),
|
|
||||||
_vertexBufferData.data(),
|
|
||||||
GL_STATIC_DRAW
|
|
||||||
);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(TrailVBOLayout), nullptr);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
glVertexAttribPointer(1, 2, GL_DOUBLE, GL_FALSE, sizeof(TrailVBOLayout), (GLvoid*)(4 * sizeof(GL_FLOAT)));
|
|
||||||
|
|
||||||
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__
|
#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__
|
||||||
#define __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__
|
#define __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__
|
||||||
|
|
||||||
|
#include <modules/space/rendering/renderableorbitalkepler.h>
|
||||||
#include <openspace/rendering/renderable.h>
|
#include <openspace/rendering/renderable.h>
|
||||||
|
|
||||||
#include <modules/base/rendering/renderabletrail.h>
|
#include <modules/base/rendering/renderabletrail.h>
|
||||||
@@ -37,98 +38,13 @@
|
|||||||
|
|
||||||
namespace openspace {
|
namespace openspace {
|
||||||
|
|
||||||
const std::vector<int> LeapYears = {
|
class RenderableSatellites : private RenderableOrbitalKepler {
|
||||||
1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996,
|
|
||||||
2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040,
|
|
||||||
2044, 2048, 2052, 2056
|
|
||||||
};
|
|
||||||
int countDays(int year);
|
|
||||||
int countLeapSeconds(int year, int dayOfYear);
|
|
||||||
double calculateSemiMajorAxis(double meanMotion);
|
|
||||||
double epochFromSubstring(const std::string& epochString);
|
|
||||||
|
|
||||||
class RenderableSatellites : public Renderable {
|
|
||||||
public:
|
public:
|
||||||
RenderableSatellites(const ghoul::Dictionary& dictionary);
|
RenderableSatellites(const ghoul::Dictionary& dictionary);
|
||||||
|
|
||||||
void initializeGL() override;
|
|
||||||
void deinitializeGL() override;
|
|
||||||
|
|
||||||
bool isReady() const override;
|
|
||||||
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
|
||||||
|
|
||||||
static documentation::Documentation Documentation();
|
static documentation::Documentation Documentation();
|
||||||
/**
|
|
||||||
* Reads the provided TLE file and calls the KeplerTranslation::setKeplerElments
|
|
||||||
* method with the correct values. If \p filename is a valid TLE file but contains
|
|
||||||
* disallowed values (see KeplerTranslation::setKeplerElements), a
|
|
||||||
* KeplerTranslation::RangeError is thrown.
|
|
||||||
*
|
|
||||||
* \param filename The path to the file that contains the TLE file.
|
|
||||||
*
|
|
||||||
* \throw ghoul::RuntimeError if the TLE file does not exist or there is a
|
|
||||||
* problem with its format.
|
|
||||||
* \pre The \p filename must exist
|
|
||||||
*/
|
|
||||||
void readTLEFile(const std::string& filename);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Vertex {
|
|
||||||
glm::vec3 position = glm::vec3(0.f);
|
|
||||||
glm::vec3 color = glm::vec3(0.f);
|
|
||||||
glm::vec2 texcoord = glm::vec2(0.f);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct KeplerParameters {
|
|
||||||
double inclination = 0.0;
|
|
||||||
double semiMajorAxis = 0.0;
|
|
||||||
double ascendingNode = 0.0;
|
|
||||||
double eccentricity = 0.0;
|
|
||||||
double argumentOfPeriapsis = 0.0;
|
|
||||||
double meanAnomaly = 0.0;
|
|
||||||
double meanMotion = 0.0;
|
|
||||||
double epoch = 0.0;
|
|
||||||
double period = 0.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The layout of the VBOs
|
|
||||||
struct TrailVBOLayout {
|
|
||||||
float x = 0.f;
|
|
||||||
float y = 0.f;
|
|
||||||
float z = 0.f;
|
|
||||||
float time = 0.f;
|
|
||||||
double epoch = 0.0;
|
|
||||||
double period = 0.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
KeplerTranslation _keplerTranslator;
|
|
||||||
std::vector<KeplerParameters> _TLEData;
|
|
||||||
|
|
||||||
/// The backend storage for the vertex buffer object containing all points for this
|
|
||||||
/// trail.
|
|
||||||
std::vector<TrailVBOLayout> _vertexBufferData;
|
|
||||||
|
|
||||||
GLuint _vertexArray;
|
|
||||||
GLuint _vertexBuffer;
|
|
||||||
GLuint _indexBuffer;
|
|
||||||
|
|
||||||
//GLuint _vaoTest; // vertexArrayObject
|
|
||||||
//GLuint _vboTest; // vertextBufferObject
|
|
||||||
//GLuint _eboTest; // elementBufferObject/ indexBufferObject
|
|
||||||
|
|
||||||
void updateBuffers();
|
|
||||||
|
|
||||||
ghoul::opengl::ProgramObject* _programObject;
|
|
||||||
|
|
||||||
properties::StringProperty _path;
|
|
||||||
properties::UIntProperty _nSegments;
|
|
||||||
|
|
||||||
RenderableTrail::Appearance _appearance;
|
|
||||||
|
|
||||||
glm::vec3 _position = glm::vec3(0.f);
|
|
||||||
|
|
||||||
UniformCache(modelView, projection, lineFade, inGameTime, color, opacity,
|
|
||||||
numberOfSegments) _uniformCache;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace openspace
|
} // namespace openspace
|
||||||
|
|||||||
@@ -54,138 +54,10 @@ namespace {
|
|||||||
"Path",
|
"Path",
|
||||||
"The file path to the SBDB file to read"
|
"The file path to the SBDB file to read"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const openspace::properties::Property::PropertyInfo SegmentsInfo = {
|
|
||||||
"Segments",
|
|
||||||
"Segments",
|
|
||||||
"The number of segments to use for each orbit ellipse"
|
|
||||||
};
|
|
||||||
static const openspace::properties::Property::PropertyInfo UpperLimitInfo = {
|
|
||||||
"UpperLimit",
|
|
||||||
"Upper Limit",
|
|
||||||
"Upper limit on the number of objects for this renderable, regardless of "
|
|
||||||
"how many objects are contained in the data file"
|
|
||||||
};
|
|
||||||
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
|
||||||
"LineWidth",
|
|
||||||
"Line Width",
|
|
||||||
"This value specifies the line width of the trail if the selected rendering "
|
|
||||||
"method includes lines. If the rendering mode is set to Points, this value is "
|
|
||||||
"ignored."
|
|
||||||
};
|
|
||||||
constexpr openspace::properties::Property::PropertyInfo FadeInfo = {
|
|
||||||
"Fade",
|
|
||||||
"Line fade",
|
|
||||||
"The fading factor that is applied to the trail if the 'EnableFade' value is "
|
|
||||||
"'true'. If it is 'false', this setting has no effect. The higher the number, "
|
|
||||||
"the less fading is applied."
|
|
||||||
};
|
|
||||||
constexpr openspace::properties::Property::PropertyInfo LineColorInfo = {
|
|
||||||
"Color",
|
|
||||||
"Color",
|
|
||||||
"This value determines the RGB main color for the lines and points of the trail."
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr const char* KeyFile = "Path";
|
|
||||||
constexpr const char* KeyLineNum = "LineNumber";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace openspace {
|
namespace openspace {
|
||||||
|
|
||||||
const std::vector<int> DaysOfMonths = {
|
|
||||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Months {
|
|
||||||
January = 0,
|
|
||||||
February,
|
|
||||||
March,
|
|
||||||
April,
|
|
||||||
May,
|
|
||||||
June,
|
|
||||||
July,
|
|
||||||
August,
|
|
||||||
September,
|
|
||||||
October,
|
|
||||||
November,
|
|
||||||
December
|
|
||||||
};
|
|
||||||
|
|
||||||
int daysIntoGivenYear(int month, int dayOfMonth) {
|
|
||||||
//month and dayCount are zero-based. Does NOT account for leap year.
|
|
||||||
month -= 1;
|
|
||||||
int dayCount = dayOfMonth - 1;
|
|
||||||
|
|
||||||
for (int m = Months::January; m < month; ++m) {
|
|
||||||
dayCount += DaysOfMonths[m];
|
|
||||||
}
|
|
||||||
return dayCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
double epochFromYMDdSubstring(const std::string& epochString) {
|
|
||||||
// The epochString is in the form:
|
|
||||||
// YYYYMMDD.ddddddd
|
|
||||||
// With YYYY as the year, MM the month (1 - 12), DD the day of month (1-31),
|
|
||||||
// and dddd the fraction of that day.
|
|
||||||
|
|
||||||
// The main overview of this function:
|
|
||||||
// 1. Read the year value
|
|
||||||
// 2. Calculate the number of seconds since the beginning of the year
|
|
||||||
// 2.a Get the number of full days since the beginning of the year
|
|
||||||
// 2.b If the year is a leap year, modify the number of days
|
|
||||||
// 3. Convert the number of days to a number of seconds
|
|
||||||
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
|
|
||||||
// 5. Adjust for the fact the epoch starts on 1st January at 12:00:00, not
|
|
||||||
// midnight
|
|
||||||
|
|
||||||
// 1
|
|
||||||
int year = std::atoi(epochString.substr(0, 4).c_str());
|
|
||||||
const int daysSince2000 = countDays(year);
|
|
||||||
|
|
||||||
// 2.
|
|
||||||
// 2.a
|
|
||||||
int monthNum = std::atoi(epochString.substr(4, 2).c_str());
|
|
||||||
int dayOfMonthNum = std::atoi(epochString.substr(6, 2).c_str());
|
|
||||||
int wholeDaysInto = daysIntoGivenYear(monthNum, dayOfMonthNum);
|
|
||||||
double fractionOfDay = std::atof(epochString.substr(9, 7).c_str());
|
|
||||||
double daysInYear = static_cast<double>(wholeDaysInto) + fractionOfDay;
|
|
||||||
|
|
||||||
// 2.b
|
|
||||||
const bool isInLeapYear = std::find(
|
|
||||||
LeapYears.begin(),
|
|
||||||
LeapYears.end(),
|
|
||||||
year
|
|
||||||
) != LeapYears.end();
|
|
||||||
if (isInLeapYear && daysInYear >= 60) {
|
|
||||||
// We are in a leap year, so we have an effective day more if we are
|
|
||||||
// beyond the end of february (= 31+29 days)
|
|
||||||
--daysInYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3
|
|
||||||
using namespace std::chrono;
|
|
||||||
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
|
|
||||||
//Need to subtract 1 from daysInYear since it is not a zero-based count
|
|
||||||
const double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay;
|
|
||||||
|
|
||||||
// 4
|
|
||||||
// We need to remove additional leap seconds past 2000 and add them prior to
|
|
||||||
// 2000 to sync up the time zones
|
|
||||||
const double nLeapSecondsOffset = -countLeapSeconds(
|
|
||||||
year,
|
|
||||||
static_cast<int>(std::floor(daysInYear))
|
|
||||||
);
|
|
||||||
|
|
||||||
// 5
|
|
||||||
const double nSecondsEpochOffset = static_cast<double>(
|
|
||||||
seconds(hours(12)).count()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Combine all of the values
|
|
||||||
const double epoch = nSecondsSince2000 + nLeapSecondsOffset - nSecondsEpochOffset;
|
|
||||||
return epoch;
|
|
||||||
}
|
|
||||||
|
|
||||||
documentation::Documentation RenderableSmallBody::Documentation() {
|
documentation::Documentation RenderableSmallBody::Documentation() {
|
||||||
using namespace documentation;
|
using namespace documentation;
|
||||||
return {
|
return {
|
||||||
@@ -233,71 +105,17 @@ documentation::Documentation RenderableSmallBody::Documentation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RenderableSmallBody::RenderableSmallBody(const ghoul::Dictionary& dictionary)
|
RenderableSmallBody::RenderableSmallBody(const ghoul::Dictionary& dictionary)
|
||||||
: Renderable(dictionary)
|
: RenderableOrbitalKepler(dictionary)
|
||||||
, _path(PathInfo)
|
|
||||||
, _nSegments(SegmentsInfo, 100, 10, 4000)
|
|
||||||
, _upperLimit(UpperLimitInfo, 1000, 1, 1000000)
|
|
||||||
{
|
{
|
||||||
documentation::testSpecificationAndThrow(
|
documentation::testSpecificationAndThrow(
|
||||||
Documentation(),
|
Documentation(),
|
||||||
dictionary,
|
dictionary,
|
||||||
"RenderableSmallBody"
|
"RenderableSmallBody"
|
||||||
);
|
);
|
||||||
|
|
||||||
_path = dictionary.value<std::string>(PathInfo.identifier);
|
|
||||||
_nSegments = static_cast<unsigned int>(
|
|
||||||
dictionary.value<double>(SegmentsInfo.identifier)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (dictionary.hasKeyAndValue<glm::vec3>(LineColorInfo.identifier)) {
|
|
||||||
_appearance.lineColor = dictionary.value<glm::vec3>(LineColorInfo.identifier);
|
|
||||||
}
|
|
||||||
if (dictionary.hasKeyAndValue<double>(FadeInfo.identifier)) {
|
|
||||||
_appearance.lineFade = static_cast<float>(
|
|
||||||
dictionary.value<double>(FadeInfo.identifier)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_appearance.lineFade = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dictionary.hasKeyAndValue<double>(UpperLimitInfo.identifier)) {
|
|
||||||
_upperLimit = static_cast<unsigned int>(
|
|
||||||
dictionary.value<double>(UpperLimitInfo.identifier)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_upperLimit = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dictionary.hasKeyAndValue<double>(LineWidthInfo.identifier)) {
|
|
||||||
_appearance.lineWidth = static_cast<float>(
|
|
||||||
dictionary.value<double>(LineWidthInfo.identifier)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_appearance.lineWidth = 2.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reinitializeTrailBuffers = [this]() {
|
|
||||||
initializeGL();
|
|
||||||
};
|
|
||||||
|
|
||||||
_path.onChange(reinitializeTrailBuffers);
|
|
||||||
_nSegments.onChange(reinitializeTrailBuffers);
|
|
||||||
_upperLimit.onChange(reinitializeTrailBuffers);
|
|
||||||
|
|
||||||
addPropertySubOwner(_appearance);
|
|
||||||
addProperty(_path);
|
|
||||||
addProperty(_nSegments);
|
|
||||||
addProperty(_opacity);
|
|
||||||
addProperty(_upperLimit);
|
|
||||||
|
|
||||||
setRenderBin(Renderable::RenderBin::Overlay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RenderableSmallBody::readJplSbDb(const std::string& filename) {
|
void RenderableSmallBody::readDataFile(const std::string& filename) {
|
||||||
if (!FileSys.fileExists(filename)) {
|
if (!FileSys.fileExists(filename)) {
|
||||||
throw ghoul::RuntimeError(fmt::format(
|
throw ghoul::RuntimeError(fmt::format(
|
||||||
"JPL SBDB file {} does not exist.", filename
|
"JPL SBDB file {} does not exist.", filename
|
||||||
@@ -311,7 +129,7 @@ void RenderableSmallBody::readJplSbDb(const std::string& filename) {
|
|||||||
std::streamoff numberOfLines = std::count(std::istreambuf_iterator<char>(file),
|
std::streamoff numberOfLines = std::count(std::istreambuf_iterator<char>(file),
|
||||||
std::istreambuf_iterator<char>(), '\n' );
|
std::istreambuf_iterator<char>(), '\n' );
|
||||||
file.seekg(std::ios_base::beg); // reset iterator to beginning of file
|
file.seekg(std::ios_base::beg); // reset iterator to beginning of file
|
||||||
_sbData.clear();
|
_data.clear();
|
||||||
_sbNames.clear();
|
_sbNames.clear();
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
@@ -461,7 +279,7 @@ void RenderableSmallBody::readOrbitalParamsFromThisLine(int& fieldCount,
|
|||||||
keplerElements.period *= convertDaysToSecs;
|
keplerElements.period *= convertDaysToSecs;
|
||||||
fieldCount++;
|
fieldCount++;
|
||||||
|
|
||||||
_sbData.push_back(keplerElements);
|
_data.push_back(keplerElements);
|
||||||
_sbNames.push_back(name);
|
_sbNames.push_back(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,158 +291,5 @@ static double importAngleValue(const std::string& angle) {
|
|||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableSmallBody::initializeGL() {
|
|
||||||
glGenVertexArrays(1, &_vertexArray);
|
|
||||||
glGenBuffers(1, &_vertexBuffer);
|
|
||||||
|
|
||||||
_programObject = SpaceModule::ProgramObjectManager.request(
|
|
||||||
ProgramName,
|
|
||||||
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
|
|
||||||
return global::renderEngine.buildRenderProgram(
|
|
||||||
ProgramName,
|
|
||||||
absPath("${MODULE_SPACE}/shaders/debrisViz_vs.glsl"),
|
|
||||||
absPath("${MODULE_SPACE}/shaders/debrisViz_fs.glsl")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
_uniformCache.modelView = _programObject->uniformLocation("modelViewTransform");
|
|
||||||
_uniformCache.projection = _programObject->uniformLocation("projectionTransform");
|
|
||||||
_uniformCache.lineFade = _programObject->uniformLocation("lineFade");
|
|
||||||
_uniformCache.inGameTime = _programObject->uniformLocation("inGameTime");
|
|
||||||
_uniformCache.color = _programObject->uniformLocation("color");
|
|
||||||
_uniformCache.opacity = _programObject->uniformLocation("opacity");
|
|
||||||
|
|
||||||
updateBuffers();
|
|
||||||
setRenderBin(Renderable::RenderBin::Overlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableSmallBody::deinitializeGL() {
|
|
||||||
glDeleteBuffers(1, &_vertexBuffer);
|
|
||||||
glDeleteVertexArrays(1, &_vertexArray);
|
|
||||||
|
|
||||||
SpaceModule::ProgramObjectManager.release(
|
|
||||||
ProgramName,
|
|
||||||
[](ghoul::opengl::ProgramObject* p) {
|
|
||||||
global::renderEngine.removeRenderProgram(p);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
_programObject = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RenderableSmallBody::isReady() const {
|
|
||||||
return _programObject != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableSmallBody::render(const RenderData& data, RendererTasks&) {
|
|
||||||
if (_sbData.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
_programObject->activate();
|
|
||||||
_programObject->setUniform(_uniformCache.opacity, _opacity);
|
|
||||||
_programObject->setUniform(_uniformCache.inGameTime, data.time.j2000Seconds());
|
|
||||||
|
|
||||||
|
|
||||||
glm::dmat4 modelTransform =
|
|
||||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
|
||||||
glm::dmat4(data.modelTransform.rotation) *
|
|
||||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
|
|
||||||
|
|
||||||
_programObject->setUniform(
|
|
||||||
_uniformCache.modelView,
|
|
||||||
data.camera.combinedViewMatrix() * modelTransform
|
|
||||||
);
|
|
||||||
// Because we want the property to work similar to the planet trails
|
|
||||||
float fade = static_cast<float>(pow(_appearance.lineFade.maxValue() - _appearance.lineFade, 2.0));
|
|
||||||
|
|
||||||
_programObject->setUniform(_uniformCache.projection, data.camera.projectionMatrix());
|
|
||||||
_programObject->setUniform(_uniformCache.color, _appearance.lineColor);
|
|
||||||
_programObject->setUniform(_uniformCache.lineFade, fade);
|
|
||||||
|
|
||||||
glLineWidth(_appearance.lineWidth);
|
|
||||||
|
|
||||||
const size_t nrOrbits = _sbData.size();
|
|
||||||
gl::GLint vertices = 0;
|
|
||||||
|
|
||||||
//glDepthMask(false);
|
|
||||||
//glBlendFunc(GL_SRC_ALPHA, GL_ONE)
|
|
||||||
|
|
||||||
glBindVertexArray(_vertexArray);
|
|
||||||
for (size_t i = 0; i < nrOrbits; ++i) {
|
|
||||||
glDrawArrays(GL_LINE_STRIP, vertices, _nSegments + 1);
|
|
||||||
vertices = vertices + _nSegments + 1;
|
|
||||||
}
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
_programObject->deactivate();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableSmallBody::updateBuffers() {
|
|
||||||
readJplSbDb(_path);
|
|
||||||
|
|
||||||
const size_t nVerticesPerOrbit = _nSegments + 1;
|
|
||||||
_vertexBufferData.resize(_sbData.size() * nVerticesPerOrbit);
|
|
||||||
size_t orbitindex = 0;
|
|
||||||
|
|
||||||
for (const auto& orbit : _sbData) {
|
|
||||||
_keplerTranslator.setKeplerElements(
|
|
||||||
orbit.eccentricity,
|
|
||||||
orbit.semiMajorAxis,
|
|
||||||
orbit.inclination,
|
|
||||||
orbit.ascendingNode,
|
|
||||||
orbit.argumentOfPeriapsis,
|
|
||||||
orbit.meanAnomaly,
|
|
||||||
orbit.period,
|
|
||||||
orbit.epoch
|
|
||||||
);
|
|
||||||
|
|
||||||
for (size_t i=0 ; i < nVerticesPerOrbit; ++i) {
|
|
||||||
size_t index = orbitindex * nVerticesPerOrbit + i;
|
|
||||||
|
|
||||||
double timeOffset = orbit.period *
|
|
||||||
static_cast<double>(i)/ static_cast<double>(_nSegments);
|
|
||||||
|
|
||||||
glm::dvec3 position = _keplerTranslator.position({
|
|
||||||
{},
|
|
||||||
Time(timeOffset + orbit.epoch),
|
|
||||||
Time(0.0),
|
|
||||||
false
|
|
||||||
});
|
|
||||||
|
|
||||||
double positionX = position.x;
|
|
||||||
double positionY = position.y;
|
|
||||||
double positionZ = position.z;
|
|
||||||
|
|
||||||
_vertexBufferData[index].x = static_cast<float>(positionX);
|
|
||||||
_vertexBufferData[index].y = static_cast<float>(positionY);
|
|
||||||
_vertexBufferData[index].z = static_cast<float>(positionZ);
|
|
||||||
_vertexBufferData[index].time = static_cast<float>(timeOffset);
|
|
||||||
_vertexBufferData[index].epoch = orbit.epoch;
|
|
||||||
_vertexBufferData[index].period = orbit.period;
|
|
||||||
}
|
|
||||||
|
|
||||||
++orbitindex;
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray(_vertexArray);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
|
||||||
glBufferData(
|
|
||||||
GL_ARRAY_BUFFER,
|
|
||||||
_vertexBufferData.size() * sizeof(TrailVBOLayout),
|
|
||||||
_vertexBufferData.data(),
|
|
||||||
GL_STATIC_DRAW
|
|
||||||
);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(TrailVBOLayout), nullptr);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
glVertexAttribPointer(1, 2, GL_DOUBLE, GL_FALSE, sizeof(TrailVBOLayout), (GLvoid*)(4*sizeof(GL_FLOAT)) );
|
|
||||||
|
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__
|
#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__
|
||||||
#define __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__
|
#define __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__
|
||||||
|
|
||||||
|
#include <modules/space/rendering/renderableorbitalkepler.h>
|
||||||
#include <openspace/rendering/renderable.h>
|
#include <openspace/rendering/renderable.h>
|
||||||
|
|
||||||
#include <modules/base/rendering/renderabletrail.h>
|
#include <modules/base/rendering/renderabletrail.h>
|
||||||
@@ -37,101 +38,27 @@
|
|||||||
|
|
||||||
namespace openspace {
|
namespace openspace {
|
||||||
|
|
||||||
class RenderableSmallBody : public Renderable {
|
static double importAngleValue(const std::string& angle);
|
||||||
|
|
||||||
|
class RenderableSmallBody : public RenderableOrbitalKepler {
|
||||||
public:
|
public:
|
||||||
RenderableSmallBody(const ghoul::Dictionary& dictionary);
|
RenderableSmallBody(const ghoul::Dictionary& dictionary);
|
||||||
|
|
||||||
void initializeGL() override;
|
|
||||||
void deinitializeGL() override;
|
|
||||||
|
|
||||||
bool isReady() const override;
|
|
||||||
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
|
||||||
|
|
||||||
static documentation::Documentation Documentation();
|
static documentation::Documentation Documentation();
|
||||||
/**
|
|
||||||
* Reads the provided file downloaded from the JPL Small Body Database and calls
|
|
||||||
* the KeplerTranslation::setKeplerElments method with the correct values.
|
|
||||||
* If \p filename is a valid JPL SBDB file but contains
|
|
||||||
* disallowed values (see KeplerTranslation::setKeplerElements), a
|
|
||||||
* KeplerTranslation::RangeError is thrown.
|
|
||||||
*
|
|
||||||
* \param filename The path to the file that contains the file.
|
|
||||||
*
|
|
||||||
* \throw ghoul::RuntimeError if the file does not exist or there is a
|
|
||||||
* problem with its format.
|
|
||||||
* \pre The \p filename must exist
|
|
||||||
*/
|
|
||||||
void readJplSbDb(const std::string& filename);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Vertex {
|
|
||||||
glm::vec3 position;
|
|
||||||
glm::vec3 color;
|
|
||||||
glm::vec2 texcoord;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct KeplerParameters {
|
|
||||||
double inclination = 0.0;
|
|
||||||
double semiMajorAxis = 0.0;
|
|
||||||
double ascendingNode = 0.0;
|
|
||||||
double eccentricity = 0.0;
|
|
||||||
double argumentOfPeriapsis = 0.0;
|
|
||||||
double meanAnomaly = 0.0;
|
|
||||||
double meanMotion = 0.0;
|
|
||||||
double epoch = 0.0;
|
|
||||||
double period = 0.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The layout of the VBOs
|
|
||||||
struct TrailVBOLayout {
|
|
||||||
float x, y, z, time;
|
|
||||||
double epoch, period;
|
|
||||||
};
|
|
||||||
|
|
||||||
void readOrbitalParamsFromThisLine(int& fieldCount, std::streamoff& csvLine,
|
void readOrbitalParamsFromThisLine(int& fieldCount, std::streamoff& csvLine,
|
||||||
std::ifstream& file);
|
std::ifstream& file);
|
||||||
|
|
||||||
KeplerTranslation _keplerTranslator;
|
|
||||||
std::vector<KeplerParameters> _sbData;
|
|
||||||
std::vector<std::string> _sbNames;
|
std::vector<std::string> _sbNames;
|
||||||
|
|
||||||
/// The backend storage for the vertex buffer object containing all points for this
|
|
||||||
/// trail.
|
|
||||||
std::vector<TrailVBOLayout> _vertexBufferData;
|
|
||||||
|
|
||||||
/// The index array that is potentially used in the draw call. If this is empty, no
|
/// The index array that is potentially used in the draw call. If this is empty, no
|
||||||
/// element draw call is used.
|
/// element draw call is used.
|
||||||
std::vector<unsigned int> _indexBufferData;
|
std::vector<unsigned int> _indexBufferData;
|
||||||
|
|
||||||
GLuint _vertexArray;
|
|
||||||
GLuint _vertexBuffer;
|
|
||||||
GLuint _indexBuffer;
|
|
||||||
|
|
||||||
//GLuint _vaoTest; // vertexArrayObject
|
|
||||||
//GLuint _vboTest; // vertextBufferObject
|
|
||||||
//GLuint _eboTest; // elementBufferObject/ indexBufferObject
|
|
||||||
|
|
||||||
void updateBuffers();
|
|
||||||
|
|
||||||
ghoul::opengl::ProgramObject* _programObject;
|
|
||||||
|
|
||||||
properties::StringProperty _path;
|
|
||||||
properties::UIntProperty _nSegments;
|
|
||||||
properties::UIntProperty _upperLimit;
|
|
||||||
|
|
||||||
RenderableTrail::Appearance _appearance;
|
|
||||||
|
|
||||||
glm::vec3 _position;
|
|
||||||
|
|
||||||
UniformCache(modelView, projection, lineFade, inGameTime, color, opacity,
|
|
||||||
numberOfSegments) _uniformCache;
|
|
||||||
|
|
||||||
const double convertAuToKm = 1.496e8;
|
const double convertAuToKm = 1.496e8;
|
||||||
const double convertDaysToSecs = 86400.;
|
const double convertDaysToSecs = 86400.;
|
||||||
};
|
};
|
||||||
|
|
||||||
static double importAngleValue(const std::string& angle);
|
|
||||||
|
|
||||||
} // namespace openspace
|
} // namespace openspace
|
||||||
|
|
||||||
#endif // __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__
|
#endif // __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__
|
||||||
|
|||||||
Reference in New Issue
Block a user