Pr/trails (#170)

* Implement new RenderableTrails as abstract base class
  - Implement RenderableTrailsOrbit and RenderableTrailsTrajectory as concrete instances
Remove old RenderableTrails and RenderableTrailsNew classes
Adapt mod files to the new structure

* Addressed Pull Request comments
This commit is contained in:
Alexander Bock
2016-11-23 10:35:46 +01:00
committed by GitHub
parent db923209c3
commit 683fc8ee53
73 changed files with 2002 additions and 1653 deletions

View File

@@ -23,199 +23,351 @@
****************************************************************************************/
#include <modules/base/rendering/renderabletrail.h>
#include <openspace/util/time.h>
#include <openspace/util/spicemanager.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/opengl/programobject.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/interaction/interactionhandler.h>
#include <limits>
#include <stdint.h>
/* TODO for this class:
* In order to add geometry shader (for pretty-draw),
* need to pack each consecutive point pair into a vec2
* in order to draw quad between them.
*/
#include <openspace/scene/translation.h>
namespace {
const std::string _loggerCat = "RenderableTrail";
//constants
const std::string keyName = "Name";
const std::string keyBody = "Body";
const std::string keyObserver = "Observer";
const std::string keyFrame = "Frame";
const std::string keyPathModule = "ModulePath";
const std::string keyColor = "RGB";
const std::string keyTropicalOrbitPeriod = "TropicalOrbitPeriod";
const std::string keyEarthOrbitRatio = "EarthOrbitRatio";
const std::string keyDayLength = "DayLength";
const std::string keyStamps = "TimeStamps";
static const char* KeyTranslation = "Translation";
static const char* KeyColor = "Color";
static const char* KeyEnableFade = "EnableFade";
static const char* KeyFade = "Fade";
static const char* KeyLineWidth = "LineWidth";
static const char* KeyPointSize = "PointSize";
static const char* KeyRendering = "Rendering";
// The possible values for the _renderingModes property
enum RenderingMode {
RenderingModeLines = 0,
RenderingModePoints,
RenderingModeLinesPoints
};
// Fragile! Keep in sync with documentation
static const std::map<std::string, RenderingMode> RenderingModeConversion = {
{ "Lines", RenderingModeLines },
{ "Points", RenderingModePoints },
{ "Lines+Points", RenderingModeLinesPoints },
{ "Points+Lines", RenderingModeLinesPoints }
};
}
namespace openspace {
Documentation RenderableTrail::Documentation() {
using namespace documentation;
return {
"RenderableTrail",
"base_renderable_renderabletrail",
{
{
KeyTranslation,
new ReferencingVerifier("core_transform_translation"),
"This object is used to compute locations along the path. Any "
"Translation object can be used here.",
Optional::No
},
{
KeyColor,
new DoubleVector3Verifier,
"The main color the for lines and points on this trail. The value is "
"interpreted as an RGB value.",
Optional::No
},
{
KeyEnableFade,
new BoolVerifier,
"Toggles whether the trail should fade older points out. If this value "
"is 'true', the 'Fade' parameter determines the speed of fading. If this "
"value is 'false', the entire trail is rendered at full opacity and "
"color. The default value is 'true'.",
Optional::Yes
},
{
KeyFade,
new DoubleVerifier,
"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. This value defaults to "
"1.0.",
Optional::Yes
},
{
KeyLineWidth,
new DoubleVerifier,
"This value specifies the line width of the trail if this rendering "
"method is selected. It defaults to 2.0.",
Optional::Yes
},
{
KeyPointSize,
new DoubleVerifier,
"This value specifies the base size of the points along the line if this "
"rendering method is selected. If a subsampling of the values is "
"performed, the subsampled values are half this size. The default value "
"is 1.0.",
Optional::Yes
},
{
KeyRendering,
new StringInListVerifier(
// Taken from the RenderingModeConversion map above
{ "Lines", "Points", "Lines+Points", "Points + Lines" }
),
"Determines how the trail should be rendered to the screen. If 'Lines' "
"is selected, only the line part is visible, if 'Points' is selected, "
"only the corresponding points (and subpoints) are shown. "
"Lines+Points' shows both parts. On default, only the lines are rendered",
Optional::Yes
}
},
Exhaustive::No
};
}
RenderableTrail::RenderableTrail(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _lineColor("lineColor", "Line Color")
, _lineFade("lineFade", "Line Fade", 0.75f, 0.f, 5.f)
, _lineColor("lineColor", "Color", glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
, _useLineFade("useLineFade", "Use Line Fade", true)
, _lineFade("lineFade", "Line Fade", 1.f, 0.f, 20.f)
, _lineWidth("lineWidth", "Line Width", 2.f, 1.f, 20.f)
, _showTimestamps("timestamps", "Show Timestamps", false)
, _programObject(nullptr)
, _successfullDictionaryFetch(true)
, _vaoID(0)
, _vBufferID(0)
, _needsSweep(true)
, _oldTime(std::numeric_limits<double>::max())
, _tropic(0.f)
, _ratio(0.f)
, _day(0.f)
, _increment(0.f)
, _pointSize("pointSize", "Point Size", 1, 1, 64)
, _renderingModes(
"renderingMode",
"Rendering Mode",
properties::OptionProperty::DisplayType::Dropdown
)
{
_successfullDictionaryFetch &= dictionary.getValue(keyBody, _target);
_successfullDictionaryFetch &= dictionary.getValue(keyObserver, _observer);
_successfullDictionaryFetch &= dictionary.getValue(keyFrame, _frame);
_successfullDictionaryFetch &= dictionary.getValue(keyTropicalOrbitPeriod, _tropic);
_successfullDictionaryFetch &= dictionary.getValue(keyEarthOrbitRatio, _ratio);
_successfullDictionaryFetch &= dictionary.getValue(keyDayLength, _day);
// values in modfiles set from here
// http://nssdc.gsfc.nasa.gov/planetary/factsheet/marsfact.html
_translation = std::unique_ptr<Translation>(Translation::createFromDictionary(
dictionary.value<ghoul::Dictionary>(KeyTranslation)
));
glm::vec3 color(0.f);
if (dictionary.hasKeyAndValue<glm::vec3>(keyColor))
dictionary.getValue(keyColor, color);
_lineColor = color;
bool timeStamps = false;
if (dictionary.hasKeyAndValue<bool>(keyStamps))
dictionary.getValue(keyStamps, timeStamps);
_showTimestamps = timeStamps;
addProperty(_showTimestamps);
_lineColor.setViewOption(properties::Property::ViewOptions::Color);
_lineColor = dictionary.value<glm::vec3>(KeyColor);
addProperty(_lineColor);
if (dictionary.hasKeyAndValue<bool>(KeyEnableFade)) {
_useLineFade = dictionary.value<bool>(KeyEnableFade);
}
addProperty(_useLineFade);
if (dictionary.hasKeyAndValue<double>(KeyFade)) {
_lineFade = dictionary.value<double>(KeyFade);
}
addProperty(_lineFade);
if (dictionary.hasKeyAndValue<double>(KeyLineWidth)) {
_lineWidth = dictionary.value<double>(KeyLineWidth);
}
addProperty(_lineWidth);
_distanceFade = 1.0;
if (dictionary.hasKeyAndValue<double>(KeyPointSize)) {
_pointSize = dictionary.value<double>(KeyPointSize);
}
addProperty(_pointSize);
_renderingModes.addOptions({
{ RenderingModeLines, "Lines" },
{ RenderingModePoints, "Points" },
{ RenderingModeLinesPoints, "Lines+Points" }
});
// This map is not accessed out of order as long as the Documentation is adapted
// whenever the map changes. The documentation will check for valid values
if (dictionary.hasKeyAndValue<std::string>(KeyRendering)) {
_renderingModes = RenderingModeConversion.at(
dictionary.value<std::string>(KeyRendering)
);
}
else {
_renderingModes = RenderingModeLines;
}
addProperty(_renderingModes);
}
bool RenderableTrail::initialize() {
if (!_successfullDictionaryFetch) {
LERROR("The following keys need to be set in the Dictionary. Cannot initialize!");
LERROR(keyBody << ": " << _target);
LERROR(keyObserver << ": " << _observer);
LERROR(keyFrame << ": " << _frame);
LERROR(keyTropicalOrbitPeriod << ": " << _tropic);
LERROR(keyEarthOrbitRatio << ": " << _ratio);
LERROR(keyDayLength << ": " << _day);
return false;
}
bool completeSuccess = true;
RenderEngine& renderEngine = OsEng.renderEngine();
_programObject = renderEngine.buildRenderProgram("EphemerisProgram",
"${MODULE_BASE}/shaders/ephemeris_vs.glsl",
"${MODULE_BASE}/shaders/ephemeris_fs.glsl");
_programObject = renderEngine.buildRenderProgram(
"EphemerisProgram",
"${MODULE_BASE}/shaders/renderabletrail_vs.glsl",
"${MODULE_BASE}/shaders/renderabletrail_fs.glsl"
);
setRenderBin(Renderable::RenderBin::Overlay);
if (!_programObject)
return false;
return completeSuccess;
return true;
}
bool RenderableTrail::deinitialize() {
glDeleteVertexArrays(1, &_vaoID);
glDeleteBuffers(1, &_vBufferID);
RenderEngine& renderEngine = OsEng.renderEngine();
if (_programObject) {
renderEngine.removeRenderProgram(_programObject);
_programObject = nullptr;
}
return true;
}
bool RenderableTrail::isReady() const {
return (_programObject != nullptr) && _successfullDictionaryFetch;
return _programObject != nullptr;
}
void RenderableTrail::render(const RenderData& data) {
void RenderableTrail::render(const RenderData & data) {
_programObject->activate();
//psc currentPosition = data.position;
//psc campos = data.camera.position();
//glm::mat4 camrot = glm::mat4(data.camera.viewRotationMatrix());
//glm::mat4 transform = glm::mat4(1);
// setup the data to the shader
//_programObject->setUniform("ViewProjection", data.camera.viewProjectionMatrix());
//_programObject->setUniform("ModelTransform", transform);
//setPscUniforms(*_programObject.get(), data.camera, data.position);
// Calculate variables to be used as uniform variables in shader
glm::dvec3 bodyPosition = data.modelTransform.translation;
// Model transform and view transform needs to be in double precision
glm::dmat4 modelTransform =
glm::translate(glm::dmat4(1.0), bodyPosition) *
glm::dmat4(data.modelTransform.rotation) * // Spice rotation
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
glm::dmat4(data.modelTransform.rotation) *
glm::dmat4(glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)));
glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform;
_programObject->setUniform("modelViewTransform", glm::mat4(modelViewTransform));
_programObject->setUniform("projectionTransform", data.camera.projectionMatrix());
_programObject->setUniform("color", _lineColor);
_programObject->setUniform("nVertices", static_cast<unsigned int>(_vertexArray.size()));
_programObject->setUniform("lineFade", _lineFade);
_programObject->setUniform("forceFade", _distanceFade);
_programObject->setUniform("useLineFade", _useLineFade);
if (_useLineFade) {
_programObject->setUniform("lineFade", _lineFade);
}
//const psc& position = data.camera.position();
//const psc& origin = openspace::OpenSpaceEngine::ref().interactionHandler()->focusNode()->worldPosition();
//const PowerScaledScalar& pssl = (position - origin).length();
//
//if (pssl[0] < 0.000001){
// if (_distanceFade > 0.0f) _distanceFade -= 0.05f;
// _programObject->setUniform("forceFade", _distanceFade);
//}
//else{
// if (_distanceFade < 1.0f) _distanceFade += 0.05f;
// _programObject->setUniform("forceFade", _distanceFade);
//}
static std::map<RenderInformation::VertexSorting, int> SortingMapping = {
// Fragile! Keep in sync with shader
{ RenderInformation::VertexSorting::NewestFirst, 0 },
{ RenderInformation::VertexSorting::OldestFirst, 1 },
{ RenderInformation::VertexSorting::NoSorting, 2}
};
bool usingFramebufferRenderer =
OsEng.renderEngine().rendererImplementation() == RenderEngine::RendererImplementation::Framebuffer;
OsEng.renderEngine().rendererImplementation() ==
RenderEngine::RendererImplementation::Framebuffer;
if (usingFramebufferRenderer) {
glDepthMask(false);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
glLineWidth(_lineWidth);
bool renderLines =
_renderingModes == RenderingModeLines |
_renderingModes == RenderingModeLinesPoints;
glBindVertexArray(_vaoID);
glDrawArrays(GL_LINE_STRIP, 0, static_cast<GLsizei>(_vertexArray.size()));
glBindVertexArray(0);
bool renderPoints =
_renderingModes == RenderingModePoints |
_renderingModes == RenderingModeLinesPoints;
glLineWidth(1.f);
if (_showTimestamps){
glPointSize(5.f);
glBindVertexArray(_vaoID);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_vertexArray.size()));
glBindVertexArray(0);
if (renderLines) {
glLineWidth(_lineWidth);
}
if (renderPoints) {
glEnable(GL_PROGRAM_POINT_SIZE);
}
auto render = [renderLines, renderPoints, p = _programObject.get(), &data,
&modelTransform, pointSize = _pointSize.value()]
(RenderInformation& info, int nVertices, int offset)
{
// We pass in the model view transformation matrix as double in order to maintain
// high precision for vertices; especially for the trails, a high vertex precision
// is necessary as they are usually far away from their reference
p->setUniform(
"modelViewTransform",
data.camera.combinedViewMatrix() * modelTransform * info._localTransform
);
// The vertex sorting method is used to tweak the fading along the trajectory
p->setUniform(
"vertexSortingMethod",
SortingMapping[info.sorting]
);
// This value is subtracted from the vertex id in the case of a potential ring
// buffer (as used in RenderableTrailOrbit) to keep the first vertex at its
// brightest
p->setUniform(
"idOffset",
offset
);
p->setUniform("nVertices", nVertices);
if (renderPoints) {
// The stride parameter determines the distance between larger points and
// smaller ones
p->setUniform("stride", info.stride);
p->setUniform("pointSize", pointSize);
}
// Fragile! Keep in sync with fragment shader
enum RenderPhase {
RenderPhaseLines = 0,
RenderPhasePoints
};
glBindVertexArray(info._vaoID);
if (renderLines) {
p->setUniform("renderPhase", RenderPhaseLines);
// Subclasses of this renderer might be using the index array or might now be
// so we check if there is data available and if there isn't, we use the
// glDrawArrays draw call; otherwise the glDrawElements
if (info._iBufferID == 0) {
glDrawArrays(
GL_LINE_STRIP,
info.first,
info.count
);
}
else {
glDrawElements(
GL_LINE_STRIP,
info.count,
GL_UNSIGNED_INT,
reinterpret_cast<void*>(info.first * sizeof(unsigned int))
);
}
}
if (renderPoints) {
// Subclasses of this renderer might be using the index array or might now be
// so we check if there is data available and if there isn't, we use the
// glDrawArrays draw call; otherwise the glDrawElements
p->setUniform("renderPhase", RenderPhasePoints);
if (info._iBufferID == 0) {
glDrawArrays(GL_POINTS, info.first, info.count);
}
else {
glDrawElements(
GL_POINTS,
info.count,
GL_UNSIGNED_INT,
reinterpret_cast<void*>(info.first * sizeof(unsigned int))
);
}
}
};
// The combined size of vertices; -1 because we duplicate the penultimate point
int totalNumber =
_primaryRenderInformation.count + _floatingRenderInformation.count - 1;
// The primary information might use an index buffer, so we might need to start at an
// offset
int primaryOffset =
_primaryRenderInformation._iBufferID == 0 ? 0 : _primaryRenderInformation.first;
// Render the primary batch of vertices
render(_primaryRenderInformation, totalNumber, primaryOffset);
// The secondary batch is optional,. so we need to check whether we have any data here
if (_floatingRenderInformation._vaoID == 0 || _floatingRenderInformation.count == 0) {
render(
_floatingRenderInformation,
totalNumber,
// -1 because we duplicate the penultimate point between the vertices
primaryOffset + _primaryRenderInformation.count - 1
);
}
if (renderPoints) {
glDisable(GL_PROGRAM_POINT_SIZE);
}
glBindVertexArray(0);
if (usingFramebufferRenderer) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -225,144 +377,4 @@ void RenderableTrail::render(const RenderData& data) {
_programObject->deactivate();
}
void RenderableTrail::update(const UpdateData& data) {
if (data.isTimeJump)
_needsSweep = true;
if (_needsSweep) {
fullYearSweep(data.time);
sendToGPU();
_needsSweep = false;
return;
}
double lightTime = 0.0;
bool intervalSet = hasTimeInterval();
double start = -DBL_MAX;
double end = DBL_MAX;
if (intervalSet) {
getInterval(start, end);
}
// Points in the vertex array should always have a fixed distance. For this reason we
// keep the first entry in the array floating and always pointing to the current date
// As soon as the time difference between the current time and the last time is bigger
// than the fixed distance, we need to create a new fixed point
double deltaTime = std::abs(data.time - _oldTime);
int nValues = static_cast<int>(floor(deltaTime / _increment));
glm::dvec3 p;
// Update the floating current time
if (start > data.time)
p = SpiceManager::ref().targetPosition(_target, _observer, _frame, {}, start, lightTime);
else if (end < data.time)
p = SpiceManager::ref().targetPosition(_target, _observer, _frame, {}, end, lightTime);
else
p = SpiceManager::ref().targetPosition(_target, _observer, _frame, {}, data.time, lightTime);
psc pscPos = PowerScaledCoordinate::CreatePowerScaledCoordinate(p.x, p.y, p.z);
pscPos[3] += 3; // KM to M
_vertexArray[0] = { pscPos[0], pscPos[1], pscPos[2], pscPos[3] };
if (nValues != 0) {
std::vector<TrailVBOLayout> tmp(nValues);
for (int i = nValues; i > 0; --i) {
double et = _oldTime + i * _increment;
if (start > et)
et = start;
else if (end < et)
et = end;
glm::dvec3 p =
SpiceManager::ref().targetPosition(_target, _observer, _frame, {}, et, lightTime);
pscPos = PowerScaledCoordinate::CreatePowerScaledCoordinate(p.x, p.y, p.z);
pscPos[3] += 3;
tmp[nValues - i] = { pscPos[0], pscPos[1], pscPos[2], pscPos[3] };
}
size_t size = _vertexArray.size();
_vertexArray.insert(_vertexArray.begin() + 1, tmp.begin(), tmp.end());
_vertexArray.resize(size);
_oldTime += nValues * _increment;
}
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
glBufferSubData(GL_ARRAY_BUFFER, 0, _vertexArray.size() * sizeof(TrailVBOLayout), &_vertexArray[0]);
}
/* This algorithm estimates and precomputes the number of segments required for
* any planetary object in space, given a tropical orbit period and earth-to-planet
* orbit ratio. In doing so, it finds the exact increment of time corresponding
* to a planetary year.
* Therefore all planets need said constants, for other objects we need a different,
* and most likely heuristic measure to easily estimate a nodal time-increment.
* Trivial, yet - a TODO.
*/
void RenderableTrail::fullYearSweep(double time) {
const int SecondsPerEarthYear = 31540000;
double lightTime = 0.0;
float planetYear = SecondsPerEarthYear * _ratio;
int segments = static_cast<int>(_tropic);
bool intervalSet = hasTimeInterval();
double start = -DBL_MAX;
double end = DBL_MAX;
if (intervalSet) {
intervalSet &= getInterval(start, end);
}
_increment = planetYear / _tropic;
_oldTime = time;
_vertexArray.resize(segments+2);
glm::dvec3 p;
bool failed = false;
for (int i = 0; i < segments+2; i++) {
if (start > time && intervalSet) {
time = start;
}
else if (end < time && intervalSet) {
time = end;
}
if (!failed || intervalSet) {
try {
p =
SpiceManager::ref().targetPosition(_target, _observer, _frame, {}, time, lightTime);
}
catch (const SpiceManager::SpiceException& e) {
// This fires for PLUTO BARYCENTER and SUN and uses the only value sometimes?
// ---abock
//LERROR(e.what());
failed = true;
}
}
psc pscPos = PowerScaledCoordinate::CreatePowerScaledCoordinate(p.x, p.y, p.z);
pscPos[3] += 3;
_vertexArray[i] = {pscPos[0], pscPos[1], pscPos[2], pscPos[3]};
time -= _increment;
}
}
void RenderableTrail::sendToGPU() {
glGenVertexArrays(1, &_vaoID);
glGenBuffers(1, &_vBufferID);
glBindVertexArray(_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
glBufferData(GL_ARRAY_BUFFER, _vertexArray.size() * sizeof(TrailVBOLayout), NULL, GL_STREAM_DRAW); // orphaning the buffer, sending NULL data.
glBufferSubData(GL_ARRAY_BUFFER, 0, _vertexArray.size() * sizeof(TrailVBOLayout), &_vertexArray[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
}
} // namespace openspace

View File

@@ -26,6 +26,10 @@
#define __RENDERABLETRAIL_H__
#include <openspace/rendering/renderable.h>
#include <openspace/documentation/documentation.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/scalarproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/vectorproperty.h>
@@ -40,53 +44,117 @@ namespace opengl {
namespace openspace {
class Translation;
/**
* This is the base class for a trail that is drawn behind an arbitrary object. The two
* concreate implementations are RenderableTrailOrbit, for objects that have a (roughly)
* repeating orbit, and RenderableTrailTrajectory, for objects that are less orbit-like.
* The main difference between two subclasses is that RenderableTrailOrbit updates itself
* continously, whereas RenderableTrailTrajectory precomputes the entire trail in advance.
*
* This class is responsible for the rendering of the vertex buffer objects which are
* filled by the subclasses. The buffers contain a list of TrailVBOLayout objects that is
* the three dimensional position for each point along the line.
*
* Trails can be rendered either as lines, as points, or a combination of both with
* varying colors, line thicknesses, or fading settings. If trails are rendered as points,
* the RenderInformation's \c stride parameter determines the number of points between
* larger points. A potential use case for this is showing the passage of time along a
* trail by using a point separation of one hour and a subsampling of 4, you would get a
* point every 15 minutes with every hourly point being bigger.
*
* The positions for each point along the trail is provided through a Translation object,
* the type of which is specified in the dictionary that is passed to the constructor. A
* typical implementation of Translation used for the trail would be a SpiceTranslation.
*/
class RenderableTrail : public Renderable {
public:
explicit RenderableTrail(const ghoul::Dictionary& dictionary);
bool initialize() override;
bool deinitialize() override;
bool isReady() const override;
/**
* The render method will set up the shader information and then render first the
* information contained in the the \c _primaryRenderInformation, then the optional
* \c _floatingRenderInformation using the provided \p data
* \param data The data that is necessary to render this Renderable
*/
void render(const RenderData& data) override;
void update(const UpdateData& data) override;
private:
protected:
explicit RenderableTrail(const ghoul::Dictionary& dictionary);
/// Returns the documentation entries that the con
static openspace::Documentation Documentation();
/// The layout of the VBOs
struct TrailVBOLayout {
float x, y, z, e;
float x, y, z;
};
void fullYearSweep(double time);
void sendToGPU();
properties::Vec3Property _lineColor;
properties::FloatProperty _lineFade;
properties::FloatProperty _lineWidth;
properties::BoolProperty _showTimestamps;
std::unique_ptr<ghoul::opengl::ProgramObject> _programObject;
bool _successfullDictionaryFetch;
std::string _target;
std::string _observer;
std::string _frame;
float _tropic;
float _ratio;
float _day;
GLuint _vaoID;
GLuint _vBufferID;
bool _needsSweep;
/// The backend storage for the vertex buffer object containing all points for this
/// trail.
std::vector<TrailVBOLayout> _vertexArray;
float _increment;
double _oldTime = 0.0;
float _distanceFade;
/// The index array that is potentially used in the draw call. If this is empty, no
/// element draw call is used.
std::vector<unsigned int> _indexArray;
/// The Translation object that provides the position of the individual trail points
std::unique_ptr<Translation> _translation;
/// The RenderInformation contains information filled in by the concrete subclasses to
/// be used by this class.
struct RenderInformation {
enum class VertexSorting {
NewestFirst = 0, ///< Newer vertices have a lower index than older ones
OldestFirst, ///< Older vertices have a lower index than newer ones
NoSorting ///< No ordering in the vertices; no fading applied
};
/// The first element in the vertex buffer to be rendered
GLint first = 0;
/// The number of values to be rendered
GLsizei count = 0;
/// The stride between 'major' points in the array
int stride = 1;
/// Sorting of the vertices; required for correct fading
VertexSorting sorting = VertexSorting::NoSorting;
/// Local model matrix transformation, used for rendering in camera space
glm::dmat4 _localTransform = glm::dmat4(1.0);
/// The vertex array object for this RenderInformation
GLuint _vaoID = 0;
/// The main vertex buffer object
GLuint _vBufferID = 0;
/// The optional index buffer object
GLuint _iBufferID = 0;
};
/// Primary set of information about the main rendering parts
RenderInformation _primaryRenderInformation;
/// Optional render information that contains information about the last, floating
/// part of the trail
RenderInformation _floatingRenderInformation;
private:
/// Specifies the base color of the line before fading
properties::Vec3Property _lineColor;
/// Settings that enables or disables the line fading
properties::BoolProperty _useLineFade;
/// Specifies a multiplicative factor that fades out the line
properties::FloatProperty _lineFade;
/// Line width for the line rendering part
properties::FloatProperty _lineWidth;
/// Point size for the point rendering part
properties::IntProperty _pointSize;
/// The option determining which rendering method to use
properties::OptionProperty _renderingModes;
/// Program object used to render the data stored in RenderInformation
std::unique_ptr<ghoul::opengl::ProgramObject> _programObject;
};
} // namespace openspace

View File

@@ -1,416 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2016 *
* *
* 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/base/rendering/renderabletrailnew.h>
#include <openspace/util/time.h>
#include <openspace/util/spicemanager.h>
#include <openspace/util/updatestructures.h>
#include <openspace/util/timerange.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/interaction/interactionhandler.h>
#include <ghoul/opengl/programobject.h>
#include <limits>
#include <stdint.h>
namespace {
const std::string _loggerCat = "RenderableTrailNew";
// Spice
const std::string keyBody = "Body";
const std::string keyFrame = "Frame";
const std::string keyObserver = "Observer";
// Rendering properties
const std::string keyLineColor = "LineColor";
const std::string keyPointColor = "PointColor";
const std::string keyLineFade = "LineFade";
const std::string keyLineWidth = "LineWidth";
const std::string keyRenderPart = "RenderPart";
const std::string keyShowTimeStamps = "ShowTimeStamps";
const std::string keyRenderFullTrail = "RenderFullTrail";
// Time interval
const std::string keyTimeRange = "TimeRange";
const std::string keySampleDeltaTime = "SampleDeltaTime";
const std::string keySubSamples = "SubSamples";
// Static Constants
static const glm::vec3 DEFAULT_COLOR = glm::vec3(1.0f);
static const float DEFAULT_LINE_FADE = 0.5f;
static const float DEFAULT_LINE_WIDTH = 2.0f;
static const int DEFAULT_POINT_STEPS = 1;
static const int DEFAULT_RENDER_PART = 1;
static const bool DEFAULT_SHOW_TIME_STAMPS = false;
static const bool DEFAULT_RENDER_FULL_TRAIL = false;
}
namespace openspace {
RenderableTrailNew::RenderableTrailNew(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
// Properties
, _lineColor("lineColor", "Line Color", DEFAULT_COLOR, glm::vec3(0), glm::vec3(1))
, _pointColor("pointColor", "Point Color", DEFAULT_COLOR, glm::vec3(0), glm::vec3(1))
, _lineFade("lineFade", "Line Fade", DEFAULT_LINE_FADE, 0, 1)
, _lineWidth("lineWidth", "Line Width", DEFAULT_LINE_WIDTH, 1, 10)
, _renderPart("renderPart", "Render Part", DEFAULT_RENDER_PART, 0, DEFAULT_RENDER_PART)
, _showTimeStamps("showTimeStamps", "Show TimeStamps", DEFAULT_SHOW_TIME_STAMPS)
, _renderFullTrail("renderFullTrail", "Render Full Trail", DEFAULT_RENDER_FULL_TRAIL)
// OpenGL
, _vaoGlobalID(0)
, _vBufferGlobalID(0)
, _vaoLocalID(0)
, _vBufferLocalID(0)
// Other
, _programObject(nullptr)
, _successfullDictionaryFetch(true)
, _currentTimeClamped(0)
, _subSamples(0)
{
ghoul::Dictionary timeRangeDict;
// Values that needs to be set
_successfullDictionaryFetch &= dictionary.getValue(keyBody, _body);
_successfullDictionaryFetch &= dictionary.getValue(keyObserver, _observer);
_successfullDictionaryFetch &= dictionary.getValue(keyFrame, _frame);
_successfullDictionaryFetch &= dictionary.getValue(keySampleDeltaTime, _sampleDeltaTime);
_successfullDictionaryFetch &= dictionary.getValue(keyTimeRange, timeRangeDict);
_successfullDictionaryFetch &= TimeRange::initializeFromDictionary(
timeRangeDict, _timeRange);
// Validate
_successfullDictionaryFetch &= _sampleDeltaTime > 0;
// Initialize optional values
glm::vec3 lineColor = glm::vec3(DEFAULT_COLOR);
glm::vec3 pointColor = glm::vec3(DEFAULT_COLOR);
float lineFade = DEFAULT_LINE_FADE;
float lineWidth = DEFAULT_LINE_WIDTH;
float pointSteps = DEFAULT_POINT_STEPS;
double renderPart = DEFAULT_RENDER_PART;
bool showTimeStamps = DEFAULT_SHOW_TIME_STAMPS;
bool renderFullTrail = DEFAULT_RENDER_FULL_TRAIL;
// Fetch from dictionary
dictionary.getValue(keyLineColor, lineColor);
dictionary.getValue(keyPointColor, pointColor);
dictionary.getValue(keyLineFade, lineFade);
dictionary.getValue(keyLineWidth, lineWidth);
dictionary.getValue(keyRenderPart, renderPart);
dictionary.getValue(keyShowTimeStamps, showTimeStamps);
dictionary.getValue(keyRenderFullTrail, renderFullTrail);
float fSubSamples; // ghoul can not read ints from dictionaries...
if (dictionary.getValue(keySubSamples, fSubSamples))
_subSamples = fSubSamples;
// Set property values
_lineColor = lineColor;
_pointColor = pointColor;
_lineFade = lineFade;
_lineWidth = lineWidth;
_renderPart = renderPart;
_showTimeStamps = showTimeStamps;
_renderFullTrail = renderFullTrail;
// Add all properties and set view options
addProperty(_lineColor);
addProperty(_pointColor);
addProperty(_lineFade);
addProperty(_lineWidth);
addProperty(_renderPart);
addProperty(_showTimeStamps);
addProperty(_renderFullTrail);
_lineColor.setViewOption(properties::Property::ViewOptions::Color);
_pointColor.setViewOption(properties::Property::ViewOptions::Color);
}
bool RenderableTrailNew::initialize() {
if (!_successfullDictionaryFetch) {
LERROR("The following keys need to be set in the Dictionary. Cannot initialize!");
LERROR(keyBody << ": " << _body);
LERROR(keyObserver << ": " << _observer);
LERROR(keyFrame << ": " << _frame);
LERROR(keyTimeRange);
return false;
}
if (_subSamples < 0)
LERROR("SubSamples must not be less than 0: " << _subSamples);
RenderEngine& renderEngine = OsEng.renderEngine();
_programObject = renderEngine.buildRenderProgram("RenderableTrailNewProgram",
"${MODULE_BASE}/shaders/renderabletrailnew_vs.glsl",
"${MODULE_BASE}/shaders/renderabletrailnew_fs.glsl");
if (!_programObject)
return false;
sweepTimeRange();
initializeGlobalOpenGLPathData();
initializeLocalOpenGLPathData();
return true;
}
void RenderableTrailNew::initializeGlobalOpenGLPathData() {
glGenVertexArrays(1, &_vaoGlobalID);
glGenBuffers(1, &_vBufferGlobalID);
glBindVertexArray(_vaoGlobalID);
glBindBuffer(GL_ARRAY_BUFFER, _vBufferGlobalID);
// No need to update the trail several times, no need for stream draw.
glBufferData(
GL_ARRAY_BUFFER,
_vertexPositionArray.size() * sizeof(glm::vec3),
&_vertexPositionArray[0],
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
}
void RenderableTrailNew::initializeLocalOpenGLPathData() {
glGenVertexArrays(1, &_vaoLocalID);
glGenBuffers(1, &_vBufferLocalID);
glBindVertexArray(_vaoLocalID);
glBindBuffer(GL_ARRAY_BUFFER, _vBufferLocalID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
}
void RenderableTrailNew::deInitializeGlobalOpenGLPathData() {
glDeleteVertexArrays(1, &_vaoGlobalID);
glDeleteBuffers(1, &_vBufferGlobalID);
}
void RenderableTrailNew::deInitializeLocalOpenGLPathData() {
glDeleteVertexArrays(1, &_vaoLocalID);
glDeleteBuffers(1, &_vBufferLocalID);
}
bool RenderableTrailNew::deinitialize() {
deInitializeGlobalOpenGLPathData();
deInitializeLocalOpenGLPathData();
RenderEngine& renderEngine = OsEng.renderEngine();
if (_programObject) {
renderEngine.removeRenderProgram(_programObject);
_programObject = nullptr;
}
return true;
}
void RenderableTrailNew::sweepTimeRange() {
double lightTime = 0.0;
double subDeltaTime = _sampleDeltaTime / (1 + _subSamples);
glm::dvec3 bodyPosition;
// Loop through all points from time range start to end
for (double t = _timeRange.start; t < _timeRange.end; t += subDeltaTime) {
try {
bodyPosition = SpiceManager::ref().targetPosition(
_body, _observer, _frame, {}, t, lightTime);
}
catch (const SpiceManager::SpiceException& e) {
LERROR(e.what());
break;
}
// Convert from km used by SPICE to meters used by OpenSpace
bodyPosition *= 1000;
_vertexPositionArray.push_back(glm::vec3(bodyPosition));
}
// Last point
bodyPosition = SpiceManager::ref().targetPosition(
_body, _observer, _frame, {}, _timeRange.end, lightTime);
_vertexPositionArray.push_back(glm::vec3(bodyPosition));
}
bool RenderableTrailNew::isReady() const {
return (_programObject != nullptr) && _successfullDictionaryFetch;
}
void RenderableTrailNew::render(const RenderData& data) {
_programObject->activate();
if (_renderFullTrail.value() == true) {
preRender(_vertexPositionArray.size());
preRenderSubPathGlobally(data);
// Easy but not beautiful solution to render all vertices with max alpha
_programObject->setUniform(
"vertexIDPadding", static_cast<int>(_vertexPositionArray.size()));
renderLines(_vaoGlobalID, _vertexPositionArray.size() - 1);
if (_showTimeStamps) {
renderPoints(_vaoGlobalID, _vertexPositionArray.size() - 1);
}
}
else { // Only render the trail up to the point of the object body
int nVerticesToDraw = glm::ceil(_vertexPositionArray.size() *
(_currentTimeClamped - _timeRange.start) / (_timeRange.end - _timeRange.start));
nVerticesToDraw = glm::min(
nVerticesToDraw, static_cast<int>(_vertexPositionArray.size())) - 1;
if (nVerticesToDraw > 1) {
preRender(nVerticesToDraw);
// Perform rendering of the bulk of the trail in single floating point precision
preRenderSubPathGlobally(data);
// The last vertex is drawn with higher precision after this
// Hence we subtract one vertex from the ones to draw globally
int nVerticesToDrawGlobally = nVerticesToDraw - 1;
renderLines(_vaoGlobalID, nVerticesToDrawGlobally);
if (_showTimeStamps) {
renderPoints(_vaoGlobalID, nVerticesToDrawGlobally);
}
// The last line segment is rendered relative to body to achieve high precision
preRenderSubPathLocally(data, nVerticesToDraw);
renderLines(_vaoLocalID, 2);
if (_showTimeStamps) {
renderPoints(_vaoLocalID, 2);
}
}
else if (_currentTimeClamped > _timeRange.start) {
preRenderSubPathLocally(data, 2);
renderLines(_vaoLocalID, 2);
}
}
_programObject->deactivate();
}
void RenderableTrailNew::preRender(int totalNumVerticesToDraw) {
// Upload uniforms that are the same for global and local rendering to the program
_programObject->setUniform("lineFade", _lineFade.value());
_programObject->setUniform("subSamples", _subSamples);
_programObject->setUniform("maxNumVertices",
static_cast<int>(_renderPart.value() * _vertexPositionArray.size()));
_programObject->setUniform("numVertices", totalNumVerticesToDraw);
}
void RenderableTrailNew::preRenderSubPathGlobally(const RenderData& data) {
// Model transform and view transform needs to be in double precision
glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * _modelTransform;
glm::mat4 modelViewProjectionTransform =
data.camera.projectionMatrix() * glm::mat4(modelViewTransform);
// Upload uniforms that are specific to global rendering to the shader program
_programObject->setUniform(
"modelViewProjectionTransform", modelViewProjectionTransform);
_programObject->setUniform("vertexIDPadding", 0);
}
void RenderableTrailNew::preRenderSubPathLocally(
const RenderData& data, int totalNumVerticesToDraw) {
glm::dvec3 v0; // Vertex that connects to the global part of the trail
glm::dvec3 v1; // last vertex of the trail is in the position of the body
v0 = _vertexPositionArray[totalNumVerticesToDraw - 2];
v1 = _clampedBodyPosition;
// Define positions relative to body (v1) which gives the high precision
glm::vec3 vertexData[2] = {glm::vec3(v0 - v1), glm::vec3(0)};
// Translation translates from the position of body so vertices should
// be defined relative to body (hence the added v1 in translation part of model matrix)
glm::dmat4 localTranslation = glm::translate(glm::dmat4(1.0), v1);
glm::dmat4 modelTransformLocal = _modelTransform * localTranslation;
glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransformLocal;
glm::mat4 modelViewProjectionTransform =
data.camera.projectionMatrix() * glm::mat4(modelViewTransform);
// Upload the new MVP matrix to the shader program
_programObject->setUniform(
"modelViewProjectionTransform", modelViewProjectionTransform);
_programObject->setUniform("vertexIDPadding", totalNumVerticesToDraw - 2);
// Update the attribute data on the GPU
glBindBuffer(GL_ARRAY_BUFFER, _vBufferLocalID);
glBufferData(
GL_ARRAY_BUFFER,
2 * sizeof(glm::vec3), // Only two vertices for this part of the trail
vertexData, // Two vertices
GL_DYNAMIC_DRAW); // This part of the path is rendered dynamically
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
void RenderableTrailNew::renderLines(GLuint vao, int numberOfVertices) {
glLineWidth(_lineWidth);
_programObject->setUniform("color", _lineColor.value());
glBindVertexArray(vao);
glDrawArrays(GL_LINE_STRIP, 0, static_cast<GLsizei>(numberOfVertices));
glBindVertexArray(0);
glLineWidth(1.f);
}
void RenderableTrailNew::renderPoints(GLuint vao, int numberOfVertices) {
glEnable(GL_PROGRAM_POINT_SIZE);
_programObject->setUniform("color", _pointColor.value());
_programObject->setUniform("pointSize", static_cast<float>(_lineWidth * 3));
glBindVertexArray(vao);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(numberOfVertices));
glBindVertexArray(0);
glDisable(GL_PROGRAM_POINT_SIZE);
}
void RenderableTrailNew::update(const UpdateData& data) {
_currentTimeClamped = glm::clamp(data.time, _timeRange.start, _timeRange.end);
_modelTransform =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
glm::dmat4(data.modelTransform.rotation) *
glm::dmat4(glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)));
// Fetch the body position using SPICE
double lightTime = 0.0;
try {
_clampedBodyPosition = SpiceManager::ref().targetPosition(
_body, _observer, _frame, {}, _currentTimeClamped, lightTime);
}
catch (const SpiceManager::SpiceException& e) {
try {
_clampedBodyPosition = SpiceManager::ref().targetPosition(
_body, _observer, _frame, {}, _currentTimeClamped, _timeRange.end);
}
catch (const SpiceManager::SpiceException& e) {
return;
}
}
// Convert from km used by SPICE to meters used by OpenSpace
_clampedBodyPosition *= 1000;
}
} // namespace openspace

View File

@@ -0,0 +1,455 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2016 *
* *
* 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/base/rendering/renderabletrailorbit.h>
#include <openspace/documentation/verifier.h>
#include <openspace/scene/translation.h>
#include <numeric>
// This class is using a VBO ring buffer + a constantly updated point as follows:
// Structure of the array with a _resolution of 16. FF denotes the floating position that
// is updated every frame:
// ---------------------------------------------------------------------------------
// | FF | | | | | | | | | | | | | | | |
// ---------------------------------------------------------------------------------
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// <------ newer in time oldest
//
// In the begining the floating value starts at 0; this means that array element 0 is
// updated and uploaded to the GPU at every frame. The FF+1 element is the newest fixed
// location and FF-1 element is the oldest fixed location (including wrapping around the
// array) with the times of _lastPointTime and _firstPointTime.
//
// If the time progresses forwards and abs(time - _lastPointTime) becomes big enough, the
// oldest point is removed and a new fixed location is added. In the ring buffer this
// would be represented as:
// ---------------------------------------------------------------------------------
// | | | | | | | | | | | | | | | | FF |
// ---------------------------------------------------------------------------------
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// <------ newer in time oldest
//
// Thus making the floating point traverse backwards through the array and element 0 being
// the newest fixed point. If the time processes backwards, the floating point moves
// towards the upper areas of the array instead.
// In both cases, only the values that have been changed will be uploaded to the GPU.
//
// For the rendering, this is achieved by using an index buffer that is twice the size of
// the vertex buffer containing identical two sequences indexing the vertex array.
// In an example of size 8:
// ---------------------------------------------------------------------------------------
// |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|
// ---------------------------------------------------------------------------------------
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
//
// The rendering step needs to know only the offset into the array (denoted by FF as the
// floating position above) and use the index array from the position. Since the indices
// in this array wrap around, so will the rendering of the vertices. Example:
// FF := 10
// Rendering 16 elements will 'generate' the index buffer:
// 10 11 12 13 14 15 00 01 02 03 04 05 06 07 08 09
//
//
// NB: This method was implemented without a ring buffer before by manually shifting the
// items in memory as was shown to be much slower than the current system. ---abock
namespace {
const char* KeyPeriod = "Period";
const char* KeyResolution = "Resolution";
}
namespace openspace {
openspace::Documentation RenderableTrailOrbit::Documentation() {
using namespace documentation;
openspace::Documentation doc{
"RenderableTrailOrbit",
"base_renderable_renderabletrailorbit",
{
{
"Type",
new StringEqualVerifier("RenderableTrailOrbit"),
"",
Optional::No
},
{
KeyPeriod,
new DoubleVerifier,
"The objects period, i.e. the length of its orbit around the parent "
"object given in (Earth) days. In the case of Earth, this would be a "
"sidereal year (=365.242 days). If this values is specified as multiples "
"of the period, it is possible to show the effects of precession.",
Optional::No
},
{
KeyResolution,
new IntVerifier,
"The number of samples along the orbit. This determines the resolution "
"of the trail; the tradeoff being that a higher resolution is able to "
"resolve more detail, but will take more resources while rendering, too. "
"The higher, the smoother the trail, but also more memory will be used.",
Optional::No
}
}
};
// Insert the parents documentation entries until we have a verifier that can deal
// with class hierarchy
openspace::Documentation parentDoc = RenderableTrail::Documentation();
doc.entries.insert(
doc.entries.end(),
parentDoc.entries.begin(),
parentDoc.entries.end()
);
return doc;
}
RenderableTrailOrbit::RenderableTrailOrbit(const ghoul::Dictionary& dictionary)
: RenderableTrail(dictionary)
, _period("period", "Period in days", 0.0, 0.0, 1e9)
, _resolution("resoluion", "Number of Samples along Orbit", 10000, 1, 1e6)
, _needsFullSweep(true)
, _indexBufferDirty(true)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableTrailOrbit"
);
// Period is in days
using namespace std::chrono;
int factor = duration_cast<seconds>(hours(24)).count();
_period = dictionary.value<double>(KeyPeriod) * factor;
_period.onChange([&] { _needsFullSweep = true; _indexBufferDirty = true; });
addProperty(_period);
_resolution = static_cast<int>(dictionary.value<double>(KeyResolution));
_resolution.onChange([&] { _needsFullSweep = true; _indexBufferDirty = true; });
addProperty(_resolution);
// We store the vertices with (excluding the wrapping) decending temporal order
_primaryRenderInformation.sorting = RenderInformation::VertexSorting::NewestFirst;
}
bool RenderableTrailOrbit::initialize() {
bool res = RenderableTrail::initialize();
glGenVertexArrays(1, &_primaryRenderInformation._vaoID);
glGenBuffers(1, &_primaryRenderInformation._vBufferID);
glGenBuffers(1, &_primaryRenderInformation._iBufferID);
return res;
}
bool RenderableTrailOrbit::deinitialize() {
glDeleteVertexArrays(1, &_primaryRenderInformation._vaoID);
glDeleteBuffers(1, &_primaryRenderInformation._vBufferID);
glDeleteBuffers(1, &_primaryRenderInformation._iBufferID);
return RenderableTrail::deinitialize();
}
void RenderableTrailOrbit::update(const UpdateData& data) {
// Overview:
// 1. Update trails
// 2. Update floating position
// 3. Determine which parts of the array to upload and upload the data
// Early bailout when we don't move in time
if (data.timePaused || data.delta == 0.0) {
return;
}
// 1
// Update the trails; the report contains whether any of the other values has been
// touched and if so, how many
UpdateReport report = updateTrails(data);
// 2
// Write the current location into the floating position
glm::vec3 p = _translation->position(data.time);
_vertexArray[_primaryRenderInformation.first] = { p.x, p.y, p.z };
glBindVertexArray(_primaryRenderInformation._vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _primaryRenderInformation._vBufferID);
// 3
if (!report.needsUpdate) {
// If no other values have been touched, we only need to upload the floating value
glBufferSubData(
GL_ARRAY_BUFFER,
_primaryRenderInformation.first * sizeof(TrailVBOLayout),
sizeof(TrailVBOLayout),
_vertexArray.data() + _primaryRenderInformation.first
);
}
else {
// Otherwise we need to check how many values have been changed
if (report.nUpdated == UpdateReport::All) {
// If all of the values have been invalidated, we need to upload the entire
// array
glBufferData(
GL_ARRAY_BUFFER,
_vertexArray.size() * sizeof(TrailVBOLayout),
_vertexArray.data(),
GL_STREAM_DRAW
);
if (_indexBufferDirty) {
// We only need to upload the index buffer if it has been invalidated
// by changing the number of values we want to represent
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _primaryRenderInformation._iBufferID);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
_indexArray.size() * sizeof(unsigned int),
_indexArray.data(),
GL_STATIC_DRAW
);
_indexBufferDirty = false;
}
}
else {
// The lambda expression that will upload parts of the array starting at
// begin and containing length number of elements
auto upload = [this](int begin, int length) {
glBufferSubData(
GL_ARRAY_BUFFER,
begin * sizeof(TrailVBOLayout),
sizeof(TrailVBOLayout) * length,
_vertexArray.data() + begin
);
};
// Only update the changed ones
// Since we are using a ring buffer, the number of updated needed might be
// bigger than our current points, which means we have to split the upload
// into two calls.
if (report.nUpdated > 0) {
// deltaT is positive, so the pointer is moving backwards and update has
// to happen towards the front
// Starting index
int i = _primaryRenderInformation.first;
// Number of values
int n = report.nUpdated + 1; // +1 for the floating position
// Total size of the array
int s = _primaryRenderInformation.count;
if (i + n <= s) {
// The current index is small enough to just use one upload call
upload(i, n);
}
else {
// The current index is too close to the wrap around part, so we need
// to split the upload into two parts:
// 1. from the current index to the end of the array
// 2. the rest starting from the beginning of the array
int first = s - i;
int second = n - first;
upload(i, first); // 1
upload(0, second); // 2
}
}
else {
// deltaT is negative, so the pointer is moving forwards
// The current index
int i = _primaryRenderInformation.first;
// Number of values
int n = report.nUpdated + 1; // +1 for the floating position
// Total size of the array
int s = _primaryRenderInformation.count;
if (i + 1 >= n) {
// The current index is big enough to fit everything into one call
upload(i+1 - n, n);
}
else {
// The current index is too close to the beginning of the array, so we
// need to split the upload into two parts:
// 1. from the beginning of the array to the current index
// 2. filling the back of the array with the rest
int b = n - (i + 1);
upload(0, i + 1); // 1
upload(s-b, b); // 2
}
}
}
}
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
}
RenderableTrailOrbit::UpdateReport RenderableTrailOrbit::updateTrails(
const UpdateData& data)
{
// If we are doing a time jump, it is in general faster to recalculate everything
// than to only update parts of the array
if (data.isTimeJump) {
_needsFullSweep = true;
}
if (_needsFullSweep) {
fullSweep(data.time);
return { true, UpdateReport::All } ;
}
// When time stands still (at the iron hill), we don't need to perform any work
if (data.delta == 0.0) {
return { false, 0 };
}
double secondsPerPoint = _period / (_resolution - 1);
// How much time has passed since the last permanent point
double delta = data.time - _lastPointTime;
// We'd like to test for equality with 0 here, but due to rounding issues, we won't
// get there. If this check is not here, we will trigger the positive or negative
// branch below even though we don't have to
//
// This might become a bigger issue if we are starting to look at very short time
// intervals
const double Epsilon = 1e-7;
if (abs(delta) < Epsilon) {
return { false, 0 };
}
if (delta > 0.0) {
// Check whether we need to drop a new permanent point. This is only the case if
// enough (> secondsPerPoint) time has passed since the last permanent point
if (abs(delta) < secondsPerPoint) {
return { false, 0 };
}
// See how many points we need to drop
int nNewPoints = floor(delta / secondsPerPoint);
// If we would need to generate more new points than there are total points in the
// array, it is faster to regenerate the entire array
if (nNewPoints >= _resolution) {
fullSweep(data.time);
return { true, UpdateReport::All };
}
for (int i = 0; i < nNewPoints; ++i) {
_lastPointTime += secondsPerPoint;
// Get the new permanent point and write it into the (previously) floating
// location
glm::vec3 p = _translation->position(_lastPointTime);
_vertexArray[_primaryRenderInformation.first] = { p.x, p.y, p.z };
// Move the current pointer back one step to be used as the new floating
// location
--_primaryRenderInformation.first;
// And loop around if necessary
if (_primaryRenderInformation.first < 0) {
_primaryRenderInformation.first += _primaryRenderInformation.count;
}
}
// The previously oldest permanent point has been moved nNewPoints steps into the
// future
_firstPointTime += nNewPoints * secondsPerPoint;
return { true, nNewPoints };
}
else {
// See how many new points needs to be generated. Delta is negative, so we need
// to invert the ratio
int nNewPoints = -(floor(delta / secondsPerPoint));
// If we would need to generate more new points than there are total points in the
// array, it is faster to regenerate the entire array
if (nNewPoints >= _resolution) {
fullSweep(data.time);
return { true, UpdateReport::All };
}
for (int i = 0; i < nNewPoints; ++i) {
_firstPointTime -= secondsPerPoint;
// Get the new permanent point and write it into the (previously) floating
// location
glm::vec3 p = _translation->position(_firstPointTime);
_vertexArray[_primaryRenderInformation.first] = { p.x, p.y, p.z };
// if we are on the upper bounds of the array, we start at 0
if (_primaryRenderInformation.first == _primaryRenderInformation.count - 1) {
// If it is at the beginning, set it to the end first
_primaryRenderInformation.first = 0;
}
else {
// Move the current pointer fowards one step to be used as the new floating
++_primaryRenderInformation.first;
}
}
// The previously youngest point has become nNewPoints steps older
_lastPointTime -= nNewPoints * secondsPerPoint;
return { true, -nNewPoints };
}
}
void RenderableTrailOrbit::fullSweep(double time) {
// Reserve the space for the vertices
_vertexArray.clear();
_vertexArray.resize(_resolution);
// The index buffer stays constant until we change the size of the array
if (_indexBufferDirty) {
// Create the index buffer and fill it with two ranges for [0, _resolution)
_indexArray.clear();
_indexArray.resize(_resolution * 2);
std::iota(_indexArray.begin(), _indexArray.begin() + _resolution, 0);
std::iota(_indexArray.begin() + _resolution, _indexArray.end(), 0);
}
_lastPointTime = time;
double secondsPerPoint = _period / (_resolution - 1);
// starting at 1 because the first position is a floating current one
for (int i = 1; i < _resolution; ++i) {
glm::vec3 p = _translation->position(time);
_vertexArray[i] = { p.x, p.y, p.z };
time -= secondsPerPoint;
}
_primaryRenderInformation.first = 0;
_primaryRenderInformation.count = _resolution;
_firstPointTime = time + secondsPerPoint;
_needsFullSweep = false;
}
} // namespace openspace

View File

@@ -0,0 +1,104 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2016 *
* *
* 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 __RENDERABLETRAILORBIT_H__
#define __RENDERABLETRAILORBIT_H__
#include <modules/base/rendering/renderabletrail.h>
#include <openspace/documentation/documentation.h>
#include <openspace/properties/scalarproperty.h>
namespace openspace {
/**
* This concrete implementation of a RenderableTrail renders an updated trail behind an
* object that is likely to have an orbit-like path. However, this is not required and
* this class can render any kind of trail as long as it's individual positions can be
* dynamically queried at runtime. This class always renders a number of points equal to
* the _resolution number. If the time progresses, old points are discarded and new points
* are rendered. Each of these fixed points are fixed time steps apart, where as the most
* current point is floating and updated every frame. The _period determines the length of
* the trail (the distance between the newest and oldest point being _period days).
*/
class RenderableTrailOrbit : public RenderableTrail {
public:
explicit RenderableTrailOrbit(const ghoul::Dictionary& dictionary);
bool initialize() override;
bool deinitialize() override;
void update(const UpdateData& data) override;
static openspace::Documentation Documentation();
private:
/**
* Performs a full sweep of the orbit and fills the entire vertex buffer object.
* \param time The current time up to which the full sweep should be performed
*/
void fullSweep(double time);
/// This structure is returned from the #updateTrails method and gives information
/// about which parts of the vertex array to update
struct UpdateReport {
static const int All = 0; ///< The entire array was touched in the update
/// If \c true at least one point was touched
bool needsUpdate;
/// Returns the number of fixed points that were touched in the update method
/// If this value is negative, the newest values were replaced, if positive the
/// oldest
int nUpdated;
};
/**
* Updates the trail based on the new incoming UpdateData information. This function
* might update an arbitrary number of values in the vertex buffer and returns an
* UpdateReport that will tell the #update method how many values were modified.
* \param data The UpdateData struct that comes from the #update method
* \return The UpdateReport containing information which array parts were touched
*/
UpdateReport updateTrails(const UpdateData& data);
/// The orbital period of the RenderableTrail in days
properties::DoubleProperty _period;
/// The number of points that should be sampled between _period and now
properties::IntProperty _resolution;
/// A dirty flag that determines whether a full sweep (recomputing of all values)
/// is necessary
bool _needsFullSweep;
/// A dirty flag to determine whether the index buffer needs to be regenerated and
/// then reuploaded
bool _indexBufferDirty;
/// The time stamp of the oldest point in the array
double _firstPointTime;
/// The time stamp of the newest fixed point in the array
double _lastPointTime;
};
} // namespace openspace
#endif // __RENDERABLETRAILORBIT_H__

View File

@@ -0,0 +1,314 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2016 *
* *
* 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/base/rendering/renderabletrailtrajectory.h>
#include <openspace/documentation/verifier.h>
#include <openspace/scene/translation.h>
#include <openspace/util/spicemanager.h>
// This class creates the entire trajectory at once and keeps it in memory the entire
// time. This means that there is no need for updating the trail at runtime, but also that
// the whole trail has to fit in memory.
// Opposed to the RenderableTrailOrbit, no index buffer is needed as the vertex can be
// written into the vertex buffer object continuously and then selected by using the
// count variable from the RenderInformation struct to toggle rendering of the entire path
// or subpath.
// In addition, this RenderableTrail implementation uses an additional RenderInformation
// bucket that contains the line from the last shown point to the current location of the
// object iff not the entire path is shown and the object is between _startTime and
// _endTime. This buffer is updated every frame.
namespace {
const char* KeyTranslation = "Translation";
const char* KeyStartTime = "StartTime";
const char* KeyEndTime = "EndTime";
const char* KeySampleInterval = "SampleInterval";
const char* KeyTimeStampSubsample = "TimeStampSubsampleFactor";
const char* KeyShowFullTrail = "ShowFullTrail";
}
namespace openspace {
openspace::Documentation RenderableTrailTrajectory::Documentation() {
using namespace documentation;
openspace::Documentation doc {
"RenderableTrailTrajectory",
"base_renderable_renderabletrailtrajectory",
{
{
"Type",
new StringEqualVerifier("RenderableTrailTrajectory"),
"",
Optional::No
},
{
KeyStartTime,
new StringAnnotationVerifier("A valid date"),
"The start time for the range of this trajectory. The date must be in "
"ISO 8601 format: YYYY MM DD HH:mm:ss.xxx",
Optional::No
},
{
KeyEndTime,
new StringAnnotationVerifier("A valid date"),
"The end time for the range of this trajectory. The date must be in "
"ISO 8601 format: YYYY MM DD HH:mm:ss.xxx",
Optional::No
},
{
KeySampleInterval,
new DoubleVerifier,
"The interval between samples of the trajectory. This value (together "
"with 'TimeStampSubsampleFactor') determines how far apart (in time) the "
"samples are spaced along the trajectory. The time interval between "
"'StartTime' and 'EndTime' is split into 'SampleInterval' * "
"'TimeStampSubsampleFactor' segments.",
Optional::No
},
{
KeyTimeStampSubsample,
new IntVerifier,
"The factor that is used to create subsamples along the trajectory. This "
"value (together with 'SampleInterval') determines how far apart (in "
"time) the samples are spaced along the trajectory. The time interval "
"between 'StartTime' and 'EndTime' is split into 'SampleInterval' * "
"'TimeStampSubsampleFactor' segments. The default value for this is 1",
Optional::Yes
},
{
KeyShowFullTrail,
new BoolVerifier,
"If this value is set to 'true', the entire trail will be rendered; if "
"it is 'false', only the trail until the current time in the application "
"will be shown. The default value for this setting is 'false'.",
Optional::Yes
}
}
};
// Insert the parents documentation entries until we have a verifier that can deal
// with class hierarchy
openspace::Documentation parentDoc = RenderableTrail::Documentation();
doc.entries.insert(
doc.entries.end(),
parentDoc.entries.begin(),
parentDoc.entries.end()
);
return doc;
}
RenderableTrailTrajectory::RenderableTrailTrajectory(const ghoul::Dictionary& dictionary)
: RenderableTrail(dictionary)
, _startTime("startTime", "Start Time")
, _endTime("endTime", "End Time")
, _sampleInterval("sampleInterval", "Sample Interval", 2.0, 2.0, 1e6)
, _timeStampSubsamplingFactor(
"subSample",
"Time Stamp Subsampling Factor",
1, 1, 1e9
)
, _renderFullTrail("renderFullTrail", "Render Full Trail", false)
, _needsFullSweep(true)
, _subsamplingIsDirty(true)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableTrailTrajectory"
);
_startTime = dictionary.value<std::string>(KeyStartTime);
_startTime.onChange([this] { _needsFullSweep = true; });
addProperty(_startTime);
_endTime = dictionary.value<std::string>(KeyEndTime);
_endTime.onChange([this] { _needsFullSweep = true; });
addProperty(_endTime);
_sampleInterval = dictionary.value<double>(KeySampleInterval);
_sampleInterval.onChange([this] { _needsFullSweep = true; });
addProperty(_sampleInterval);
if (dictionary.hasKeyAndValue<double>(KeyTimeStampSubsample)) {
_timeStampSubsamplingFactor = dictionary.value<double>(KeyTimeStampSubsample);
}
_timeStampSubsamplingFactor.onChange([this] { _subsamplingIsDirty = true; });
addProperty(_timeStampSubsamplingFactor);
if (dictionary.hasKeyAndValue<bool>(KeyShowFullTrail)) {
_renderFullTrail = dictionary.value<bool>(KeyShowFullTrail);
}
addProperty(_renderFullTrail);
// We store the vertices with ascending temporal order
_primaryRenderInformation.sorting = RenderInformation::VertexSorting::OldestFirst;
}
bool RenderableTrailTrajectory::initialize() {
bool res = RenderableTrail::initialize();
// We don't need an index buffer, so we keep it at the default value of 0
glGenVertexArrays(1, &_primaryRenderInformation._vaoID);
glGenBuffers(1, &_primaryRenderInformation._vBufferID);
// We do need an additional render information bucket for the additional line from the
// last shown permanent line to the current position of the object
glGenVertexArrays(1, &_floatingRenderInformation._vaoID);
glGenBuffers(1, &_floatingRenderInformation._vBufferID);
_floatingRenderInformation.sorting = RenderInformation::VertexSorting::NoSorting;
return res;
}
bool RenderableTrailTrajectory::deinitialize() {
glDeleteVertexArrays(1, &_primaryRenderInformation._vaoID);
glDeleteBuffers(1, &_primaryRenderInformation._vBufferID);
glDeleteVertexArrays(1, &_floatingRenderInformation._vaoID);
glDeleteBuffers(1, &_floatingRenderInformation._vBufferID);
return RenderableTrail::deinitialize();
}
void RenderableTrailTrajectory::update(const UpdateData& data) {
if (_needsFullSweep) {
// Convert the start and end time from string representations to J2000 seconds
_start = SpiceManager::ref().ephemerisTimeFromDate(_startTime);
_end = SpiceManager::ref().ephemerisTimeFromDate(_endTime);
double totalSampleInterval = _sampleInterval / _timeStampSubsamplingFactor;
// How many values do we need to compute given the distance between the start and
// end date and the desired sample interval
int nValues = (_end - _start) / totalSampleInterval;
// Make space for the vertices
_vertexArray.clear();
_vertexArray.resize(nValues);
// ... fill all of the values
for (int i = 0; i < nValues; ++i) {
glm::vec3 p = _translation->position(_start + i * totalSampleInterval);
_vertexArray[i] = { p.x, p.y, p.z };
}
// ... and upload them to the GPU
glBindVertexArray(_primaryRenderInformation._vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _primaryRenderInformation._vBufferID);
glBufferData(
GL_ARRAY_BUFFER,
_vertexArray.size() * sizeof(TrailVBOLayout),
_vertexArray.data(),
GL_STATIC_DRAW
);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
// We clear the indexArray just in case. The base class will take care not to use
// it if it is empty
_indexArray.clear();
_subsamplingIsDirty = true;
_needsFullSweep = false;
}
// This has to be done every update step;
if (_renderFullTrail) {
// If the full trail should be rendered at all times, we can directly render the
// entire set
_primaryRenderInformation.first = 0;
_primaryRenderInformation.count = _vertexArray.size();
}
else {
// If only trail so far should be rendered, we need to find the corresponding time
// in the array and only render it until then
_primaryRenderInformation.first = 0;
double t = (data.time - _start) / (_end - _start);
_primaryRenderInformation.count = std::min<GLsizei>(
ceil(_vertexArray.size() * t),
_vertexArray.size() - 1
);
}
// If we are inside the valid time, we additionally want to draw a line from the last
// correct point to the current location of the object
if (data.time >= _start && data.time <= _end && !_renderFullTrail) {
// Copy the last valid location
glm::dvec3 v0(
_vertexArray[_primaryRenderInformation.count - 1].x,
_vertexArray[_primaryRenderInformation.count - 1].y,
_vertexArray[_primaryRenderInformation.count - 1].z
);
// And get the current location of the object
glm::dvec3 p = _translation->position(data.time);
glm::dvec3 v1 = { p.x, p.y, p.z };
// Comptue the difference between the points in double precision
glm::dvec3 p0 = v0 - v1;
_auxiliaryVboData[0] = {
static_cast<float>(p0.x),
static_cast<float>(p0.y),
static_cast<float>(p0.z)
};
_auxiliaryVboData[1] = { 0.f, 0.f, 0.f };
// Fill the render info with the data
_floatingRenderInformation.first = 0;
_floatingRenderInformation.count = 2;
_floatingRenderInformation._localTransform = glm::translate(glm::dmat4(1.0), v1);
glBindVertexArray(_floatingRenderInformation._vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _floatingRenderInformation._vBufferID);
glBufferData(
GL_ARRAY_BUFFER,
_auxiliaryVboData.size() * sizeof(TrailVBOLayout),
_auxiliaryVboData.data(),
GL_DYNAMIC_DRAW
);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
else {
// if we are outside of the valid range, we don't render anything
_floatingRenderInformation.first = 0;
_floatingRenderInformation.count = 0;
}
if (_subsamplingIsDirty) {
// If the subsampling information has changed (either by a property change or by
// a request of a full sweep) we update it here
_primaryRenderInformation.stride = _timeStampSubsamplingFactor;
_floatingRenderInformation.stride = _timeStampSubsamplingFactor;
_subsamplingIsDirty = false;
}
glBindVertexArray(0);
}
} // namespace openspace

View File

@@ -22,95 +22,66 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __RENDERABLETRAILNEW_H__
#define __RENDERABLETRAILNEW_H__
#ifndef __RENDERABLETRAILTRAJECTORY_H__
#define __RENDERABLETRAILTRAJECTORY_H__
#include <openspace/rendering/renderable.h>
#include <modules/base/rendering/renderabletrail.h>
#include <openspace/documentation/documentation.h>
#include <openspace/properties/scalarproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/vectorproperty.h>
#include <openspace/util/timerange.h>
#include <ghoul/opengl/ghoul_gl.h>
namespace ghoul {
namespace opengl {
class ProgramObject;
class Texture;
}
}
namespace openspace {
/**
* This class currently has a temporary name until it is merged with or replaces
* RenderableTrail. It renders the trail with higher precision close to the
* position of the body of a renderable. The lua mod dictionary describing the
* renderable trail is changed.
*/
class RenderableTrailNew : public Renderable {
* This concrete implementation of a RenderableTrail renders a fixed trail, regardless of
* its shape. The trail is sampled equitemporal (with an interval of _sampleInterval in
* seconds) between the _startTime and the _endTime. No further update is needed until any
* of these three values is changed. If _renderFullTrail is true, the entirety of the
* trail is rendered, regardless of the simulation time. If it is false, the trail is only
* rendered from the past to the current simulation time, not showing any part of the
* trail in the future. If _renderFullTrail is false, the current position of the object
* has to be updated constantly to make the trail connect to the object that has the
* trail.
*/
class RenderableTrailTrajectory : public RenderableTrail {
public:
explicit RenderableTrailNew(const ghoul::Dictionary& dictionary);
explicit RenderableTrailTrajectory(const ghoul::Dictionary& dictionary);
bool initialize() override;
bool deinitialize() override;
bool isReady() const override;
void render(const RenderData& data) override;
void update(const UpdateData& data) override;
static openspace::Documentation Documentation();
private:
void sweepTimeRange();
/// The start time of the trail
properties::StringProperty _startTime;
/// The end time of the trail
properties::StringProperty _endTime;
/// The interval (in seconds) between sample points
properties::DoubleProperty _sampleInterval;
/// The factor that determines the time stamp subsampling, using different sized
/// points along the trajectory
properties::IntProperty _timeStampSubsamplingFactor;
/// Determines whether the full trail should be rendered or the future trail removed
properties::BoolProperty _renderFullTrail;
void initializeGlobalOpenGLPathData();
void initializeLocalOpenGLPathData();
/// Dirty flag that determines whether the full vertex buffer needs to be resampled
bool _needsFullSweep;
void deInitializeGlobalOpenGLPathData();
void deInitializeLocalOpenGLPathData();
/// Dirty flag to determine whether the stride information needs to be changed
bool _subsamplingIsDirty;
void preRender(int totalNumVerticesToDraw);
void preRenderSubPathGlobally(const RenderData& renderData);
void preRenderSubPathLocally(const RenderData& renderData, int totalNumVerticesToDraw);
std::array<TrailVBOLayout, 2> _auxiliaryVboData;
void renderLines(GLuint vao, int numberOfVertices);
void renderPoints(GLuint vao, int numberOfVertices);
// Spice
std::string _body;
std::string _observer;
std::string _frame;
// Properties
properties::Vec3Property _lineColor;
properties::Vec3Property _pointColor;
properties::FloatProperty _lineFade;
properties::FloatProperty _lineWidth;
properties::FloatProperty _renderPart;
properties::BoolProperty _showTimeStamps;
properties::BoolProperty _renderFullTrail;
// OpenGL
GLuint _vaoGlobalID;
GLuint _vBufferGlobalID;
GLuint _vaoLocalID;
GLuint _vBufferLocalID;
// Other
bool _successfullDictionaryFetch;
TimeRange _timeRange;
double _sampleDeltaTime;
int _subSamples;
std::unique_ptr<ghoul::opengl::ProgramObject> _programObject;
std::vector<glm::vec3> _vertexPositionArray;
// Data updated in update function
double _currentTimeClamped; // Time clamped to time range
glm::dmat4 _modelTransform;
glm::dvec3 _clampedBodyPosition; // Position of body clamped to time range
/// The conversion of the _startTime into the internal time format
double _start;
/// The conversion of the _endTime into the internal time format
double _end;
};
} // namespace openspace
#endif // __RENDERABLETRAILNEW_H__
#endif // __RENDERABLETRAILTRAJECTORY_H__