diff --git a/modules/space/CMakeLists.txt b/modules/space/CMakeLists.txt index 612c2686e4..e191a4eff1 100644 --- a/modules/space/CMakeLists.txt +++ b/modules/space/CMakeLists.txt @@ -28,6 +28,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/planetgeometry.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableconstellationbounds.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/renderablesmallbody.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/renderableconstellationbounds.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/renderablesmallbody.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestars.cpp diff --git a/modules/space/rendering/renderableorbitalkepler.cpp b/modules/space/rendering/renderableorbitalkepler.cpp new file mode 100644 index 0000000000..7705c466ff --- /dev/null +++ b/modules/space/rendering/renderableorbitalkepler.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(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 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(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() * glm::pi(); + 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(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(std::floor(daysInYear)) + ); + + // 5 + const double nSecondsEpochOffset = static_cast( + 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(PathInfo.identifier); + _nSegments = static_cast(dictionary.value(SegmentsInfo.identifier)); + + if (dictionary.hasKeyAndValue(LineColorInfo.identifier)) { + _appearance.lineColor = dictionary.value(LineColorInfo.identifier); + } + if (dictionary.hasKeyAndValue(TrailFadeInfo.identifier)) { + _appearance.lineFade = static_cast( + dictionary.value(TrailFadeInfo.identifier) + ); + } + else { + _appearance.lineFade = 20; + } + + if (dictionary.hasKeyAndValue(UpperLimitInfo.identifier)) { + _upperLimit = static_cast( + dictionary.value(UpperLimitInfo.identifier) + ); + } + else { + _upperLimit = 0; + } + + if (dictionary.hasKeyAndValue(LineWidthInfo.identifier)) { + _appearance.lineWidth = static_cast( + dictionary.value(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 { + 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(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(i)/ static_cast(_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(positionX); + _vertexBufferData[index].y = static_cast(positionY); + _vertexBufferData[index].z = static_cast(positionZ); + _vertexBufferData[index].time = static_cast(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); + +} + +} diff --git a/modules/space/rendering/renderableorbitalkepler.h b/modules/space/rendering/renderableorbitalkepler.h new file mode 100644 index 0000000000..b1c36ea7fd --- /dev/null +++ b/modules/space/rendering/renderableorbitalkepler.h @@ -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 + +#include +#include +#include +#include +#include +#include +#include + +namespace openspace { + +const std::vector 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 _data; + + /// The backend storage for the vertex buffer object containing all points for this + /// trail. + std::vector _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__ + diff --git a/modules/space/rendering/renderablesatellites.cpp b/modules/space/rendering/renderablesatellites.cpp index a2e34ac2ba..62f80ed4bd 100644 --- a/modules/space/rendering/renderablesatellites.cpp +++ b/modules/space/rendering/renderablesatellites.cpp @@ -53,220 +53,10 @@ namespace { "Path", "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 { - // 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(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 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(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() * glm::pi(); - 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(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(std::floor(daysInYear)) - ); - - // 5 - const double nSecondsEpochOffset = static_cast( - seconds(hours(12)).count() - ); - - // Combine all of the values - const double epoch = nSecondsSince2000 + nLeapSecondsOffset - nSecondsEpochOffset; - return epoch; - } - documentation::Documentation RenderableSatellites::Documentation() { using namespace documentation; return { @@ -302,57 +92,17 @@ documentation::Documentation RenderableSatellites::Documentation() { } RenderableSatellites::RenderableSatellites(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _path(PathInfo) - , _nSegments(SegmentsInfo, 120, 4, 1024) + : RenderableOrbitalKepler(dictionary) { documentation::testSpecificationAndThrow( Documentation(), dictionary, "RenderableSatellites" ); - - _path = dictionary.value(PathInfo.identifier); - _nSegments = static_cast(dictionary.value(SegmentsInfo.identifier)); - - if (dictionary.hasKeyAndValue(LineColorInfo.identifier)) { - _appearance.lineColor = dictionary.value(LineColorInfo.identifier); - } - if (dictionary.hasKeyAndValue(TrailFadeInfo.identifier)) { - _appearance.lineFade = static_cast( - dictionary.value(TrailFadeInfo.identifier) - ); - } - else { - _appearance.lineFade = 20; - } - - if (dictionary.hasKeyAndValue(LineWidthInfo.identifier)) { - _appearance.lineWidth = static_cast( - dictionary.value(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)) { throw ghoul::RuntimeError(fmt::format( "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; keplerElements.period = period; - _TLEData.push_back(keplerElements); + _data.push_back(keplerElements); } file.close(); } - -void RenderableSatellites::initializeGL() { - glGenVertexArrays(1, &_vertexArray); - glGenBuffers(1, &_vertexBuffer); - - _programObject = SpaceModule::ProgramObjectManager.request( - ProgramName, - []() -> std::unique_ptr { - 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(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(i)/ static_cast(_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(positionX); - _vertexBufferData[index].y = static_cast(positionY); - _vertexBufferData[index].z = static_cast(positionZ); - _vertexBufferData[index].time = static_cast(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); - -} } diff --git a/modules/space/rendering/renderablesatellites.h b/modules/space/rendering/renderablesatellites.h index 9043c4ab46..05cf7b7498 100644 --- a/modules/space/rendering/renderablesatellites.h +++ b/modules/space/rendering/renderablesatellites.h @@ -25,6 +25,7 @@ #ifndef __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__ #define __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__ +#include #include #include @@ -37,98 +38,13 @@ namespace openspace { -const std::vector 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 RenderableSatellites : public Renderable { +class RenderableSatellites : private RenderableOrbitalKepler { public: 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(); - /** - * 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: - 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 _TLEData; - - /// The backend storage for the vertex buffer object containing all points for this - /// trail. - std::vector _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 diff --git a/modules/space/rendering/renderablesmallbody.cpp b/modules/space/rendering/renderablesmallbody.cpp index 9757bac9d6..abfef75e47 100644 --- a/modules/space/rendering/renderablesmallbody.cpp +++ b/modules/space/rendering/renderablesmallbody.cpp @@ -54,138 +54,10 @@ namespace { "Path", "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 { -const std::vector 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(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(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(std::floor(daysInYear)) - ); - - // 5 - const double nSecondsEpochOffset = static_cast( - seconds(hours(12)).count() - ); - - // Combine all of the values - const double epoch = nSecondsSince2000 + nLeapSecondsOffset - nSecondsEpochOffset; - return epoch; -} - documentation::Documentation RenderableSmallBody::Documentation() { using namespace documentation; return { @@ -233,71 +105,17 @@ documentation::Documentation RenderableSmallBody::Documentation() { } RenderableSmallBody::RenderableSmallBody(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _path(PathInfo) - , _nSegments(SegmentsInfo, 100, 10, 4000) - , _upperLimit(UpperLimitInfo, 1000, 1, 1000000) + : RenderableOrbitalKepler(dictionary) { documentation::testSpecificationAndThrow( Documentation(), dictionary, "RenderableSmallBody" ); - - _path = dictionary.value(PathInfo.identifier); - _nSegments = static_cast( - dictionary.value(SegmentsInfo.identifier) - ); - - if (dictionary.hasKeyAndValue(LineColorInfo.identifier)) { - _appearance.lineColor = dictionary.value(LineColorInfo.identifier); - } - if (dictionary.hasKeyAndValue(FadeInfo.identifier)) { - _appearance.lineFade = static_cast( - dictionary.value(FadeInfo.identifier) - ); - } - else { - _appearance.lineFade = 20; - } - - if (dictionary.hasKeyAndValue(UpperLimitInfo.identifier)) { - _upperLimit = static_cast( - dictionary.value(UpperLimitInfo.identifier) - ); - } - else { - _upperLimit = 0; - } - - if (dictionary.hasKeyAndValue(LineWidthInfo.identifier)) { - _appearance.lineWidth = static_cast( - dictionary.value(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)) { throw ghoul::RuntimeError(fmt::format( "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(file), std::istreambuf_iterator(), '\n' ); file.seekg(std::ios_base::beg); // reset iterator to beginning of file - _sbData.clear(); + _data.clear(); _sbNames.clear(); std::string line; @@ -461,7 +279,7 @@ void RenderableSmallBody::readOrbitalParamsFromThisLine(int& fieldCount, keplerElements.period *= convertDaysToSecs; fieldCount++; - _sbData.push_back(keplerElements); + _data.push_back(keplerElements); _sbNames.push_back(name); } @@ -473,158 +291,5 @@ static double importAngleValue(const std::string& angle) { } return output; } - -void RenderableSmallBody::initializeGL() { - glGenVertexArrays(1, &_vertexArray); - glGenBuffers(1, &_vertexBuffer); - - _programObject = SpaceModule::ProgramObjectManager.request( - ProgramName, - []() -> std::unique_ptr { - 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(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(i)/ static_cast(_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(positionX); - _vertexBufferData[index].y = static_cast(positionY); - _vertexBufferData[index].z = static_cast(positionZ); - _vertexBufferData[index].time = static_cast(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); -} - + } diff --git a/modules/space/rendering/renderablesmallbody.h b/modules/space/rendering/renderablesmallbody.h index 531270ce09..5c9fa5b9f8 100644 --- a/modules/space/rendering/renderablesmallbody.h +++ b/modules/space/rendering/renderablesmallbody.h @@ -25,6 +25,7 @@ #ifndef __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__ #define __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__ +#include #include #include @@ -37,101 +38,27 @@ namespace openspace { -class RenderableSmallBody : public Renderable { +static double importAngleValue(const std::string& angle); + +class RenderableSmallBody : public RenderableOrbitalKepler { public: 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(); - /** - * 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: - 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, std::ifstream& file); - KeplerTranslation _keplerTranslator; - std::vector _sbData; std::vector _sbNames; - /// The backend storage for the vertex buffer object containing all points for this - /// trail. - std::vector _vertexBufferData; - /// The index array that is potentially used in the draw call. If this is empty, no /// element draw call is used. std::vector _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 convertDaysToSecs = 86400.; }; -static double importAngleValue(const std::string& angle); - } // namespace openspace #endif // __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__