mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-03-03 10:58:34 -06:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
455
modules/base/rendering/renderabletrailorbit.cpp
Normal file
455
modules/base/rendering/renderabletrailorbit.cpp
Normal 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
|
||||
104
modules/base/rendering/renderabletrailorbit.h
Normal file
104
modules/base/rendering/renderabletrailorbit.h
Normal 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__
|
||||
314
modules/base/rendering/renderabletrailtrajectory.cpp
Normal file
314
modules/base/rendering/renderabletrailtrajectory.cpp
Normal 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
|
||||
@@ -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__
|
||||
Reference in New Issue
Block a user