Merge branch 'master' into feature/model-animation

* Resolve conflicts
This commit is contained in:
Malin Ejdbo
2021-03-30 16:31:25 +02:00
71 changed files with 2203 additions and 3431 deletions

2
Jenkinsfile vendored
View File

@@ -107,7 +107,7 @@ linux_gcc_make: {
}
stage('linux-gcc-make/build') {
def cmakeCompileOptions = moduleCMakeFlags();
cmakeCompileOptions += ' -DMAKE_BUILD_TYPE=Release';
cmakeCompileOptions += ' -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS:STRING="-DGLM_ENABLE_EXPERIMENTAL" -DOpenGL_GL_PREFERENCE:STRING=GLVND';
// Not sure why the linking of OpenSpaceTest takes so long
compileHelper.build(compileHelper.Make(), compileHelper.Gcc(), cmakeCompileOptions, 'OpenSpace', 'build-make');
compileHelper.recordCompileIssues(compileHelper.Gcc());

View File

@@ -348,8 +348,8 @@ local Rex = {
Target = "EARTH",
Instrument = "NH_REX",
Color = {
Start = { 1.0, 0.7, 0.0, 1.0},
End = {0.0, 0.0, 0.0, 0.0 }
Start = { 1.0, 0.7, 0.0, 1.0 },
End = { 0.0, 0.0, 0.0, 0.0 }
}
},
Transform = {

View File

@@ -45,17 +45,27 @@ template <typename T>
TestResult TemplateVerifier<T>::operator()(const ghoul::Dictionary& dict,
const std::string& key) const
{
TestResult res;
if (dict.hasValue<Type>(key)) {
return { true, {}, {} };
res.success = true;
}
else {
res.success = false;
if (dict.hasKey(key)) {
return { false, { { key, TestResult::Offense::Reason::WrongType } }, {} };
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
}
else {
return { false, { { key, TestResult::Offense::Reason::MissingKey } }, {} };
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
}
}
return res;
}
template <typename T>
@@ -143,7 +153,13 @@ TestResult OperatorVerifier<T, Operator>::operator()(const ghoul::Dictionary& di
val = static_cast<int>(d);
}
else {
return { false, { { key, TestResult::Offense::Reason::WrongType } }, {} };
TestResult r;
r.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
r.offenses.push_back(o);
return r;
}
}
else {
@@ -153,7 +169,13 @@ TestResult OperatorVerifier<T, Operator>::operator()(const ghoul::Dictionary& di
return { true, {}, {} };
}
else {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult r;
r.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
r.offenses.push_back(o);
return r;
}
}
else {
@@ -210,7 +232,13 @@ TestResult InListVerifier<T>::operator()(const ghoul::Dictionary& dict,
return { true, {}, {} };
}
else {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult r;
r.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
r.offenses.push_back(o);
return r;
}
}
else {
@@ -256,7 +284,13 @@ TestResult NotInListVerifier<T>::operator()(const ghoul::Dictionary& dict,
return { true, {}, {} };
}
else {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult r;
r.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
r.offenses.push_back(o);
return r;
}
}
else {
@@ -306,7 +340,13 @@ TestResult InRangeVerifier<T>::operator()(const ghoul::Dictionary& dict,
val = static_cast<int>(d);
}
else {
return { false, { { key, TestResult::Offense::Reason::WrongType } }, {} };
TestResult r;
r.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
r.offenses.push_back(o);
return r;
}
}
else {
@@ -317,7 +357,13 @@ TestResult InRangeVerifier<T>::operator()(const ghoul::Dictionary& dict,
return { true, {}, {} };
}
else {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult r;
r.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
r.offenses.push_back(o);
return r;
}
}
else {
@@ -353,7 +399,13 @@ TestResult NotInRangeVerifier<T>::operator()(const ghoul::Dictionary& dict,
val = static_cast<int>(d);
}
else {
return { false, { { key, TestResult::Offense::Reason::WrongType } }, {} };
TestResult r;
r.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
r.offenses.push_back(o);
return r;
}
}
else {
@@ -361,7 +413,13 @@ TestResult NotInRangeVerifier<T>::operator()(const ghoul::Dictionary& dict,
}
if (val >= lower && val <= upper) {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult r;
r.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
r.offenses.push_back(o);
return r;
}
else {
return { true, {}, {} };
@@ -396,9 +454,10 @@ TestResult DeprecatedVerifier<T>::operator()(const ghoul::Dictionary& dict,
const std::string& key) const
{
TestResult res = T::operator()(dict, key);
res.warnings.push_back(
TestResult::Warning{ key, TestResult::Warning::Reason::Deprecated }
);
TestResult::Warning w;
w.offender = key;
w.reason = TestResult::Warning::Reason::Deprecated;
res.warnings.push_back(w);
return res;
}

View File

@@ -53,4 +53,4 @@ protected:
} // openspace
#endif // __OPENSPACE_CORE___DASHBOARDITEM___H__
#endif // __OPENSPACE_CORE___DASHBOARDTEXTITEM___H__

View File

@@ -47,6 +47,8 @@ struct LuaLibrary;
class ScriptScheduler {
public:
struct ScheduledScript {
ScheduledScript() = default;
ScheduledScript(const ghoul::Dictionary& dict);
double time = -std::numeric_limits<double>::max();
std::string forwardScript;

View File

@@ -179,7 +179,8 @@ namespace {
// A list of objects that cast light on this atmosphere
std::vector<CasterElement> casters;
};
// Declares shadow groups, meaning which nodes are considered in shadow calculations
// Declares shadow groups, meaning which nodes are considered in shadow
// calculations
std::optional<ShadowGroup> shadowGroup;
// [[codegen::verbatim(AtmosphereHeightInfo.description)]]
@@ -360,7 +361,7 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary)
_mieScatteringExtinctionPropCoefficient.onChange(updateWithCalculation);
addProperty(_mieScatteringExtinctionPropCoefficient);
if (p.debug.has_value()) {
_preCalculatedTexturesScale =
p.debug->preCalculatedTextureScale.value_or(_preCalculatedTexturesScale);

View File

@@ -55,7 +55,7 @@ namespace {
struct [[codegen::Dictionary(DashboardItemDate)]] Parameters {
// [[codegen::verbatim(FormatStringInfo.description)]]
std::optional<std::string> formatString;
// [[codegen::verbatim(TimeFormatInfo.description)]]
std::optional<std::string> timeFormat;
};

View File

@@ -132,7 +132,8 @@ namespace {
DtAvg [[codegen::key("Average Deltatime")]],
DtExtremes [[codegen::key("Deltatime extremes")]],
DtStandardDeviation [[codegen::key("Deltatime standard deviation")]],
DtCoefficientOfVariation [[codegen::key("Deltatime coefficient of variation")]],
DtCoefficientOfVariation
[[codegen::key("Deltatime coefficient of variation")]],
FPS [[codegen::key("Frames per second")]],
FPSAvg [[codegen::key("Average frames per second")]]
};
@@ -187,7 +188,8 @@ DashboardItemFramerate::DashboardItemFramerate(const ghoul::Dictionary& dictiona
_frametimeType = static_cast<int>(FrametimeType::DtStandardDeviation);
break;
case Parameters::Type::DtCoefficientOfVariation:
_frametimeType = static_cast<int>(FrametimeType::DtCoefficientOfVariation);
_frametimeType =
static_cast<int>(FrametimeType::DtCoefficientOfVariation);
break;
case Parameters::Type::FPS:
_frametimeType = static_cast<int>(FrametimeType::FPS);

View File

@@ -30,6 +30,7 @@
#include <openspace/util/updatestructures.h>
#include <ghoul/opengl/programobject.h>
#include <numeric>
#include <optional>
// 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
@@ -104,36 +105,32 @@ namespace {
"Opaque, Transparent, or Overlay rendering step. Default is Transparent."
};
struct [[codegen::Dictionary(RenderableTrailOrbit)]] Parameters {
// [[codegen::verbatim(PeriodInfo.description)]]
double period;
// [[codegen::verbatim(ResolutionInfo.description)]]
int resolution;
enum class RenderableType {
Background,
Opaque,
PreDeferredTransparent,
PostDeferredTransparent,
Overlay
};
// [[codegen::verbatim(RenderableTypeInfo.description)]]
std::optional<RenderableType> renderableType;
};
#include "renderabletrailorbit_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderableTrailOrbit::Documentation() {
using namespace documentation;
documentation::Documentation doc {
"RenderableTrailOrbit",
"base_renderable_renderabletrailorbit",
{
{
PeriodInfo.identifier,
new DoubleVerifier,
Optional::No,
PeriodInfo.description
},
{
ResolutionInfo.identifier,
new IntVerifier,
Optional::No,
ResolutionInfo.description
},
{
RenderableTypeInfo.identifier,
new StringVerifier,
Optional::Yes,
RenderableTypeInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "base_renderable_renderabletrailorbit";
// Insert the parents documentation entries until we have a verifier that can deal
// with class hierarchy
@@ -152,46 +149,42 @@ RenderableTrailOrbit::RenderableTrailOrbit(const ghoul::Dictionary& dictionary)
, _period(PeriodInfo, 0.0, 0.0, 1e9)
, _resolution(ResolutionInfo, 10000, 1, 1000000)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableTrailOrbit"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_translation->onParameterChange([this]() { _needsFullSweep = true; });
// Period is in days
using namespace std::chrono;
const long long sph = duration_cast<seconds>(hours(24)).count();
_period = dictionary.value<double>(PeriodInfo.identifier) * sph;
_period = p.period * duration_cast<seconds>(hours(24)).count();
_period.onChange([&] { _needsFullSweep = true; _indexBufferDirty = true; });
addProperty(_period);
_resolution = static_cast<int>(dictionary.value<double>(ResolutionInfo.identifier));
_resolution = p.resolution;
_resolution.onChange([&] { _needsFullSweep = true; _indexBufferDirty = true; });
addProperty(_resolution);
// We store the vertices with (excluding the wrapping) decending temporal order
_primaryRenderInformation.sorting = RenderInformation::VertexSorting::NewestFirst;
if (dictionary.hasKey(RenderableTypeInfo.identifier)) {
std::string renderType = dictionary.value<std::string>(
RenderableTypeInfo.identifier
);
if (renderType == "Background") {
setRenderBin(Renderable::RenderBin::Background);
}
else if (renderType == "Opaque") {
setRenderBin(Renderable::RenderBin::Opaque);
}
else if (renderType == "PreDeferredTransparent") {
setRenderBin(Renderable::RenderBin::PreDeferredTransparent);
}
else if (renderType == "PostDeferredTransparent") {
setRenderBin(Renderable::RenderBin::PostDeferredTransparent);
}
else if (renderType == "Overlay") {
setRenderBin(Renderable::RenderBin::Overlay);
if (p.renderableType.has_value()) {
switch (*p.renderableType) {
case Parameters::RenderableType::Background:
setRenderBin(Renderable::RenderBin::Background);
break;
case Parameters::RenderableType::Opaque:
setRenderBin(Renderable::RenderBin::Opaque);
break;
case Parameters::RenderableType::PreDeferredTransparent:
setRenderBin(Renderable::RenderBin::PreDeferredTransparent);
break;
case Parameters::RenderableType::PostDeferredTransparent:
setRenderBin(Renderable::RenderBin::PostDeferredTransparent);
break;
case Parameters::RenderableType::Overlay:
setRenderBin(Renderable::RenderBin::Overlay);
break;
default:
throw ghoul::MissingCaseException();
}
}
else {

View File

@@ -29,6 +29,7 @@
#include <openspace/scene/translation.h>
#include <openspace/util/spicemanager.h>
#include <openspace/util/updatestructures.h>
#include <optional>
// 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
@@ -81,49 +82,31 @@ namespace {
"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."
};
struct [[codegen::Dictionary(RenderableTrailTrajectory)]] Parameters {
// [[codegen::verbatim(StartTimeInfo.description)]]
std::string startTime [[codegen::annotation("A valid date in ISO 8601 format")]];
// [[codegen::verbatim(EndTimeInfo.description)]]
std::string endTime [[codegen::annotation("A valid date in ISO 8601 format")]];
// [[codegen::verbatim(SampleIntervalInfo.description)]]
double sampleInterval;
// [[codegen::verbatim(TimeSubSampleInfo.description)]]
std::optional<int> timeStampSubsampleFactor;
// [[codegen::verbatim(RenderFullPathInfo.description)]]
std::optional<bool> showFullTrail;
};
#include "renderabletrailtrajectory_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderableTrailTrajectory::Documentation() {
using namespace documentation;
documentation::Documentation doc {
"RenderableTrailTrajectory",
"base_renderable_renderabletrailtrajectory",
{
{
StartTimeInfo.identifier,
new StringAnnotationVerifier("A valid date in ISO 8601 format"),
Optional::No,
StartTimeInfo.description
},
{
EndTimeInfo.identifier,
new StringAnnotationVerifier("A valid date in ISO 8601 format"),
Optional::No,
EndTimeInfo.description
},
{
SampleIntervalInfo.identifier,
new DoubleVerifier,
Optional::No,
SampleIntervalInfo.description
},
{
TimeSubSampleInfo.identifier,
new IntVerifier,
Optional::Yes,
TimeSubSampleInfo.description
},
{
RenderFullPathInfo.identifier,
new BoolVerifier,
Optional::Yes,
RenderFullPathInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "base_renderable_renderabletrailtrajectory";
// @TODO cleanup
// Insert the parents documentation entries until we have a verifier that can deal
@@ -146,37 +129,28 @@ RenderableTrailTrajectory::RenderableTrailTrajectory(const ghoul::Dictionary& di
, _timeStampSubsamplingFactor(TimeSubSampleInfo, 1, 1, 1000000000)
, _renderFullTrail(RenderFullPathInfo, false)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableTrailTrajectory"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_translation->onParameterChange([this]() { _needsFullSweep = true; });
_startTime = dictionary.value<std::string>(StartTimeInfo.identifier);
_startTime = p.startTime;
_startTime.onChange([this] { _needsFullSweep = true; });
addProperty(_startTime);
_endTime = dictionary.value<std::string>(EndTimeInfo.identifier);
_endTime = p.endTime;
_endTime.onChange([this] { _needsFullSweep = true; });
addProperty(_endTime);
_sampleInterval = dictionary.value<double>(SampleIntervalInfo.identifier);
_sampleInterval = p.sampleInterval;
_sampleInterval.onChange([this] { _needsFullSweep = true; });
addProperty(_sampleInterval);
if (dictionary.hasValue<double>(TimeSubSampleInfo.identifier)) {
_timeStampSubsamplingFactor = static_cast<int>(
dictionary.value<double>(TimeSubSampleInfo.identifier)
);
}
_timeStampSubsamplingFactor =
p.timeStampSubsampleFactor.value_or(_timeStampSubsamplingFactor);
_timeStampSubsamplingFactor.onChange([this] { _subsamplingIsDirty = true; });
addProperty(_timeStampSubsamplingFactor);
if (dictionary.hasValue<bool>(RenderFullPathInfo.identifier)) {
_renderFullTrail = dictionary.value<bool>(RenderFullPathInfo.identifier);
}
_renderFullTrail = p.showFullTrail.value_or(_renderFullTrail);
addProperty(_renderFullTrail);
// We store the vertices with ascending temporal order

View File

@@ -185,7 +185,7 @@ namespace {
struct [[codegen::Dictionary(FixedRotation)]] Parameters {
// This value specifies the direction of the new X axis. If this value is not
// specified, it will be computed by completing a right handed coordinate system
// from the Y and Z axis, which must be specified instead. If this value is a
// from the Y and Z axis, which must be specified instead. If this value is a
// string, it is interpreted as the identifier of another scenegraph node. If this
// value is a 3-vector, it is interpreted as a direction vector
std::optional<std::variant<std::string, glm::vec3>> xAxis;

View File

@@ -30,48 +30,32 @@
#include <openspace/util/time.h>
namespace {
constexpr const char* KeyKeyframes = "Keyframes";
struct [[codegen::Dictionary(TimelineRotation)]] Parameters {
// A table of keyframes, with keys formatted as YYYY-MM-DDTHH:MM:SS and values
// that are valid Rotation objects
std::map<std::string, ghoul::Dictionary> keyframes
[[codegen::reference("core_transform_rotation")]];
};
#include "timelinerotation_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation TimelineRotation::Documentation() {
using namespace documentation;
return {
"Timeline Rotation",
"base_transform_rotation_keyframe",
{
{
KeyKeyframes,
new TableVerifier({
{ "*", new TableVerifier(), Optional::No, "Any translation object" }
}),
Optional::No,
"A table of keyframes, with keys formatted as YYYY-MM-DDTHH:MM:SS"
"and values that are valid Rotation objects."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "base_transform_rotation_keyframe";
return doc;
}
TimelineRotation::TimelineRotation(const ghoul::Dictionary& dictionary) {
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"TimelineTranslation"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
const ghoul::Dictionary& keyframes =
dictionary.value<ghoul::Dictionary>(KeyKeyframes);
for (std::string_view timeString : keyframes.keys()) {
const double t = Time::convertTime(std::string(timeString));
ghoul::mm_unique_ptr<Rotation> rotation =
Rotation::createFromDictionary(
keyframes.value<ghoul::Dictionary>(timeString)
);
for (const std::pair<const std::string, ghoul::Dictionary>& kf : p.keyframes) {
const double t = Time::convertTime(kf.first);
ghoul::mm_unique_ptr<Rotation> rotation = Rotation::createFromDictionary(
kf.second
);
if (rotation) {
_timeline.addKeyframe(t, std::move(rotation));
}

View File

@@ -30,48 +30,31 @@
#include <openspace/util/time.h>
namespace {
constexpr const char* KeyKeyframes = "Keyframes";
struct [[codegen::Dictionary(TimelineTranslation)]] Parameters {
// A table of keyframes, with keys formatted as YYYY-MM-DDTHH:MM:SS and values
// that are valid Translation objects
std::map<std::string, ghoul::Dictionary> keyframes
[[codegen::reference("core_transform_translation")]];
};
#include "timelinetranslation_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation TimelineTranslation::Documentation() {
using namespace documentation;
return {
"Timeline Translation",
"base_transform_translation_keyframe",
{
{
KeyKeyframes,
new TableVerifier({
{ "*", new TableVerifier(), Optional::No, "Any translation object" }
}),
Optional::No,
"A table of keyframes, with keys formatted as YYYY-MM-DDTHH:MM:SS"
"and values that are valid Translation objects."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "base_transform_translation_keyframe";
return doc;
}
TimelineTranslation::TimelineTranslation(const ghoul::Dictionary& dictionary) {
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"TimelineTranslation"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
const ghoul::Dictionary& keyframes =
dictionary.value<ghoul::Dictionary>(KeyKeyframes);
for (std::string_view timeString : keyframes.keys()) {
const double t = Time::convertTime(std::string(timeString));
for (const std::pair<const std::string, ghoul::Dictionary>& kf : p.keyframes) {
const double t = Time::convertTime(kf.first);
ghoul::mm_unique_ptr<Translation> translation =
Translation::createFromDictionary(
keyframes.value<ghoul::Dictionary>(timeString)
);
Translation::createFromDictionary(kf.second);
if (translation) {
_timeline.addKeyframe(t, std::move(translation));
}

View File

@@ -37,6 +37,7 @@
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <optional>
namespace {
enum Origin {
@@ -72,47 +73,38 @@ namespace {
"Texture Coordinate Origin",
"The origin of the texture coorinate system."
};
struct [[codegen::Dictionary(RenderableDebugPlane)]] Parameters {
// [[codegen::verbatim(TextureInfo.description)]]
std::optional<int> texture;
// [[codegen::verbatim(BillboardInfo.description)]]
std::optional<bool> billboard;
// [[codegen::verbatim(SizeInfo.description)]]
std::optional<float> size;
enum class Origin {
LowerLeft,
LowerRight,
UpperLeft,
UpperRight,
Center
};
// [[codegen::verbatim(OriginInfo.description)]]
std::optional<Origin> origin;
};
#include "renderabledebugplane_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderableDebugPlane::Documentation() {
using namespace documentation;
return {
"RenderableDebugPlane",
"debugging_renderable_debugplane",
{
{
TextureInfo.identifier,
new IntVerifier,
Optional::Yes,
TextureInfo.description
},
{
BillboardInfo.identifier,
new BoolVerifier,
Optional::Yes,
BillboardInfo.description
},
{
SizeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
SizeInfo.description
},
{
OriginInfo.identifier,
new StringInListVerifier(
{ "LowerLeft", "LowerRight", "UpperLeft", "UpperRight", "Center" }
),
Optional::Yes,
OriginInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "debugging_renderable_debugplane";
return doc;
}
RenderableDebugPlane::RenderableDebugPlane(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _texture(TextureInfo, -1, -1, 512)
@@ -120,17 +112,18 @@ RenderableDebugPlane::RenderableDebugPlane(const ghoul::Dictionary& dictionary)
, _size(SizeInfo, 10.f, 0.f, 1e25f)
, _origin(OriginInfo, properties::OptionProperty::DisplayType::Dropdown)
{
if (dictionary.hasKey(TextureInfo.identifier)) {
_texture = static_cast<int>(dictionary.value<double>(TextureInfo.identifier));
}
const Parameters p = codegen::bake<Parameters>(dictionary);
if (dictionary.hasKey(SizeInfo.identifier)) {
_size = static_cast<float>(dictionary.value<double>(SizeInfo.identifier));
}
if (dictionary.hasKey(BillboardInfo.identifier)) {
_billboard = dictionary.value<bool>(BillboardInfo.identifier);
}
_texture = p.texture.value_or(_texture);
addProperty(_texture);
_size.onChange([this](){ _planeIsDirty = true; });
_size = p.size.value_or(_size);
setBoundingSphere(_size);
addProperty(_size);
_billboard = p.billboard.value_or(_billboard);
addProperty(_billboard);
_origin.addOptions({
{ LowerLeft, "LowerLeft" },
@@ -141,36 +134,30 @@ RenderableDebugPlane::RenderableDebugPlane(const ghoul::Dictionary& dictionary)
});
_origin.setValue(Center);
if (dictionary.hasKey(OriginInfo.identifier)) {
const std::string origin = dictionary.value<std::string>(OriginInfo.identifier);
if (origin == "LowerLeft") {
_origin = LowerLeft;
}
else if (origin == "LowerRight") {
_origin = LowerRight;
}
else if (origin == "UpperLeft") {
_origin = UpperLeft;
}
else if (origin == "UpperRight") {
_origin = UpperRight;
}
else if (origin == "Center") {
_origin = Center;
if (p.origin.has_value()) {
switch (*p.origin) {
case Parameters::Origin::LowerLeft:
_origin = LowerLeft;
break;
case Parameters::Origin::LowerRight:
_origin = LowerRight;
break;
case Parameters::Origin::UpperLeft:
_origin = UpperLeft;
break;
case Parameters::Origin::UpperRight:
_origin = UpperRight;
break;
case Parameters::Origin::Center:
_origin = Center;
break;
default:
throw ghoul::MissingCaseException();
}
}
else {
_origin = Center;
}
addProperty(_texture);
addProperty(_billboard);
addProperty(_size);
_size.onChange([this](){ _planeIsDirty = true; });
setBoundingSphere(_size);
}
bool RenderableDebugPlane::isReady() const {

View File

@@ -68,7 +68,7 @@ private:
properties::FloatProperty _size;
properties::OptionProperty _origin;
bool _planeIsDirty;
bool _planeIsDirty = true;
std::unique_ptr<ghoul::opengl::ProgramObject> _shader;

View File

@@ -56,7 +56,8 @@ namespace openspace::exoplanets::luascriptfunctions {
constexpr const float AU = static_cast<float>(distanceconstants::AstronomicalUnit);
constexpr const float SolarRadius = static_cast<float>(distanceconstants::SolarRadius);
constexpr const float JupiterRadius = static_cast<float>(distanceconstants::JupiterRadius);
constexpr const float JupiterRadius =
static_cast<float>(distanceconstants::JupiterRadius);
ExoplanetSystem findExoplanetSystemInData(std::string_view starName) {
const ExoplanetsModule* module = global::moduleEngine->module<ExoplanetsModule>();

View File

@@ -126,7 +126,7 @@ RenderableFieldlines::RenderableFieldlines(const ghoul::Dictionary& dictionary)
else {
_vectorFieldInfo = dictionary.value<ghoul::Dictionary>(KeyVectorField);
}
if (!dictionary.hasValue<ghoul::Dictionary>(KeyFieldlines)) {
LERROR(fmt::format("Renderable does not contain a key for '{}'", KeyFieldlines));
}

View File

@@ -300,199 +300,123 @@ namespace {
"Report GL Errors",
"If set to true, any OpenGL errors will be reported if encountered"
};
struct [[codegen::Dictionary(RenderableGaiaStars)]] Parameters {
// [[codegen::verbatim(FilePathInfo.description)]]
std::string file;
enum class FileReader {
Fits,
Speck,
BinaryRaw,
BinaryOctree,
StreamOctree
};
// [[codegen::verbatim(FileReaderOptionInfo.description)]]
FileReader fileReaderOption;
enum class RenderOption {
Static,
Color,
Motion
};
// [[codegen::verbatim(RenderOptionInfo.description)]]
std::optional<RenderOption> renderOption;
enum class ShaderOption {
PointSSBO [[codegen::key("Point_SSBO")]],
PointVBO [[codegen::key("Point_VBO")]],
BillboardSSBO [[codegen::key("Billboard_SSBO")]],
BillboardVBO [[codegen::key("Billboard_VBO")]],
BillboardSSBONoFBO [[codegen::key("Billboard_SSBO_noFBO")]]
};
// [codegen::verbatim(ShaderOptionInfo.description)]]
std::optional<ShaderOption> shaderOption;
// [codegen::verbatim(PsfTextureInfo.description)]]
std::string texture;
// [codegen::verbatim(ColorTextureInfo.description)]]
std::string colorMap;
// [codegen::verbatim(LuminosityMultiplierInfo.description)]]
std::optional<float> luminosityMultiplier;
// [codegen::verbatim(MagnitudeBoostInfo.description)]]
std::optional<float> magnitudeBoost;
// [codegen::verbatim(CutOffThresholdInfo.description)]]
std::optional<float> cutOffThreshold;
// [codegen::verbatim(SharpnessInfo.description)]]
std::optional<float> sharpness;
// [codegen::verbatim(BillboardSizeInfo.description)]]
std::optional<float> billboardSize;
// [codegen::verbatim(CloseUpBoostDistInfo.description)]]
std::optional<float> closeUpBoostDist;
// [codegen::verbatim(TmPointFilterSizeInfo.description)]]
std::optional<int> filterSize;
// [codegen::verbatim(TmPointSigmaInfo.description)]]
std::optional<float> sigma;
// [codegen::verbatim(AdditionalNodesInfo.description)]]
std::optional<glm::ivec2> additionalNodes;
// [codegen::verbatim(TmPointPxThresholdInfo.description)]]
std::optional<float> pixelWeightThreshold;
// [codegen::verbatim(FirstRowInfo.description)]]
std::optional<int> firstRow;
// [codegen::verbatim(LastRowInfo.description)]]
std::optional<int> lastRow;
// [codegen::verbatim(ColumnNamesInfo.description)]]
std::optional<std::vector<std::string>> columnNames;
// [codegen::verbatim(LodPixelThresholdInfo.description)]]
std::optional<float> lodPixelThreshold;
// [codegen::verbatim(MaxGpuMemoryPercentInfo.description)]]
std::optional<float> maxGpuMemoryPercent;
// [codegen::verbatim(MaxCpuMemoryPercentInfo.description)]]
std::optional<float> maxCpuMemoryPercent;
// [codegen::verbatim(FilterPosXInfo.description)]]
std::optional<glm::vec2> filterPosX;
// [codegen::verbatim(FilterPosYInfo.description)]]
std::optional<glm::vec2> filterPosY;
// [codegen::verbatim(FilterPosZInfo.description)]]
std::optional<glm::vec2> filterPosZ;
// [codegen::verbatim(FilterGMagInfo.description)]]
std::optional<glm::vec2> filterGMag;
// [codegen::verbatim(FilterBpRpInfo.description)]]
std::optional<glm::vec2> filterBpRp;
// [codegen::verbatim(FilterDistInfo.description)]]
std::optional<glm::vec2> filterDist;
// [codegen::verbatim(ReportGlErrorsInfo.description)]]
std::optional<bool> reportGlErrors;
};
#include "renderablegaiastars_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderableGaiaStars::Documentation() {
using namespace documentation;
return {
"RenderableGaiaStars",
"gaiamission_renderablegaiastars",
{
{
FilePathInfo.identifier,
new StringVerifier,
Optional::No,
FilePathInfo.description
},
{
FileReaderOptionInfo.identifier,
new StringInListVerifier({
"Fits", "Speck", "BinaryRaw", "BinaryOctree", "StreamOctree"
}),
Optional::No,
FileReaderOptionInfo.description
},
{
RenderOptionInfo.identifier,
new StringInListVerifier({
"Static", "Color", "Motion"
}),
Optional::Yes,
RenderOptionInfo.description
},
{
ShaderOptionInfo.identifier,
new StringInListVerifier({
"Point_SSBO", "Point_VBO", "Billboard_SSBO", "Billboard_VBO",
"Billboard_SSBO_noFBO"
}),
Optional::Yes,
ShaderOptionInfo.description
},
{
PsfTextureInfo.identifier,
new StringVerifier,
Optional::No,
PsfTextureInfo.description
},
{
ColorTextureInfo.identifier,
new StringVerifier,
Optional::No,
ColorTextureInfo.description
},
{
LuminosityMultiplierInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LuminosityMultiplierInfo.description
},
{
MagnitudeBoostInfo.identifier,
new DoubleVerifier,
Optional::Yes,
MagnitudeBoostInfo.description
},
{
CutOffThresholdInfo.identifier,
new DoubleVerifier,
Optional::Yes,
CutOffThresholdInfo.description
},
{
SharpnessInfo.identifier,
new DoubleVerifier,
Optional::Yes,
SharpnessInfo.description
},
{
BillboardSizeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
BillboardSizeInfo.description
},
{
CloseUpBoostDistInfo.identifier,
new DoubleVerifier,
Optional::Yes,
CloseUpBoostDistInfo.description
},
{
TmPointFilterSizeInfo.identifier,
new IntVerifier,
Optional::Yes,
TmPointFilterSizeInfo.description
},
{
TmPointSigmaInfo.identifier,
new DoubleVerifier,
Optional::Yes,
TmPointSigmaInfo.description
},
{
AdditionalNodesInfo.identifier,
new Vector2Verifier<double>,
Optional::Yes,
AdditionalNodesInfo.description
},
{
TmPointPxThresholdInfo.identifier,
new DoubleVerifier,
Optional::Yes,
TmPointPxThresholdInfo.description
},
{
FirstRowInfo.identifier,
new IntVerifier,
Optional::Yes,
FirstRowInfo.description
},
{
LastRowInfo.identifier,
new IntVerifier,
Optional::Yes,
LastRowInfo.description
},
{
ColumnNamesInfo.identifier,
new StringListVerifier,
Optional::Yes,
ColumnNamesInfo.description
},
{
LodPixelThresholdInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LodPixelThresholdInfo.description
},
{
MaxGpuMemoryPercentInfo.identifier,
new DoubleVerifier,
Optional::Yes,
MaxGpuMemoryPercentInfo.description
},
{
MaxCpuMemoryPercentInfo.identifier,
new DoubleVerifier,
Optional::Yes,
MaxCpuMemoryPercentInfo.description
},
{
FilterPosXInfo.identifier,
new Vector2Verifier<double>,
Optional::Yes,
FilterPosXInfo.description
},
{
FilterPosYInfo.identifier,
new Vector2Verifier<double>,
Optional::Yes,
FilterPosYInfo.description
},
{
FilterPosZInfo.identifier,
new Vector2Verifier<double>,
Optional::Yes,
FilterPosZInfo.description
},
{
FilterGMagInfo.identifier,
new Vector2Verifier<double>,
Optional::Yes,
FilterGMagInfo.description
},
{
FilterBpRpInfo.identifier,
new Vector2Verifier<double>,
Optional::Yes,
FilterBpRpInfo.description
},
{
FilterDistInfo.identifier,
new Vector2Verifier<double>,
Optional::Yes,
FilterDistInfo.description
},
{
ReportGlErrorsInfo.identifier,
new BoolVerifier,
Optional::Yes,
ReportGlErrorsInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "gaiamission_renderablegaiastars";
return doc;
}
RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
@@ -536,13 +460,9 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
{
using File = ghoul::filesystem::File;
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableGaiaStars"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_filePath = absPath(dictionary.value<std::string>(FilePathInfo.identifier));
_filePath = absPath(p.file);
_dataFile = std::make_unique<File>(_filePath);
_dataFile->setCallback([&](const File&) { _dataIsDirty = true; });
@@ -556,25 +476,24 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
{ gaia::FileReaderOption::BinaryOctree, "BinaryOctree" },
{ gaia::FileReaderOption::StreamOctree, "StreamOctree" }
});
if (dictionary.hasKey(FileReaderOptionInfo.identifier)) {
const std::string fileReaderOption = dictionary.value<std::string>(
FileReaderOptionInfo.identifier
);
if (fileReaderOption == "Fits") {
switch (p.fileReaderOption) {
case Parameters::FileReader::Fits:
_fileReaderOption = gaia::FileReaderOption::Fits;
}
else if (fileReaderOption == "Speck") {
break;
case Parameters::FileReader::Speck:
_fileReaderOption = gaia::FileReaderOption::Speck;
}
else if (fileReaderOption == "BinaryRaw") {
break;
case Parameters::FileReader::BinaryRaw:
_fileReaderOption = gaia::FileReaderOption::BinaryRaw;
}
else if (fileReaderOption == "BinaryOctree") {
break;
case Parameters::FileReader::BinaryOctree:
_fileReaderOption = gaia::FileReaderOption::BinaryOctree;
}
else {
break;
case Parameters::FileReader::StreamOctree:
_fileReaderOption = gaia::FileReaderOption::StreamOctree;
}
break;
default:
throw ghoul::MissingCaseException();
}
_renderOption.addOptions({
@@ -582,18 +501,19 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
{ gaia::RenderOption::Color, "Color" },
{ gaia::RenderOption::Motion, "Motion" }
});
if (dictionary.hasKey(RenderOptionInfo.identifier)) {
const std::string renderOption = dictionary.value<std::string>(
RenderOptionInfo.identifier
);
if (renderOption == "Static") {
_renderOption = gaia::RenderOption::Static;
}
else if (renderOption == "Color") {
_renderOption = gaia::RenderOption::Color;
}
else {
_renderOption = gaia::RenderOption::Motion;
if (p.renderOption.has_value()) {
switch (*p.renderOption) {
case Parameters::RenderOption::Static:
_renderOption = gaia::RenderOption::Static;
break;
case Parameters::RenderOption::Color:
_renderOption = gaia::RenderOption::Color;
break;
case Parameters::RenderOption::Motion:
_renderOption = gaia::RenderOption::Motion;
break;
default:
throw ghoul::MissingCaseException();
}
}
_renderOption.onChange([&]() { _buffersAreDirty = true; });
@@ -614,31 +534,31 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
});
#endif // __APPLE__
if (dictionary.hasKey(ShaderOptionInfo.identifier)) {
// Default shader option:
_shaderOption = gaia::ShaderOption::Billboard_VBO;
const std::string shaderOption = dictionary.value<std::string>(
ShaderOptionInfo.identifier
);
#ifndef __APPLE__
if (shaderOption == "Point_SSBO") {
_shaderOption = gaia::ShaderOption::Point_SSBO;
}
else if (shaderOption == "Billboard_SSBO") {
_shaderOption = gaia::ShaderOption::Billboard_SSBO;
}
else if (shaderOption == "Billboard_SSBO_noFBO") {
_shaderOption = gaia::ShaderOption::Billboard_SSBO_noFBO;
}
if (p.shaderOption.has_value()) {
switch (*p.shaderOption) {
case Parameters::ShaderOption::PointSSBO:
_shaderOption = gaia::ShaderOption::Point_SSBO;
break;
case Parameters::ShaderOption::PointVBO:
#ifdef __APPLE__
throw ghoul::RuntimeError("Shader option is not supported on MacOS");
#endif // __APPLE__
if (shaderOption == "Point_VBO") {
_shaderOption = gaia::ShaderOption::Point_VBO;
}
else if (shaderOption == "Billboard_VBO") {
_shaderOption = gaia::ShaderOption::Billboard_VBO;
_shaderOption = gaia::ShaderOption::Point_VBO;
break;
case Parameters::ShaderOption::BillboardSSBO:
_shaderOption = gaia::ShaderOption::Billboard_SSBO;
break;
case Parameters::ShaderOption::BillboardVBO:
#ifdef __APPLE__
throw ghoul::RuntimeError("Shader option is not supported on MacOS");
#endif // __APPLE__
_shaderOption = gaia::ShaderOption::Billboard_VBO;
break;
case Parameters::ShaderOption::BillboardSSBONoFBO:
_shaderOption = gaia::ShaderOption::Billboard_SSBO_noFBO;
break;
default:
throw ghoul::MissingCaseException();
}
}
_shaderOption.onChange([&]() {
@@ -647,95 +567,34 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
});
addProperty(_shaderOption);
_pointSpreadFunctionTexturePath = absPath(dictionary.value<std::string>(
PsfTextureInfo.identifier
));
_pointSpreadFunctionFile = std::make_unique<File>(_pointSpreadFunctionTexturePath);
_pointSpreadFunctionTexturePath = absPath(p.texture);
_pointSpreadFunctionTexturePath.onChange(
[&](){ _pointSpreadFunctionTextureIsDirty = true; }
);
_pointSpreadFunctionFile = std::make_unique<File>(_pointSpreadFunctionTexturePath);
_pointSpreadFunctionFile->setCallback(
[&](const File&) { _pointSpreadFunctionTextureIsDirty = true; }
);
_colorTexturePath = absPath(dictionary.value<std::string>(
ColorTextureInfo.identifier
));
_colorTexturePath = absPath(p.colorMap);
_colorTextureFile = std::make_unique<File>(_colorTexturePath);
_colorTexturePath.onChange([&]() { _colorTextureIsDirty = true; });
_colorTextureFile->setCallback([&](const File&) { _colorTextureIsDirty = true; });
if (dictionary.hasKey(LuminosityMultiplierInfo.identifier)) {
_luminosityMultiplier = static_cast<float>(
dictionary.value<double>(LuminosityMultiplierInfo.identifier)
);
}
_luminosityMultiplier = p.luminosityMultiplier.value_or(_luminosityMultiplier);
_magnitudeBoost = p.magnitudeBoost.value_or(_magnitudeBoost);
_cutOffThreshold = p.cutOffThreshold.value_or(_cutOffThreshold);
_sharpness = p.sharpness.value_or(_sharpness);
_billboardSize = p.billboardSize.value_or(_billboardSize);
_closeUpBoostDist = p.closeUpBoostDist.value_or(_closeUpBoostDist);
_tmPointFilterSize = p.filterSize.value_or(_tmPointFilterSize);
_tmPointSigma = p.sigma.value_or(_tmPointSigma);
_tmPointPixelWeightThreshold =
p.pixelWeightThreshold.value_or(_tmPointPixelWeightThreshold);
_additionalNodes = p.additionalNodes.value_or(_additionalNodes);
_lodPixelThreshold = p.lodPixelThreshold.value_or(_lodPixelThreshold);
if (dictionary.hasKey(MagnitudeBoostInfo.identifier)) {
_magnitudeBoost = static_cast<float>(
dictionary.value<double>(MagnitudeBoostInfo.identifier)
);
}
if (dictionary.hasKey(CutOffThresholdInfo.identifier)) {
_cutOffThreshold = static_cast<float>(
dictionary.value<double>(CutOffThresholdInfo.identifier)
);
}
if (dictionary.hasKey(SharpnessInfo.identifier)) {
_sharpness = static_cast<float>(
dictionary.value<double>(SharpnessInfo.identifier)
);
}
if (dictionary.hasKey(BillboardSizeInfo.identifier)) {
_billboardSize = static_cast<float>(
dictionary.value<double>(BillboardSizeInfo.identifier)
);
}
if (dictionary.hasKey(CloseUpBoostDistInfo.identifier)) {
_closeUpBoostDist = static_cast<float>(
dictionary.value<double>(CloseUpBoostDistInfo.identifier)
);
}
if (dictionary.hasKey(TmPointFilterSizeInfo.identifier)) {
_tmPointFilterSize = static_cast<int>(
dictionary.value<double>(TmPointFilterSizeInfo.identifier)
);
}
if (dictionary.hasKey(TmPointSigmaInfo.identifier)) {
_tmPointSigma = static_cast<float>(
dictionary.value<double>(TmPointSigmaInfo.identifier)
);
}
if (dictionary.hasKey(TmPointPxThresholdInfo.identifier)) {
_tmPointPixelWeightThreshold = static_cast<float>(
dictionary.value<double>(TmPointPxThresholdInfo.identifier)
);
}
if (dictionary.hasKey(AdditionalNodesInfo.identifier)) {
_additionalNodes = static_cast<glm::ivec2>(
dictionary.value<glm::dvec2>(AdditionalNodesInfo.identifier)
);
}
if (dictionary.hasKey(LodPixelThresholdInfo.identifier)) {
_lodPixelThreshold = static_cast<float>(
dictionary.value<double>(LodPixelThresholdInfo.identifier)
);
}
if (dictionary.hasKey(MaxGpuMemoryPercentInfo.identifier)) {
_maxGpuMemoryPercent = static_cast<float>(
dictionary.value<double>(MaxGpuMemoryPercentInfo.identifier)
);
}
_maxGpuMemoryPercent = p.maxGpuMemoryPercent.value_or(_maxGpuMemoryPercent);
_maxGpuMemoryPercent.onChange([&]() {
if (_ssboData != 0) {
glDeleteBuffers(1, &_ssboData);
@@ -761,59 +620,36 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
_maxStreamingBudgetInBytes = 0;
});
if (dictionary.hasKey(MaxCpuMemoryPercentInfo.identifier)) {
_maxCpuMemoryPercent = static_cast<float>(
dictionary.value<double>(MaxCpuMemoryPercentInfo.identifier)
);
}
if (dictionary.hasKey(FilterPosXInfo.identifier)) {
_posXThreshold = dictionary.value<glm::dvec2>(FilterPosXInfo.identifier);
}
_maxCpuMemoryPercent = p.maxCpuMemoryPercent.value_or(_maxCpuMemoryPercent);
_posXThreshold = p.filterPosX.value_or(_posXThreshold);
addProperty(_posXThreshold);
if (dictionary.hasKey(FilterPosYInfo.identifier)) {
_posXThreshold = dictionary.value<glm::dvec2>(FilterPosYInfo.identifier);
}
_posYThreshold = p.filterPosY.value_or(_posYThreshold);
addProperty(_posYThreshold);
if (dictionary.hasKey(FilterPosZInfo.identifier)) {
_posZThreshold = dictionary.value<glm::dvec2>(FilterPosZInfo.identifier);
}
_posZThreshold = p.filterPosZ.value_or(_posZThreshold);
addProperty(_posZThreshold);
if (dictionary.hasKey(FilterGMagInfo.identifier)) {
_gMagThreshold = dictionary.value<glm::dvec2>(FilterGMagInfo.identifier);
}
_gMagThreshold = p.filterGMag.value_or(_gMagThreshold);
addProperty(_gMagThreshold);
if (dictionary.hasKey(FilterBpRpInfo.identifier)) {
_bpRpThreshold = dictionary.value<glm::dvec2>(FilterBpRpInfo.identifier);
}
_bpRpThreshold = p.filterBpRp.value_or(_bpRpThreshold);
addProperty(_bpRpThreshold);
if (dictionary.hasKey(FilterDistInfo.identifier)) {
_distThreshold = dictionary.value<glm::dvec2>(FilterDistInfo.identifier);
}
_distThreshold = p.filterDist.value_or(_distThreshold);
addProperty(_distThreshold);
// Only add properties correlated to fits files if we're reading from a fits file.
if (_fileReaderOption == gaia::FileReaderOption::Fits) {
if (dictionary.hasKey(FirstRowInfo.identifier)) {
_firstRow = static_cast<int>(
dictionary.value<double>(FirstRowInfo.identifier)
);
}
_firstRow = p.firstRow.value_or(_firstRow);
_firstRow.onChange([&]() { _dataIsDirty = true; });
addProperty(_firstRow);
if (dictionary.hasKey(LastRowInfo.identifier)) {
_lastRow = static_cast<int>(dictionary.value<double>(LastRowInfo.identifier));
}
_lastRow = p.lastRow.value_or(_lastRow);
_lastRow.onChange([&]() { _dataIsDirty = true; });
addProperty(_lastRow);
if (dictionary.hasKey(ColumnNamesInfo.identifier)) {
if (p.columnNames.has_value()) {
ghoul::Dictionary tmpDict = dictionary.value<ghoul::Dictionary>(
ColumnNamesInfo.identifier
);
@@ -834,13 +670,11 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
}
if (_firstRow > _lastRow) {
throw ghoul::RuntimeError("User defined FirstRow is bigger than LastRow.");
throw ghoul::RuntimeError("User defined FirstRow is bigger than LastRow");
}
}
if (dictionary.hasKey(ReportGlErrorsInfo.identifier)) {
_reportGlErrors = dictionary.value<bool>(ReportGlErrorsInfo.identifier);
}
_reportGlErrors = p.reportGlErrors.value_or(_reportGlErrors);
addProperty(_reportGlErrors);
// Add a read-only property for the number of rendered stars per frame.

View File

@@ -35,164 +35,263 @@
#include <thread>
namespace {
constexpr const char* KeyInFileOrFolderPath = "InFileOrFolderPath";
constexpr const char* KeyOutFileOrFolderPath = "OutFileOrFolderPath";
constexpr const char* KeyMaxDist = "MaxDist";
constexpr const char* KeyMaxStarsPerNode = "MaxStarsPerNode";
constexpr const char* KeySingleFileInput = "SingleFileInput";
constexpr const char* KeyFilterPosX = "FilterPosX";
constexpr const char* KeyFilterPosY = "FilterPosY";
constexpr const char* KeyFilterPosZ = "FilterPosZ";
constexpr const char* KeyFilterGMag = "FilterGMag";
constexpr const char* KeyFilterBpRp = "FilterBpRp";
constexpr const char* KeyFilterVelX = "FilterVelX";
constexpr const char* KeyFilterVelY = "FilterVelY";
constexpr const char* KeyFilterVelZ = "FilterVelZ";
constexpr const char* KeyFilterBpMag = "FilterBpMag";
constexpr const char* KeyFilterRpMag = "FilterRpMag";
constexpr const char* KeyFilterBpG = "FilterBpG";
constexpr const char* KeyFilterGRp = "FilterGRp";
constexpr const char* KeyFilterRa = "FilterRa";
constexpr const char* KeyFilterRaError = "FilterRaError";
constexpr const char* KeyFilterDec = "FilterDec";
constexpr const char* KeyFilterDecError = "FilterDecError";
constexpr const char* KeyFilterParallax = "FilterParallax";
constexpr const char* KeyFilterParallaxError = "FilterParallaxError";
constexpr const char* KeyFilterPmra = "FilterPmra";
constexpr const char* KeyFilterPmraError = "FilterPmraError";
constexpr const char* KeyFilterPmdec = "FilterPmdec";
constexpr const char* KeyFilterPmdecError = "FilterPmdecError";
constexpr const char* KeyFilterRv = "FilterRv";
constexpr const char* KeyFilterRvError = "FilterRvError";
constexpr const char* _loggerCat = "ConstructOctreeTask";
struct [[codegen::Dictionary(ConstructOctreeTask)]] Parameters {
// If SingleFileInput is set to true then this specifies the path to a single BIN
// file containing a full dataset. Otherwise this specifies the path to a folder
// with multiple BIN files containing subsets of sorted star data
std::string inFileOrFolderPath;
// If SingleFileInput is set to true then this specifies the output file name
// (including full path). Otherwise this specifies the path to the folder which to
// save all files
std::string outFileOrFolderPath;
// If set it determines what MAX_DIST to use when creating Octree
std::optional<int> maxDist;
// If set it determines what MAX_STAR_PER_NODE to use when creating Octree
std::optional<int> maxStarsPerNode;
// If true then task will read from a single file and output a single binary file
// with the full Octree. If false then task will read all files in specified
// folder and output multiple files for the Octree
std::optional<bool> singleFileInput;
// If defined then only stars with Position X values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterPosX;
// If defined then only stars with Position Y values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterPosY;
// If defined then only stars with Position Z values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterPosZ;
// If defined then only stars with G mean magnitude values between [min, max] will
// be inserted into Octree (if min is set to 20.0 it is read as -Inf, if max is
// set to 20.0 it is read as +Inf). If min = max then all values equal min|max
// will be filtered away. Default GMag = 20.0 if no value existed
std::optional<glm::vec2> filterGMag;
// If defined then only stars with Bp-Rp color values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterBpRp;
// If defined then only stars with Velocity X values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterVelX;
// If defined then only stars with Velocity Y values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterVelY;
// If defined then only stars with Velocity Z values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterVelZ;
// If defined then only stars with Bp mean magnitude values between [min, max]
// will be inserted into Octree (if min is set to 20.0 it is read as -Inf, if max
// is set to 20.0 it is read as +Inf). If min = max then all values equal min|max
// will be filtered away. Default BpMag = 20.0 if no value existed
std::optional<glm::vec2> filterBpMag;
// If defined then only stars with Rp mean magnitude values between [min, max]
// will be inserted into Octree (if min is set to 20.0 it is read as -Inf, if max
// is set to 20.0 it is read as +Inf). If min = max then all values equal min|max
// will be filtered away. Default RpMag = 20.0 if no value existed
std::optional<glm::vec2> filterRpMag;
// If defined then only stars with Bp-G color values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterBpG;
// If defined then only stars with G-Rp color values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterGRp;
// If defined then only stars with RA values between [min, max] will be inserted
// into Octree (if min is set to 0.0 it is read as -Inf, if max is set to 0.0 it
// is read as +Inf). If min = max then all values equal min|max will be filtered
// away
std::optional<glm::vec2> filterRa;
// If defined then only stars with RA Error values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterRaError;
// If defined then only stars with DEC values between [min, max] will be inserted
// into Octree (if min is set to 0.0 it is read as -Inf, if max is set to 0.0 it
// is read as +Inf). If min = max then all values equal min|max will be filtered
// away
std::optional<glm::vec2> filterDec;
// If defined then only stars with DEC Error values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterDecError;
// If defined then only stars with Parallax values between [min, max] will be
// inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to
// 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterParallax;
// If defined then only stars with Parallax Error values between [min, max] will
// be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set
// to 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterParallaxError;
// If defined then only stars with Proper Motion RA values between [min, max] will
// be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set
// to 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterPmra;
// If defined then only stars with Proper Motion RA Error values between
// [min, max] will be inserted into Octree (if min is set to 0.0 it is read as
// -Inf, if max is set to 0.0 it is read as +Inf). If min = max then all values
// equal min|max will be filtered away
std::optional<glm::vec2> filterPmraError;
// If defined then only stars with Proper Motion DEC values between [min, max]
// will be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max
// is set to 0.0 it is read as +Inf). If min = max then all values equal min|max
// will be filtered away
std::optional<glm::vec2> filterPmdec;
// If defined then only stars with Proper Motion DEC Error values between
// [min, max] will be inserted into Octree (if min is set to 0.0 it is read as
// -Inf, if max is set to 0.0 it is read as +Inf). If min = max then all values
// equal min|max will be filtered away
std::optional<glm::vec2> filterPmdecError;
// If defined then only stars with Radial Velocity values between [min, max] will
// be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set
// to 0.0 it is read as +Inf). If min = max then all values equal min|max will be
// filtered away
std::optional<glm::vec2> filterRv;
// If defined then only stars with Radial Velocity Error values between [min, max]
// will be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max
// is set to 0.0 it is read as +Inf). If min = max then all values equal min|max
// will be filtered away
std::optional<glm::vec2> filterRvError;
};
#include "constructoctreetask_codegen.cpp"
} // namespace
namespace openspace {
ConstructOctreeTask::ConstructOctreeTask(const ghoul::Dictionary& dictionary) {
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"ConstructOctreeTask"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_inFileOrFolderPath = absPath(dictionary.value<std::string>(KeyInFileOrFolderPath));
_outFileOrFolderPath = absPath(dictionary.value<std::string>(KeyOutFileOrFolderPath));
if (dictionary.hasKey(KeyMaxDist)) {
_maxDist = static_cast<int>(dictionary.value<double>(KeyMaxDist));
}
if (dictionary.hasKey(KeyMaxStarsPerNode)) {
_maxStarsPerNode = static_cast<int>(dictionary.value<double>(KeyMaxStarsPerNode));
}
if (dictionary.hasKey(KeySingleFileInput)) {
_singleFileInput = dictionary.value<bool>(KeySingleFileInput);
}
_inFileOrFolderPath = absPath(p.inFileOrFolderPath);
_outFileOrFolderPath = absPath(p.outFileOrFolderPath);
_maxDist = p.maxDist.value_or(_maxDist);
_maxStarsPerNode = p.maxStarsPerNode.value_or(_maxStarsPerNode);
_singleFileInput = p.singleFileInput.value_or(_singleFileInput);
_octreeManager = std::make_shared<OctreeManager>();
_indexOctreeManager = std::make_shared<OctreeManager>();
// Check for filter params.
if (dictionary.hasKey(KeyFilterPosX)) {
_posX = dictionary.value<glm::dvec2>(KeyFilterPosX);
_filterPosX = true;
}
if (dictionary.hasKey(KeyFilterPosY)) {
_posY = dictionary.value<glm::dvec2>(KeyFilterPosY);
_filterPosY = true;
}
if (dictionary.hasKey(KeyFilterPosZ)) {
_posZ = dictionary.value<glm::dvec2>(KeyFilterPosZ);
_filterPosZ = true;
}
if (dictionary.hasKey(KeyFilterGMag)) {
_gMag = dictionary.value<glm::dvec2>(KeyFilterGMag);
_filterGMag = true;
}
if (dictionary.hasKey(KeyFilterBpRp)) {
_bpRp = dictionary.value<glm::dvec2>(KeyFilterBpRp);
_filterBpRp = true;
}
if (dictionary.hasKey(KeyFilterVelX)) {
_velX = dictionary.value<glm::dvec2>(KeyFilterVelX);
_filterVelX = true;
}
if (dictionary.hasKey(KeyFilterVelY)) {
_velY = dictionary.value<glm::dvec2>(KeyFilterVelY);
_filterVelY = true;
}
if (dictionary.hasKey(KeyFilterVelZ)) {
_velZ = dictionary.value<glm::dvec2>(KeyFilterVelZ);
_filterVelZ = true;
}
if (dictionary.hasKey(KeyFilterBpMag)) {
_bpMag = dictionary.value<glm::dvec2>(KeyFilterBpMag);
_filterBpMag = true;
}
if (dictionary.hasKey(KeyFilterRpMag)) {
_rpMag = dictionary.value<glm::dvec2>(KeyFilterRpMag);
_filterRpMag = true;
}
if (dictionary.hasKey(KeyFilterBpG)) {
_bpG = dictionary.value<glm::dvec2>(KeyFilterBpG);
_filterBpG = true;
}
if (dictionary.hasKey(KeyFilterGRp)) {
_gRp = dictionary.value<glm::dvec2>(KeyFilterGRp);
_filterGRp = true;
}
if (dictionary.hasKey(KeyFilterRa)) {
_ra = dictionary.value<glm::dvec2>(KeyFilterRa);
_filterRa = true;
}
if (dictionary.hasKey(KeyFilterRaError)) {
_raError = dictionary.value<glm::dvec2>(KeyFilterRaError);
_filterRaError = true;
}
if (dictionary.hasKey(KeyFilterDec)) {
_dec = dictionary.value<glm::dvec2>(KeyFilterDec);
_filterDec = true;
}
if (dictionary.hasKey(KeyFilterDecError)) {
_decError = dictionary.value<glm::dvec2>(KeyFilterDecError);
_filterDecError = true;
}
if (dictionary.hasKey(KeyFilterParallax)) {
_parallax = dictionary.value<glm::dvec2>(KeyFilterParallax);
_filterParallax = true;
}
if (dictionary.hasKey(KeyFilterParallaxError)) {
_parallaxError = dictionary.value<glm::dvec2>(KeyFilterParallaxError);
_filterParallaxError = true;
}
if (dictionary.hasKey(KeyFilterPmra)) {
_pmra = dictionary.value<glm::dvec2>(KeyFilterPmra);
_filterPmra = true;
}
if (dictionary.hasKey(KeyFilterPmraError)) {
_pmraError = dictionary.value<glm::dvec2>(KeyFilterPmraError);
_filterPmraError = true;
}
if (dictionary.hasKey(KeyFilterPmdec)) {
_pmdec = dictionary.value<glm::dvec2>(KeyFilterPmdec);
_filterPmdec = true;
}
if (dictionary.hasKey(KeyFilterPmdecError)) {
_pmdecError = dictionary.value<glm::dvec2>(KeyFilterPmdecError);
_filterPmdecError = true;
}
if (dictionary.hasKey(KeyFilterRv)) {
_rv = dictionary.value<glm::dvec2>(KeyFilterRv);
_filterRv = true;
}
if (dictionary.hasKey(KeyFilterRvError)) {
_rvError = dictionary.value<glm::dvec2>(KeyFilterRvError);
_filterRvError = true;
}
_posX = p.filterPosX.value_or(_posX);
_filterPosX = p.filterPosX.has_value();
_posY = p.filterPosY.value_or(_posY);
_filterPosY = p.filterPosY.has_value();
_posZ = p.filterPosZ.value_or(_posZ);
_filterPosZ = p.filterPosZ.has_value();
_gMag = p.filterGMag.value_or(_gMag);
_filterGMag = p.filterGMag.has_value();
_bpRp = p.filterBpRp.value_or(_bpRp);
_filterBpRp = p.filterBpRp.has_value();
_velX = p.filterVelX.value_or(_velX);
_filterVelX = p.filterVelX.has_value();
_velY = p.filterVelY.value_or(_velY);
_filterVelY = p.filterVelY.has_value();
_velZ = p.filterVelZ.value_or(_velZ);
_filterVelZ = p.filterVelZ.has_value();
_bpMag = p.filterBpMag.value_or(_bpMag);
_filterBpMag = p.filterBpMag.has_value();
_rpMag = p.filterRpMag.value_or(_rpMag);
_filterRpMag = p.filterRpMag.has_value();
_bpG = p.filterBpG.value_or(_bpG);
_filterBpG = p.filterBpG.has_value();
_gRp = p.filterGRp.value_or(_gRp);
_filterGRp = p.filterGRp.has_value();
_ra = p.filterRa.value_or(_ra);
_filterRa = p.filterRa.has_value();
_raError = p.filterRaError.value_or(_raError);
_filterRaError = p.filterRaError.has_value();
_dec = p.filterDec.value_or(_dec);
_filterDec = p.filterDec.has_value();
_decError = p.filterDecError.value_or(_decError);
_filterDecError = p.filterDecError.has_value();
_parallax = p.filterParallax.value_or(_parallax);
_filterParallax = p.filterParallax.has_value();
_parallaxError = p.filterParallaxError.value_or(_parallaxError);
_filterParallaxError = p.filterParallaxError.has_value();
_pmra = p.filterPmra.value_or(_pmra);
_filterPmra = p.filterPmra.has_value();
_pmraError = p.filterPmraError.value_or(_pmraError);
_filterPmraError = p.filterPmraError.has_value();
_pmdec = p.filterPmdec.value_or(_pmdec);
_filterPmdec = p.filterPmdec.has_value();
_pmdecError = p.filterPmdecError.value_or(_pmdecError);
_filterPmdecError = p.filterPmdecError.has_value();
_rv = p.filterRv.value_or(_rv);
_filterRv = p.filterRv.has_value();
_rvError = p.filterRvError.value_or(_rvError);
_filterRvError = p.filterRvError.has_value();
}
std::string ConstructOctreeTask::description() {
@@ -201,7 +300,7 @@ std::string ConstructOctreeTask::description() {
}
void ConstructOctreeTask::perform(const Task::ProgressCallback& onProgress) {
onProgress(0.0f);
onProgress(0.f);
if (_singleFileInput) {
constructOctreeFromSingleFile(onProgress);
@@ -210,7 +309,7 @@ void ConstructOctreeTask::perform(const Task::ProgressCallback& onProgress) {
constructOctreeFromFolder(onProgress);
}
onProgress(1.0f);
onProgress(1.f);
}
void ConstructOctreeTask::constructOctreeFromSingleFile(
@@ -543,269 +642,9 @@ bool ConstructOctreeTask::filterStar(const glm::vec2& range, float filterValue,
}
documentation::Documentation ConstructOctreeTask::Documentation() {
using namespace documentation;
return {
"ConstructOctreeTask",
"gaiamission_constructoctreefrombin",
{
{
KeyInFileOrFolderPath,
new StringVerifier,
Optional::No,
"If SingleFileInput is set to true then this specifies the path to a "
"single BIN file containing a full dataset. Otherwise this specifies the "
"path to a folder with multiple BIN files containing subsets of sorted "
"star data.",
},
{
KeyOutFileOrFolderPath,
new StringVerifier,
Optional::No,
"If SingleFileInput is set to true then this specifies the output file "
"name (including full path). Otherwise this specifies the path to the "
"folder which to save all files.",
},
{
KeyMaxDist,
new IntVerifier,
Optional::Yes,
"If set it determines what MAX_DIST to use when creating Octree."
},
{
KeyMaxStarsPerNode,
new IntVerifier,
Optional::Yes,
"If set it determines what MAX_STAR_PER_NODE to use when creating Octree."
},
{
KeySingleFileInput,
new BoolVerifier,
Optional::Yes,
"If true then task will read from a single file and output a single "
"binary file with the full Octree. If false then task will read all "
"files in specified folder and output multiple files for the Octree."
},
{
KeyFilterPosX,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Position X values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterPosY,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Position Y values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterPosZ,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Position Z values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterGMag,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with G mean magnitude values between "
"[min, max] will be inserted into Octree (if min is set to 20.0 it is "
"read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away. Default "
"GMag = 20.0 if no value existed."
},
{
KeyFilterBpRp,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Bp-Rp color values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterVelX,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Velocity X values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterVelY,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Velocity Y values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterVelZ,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Velocity Z values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterBpMag,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Bp mean magnitude values between "
"[min, max] will be inserted into Octree (if min is set to 20.0 it is "
"read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away. Default "
"BpMag = 20.0 if no value existed."
},
{
KeyFilterRpMag,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Rp mean magnitude values between "
"[min, max] will be inserted into Octree (if min is set to 20.0 it is "
"read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away. Default RpMag = "
"20.0 if no value existed."
},
{
KeyFilterBpG,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Bp-G color values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterGRp,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with G-Rp color values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterRa,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with RA values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterRaError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with RA Error values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterDec,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with DEC values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterDecError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with DEC Error values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterParallax,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Parallax values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterParallaxError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Parallax Error values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterPmra,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Proper Motion RA values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterPmraError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Proper Motion RA Error values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterPmdec,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Proper Motion DEC values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterPmdecError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Proper Motion DEC Error values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterRv,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Radial Velocity values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterRvError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Radial Velocity Error values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "gaiamission_constructoctreefrombin";
return doc;
}
} // namespace openspace

View File

@@ -36,55 +36,61 @@
#include <fstream>
#include <set>
#include <optional>
namespace {
constexpr const char* KeyInFileOrFolderPath = "InFileOrFolderPath";
constexpr const char* KeyOutFileOrFolderPath = "OutFileOrFolderPath";
constexpr const char* KeySingleFileProcess = "SingleFileProcess";
constexpr const char* KeyThreadsToUse = "ThreadsToUse";
constexpr const char* KeyFirstRow = "FirstRow";
constexpr const char* KeyLastRow = "LastRow";
constexpr const char* KeyFilterColumnNames = "FilterColumnNames";
constexpr const char* _loggerCat = "ReadFitsTask";
struct [[codegen::Dictionary(ReadFitsTask)]] Parameters {
// If SingleFileProcess is set to true then this specifies the path to a single
// FITS file that will be read. Otherwise it specifies the path to a folder with
// multiple FITS files that are to be read
std::string inFileOrFolderPath;
// If SingleFileProcess is set to true then this specifies the name (including
// entire path) to the output file. Otherwise it specifies the path to the output
// folder which to export binary star data to
std::string outFileOrFolderPath;
// If true then task will read from a single FITS file and output a single binary
// file. If false then task will read all files in specified folder and output
// multiple files sorted by location
std::optional<bool> singleFileProcess;
// Defines how many threads to use when reading from multiple files
std::optional<int> threadsToUse [[codegen::greater(1)]];
// Defines the first row that will be read from the specified FITS file(s). If not
// defined then reading will start at first row
std::optional<int> firstRow;
// Defines the last row that will be read from the specified FITS file(s). If not
// defined (or less than FirstRow) then full file(s) will be read
std::optional<int> lastRow;
// A list of strings with the names of all the additional columns that are to be
// read from the specified FITS file(s). These columns can be used for filtering
// while constructing Octree later
std::optional<std::vector<std::string>> filterColumnNames;
};
#include "readfitstask_codegen.cpp"
} // namespace
namespace openspace {
ReadFitsTask::ReadFitsTask(const ghoul::Dictionary& dictionary) {
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"ReadFitsTask"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_inFileOrFolderPath = absPath(dictionary.value<std::string>(KeyInFileOrFolderPath));
_outFileOrFolderPath = absPath(dictionary.value<std::string>(KeyOutFileOrFolderPath));
_inFileOrFolderPath = absPath(p.inFileOrFolderPath);
_outFileOrFolderPath = absPath(p.outFileOrFolderPath);
_singleFileProcess = p.singleFileProcess.value_or(_singleFileProcess);
_threadsToUse = p.threadsToUse.value_or(_threadsToUse);
_firstRow = p.firstRow.value_or(_firstRow);
_lastRow = p.lastRow.value_or(_lastRow);
if (dictionary.hasKey(KeySingleFileProcess)) {
_singleFileProcess = dictionary.value<bool>(KeySingleFileProcess);
}
if (dictionary.hasKey(KeyThreadsToUse)) {
_threadsToUse = static_cast<size_t>(dictionary.value<double>(KeyThreadsToUse));
if (_threadsToUse < 1) {
LINFO(fmt::format(
"User defined ThreadsToUse was: {}. Will be set to 1", _threadsToUse
));
_threadsToUse = 1;
}
}
if (dictionary.hasKey(KeyFirstRow)) {
_firstRow = static_cast<int>(dictionary.value<double>(KeyFirstRow));
}
if (dictionary.hasKey(KeyLastRow)) {
_lastRow = static_cast<int>(dictionary.value<double>(KeyLastRow));
}
if (dictionary.hasKey(KeyFilterColumnNames)) {
if (p.filterColumnNames.has_value()) {
ghoul::Dictionary d = dictionary.value<ghoul::Dictionary>(KeyFilterColumnNames);
// Ugly fix for ASCII sorting when there are more columns read than 10.
@@ -322,66 +328,9 @@ int ReadFitsTask::writeOctantToFile(const std::vector<float>& octantData, int in
}
documentation::Documentation ReadFitsTask::Documentation() {
using namespace documentation;
return {
"ReadFitsFile",
"gaiamission_fitsfiletorawdata",
{
{
KeyInFileOrFolderPath,
new StringVerifier,
Optional::No,
"If SingleFileProcess is set to true then this specifies the path to a "
"single FITS file that will be read. Otherwise it specifies the path to "
"a folder with multiple FITS files that are to be read.",
},
{
KeyOutFileOrFolderPath,
new StringVerifier,
Optional::No,
"If SingleFileProcess is set to true then this specifies the name "
"(including entire path) to the output file. Otherwise it specifies the "
"path to the output folder which to export binary star data to.",
},
{
KeySingleFileProcess,
new BoolVerifier,
Optional::Yes,
"If true then task will read from a single FITS file and output a single "
"binary file. If false then task will read all files in specified folder "
"and output multiple files sorted by location."
},
{
KeyThreadsToUse,
new IntVerifier,
Optional::Yes,
"Defines how many threads to use when reading from multiple files."
},
{
KeyFirstRow,
new IntVerifier,
Optional::Yes,
"Defines the first row that will be read from the specified FITS "
"file(s). If not defined then reading will start at first row.",
},
{
KeyLastRow,
new IntVerifier,
Optional::Yes,
"Defines the last row that will be read from the specified FITS file(s). "
"If not defined (or less than FirstRow) then full file(s) will be read.",
},
{
KeyFilterColumnNames,
new StringListVerifier,
Optional::Yes,
"A list of strings with the names of all the additional columns that are "
"to be read from the specified FITS file(s). These columns can be used "
"for filtering while constructing Octree later.",
},
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "gaiamission_fitsfiletorawdata";
return doc;
}
} // namespace openspace

View File

@@ -34,23 +34,30 @@
#include <fstream>
namespace {
constexpr const char* KeyInFilePath = "InFilePath";
constexpr const char* KeyOutFilePath = "OutFilePath";
constexpr const char* _loggerCat = "ReadSpeckTask";
struct [[codegen::Dictionary(ReadSpeckTask)]] Parameters {
// The path to the SPECK file that are to be read
std::string inFilePath;
// The path to the file to export raw VBO data to
std::string outFilePath;
};
#include "readspecktask_codegen.cpp"
} // namespace
namespace openspace {
ReadSpeckTask::ReadSpeckTask(const ghoul::Dictionary& dictionary) {
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"ReadSpeckTask"
);
documentation::Documentation ReadSpeckTask::Documentation() {
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "gaiamission_speckfiletorawdata";
return doc;
}
_inFilePath = absPath(dictionary.value<std::string>(KeyInFilePath));
_outFilePath = absPath(dictionary.value<std::string>(KeyOutFilePath));
ReadSpeckTask::ReadSpeckTask(const ghoul::Dictionary& dictionary) {
const Parameters p = codegen::bake<Parameters>(dictionary);
_inFilePath = absPath(p.inFilePath);
_outFilePath = absPath(p.outFilePath);
}
std::string ReadSpeckTask::description() {
@@ -92,26 +99,4 @@ void ReadSpeckTask::perform(const Task::ProgressCallback& onProgress) {
onProgress(1.f);
}
documentation::Documentation ReadSpeckTask::Documentation() {
using namespace documentation;
return {
"ReadSpeckTask",
"gaiamission_speckfiletorawdata",
{
{
KeyInFilePath,
new StringVerifier,
Optional::No,
"The path to the SPECK file that are to be read.",
},
{
KeyOutFilePath,
new StringVerifier,
Optional::No,
"The path to the file to export raw VBO data to.",
},
}
};
}
} // namespace openspace

View File

@@ -453,7 +453,9 @@ std::vector<documentation::Documentation> GlobeBrowsingModule::documentations()
globebrowsing::Layer::Documentation(),
globebrowsing::LayerAdjustment::Documentation(),
globebrowsing::LayerManager::Documentation(),
GlobeLabelsComponent::Documentation()
GlobeLabelsComponent::Documentation(),
RingsComponent::Documentation(),
ShadowComponent::Documentation()
};
}

View File

@@ -62,36 +62,25 @@ namespace {
"Determines the number of significant digits that are shown in the location text."
};
struct [[codegen::Dictionary(DashboardItemGlobeLocation)]] Parameters {
// [[codegen::verbatim(FontNameInfo.description)]]
std::optional<std::string> fontName;
// [[codegen::verbatim(FontSizeInfo.description)]]
std::optional<float> fontSize;
// [[codegen::verbatim(SignificantDigitsInfo.description)]]
std::optional<int> significantDigits;
};
#include "dashboarditemglobelocation_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation DashboardItemGlobeLocation::Documentation() {
using namespace documentation;
return {
"DashboardItem Globe Location",
"globebrowsing_dashboarditem_globelocation",
{
{
FontNameInfo.identifier,
new StringVerifier,
Optional::Yes,
FontNameInfo.description
},
{
FontSizeInfo.identifier,
new IntVerifier,
Optional::Yes,
FontSizeInfo.description
},
{
SignificantDigitsInfo.identifier,
new IntVerifier,
Optional::Yes,
SignificantDigitsInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "globebrowsing_dashboarditem_globelocation";
return doc;
}
DashboardItemGlobeLocation::DashboardItemGlobeLocation(
@@ -102,30 +91,15 @@ DashboardItemGlobeLocation::DashboardItemGlobeLocation(
, _significantDigits(SignificantDigitsInfo, 4, 1, 12)
, _font(global::fontManager->font(KeyFontMono, 10))
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"DashboardItemGlobeLocation"
);
if (dictionary.hasKey(FontNameInfo.identifier)) {
_fontName = dictionary.value<std::string>(FontNameInfo.identifier);
}
if (dictionary.hasKey(FontSizeInfo.identifier)) {
_fontSize = static_cast<float>(dictionary.value<double>(FontSizeInfo.identifier));
}
if (dictionary.hasKey(SignificantDigitsInfo.identifier)) {
_significantDigits = static_cast<int>(
dictionary.value<double>(SignificantDigitsInfo.identifier)
);
}
const Parameters p = codegen::bake<Parameters>(dictionary);
_fontName = p.fontName.value_or(_fontName);
_fontName.onChange([this]() {
_font = global::fontManager->font(_fontName, _fontSize);
});
addProperty(_fontName);
_fontSize = p.fontSize.value_or(_fontSize);
_fontSize.onChange([this]() {
_font = global::fontManager->font(_fontName, _fontSize);
});
@@ -139,6 +113,7 @@ DashboardItemGlobeLocation::DashboardItemGlobeLocation(
_significantDigits.value()
);
};
_significantDigits = p.significantDigits.value_or(_significantDigits);
_significantDigits.onChange(updateFormatString);
addProperty(_significantDigits);
updateFormatString();

View File

@@ -42,14 +42,14 @@
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/programobject.h>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <locale>
#include <optional>
namespace {
constexpr const char* _loggerCat = "GlobeLabels";
constexpr const char* KeyLabelsFileName = "FileName";
constexpr const double LabelFadeOutLimitAltitudeMeters = 25000.0;
constexpr const double RangeAngularCoefConst = 0.8;
constexpr const float MinOpacityValueConst = 0.009f;
@@ -163,114 +163,72 @@ namespace {
"Label Alignment Option",
"Labels are aligned horizontally or circularly related to the planet."
};
struct [[codegen::Dictionary(GlobeLabelsComponent)]] Parameters {
// The path to the labels file
std::optional<std::filesystem::path> fileName;
// [[codegen::verbatim(LabelsInfo.description)]]
std::optional<bool> labels;
// [[codegen::verbatim(LabelsEnableInfo.description)]]
std::optional<bool> enable;
// [[codegen::verbatim(LabelsFontSizeInfo.description)]]
std::optional<float> labelsFontSize;
// [[codegen::verbatim(LabelsMinSizeInfo.description)]]
std::optional<int> labelsMinSize;
// [[codegen::verbatim(LabelsMaxSizeInfo.description)]]
std::optional<int> labelsMaxSize;
// [[codegen::verbatim(LabelsSizeInfo.description)]]
std::optional<float> labelsSize;
// [[codegen::verbatim(LabelsMinHeightInfo.description)]]
std::optional<float> labelsMinHeight;
// [[codegen::verbatim(LabelsColorInfo.description)]]
std::optional<glm::vec3> labelsColor [[codegen::color()]];
// [[codegen::verbatim(LabelsOpacityInfo.description)]]
std::optional<float> labelsOpacity [[codegen::inrange(0.f, 1.0)]];
// [[codegen::verbatim(LabelsFadeInStartingDistanceInfo.description)]]
std::optional<float> fadeInStartingDistance;
// [[codegen::verbatim(LabelsFadeOutStartingDistanceInfo.description)]]
std::optional<float> fadeOutStartingDistance;
// [[codegen::verbatim(LabelsFadeInEnabledInfo.description)]]
std::optional<bool> labelsFadeInEnabled;
// [[codegen::verbatim(LabelsFadeOutEnabledInfo.description)]]
std::optional<bool> labelsFadeOutEnabled;
// [[codegen::verbatim(LabelsDisableCullingEnabledInfo.description)]]
std::optional<bool> labelsDisableCullingEnabled;
// [[codegen::verbatim(LabelsDistanceEPSInfo.description)]]
std::optional<float> labelsDistanceEPS;
enum class Alignment {
Horizontally,
Circularly
};
// [[codegen::verbatim(LabelAlignmentOptionInfo.description)]]
std::optional<Alignment> labelAlignmentOption;
};
#include "globelabelscomponent_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation GlobeLabelsComponent::Documentation() {
using namespace documentation;
return {
"GlobeLabels Component",
"globebrowsing_globelabelscomponent",
{
{
LabelsInfo.identifier,
new BoolVerifier,
Optional::Yes,
LabelsInfo.description
},
{
LabelsEnableInfo.identifier,
new BoolVerifier,
Optional::Yes,
LabelsEnableInfo.description
},
{
LabelsFontSizeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsFontSizeInfo.description
},
{
LabelsMaxSizeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsMaxSizeInfo.description
},
{
LabelsMinSizeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsMinSizeInfo.description
},
{
LabelsSizeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsSizeInfo.description
},
{
LabelsMinHeightInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsMinHeightInfo.description
},
{
LabelsColorInfo.identifier,
new Color3Verifier,
Optional::Yes,
LabelsColorInfo.description
},
{
LabelsOpacityInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsOpacityInfo.description
},
{
LabelsFadeInStartingDistanceInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsFadeInStartingDistanceInfo.description
},
{
LabelsFadeOutStartingDistanceInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsFadeOutStartingDistanceInfo.description
},
{
LabelsFadeInEnabledInfo.identifier,
new BoolVerifier,
Optional::Yes,
LabelsFadeInEnabledInfo.description
},
{
LabelsFadeOutEnabledInfo.identifier,
new BoolVerifier,
Optional::Yes,
LabelsFadeOutEnabledInfo.description
},
{
LabelsDisableCullingEnabledInfo.identifier,
new BoolVerifier,
Optional::Yes,
LabelsDisableCullingEnabledInfo.description
},
{
LabelsDistanceEPSInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LabelsDistanceEPSInfo.description
},
{
LabelAlignmentOptionInfo.identifier,
new StringVerifier,
Optional::Yes,
LabelAlignmentOptionInfo.description
},
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "globebrowsing_globelabelscomponent";
return doc;
}
GlobeLabelsComponent::GlobeLabelsComponent()
@@ -293,7 +251,7 @@ GlobeLabelsComponent::GlobeLabelsComponent()
, _labelsFadeInEnabled(LabelsFadeInEnabledInfo, false)
, _labelsFadeOutEnabled(LabelsFadeOutEnabledInfo, false)
, _labelsDisableCullingEnabled(LabelsDisableCullingEnabledInfo, false)
, _labelsDistaneEPS(LabelsDistanceEPSInfo, 100000.f, 1000.f, 10000000.f)
, _labelsDistanceEPS(LabelsDistanceEPSInfo, 100000.f, 1000.f, 10000000.f)
, _labelAlignmentOption(
LabelAlignmentOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
@@ -312,7 +270,7 @@ GlobeLabelsComponent::GlobeLabelsComponent()
addProperty(_labelsFadeInEnabled);
addProperty(_labelsFadeOutEnabled);
addProperty(_labelsDisableCullingEnabled);
addProperty(_labelsDistaneEPS);
addProperty(_labelsDistanceEPS);
_labelAlignmentOption.addOption(Horizontally, "Horizontally");
_labelAlignmentOption.addOption(Circularly, "Circularly");
@@ -324,125 +282,45 @@ void GlobeLabelsComponent::initialize(const ghoul::Dictionary& dictionary,
globebrowsing::RenderableGlobe* globe)
{
ZoneScoped
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"GlobeLabelsComponent"
);
_globe = globe;
// Reads labels' file and build cache file if necessary
if (dictionary.isEmpty()) {
return;
}
if (!dictionary.hasValue<std::string>(KeyLabelsFileName)) {
const Parameters p = codegen::bake<Parameters>(dictionary);
if (!p.fileName.has_value()) {
return;
}
std::string labelsFile = dictionary.value<std::string>(KeyLabelsFileName);
bool loadSuccess = loadLabelsData(absPath(labelsFile));
const bool loadSuccess = loadLabelsData(absPath(p.fileName->string()));
if (!loadSuccess) {
return;
}
if (dictionary.hasKey(LabelsEnableInfo.identifier)) {
// In case of the label's dic is present but is disabled
_labelsEnabled = dictionary.value<bool>(LabelsEnableInfo.identifier);
}
else {
// Is the labels dic is enable in the configuration file,
// enables the label automatically.
_labelsEnabled = true;
}
if (dictionary.hasKey(LabelsFontSizeInfo.identifier)) {
_labelsFontSize = static_cast<float>(
dictionary.value<double>(LabelsFontSizeInfo.identifier)
);
_labelsFontSize.onChange([this]() { initializeFonts(); });
}
_labelsEnabled = p.enable.value_or(true);
_labelsFontSize = p.labelsFontSize.value_or(_labelsFontSize);
_labelsFontSize.onChange([this]() { initializeFonts(); });
_labelsSize = p.labelsSize.value_or(_labelsSize);
_labelsMinHeight = p.labelsMinHeight.value_or(_labelsMinHeight);
_labelsColor = p.labelsColor.value_or(_labelsColor);
_labelsOpacity = p.labelsOpacity.value_or(_labelsOpacity);
_labelsFadeInEnabled = p.labelsFadeInEnabled.value_or(_labelsFadeInEnabled);
_labelsFadeInDist = p.fadeInStartingDistance.value_or(_labelsFadeInDist);
_labelsFadeOutEnabled = p.labelsFadeOutEnabled.value_or(_labelsFadeOutEnabled);
_labelsFadeOutDist = p.fadeOutStartingDistance.value_or(_labelsFadeOutDist);
_labelsMinSize = p.labelsMinSize.value_or(_labelsMinSize);
_labelsMaxSize = p.labelsMaxSize.value_or(_labelsMaxSize);
_labelsDisableCullingEnabled =
p.labelsDisableCullingEnabled.value_or(_labelsDisableCullingEnabled);
_labelsDistanceEPS = p.labelsDistanceEPS.value_or(_labelsDistanceEPS);
if (dictionary.hasKey(LabelsSizeInfo.identifier)) {
_labelsSize = static_cast<float>(
dictionary.value<double>(LabelsSizeInfo.identifier)
);
}
if (dictionary.hasKey(LabelsMinHeightInfo.identifier)) {
_labelsMinHeight = static_cast<float>(
dictionary.value<double>(LabelsMinHeightInfo.identifier)
);
}
if (dictionary.hasKey(LabelsColorInfo.identifier)) {
_labelsColor = dictionary.value<glm::dvec3>(LabelsColorInfo.identifier);
}
if (dictionary.hasKey(LabelsOpacityInfo.identifier)) {
_labelsOpacity = static_cast<float>(
dictionary.value<double>(LabelsOpacityInfo.identifier)
);
}
if (dictionary.hasKey(LabelsFadeInEnabledInfo.identifier)) {
_labelsFadeInEnabled = dictionary.value<bool>(LabelsFadeInEnabledInfo.identifier);
}
if (dictionary.hasKey(LabelsFadeInStartingDistanceInfo.identifier)) {
_labelsFadeInDist = static_cast<float>(
dictionary.value<double>(LabelsFadeInStartingDistanceInfo.identifier)
);
}
if (dictionary.hasKey(LabelsFadeOutEnabledInfo.identifier)) {
_labelsFadeOutEnabled = dictionary.value<bool>(
LabelsFadeOutEnabledInfo.identifier
);
}
if (dictionary.hasKey(LabelsFadeOutStartingDistanceInfo.identifier)) {
_labelsFadeOutDist = static_cast<float>(
dictionary.value<double>(LabelsFadeOutStartingDistanceInfo.identifier)
);
}
if (dictionary.hasKey(LabelsMinSizeInfo.identifier)) {
_labelsMinSize = static_cast<int>(
dictionary.value<double>(LabelsMinSizeInfo.identifier)
);
}
if (dictionary.hasKey(LabelsMaxSizeInfo.identifier)) {
_labelsMaxSize = static_cast<int>(
dictionary.value<double>(LabelsMaxSizeInfo.identifier)
);
}
if (dictionary.hasKey(LabelsDisableCullingEnabledInfo.identifier)) {
bool disabled = dictionary.value<bool>(
LabelsDisableCullingEnabledInfo.identifier
);
_labelsDisableCullingEnabled = disabled;
}
if (dictionary.hasKey(LabelsDistanceEPSInfo.identifier)) {
_labelsDistaneEPS = static_cast<float>(
dictionary.value<double>(LabelsDistanceEPSInfo.identifier)
);
}
if (dictionary.hasKey(LabelAlignmentOptionInfo.identifier)) {
std::string alignment =
dictionary.value<std::string>(LabelAlignmentOptionInfo.identifier);
if (alignment == "Horizontally") {
_labelAlignmentOption = Horizontally;
}
else if (alignment == "Circularly" ) {
_labelAlignmentOption = Circularly;
}
else {
LERROR("Unknown alignment option: " + alignment);
if (p.labelAlignmentOption.has_value()) {
switch (*p.labelAlignmentOption) {
case Parameters::Alignment::Horizontally:
_labelAlignmentOption = Horizontally;
break;
case Parameters::Alignment::Circularly:
_labelAlignmentOption = Circularly;
break;
default:
throw ghoul::MissingCaseException();
}
}
@@ -732,7 +610,7 @@ void GlobeLabelsComponent::renderLabels(const RenderData& data,
glm::length(locationPositionWorld - data.camera.positionVec3());
if (_labelsDisableCullingEnabled ||
((distToCamera > (distanceCameraToLabelWorld + _labelsDistaneEPS)) &&
((distToCamera > (distanceCameraToLabelWorld + _labelsDistanceEPS)) &&
isLabelInFrustum(VP, locationPositionWorld)))
{
if (_labelAlignmentOption == Circularly) {

View File

@@ -96,7 +96,7 @@ private:
properties::BoolProperty _labelsFadeInEnabled;
properties::BoolProperty _labelsFadeOutEnabled;
properties::BoolProperty _labelsDisableCullingEnabled;
properties::FloatProperty _labelsDistaneEPS;
properties::FloatProperty _labelsDistanceEPS;
properties::OptionProperty _labelAlignmentOption;
private:

View File

@@ -74,51 +74,33 @@ namespace {
"as an offset from the heightmap. Otherwise, it will be an offset from the "
"globe's reference ellipsoid. The default value is 'false'."
};
struct [[codegen::Dictionary(GlobeTranslation)]] Parameters {
// [[codegen::verbatim(GlobeInfo.description)]]
std::string globe
[[codegen::annotation("A valid scene graph node with a RenderableGlobe")]];
// [[codegen::verbatim(LongitudeInfo.description)]]
std::optional<double> longitude;
// [[codegen::verbatim(LatitudeInfo.description)]]
std::optional<double> latitude;
// [[codegen::verbatim(AltitudeInfo.description)]]
std::optional<double> altitude;
// [[codegen::verbatim(UseHeightmapInfo.description)]]
std::optional<bool> useHeightmap;
};
#include "globetranslation_codegen.cpp"
} // namespace
namespace openspace::globebrowsing {
documentation::Documentation GlobeTranslation::Documentation() {
using namespace openspace::documentation;
return {
"Globe Translation",
"space_translation_globetranslation",
{
{
GlobeInfo.identifier,
new StringAnnotationVerifier(
"A valid scene graph node with a RenderableGlobe"
),
Optional::No,
GlobeInfo.description
},
{
LongitudeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LongitudeInfo.description
},
{
LatitudeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LatitudeInfo.description,
},
{
AltitudeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
AltitudeInfo.description
},
{
UseHeightmapInfo.identifier,
new BoolVerifier,
Optional::Yes,
UseHeightmapInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "space_translation_globetranslation";
return doc;
}
GlobeTranslation::GlobeTranslation(const ghoul::Dictionary& dictionary)
@@ -128,39 +110,28 @@ GlobeTranslation::GlobeTranslation(const ghoul::Dictionary& dictionary)
, _altitude(AltitudeInfo, 0.0, 0.0, 1e12)
, _useHeightmap(UseHeightmapInfo, false)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"GlobeTranslation"
);
_globe = dictionary.value<std::string>(GlobeInfo.identifier);
if (dictionary.hasKey(LongitudeInfo.identifier)) {
_longitude = dictionary.value<double>(LongitudeInfo.identifier);
}
if (dictionary.hasKey(LatitudeInfo.identifier)) {
_latitude = dictionary.value<double>(LatitudeInfo.identifier);
}
if (dictionary.hasKey(AltitudeInfo.identifier)) {
_altitude = dictionary.value<double>(AltitudeInfo.identifier);
}
if (dictionary.hasKey(UseHeightmapInfo.identifier)) {
_useHeightmap = dictionary.value<bool>(UseHeightmapInfo.identifier);
}
const Parameters p = codegen::bake<Parameters>(dictionary);
_globe = p.globe;
_globe.onChange([this]() {
fillAttachedNode();
_positionIsDirty = true;
});
_longitude = p.longitude.value_or(_longitude);
_longitude.onChange([this]() { _positionIsDirty = true; });
_latitude.onChange([this]() { _positionIsDirty = true; });
_altitude.onChange([this]() { _positionIsDirty = true; });
_useHeightmap.onChange([this]() { _positionIsDirty = true; });
addProperty(_longitude);
_latitude = p.latitude.value_or(_latitude);
_latitude.onChange([this]() { _positionIsDirty = true; });
addProperty(_latitude);
_altitude = p.altitude.value_or(_altitude);
_altitude.onChange([this]() { _positionIsDirty = true; });
addProperty(_altitude);
_useHeightmap = p.useHeightmap.value_or(_useHeightmap);
_useHeightmap.onChange([this]() { _positionIsDirty = true; });
addProperty(_useHeightmap);
}

View File

@@ -26,12 +26,9 @@
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <optional>
namespace {
constexpr const char* KeyType = "Type";
constexpr const char* KeyChromaKeyColor = "ChromaKeyColor";
constexpr const char* KeyChromaKeyTolerance = "ChromaKeyTolerance";
constexpr openspace::properties::Property::PropertyInfo ChromaKeyColorInfo = {
"ChromaKeyColor",
"Chroma Key Color",
@@ -50,37 +47,32 @@ namespace {
"Type",
"The type of layer adjustment that is applied to the underlying layer."
};
struct [[codegen::Dictionary(LayerAdjustment)]] Parameters {
enum class Type {
None,
ChromaKey,
TransferFunction
};
// Specifies the type of the adjustment that is applied
std::optional<Type> type;
// Specifies the chroma key used when selecting 'ChromaKey' for the 'Type'
std::optional<glm::vec3> chromaKeyColor [[codegen::color()]];
// Specifies the tolerance to match the color to the chroma key when the
// 'ChromaKey' type is selected for the 'Type'
std::optional<float> chromaKeyTolerance;
};
#include "layeradjustment_codegen.cpp"
} // namespace
namespace openspace::globebrowsing {
documentation::Documentation LayerAdjustment::Documentation() {
using namespace documentation;
return {
"LayerAdjustment",
"globebrowsing_layeradjustment",
{
{
KeyType,
new StringInListVerifier({ "None", "ChromaKey", "TransferFunction" }),
Optional::Yes,
"Specifies the type of the adjustment that is applied"
},
{
KeyChromaKeyColor,
new Color3Verifier,
Optional::Yes,
"Specifies the chroma key used when selecting 'ChromaKey' for the 'Type'."
},
{
KeyChromaKeyTolerance,
new DoubleVerifier,
Optional::Yes,
"Specifies the tolerance to match the color to the chroma key when the "
"'ChromaKey' type is selected for the 'Type'."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "globebrowsing_layeradjustment";
return doc;
}
LayerAdjustment::LayerAdjustment()
@@ -121,31 +113,27 @@ LayerAdjustment::LayerAdjustment()
}
void LayerAdjustment::setValuesFromDictionary(const ghoul::Dictionary& adjustmentDict) {
documentation::testSpecificationAndThrow(
Documentation(),
adjustmentDict,
"LayerAdjustment"
);
const Parameters p = codegen::bake<Parameters>(adjustmentDict);
if (adjustmentDict.hasValue<std::string>(KeyType)) {
std::string dictType = adjustmentDict.value<std::string>(KeyType);
_typeOption = static_cast<int>(
ghoul::from_string<layergroupid::AdjustmentTypeID>(dictType)
);
if (p.type.has_value()) {
switch (*p.type) {
case Parameters::Type::None:
_typeOption = static_cast<int>(layergroupid::AdjustmentTypeID::None);
break;
case Parameters::Type::ChromaKey:
_typeOption = static_cast<int>(layergroupid::AdjustmentTypeID::ChromaKey);
break;
case Parameters::Type::TransferFunction:
_typeOption =
static_cast<int>(layergroupid::AdjustmentTypeID::TransferFunction);
break;
default:
throw ghoul::MissingCaseException();
}
}
if (adjustmentDict.hasValue<glm::dvec3>(KeyChromaKeyColor)) {
glm::vec3 dictChromaKeyColor =
adjustmentDict.value<glm::dvec3>(KeyChromaKeyColor);
_chromaKeyColor = std::move(dictChromaKeyColor);
}
if (adjustmentDict.hasValue<double>(KeyChromaKeyTolerance)) {
float dictChromaKeyTolerance = static_cast<float>(
adjustmentDict.value<double>(KeyChromaKeyTolerance)
);
_chromaKeyTolerance = dictChromaKeyTolerance;
}
_chromaKeyColor = p.chromaKeyColor.value_or(_chromaKeyColor);
_chromaKeyTolerance = p.chromaKeyTolerance.value_or(_chromaKeyTolerance);
}
layergroupid::AdjustmentTypeID LayerAdjustment::type() const {

View File

@@ -949,7 +949,8 @@ TileMetaData RawTileDataReader::tileMetaData(RawTile& rawTile,
bool allIsMissing = true;
for (int y = 0; y < region.numPixels.y; ++y) {
const size_t yi = (static_cast<unsigned long long>(region.numPixels.y) - 1 - y) * bytesPerLine;
const size_t yi =
(static_cast<unsigned long long>(region.numPixels.y) - 1 - y) * bytesPerLine;
size_t i = 0;
for (int x = 0; x < region.numPixels.x; ++x) {
for (size_t raster = 0; raster < _initData.nRasters; ++raster) {

View File

@@ -79,11 +79,6 @@ namespace {
bool isShadowing = false;
};
constexpr const char* KeyRadii = "Radii";
constexpr const char* KeyLayers = "Layers";
constexpr const char* KeyShadowGroup = "ShadowGroup";
constexpr const char* KeyLabels = "Labels";
const openspace::globebrowsing::AABB3 CullingFrustum{
glm::vec3(-1.f, -1.f, 0.f),
glm::vec3( 1.f, 1.f, 1e35)
@@ -232,6 +227,47 @@ namespace {
"This is the number of currently active layers, if this value reaches the "
"maximum, bad things will happen."
};
struct [[codegen::Dictionary(RenderableGlobe)]] Parameters {
// Specifies the radii for this planet. If the Double version of this is used, all
// three radii are assumed to be equal
std::optional<std::variant<glm::dvec3, double>> radii;
// Specifies whether the planet should be shaded by the primary light source or
// not. If it is disabled, all parts of the planet are illuminated
std::optional<bool> performShading;
// A list of all the layers that should be added
std::map<std::string, ghoul::Dictionary> layers
[[codegen::reference("globebrowsing_layermanager")]];
// Specifies information about planetary labels that can be rendered on the
// object's surface
std::optional<ghoul::Dictionary> labels
[[codegen::reference("globebrowsing_globelabelscomponent")]];
struct ShadowGroup {
struct Source {
std::string name;
double radius;
};
std::vector<Source> sources;
struct Caster {
std::string name;
double radius;
};
std::vector<Caster> casters;
};
std::optional<ShadowGroup> shadowGroup;
std::optional<ghoul::Dictionary> rings
[[codegen::reference("globebrowsing_rings_component")]];
std::optional<ghoul::Dictionary> shadows
[[codegen::reference("globebrowsing_shadows_component")]];
};
#include "renderableglobe_codegen.cpp"
} // namespace
using namespace openspace::properties;
@@ -461,48 +497,9 @@ Chunk::Chunk(const TileIndex& ti)
{}
documentation::Documentation RenderableGlobe::Documentation() {
using namespace documentation;
return {
"RenderableGlobe",
"globebrowsing_renderableglobe",
{
{
KeyRadii,
new OrVerifier({ new DoubleVector3Verifier, new DoubleVerifier }),
Optional::Yes,
"Specifies the radii for this planet. If the Double version of this is "
"used, all three radii are assumed to be equal."
},
{
"PerformShading",
new BoolVerifier,
Optional::Yes,
"Specifies whether the planet should be shaded by the primary light "
"source or not. If it is disabled, all parts of the planet are "
"illuminated."
},
{
KeyLayers,
new TableVerifier({
{
"*",
new ReferencingVerifier("globebrowsing_layermanager"),
Optional::Yes,
"Descriptions of the individual layer groups"
}
}),
Optional::Yes,
"A list of all the layers that should be added"
},
{
KeyLabels,
new ReferencingVerifier("globebrowsing_globelabelscomponent"),
Optional::Yes,
"Specifies information about planetary labels that can be rendered on "
"the object's surface."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "globebrowsing_renderableglobe";
return doc;
}
RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
@@ -539,75 +536,52 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
, _ringsComponent(dictionary)
, _shadowComponent(dictionary)
{
const Parameters p = codegen::bake<Parameters>(dictionary);
_generalProperties.currentLodScaleFactor.setReadOnly(true);
// Read the radii in to its own dictionary
if (dictionary.hasValue<glm::dvec3>(KeyRadii)) {
_ellipsoid = Ellipsoid(dictionary.value<glm::dvec3>(KeyRadii));
setBoundingSphere(static_cast<float>(_ellipsoid.maximumRadius()));
}
else if (dictionary.hasValue<double>(KeyRadii)) {
const double radius = dictionary.value<double>(KeyRadii);
_ellipsoid = Ellipsoid({ radius, radius, radius });
setBoundingSphere(static_cast<float>(_ellipsoid.maximumRadius()));
if (p.radii.has_value()) {
if (std::holds_alternative<glm::dvec3>(*p.radii)) {
_ellipsoid = Ellipsoid(std::get<glm::dvec3>(*p.radii));
setBoundingSphere(static_cast<float>(_ellipsoid.maximumRadius()));
}
else if (std::holds_alternative<double>(*p.radii)) {
const double radius = std::get<double>(*p.radii);
_ellipsoid = Ellipsoid({ radius, radius, radius });
setBoundingSphere(static_cast<float>(_ellipsoid.maximumRadius()));
}
else {
throw ghoul::MissingCaseException();
}
}
if (dictionary.hasKey("PerformShading")) {
_generalProperties.performShading = dictionary.value<bool>("PerformShading");
}
_generalProperties.performShading =
p.performShading.value_or(_generalProperties.performShading);
// Init layer manager
ghoul::Dictionary layersDictionary;
if (!dictionary.hasValue<ghoul::Dictionary>(KeyLayers)) {
throw ghoul::RuntimeError(std::string(KeyLayers) + " must be specified");
}
layersDictionary = dictionary.value<ghoul::Dictionary>(KeyLayers);
// @TODO (abock, 2021-03-25) The layermanager should be changed to take a
// std::map<std::string, ghoul::Dictionary> instead and then we don't need to get it
// as a bare dictionary anymore and can use the value from the struct directly
ghoul::Dictionary layersDictionary = dictionary.value<ghoul::Dictionary>("Layers");
_layerManager.initialize(layersDictionary);
addProperty(_generalProperties.performShading);
addProperty(_generalProperties.useAccurateNormals);
// ================================================================
// ======== Reads Shadow (Eclipses) Entries in asset file =========
// ================================================================
if (dictionary.hasValue<ghoul::Dictionary>(KeyShadowGroup)) {
ghoul::Dictionary shadowDictionary =
dictionary.value<ghoul::Dictionary>(KeyShadowGroup);
std::vector<std::pair<std::string, double>> sourceArray;
ghoul::Dictionary sources = shadowDictionary.value<ghoul::Dictionary>("Sources");
for (std::string_view k : sources.keys()) {
ghoul::Dictionary source = sources.value<ghoul::Dictionary>(k);
std::string name = source.value<std::string>("Name");
double radius = source.value<double>("Radius");
sourceArray.emplace_back(name, radius);
}
std::vector<std::pair<std::string, double>> casterArray;
ghoul::Dictionary casters = shadowDictionary.value<ghoul::Dictionary>("Casters");
for (std::string_view k : casters.keys()) {
ghoul::Dictionary caster = casters.value<ghoul::Dictionary>(k);
std::string name = caster.value<std::string>("Name");
double radius = caster.value<double>("Radius");
casterArray.emplace_back(name, radius);
}
if (p.shadowGroup.has_value()) {
std::vector<Ellipsoid::ShadowConfiguration> shadowConfArray;
for (const std::pair<std::string, double>& source : sourceArray) {
for (const std::pair<std::string, double>& caster : casterArray) {
for (const Parameters::ShadowGroup::Source& source : p.shadowGroup->sources) {
for (const Parameters::ShadowGroup::Caster& caster : p.shadowGroup->casters) {
Ellipsoid::ShadowConfiguration sc;
sc.source = source;
sc.caster = caster;
sc.source = std::pair<std::string, double>(source.name, source.radius);
sc.caster = std::pair<std::string, double>(source.name, source.radius);
shadowConfArray.push_back(sc);
}
}
_ellipsoid.setShadowConfigurationArray(shadowConfArray);
}
if (!_ellipsoid.shadowConfigurationArray().empty()) {
addProperty(_generalProperties.eclipseShadowsEnabled);
addProperty(_generalProperties.eclipseHardShadows);
}
@@ -667,22 +641,20 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
_localChunkBuffer.resize(2048);
_traversalMemory.resize(512);
// Labels Dictionary
if (dictionary.hasValue<ghoul::Dictionary>(KeyLabels)) {
_labelsDictionary = dictionary.value<ghoul::Dictionary>(KeyLabels);
}
_labelsDictionary = p.labels.value_or(_labelsDictionary);
// Components
if (dictionary.hasValue<ghoul::Dictionary>("Rings")) {
_hasRings = p.rings.has_value();
if (_hasRings) {
_ringsComponent.initialize();
addPropertySubOwner(_ringsComponent);
_hasRings = true;
}
if (dictionary.hasKey("Shadows")) {
_hasShadows = p.shadows.has_value();
if (_hasShadows) {
_shadowComponent.initialize();
addPropertySubOwner(_shadowComponent);
_hasShadows = true;
_generalProperties.shadowMapping = true;
}
_generalProperties.shadowMapping.onChange(notifyShaderRecompilation);
@@ -1487,7 +1459,7 @@ void RenderableGlobe::renderChunkLocally(const Chunk& chunk, const RenderData& d
program.setUniform("shadowMapTexture", shadowMapUnit);
program.setUniform("zFightingPercentage", _generalProperties.zFightingPercentage);
}
}
else if (_generalProperties.shadowMapping) {
shadowMapUnit.activate();
// JCC: Avoiding a to recompiling the shaders or having more than one

View File

@@ -46,6 +46,7 @@
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/io/texture/texturereader.h>
#include <filesystem>
#include <fstream>
#include <cstdlib>
#include <locale>
@@ -155,90 +156,53 @@ namespace {
"The number of samples used during shadow mapping calculation "
"(Percentage Closer Filtering)."
};
struct [[codegen::Dictionary(RingsComponent)]] Parameters {
// [[codegen::verbatim(TextureInfo.description)]]
std::optional<std::filesystem::path> texture;
// [[codegen::verbatim(TextureFwrdInfo.description)]]
std::optional<std::filesystem::path> textureFwrd;
// [[codegen::verbatim(TextureBckwrdInfo.description)]]
std::optional<std::filesystem::path> textureBckwrd;
// [[codegen::verbatim(TextureUnlitInfo.description)]]
std::optional<std::filesystem::path> textureUnlit;
// [[codegen::verbatim(TextureColorInfo.description)]]
std::optional<std::filesystem::path> textureColor;
// [[codegen::verbatim(TextureTransparencyInfo.description)]]
std::optional<std::filesystem::path> textureTransparency;
// [[codegen::verbatim(SizeInfo.description)]]
std::optional<float> size;
// [[codegen::verbatim(OffsetInfo.description)]]
std::optional<glm::vec2> offset;
// [[codegen::verbatim(NightFactorInfo.description)]]
std::optional<float> nightFactor;
// [[codegen::verbatim(ColorFilterInfo.description)]]
std::optional<float> colorFilter;
// [[codegen::verbatim(ZFightingPercentageInfo.description)]]
std::optional<float> zFightingPercentage;
// [[codegen::verbatim(NumberShadowSamplesInfo.description)]]
std::optional<int> numberShadowSamples;
};
#include "ringscomponent_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RingsComponent::Documentation() {
using namespace documentation;
return {
"Rings Component",
"globebrowsing_rings_component",
{
{
TextureInfo.identifier,
new StringVerifier,
Optional::Yes,
TextureInfo.description
},
{
TextureFwrdInfo.identifier,
new StringVerifier,
Optional::Yes,
TextureFwrdInfo.description
},
{
TextureBckwrdInfo.identifier,
new StringVerifier,
Optional::Yes,
TextureBckwrdInfo.description
},
{
TextureUnlitInfo.identifier,
new StringVerifier,
Optional::Yes,
TextureUnlitInfo.description
},
{
TextureColorInfo.identifier,
new StringVerifier,
Optional::Yes,
TextureColorInfo.description
},
{
TextureTransparencyInfo.identifier,
new StringVerifier,
Optional::Yes,
TextureTransparencyInfo.description
},
{
SizeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
SizeInfo.description
},
{
OffsetInfo.identifier,
new DoubleVector2Verifier,
Optional::Yes,
OffsetInfo.description
},
{
NightFactorInfo.identifier,
new DoubleVerifier,
Optional::Yes,
NightFactorInfo.description
},
{
ColorFilterInfo.identifier,
new DoubleVerifier,
Optional::Yes,
ColorFilterInfo.description
},
{
ZFightingPercentageInfo.identifier,
new DoubleVerifier,
Optional::Yes,
ZFightingPercentageInfo.description
},
{
NumberShadowSamplesInfo.identifier,
new IntVerifier,
Optional::Yes,
NumberShadowSamplesInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "globebrowsing_rings_component";
return doc;
}
RingsComponent::RingsComponent(const ghoul::Dictionary& dictionary)
@@ -265,14 +229,17 @@ RingsComponent::RingsComponent(const ghoul::Dictionary& dictionary)
// term and rather extract the values directly here. This would require a bit of
// a rewrite in the RenderableGlobe class to not create the RingsComponent in the
// class-initializer list though
// @TODO (abock, 2021-03-25) Righto! The RenderableGlobe passes this dictionary
// in as-is so it would be easy to just pass it directly to the initialize method
// instead
_ringsDictionary = dictionary.value<ghoul::Dictionary>("Rings");
}
documentation::testSpecificationAndThrow(
Documentation(),
_ringsDictionary,
"RingsComponent"
);
documentation::testSpecificationAndThrow(
Documentation(),
_ringsDictionary,
"RingsComponent"
);
}
}
void RingsComponent::initialize() {
@@ -280,106 +247,78 @@ void RingsComponent::initialize() {
using ghoul::filesystem::File;
const Parameters p = codegen::bake<Parameters>(_ringsDictionary);
addProperty(_enabled);
_size = static_cast<float>(_ringsDictionary.value<double>(SizeInfo.identifier));
//setBoundingSphere(_size);
_size = p.size.value_or(_size);
_size.onChange([&]() { _planeIsDirty = true; });
addProperty(_size);
if (_ringsDictionary.hasKey(TextureInfo.identifier)) {
_texturePath = absPath(
_ringsDictionary.value<std::string>(TextureInfo.identifier)
);
if (p.texture.has_value()) {
_texturePath = absPath(p.texture->string());
_textureFile = std::make_unique<File>(_texturePath);
_texturePath.onChange([&]() { loadTexture(); });
addProperty(_texturePath);
_textureFile->setCallback([&](const File&) { _textureIsDirty = true; });
}
if (_ringsDictionary.hasKey(TextureFwrdInfo.identifier)) {
_textureFwrdPath = absPath(
_ringsDictionary.value<std::string>(TextureFwrdInfo.identifier)
);
if (p.textureFwrd.has_value()) {
_textureFwrdPath = absPath(p.textureFwrd->string());
_textureFileForwards = std::make_unique<File>(_textureFwrdPath);
_textureFwrdPath.onChange([&]() { loadTexture(); });
addProperty(_textureFwrdPath);
_textureFileForwards->setCallback([&](const File&) { _textureIsDirty = true; });
}
if (_ringsDictionary.hasKey(TextureBckwrdInfo.identifier)) {
_textureBckwrdPath = absPath(
_ringsDictionary.value<std::string>(TextureBckwrdInfo.identifier)
);
if (p.textureBckwrd.has_value()) {
_textureBckwrdPath = absPath(p.textureBckwrd->string());
_textureFileBackwards = std::make_unique<File>(_textureBckwrdPath);
_textureBckwrdPath.onChange([&]() { loadTexture(); });
addProperty(_textureBckwrdPath);
_textureFileBackwards->setCallback([&](const File&) { _textureIsDirty = true; });
}
if (_ringsDictionary.hasKey(TextureUnlitInfo.identifier)) {
_textureUnlitPath = absPath(
_ringsDictionary.value<std::string>(TextureUnlitInfo.identifier)
);
if (p.textureUnlit.has_value()) {
_textureUnlitPath = absPath(p.textureUnlit->string());
_textureFileUnlit = std::make_unique<File>(_textureUnlitPath);
_textureUnlitPath.onChange([&]() { loadTexture(); });
addProperty(_textureUnlitPath);
_textureFileUnlit->setCallback([&](const File&) { _textureIsDirty = true; });
}
if (_ringsDictionary.hasKey(TextureColorInfo.identifier)) {
_textureColorPath = absPath(
_ringsDictionary.value<std::string>(TextureColorInfo.identifier)
);
if (p.textureColor.has_value()) {
_textureColorPath = absPath(p.textureColor->string());
_textureFileColor = std::make_unique<File>(_textureColorPath);
_textureColorPath.onChange([&]() { loadTexture(); });
addProperty(_textureColorPath);
_textureFileColor->setCallback([&](const File&) { _textureIsDirty = true; });
}
if (_ringsDictionary.hasKey(TextureTransparencyInfo.identifier)) {
_textureTransparencyPath = absPath(
_ringsDictionary.value<std::string>(TextureTransparencyInfo.identifier)
);
if (p.textureTransparency.has_value()) {
_textureTransparencyPath = absPath(p.textureTransparency->string());
_textureFileTransparency = std::make_unique<File>(_textureTransparencyPath);
_textureTransparencyPath.onChange([&]() { loadTexture(); });
addProperty(_textureTransparencyPath);
_textureFileTransparency->setCallback([&](const File&) { _textureIsDirty = true; });
}
if (_ringsDictionary.hasValue<glm::dvec2>(OffsetInfo.identifier)) {
_offset = _ringsDictionary.value<glm::dvec2>(OffsetInfo.identifier);
}
_offset = p.offset.value_or(_offset);
addProperty(_offset);
if (_ringsDictionary.hasValue<double>(NightFactorInfo.identifier)) {
_nightFactor = static_cast<float>(
_ringsDictionary.value<double>(NightFactorInfo.identifier)
);
}
_nightFactor = p.nightFactor.value_or(_nightFactor);
addProperty(_nightFactor);
if (_ringsDictionary.hasValue<double>(ColorFilterInfo.identifier)) {
_colorFilter = static_cast<float>(
_ringsDictionary.value<double>(ColorFilterInfo.identifier)
);
}
_colorFilter = p.colorFilter.value_or(_colorFilter);
addProperty(_colorFilter);
// Shadow Mapping Quality Controls
if (_ringsDictionary.hasKey(ZFightingPercentageInfo.identifier)) {
_zFightingPercentage = static_cast<float>(
_ringsDictionary.value<double>(ZFightingPercentageInfo.identifier)
);
}
_zFightingPercentage = p.zFightingPercentage.value_or(_zFightingPercentage);
addProperty(_zFightingPercentage);
if (_ringsDictionary.hasKey(NumberShadowSamplesInfo.identifier)) {
_nShadowSamples = _ringsDictionary.value<int>(NumberShadowSamplesInfo.identifier);
}
_nShadowSamples = p.numberShadowSamples.value_or(_nShadowSamples);
_nShadowSamples.onChange([this]() { compileShadowShader(); });
addProperty(_nShadowSamples);
addProperty(_colorFilter);
}
bool RingsComponent::isReady() const {
@@ -476,7 +415,7 @@ void RingsComponent::draw(const RenderData& data,
_shader->setUniform(_uniformCacheAdvancedRings.colorFilterValue, _colorFilter);
_shader->setUniform(_uniformCacheAdvancedRings.nightFactor, _nightFactor);
_shader->setUniform(_uniformCacheAdvancedRings.sunPosition, _sunPosition);
const glm::dmat4 inverseModelTransform = glm::inverse(modelTransform);
glm::vec3 sunPositionObjectSpace = glm::normalize(
@@ -614,7 +553,7 @@ void RingsComponent::draw(const RenderData& data,
else {
_texture->bind();
}
_geometryOnlyShader->setUniform(_geomUniformCache.ringTexture, ringTextureUnit);
}
@@ -673,7 +612,7 @@ void RingsComponent::loadTexture() {
using namespace ghoul::opengl;
if (!_texturePath.value().empty()) {
std::unique_ptr<Texture> texture = TextureReader::ref().loadTexture(
absPath(_texturePath)
);

View File

@@ -98,7 +98,7 @@ private:
) _uniformCache;
UniformCache(modelViewProjectionMatrix, textureOffset, colorFilterValue, nightFactor,
sunPosition, sunPositionObj, camPositionObj, ringTextureFwrd, ringTextureBckwrd,
ringTextureUnlit, ringTextureColor, ringTextureTransparency, shadowMatrix,
ringTextureUnlit, ringTextureColor, ringTextureTransparency, shadowMatrix,
shadowMapTexture, zFightingPercentage
) _uniformCacheAdvancedRings;
UniformCache(modelViewProjectionMatrix, textureOffset, ringTexture

View File

@@ -141,30 +141,23 @@ namespace {
}
}
}
struct [[codegen::Dictionary(ShadowComponent)]] Parameters {
// [[codegen::verbatim(DistanceFractionInfo.description)]]
std::optional<int> distanceFraction;
// [[codegen::verbatim(DepthMapSizeInfo.description)]]
std::optional<glm::ivec2> depthMapSize;
};
#include "shadowcomponent_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation ShadowComponent::Documentation() {
using namespace documentation;
return {
"ShadowsRing Component",
"globebrowsing_shadows_component",
{
{
DistanceFractionInfo.identifier,
new DoubleVerifier,
Optional::Yes,
DistanceFractionInfo.description
},
{
DepthMapSizeInfo.identifier,
new Vector2ListVerifier<double>,
Optional::Yes,
DepthMapSizeInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "globebrowsing_shadows_component";
return doc;
}
ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary)
@@ -172,37 +165,30 @@ ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary)
, _saveDepthTexture(SaveDepthTextureInfo)
, _distanceFraction(DistanceFractionInfo, 20, 1, 10000)
, _enabled({ "Enabled", "Enabled", "Enable/Disable Shadows" }, true)
, _shadowMapDictionary(dictionary)
{
using ghoul::filesystem::File;
if (dictionary.hasValue<ghoul::Dictionary>("Shadows")) {
// @TODO (abock, 2019-12-16) It would be better to not store the dictionary long
// term and rather extract the values directly here. This would require a bit of
// a rewrite in the RenderableGlobe class to not create the ShadowComponent in the
// class-initializer list though
_shadowMapDictionary = dictionary.value<ghoul::Dictionary>("Shadows");
// @TODO (abock, 2021-03-25) This is not really a nice solution as this key name is
// coded into the RenderableGlobe. Instead, the parent should unpack the dictionary
// and pass the unpacked dictionary in here; Or maybe we don't want a dictionary at
// this state anyway?
if (!dictionary.hasValue<ghoul::Dictionary>("Shadows")) {
return;
}
ghoul::Dictionary d = dictionary.value<ghoul::Dictionary>("Shadows");
const Parameters p = codegen::bake<Parameters>(d);
documentation::testSpecificationAndThrow(
Documentation(),
_shadowMapDictionary,
"ShadowComponent"
);
addProperty(_enabled);
_distanceFraction = p.distanceFraction.value_or(_distanceFraction);
addProperty(_distanceFraction);
if (_shadowMapDictionary.hasKey(DistanceFractionInfo.identifier)) {
_distanceFraction = static_cast<int>(
_shadowMapDictionary.value<double>(DistanceFractionInfo.identifier)
);
}
_saveDepthTexture.onChange([&]() { _executeDepthTextureSave = true; });
if (_shadowMapDictionary.hasKey(DepthMapSizeInfo.identifier)) {
glm::dvec2 depthMapSize =
_shadowMapDictionary.value<glm::dvec2>(DepthMapSizeInfo.identifier);
_shadowDepthTextureWidth = static_cast<int>(depthMapSize.x);
_shadowDepthTextureHeight = static_cast<int>(depthMapSize.y);
if (p.depthMapSize.has_value()) {
_shadowDepthTextureWidth = p.depthMapSize->x;
_shadowDepthTextureHeight = p.depthMapSize->y;
_dynamicDepthTextureRes = false;
}
else {
@@ -212,13 +198,7 @@ ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary)
_dynamicDepthTextureRes = true;
}
_saveDepthTexture.onChange([&]() { _executeDepthTextureSave = true; });
_viewDepthMap = false;
addProperty(_enabled);
addProperty(_saveDepthTexture);
addProperty(_distanceFraction);
}
void ShadowComponent::initialize() {
@@ -323,7 +303,7 @@ RenderData ShadowComponent::begin(const RenderData& data) {
// Saves current state
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_currentFBO);
global::renderEngine->openglStateCache().viewport(_mViewport);
glBindFramebuffer(GL_FRAMEBUFFER, _shadowFBO);
GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE };
glDrawBuffers(3, drawBuffers);

View File

@@ -105,8 +105,6 @@ private:
properties::IntProperty _distanceFraction;
properties::BoolProperty _enabled;
ghoul::Dictionary _shadowMapDictionary;
int _shadowDepthTextureHeight = 4096;
int _shadowDepthTextureWidth = 4096;
bool _dynamicDepthTextureRes = true;

View File

@@ -184,7 +184,8 @@ void IswaCygnet::render(const RenderData& data, RendererTasks&) {
_data.spatialScale.x * _data.offset,
_data.spatialScale.w
);
glm::vec3 position = glm::vec3(pposition) * static_cast<float>(pow(10.f, pposition.w));
glm::vec3 position =
glm::vec3(pposition) * static_cast<float>(pow(10.f, pposition.w));
// Activate shader
_shader->activate();

View File

@@ -22,8 +22,8 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_EXOPLANETS___RENDERABLEHABITABLEZONE___H__
#define __OPENSPACE_MODULE_EXOPLANETS___RENDERABLEHABITABLEZONE___H__
#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLEHABITABLEZONE___H__
#define __OPENSPACE_MODULE_SPACE___RENDERABLEHABITABLEZONE___H__
#include <modules/base/rendering/renderabledisc.h>
#include <openspace/properties/scalar/boolproperty.h>
@@ -72,4 +72,4 @@ private:
} // namespace openspace
#endif // __OPENSPACE_MODULE_EXOPLANETS___RENDERABLEHABITABLEZONE___H__
#endif // __OPENSPACE_MODULE_SPACE___RENDERABLEHABITABLEZONE___H__

View File

@@ -307,7 +307,8 @@ namespace {
};
struct [[codegen::Dictionary(RenderableStars)]] Parameters {
// The path to the SPECK file containing information about the stars being rendered
// The path to the SPECK file containing information about the stars being
// rendered
std::filesystem::path speckFile [[codegen::key("File")]];
// [[codegen::verbatim(ColorTextureInfo.description)]]
@@ -336,8 +337,8 @@ namespace {
// loading. This can be used to trim the dataset's automatic value range
std::optional<float> staticFilter;
// This is the value that is used to replace statically filtered values. Setting this
// value only makes sense if 'StaticFilter' is 'true', as well
// This is the value that is used to replace statically filtered values. Setting
// this value only makes sense if 'StaticFilter' is 'true', as well
std::optional<float> staticFilterReplacement;
// [[codegen::verbatim(MagnitudeExponentInfo.description)]]

View File

@@ -34,19 +34,38 @@
#include <ghoul/glm.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/opengl/programobject.h>
#include <optional>
namespace {
constexpr const char* KeySource = "Source";
constexpr const char* KeyTarget = "Target";
constexpr const char* KeyInstrument = "Instrument";
constexpr const char* KeyColor = "Color";
constexpr const char* KeyColorStart = "Start";
constexpr const char* KeyColorEnd = "End";
struct VBOData {
float position[3];
float color[4];
};
struct [[codegen::Dictionary(RenderableCrawlingLine)]] Parameters {
// Denotes the SPICE name of the source of the renderable crawling line, for
// example, the spacecraft
std::string source;
// Denotes the SPICE name of the target of the crawling line
std::string target;
// Denotes the SPICE name of the instrument that is used to render the crawling
// line
std::string instrument;
struct Color {
// The color at the start of the line
glm::vec4 start [[codegen::color()]];
// The color at the end of the line
glm::vec4 end [[codegen::color()]];
};
// Specifies the colors that are used for the crawling line. One value determines
// the starting color of the line, the second value is the color at the end
Color color;
};
#include "renderablecrawlingline_codegen.cpp"
} // namespace
// @TODO: This class is not properly working anymore and needs to be substantially
@@ -56,76 +75,21 @@ namespace {
namespace openspace {
documentation::Documentation RenderableCrawlingLine::Documentation() {
using namespace documentation;
return {
"RenderableCrawlingLine",
"newhorizons_renderable_crawlingline",
{
{
KeySource,
new StringVerifier,
Optional::No,
"Denotes the SPICE name of the source of the renderable crawling line, "
"for example, the space craft"
},
{
KeyTarget,
new StringVerifier,
Optional::Yes,
"Denotes the SPICE name of the target of the crawling line"
},
{
KeyInstrument,
new StringVerifier,
Optional::No,
"Denotes the SPICE name of the instrument that is used to render the "
"crawling line"
},
{
KeyColor,
new TableVerifier({
{
KeyColorStart,
new Color4Verifier,
Optional::No,
"The color at the start of the line",
},
{
KeyColorEnd,
new Color4Verifier,
Optional::No,
"The color at the end of the line"
}
}),
Optional::No,
"Specifies the colors that are used for the crawling line. One value "
"determines the starting color of the line, the second value is the "
"color at the end of the line."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "newhorizons_renderable_crawlingline";
return doc;
}
RenderableCrawlingLine::RenderableCrawlingLine(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableCrawlingLine"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_source = dictionary.value<std::string>(KeySource);
_target = dictionary.value<std::string>(KeyTarget);
_instrumentName = dictionary.value<std::string>(KeyInstrument);
_lineColorBegin = dictionary.value<glm::dvec4>(
std::string(KeyColor) + "." + KeyColorStart
);
_lineColorEnd = dictionary.value<glm::dvec4>(
std::string(KeyColor) + "." + KeyColorEnd
);
_source = p.source;
_target = p.target;
_instrumentName = p.instrument;
_lineColorBegin = p.color.start;
_lineColorEnd = p.color.end;
}
bool RenderableCrawlingLine::isReady() const {

View File

@@ -35,17 +35,10 @@
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/projection.hpp>
#include <optional>
namespace {
constexpr const char* ProgramName = "FovProgram";
constexpr const char* KeyBody = "Body";
constexpr const char* KeyFrame = "Frame";
constexpr const char* KeyInstrument = "Instrument";
constexpr const char* KeyInstrumentName = "Name";
constexpr const char* KeyInstrumentAberration = "Aberration";
constexpr const char* KeyPotentialTargets = "PotentialTargets";
constexpr const char* KeyFrameConversions = "FrameConversions";
constexpr const char* KeyBoundsSimplification = "SimplifyBounds";
constexpr const std::array<const char*, 9> UniformNames = {
"modelViewProjectionTransform", "defaultColorStart", "defaultColorEnd",
@@ -153,142 +146,55 @@ namespace {
}
}
// Needs support for std::map first for the frameConversions
// struct [[codegen::Dictionary(RenderableFov)]] Parameters {
// // The SPICE name of the source body for which the field of view should be
// // rendered
// std::string body;
//
// // The SPICE name of the source body's frame in which the field of view should be
// // rendered
// std::string frame;
//
// struct Instrument {
// // The SPICE name of the instrument that is rendered
// std::string name;
//
// // The aberration correction that is used for this field of view. The default
// // is 'NONE'
// std::optional<std::string> aberration [[codegen::inlist("NONE",
// "LT", "LT+S", "CN", "CN+S", "XLT", "XLT+S", "XCN", "XCN+S")]];
// };
// // A table describing the instrument whose field of view should be rendered
// Instrument instrument;
//
// // A list of potential targets (specified as SPICE names) that the field of view
// // should be tested against
// std::vector<std::string> potentialTargets;
//
// // A list of frame conversions that should be registered with the SpiceManager
// std::optional<std::vector<std::string>> frameConversions;
//
// // [[codegen::verbatim(LineWidthInfo.description)]]
// std::optional<double> lineWidth;
//
// // [[codegen::verbatim(StandoffDistanceInfo.description)]]
// std::optional<double> standOffDistance;
//
// // If this value is set to 'true' the field-of-views bounds values will be
// // simplified on load. Bound vectors will be removed if they are the strict linear
// // interpolation between the two neighboring vectors. This value is disabled on
// // default
// std::optional<bool> simplifyBounds;
// };
//#include "renderablefov_codegen.cpp"
struct [[codegen::Dictionary(RenderableFov)]] Parameters {
// The SPICE name of the source body for which the field of view should be
// rendered
std::string body;
// The SPICE name of the source body's frame in which the field of view should be
// rendered
std::string frame;
struct Instrument {
// The SPICE name of the instrument that is rendered
std::string name;
// The aberration correction that is used for this field of view. The default
// is 'NONE'
std::optional<std::string> aberration [[codegen::inlist("NONE",
"LT", "LT+S", "CN", "CN+S", "XLT", "XLT+S", "XCN", "XCN+S")]];
};
// A table describing the instrument whose field of view should be rendered
Instrument instrument;
// A list of potential targets (specified as SPICE names) that the field of view
// should be tested against
std::vector<std::string> potentialTargets;
// A list of frame conversions that should be registered with the SpiceManager
std::optional<std::map<std::string, std::string>> frameConversions;
// [[codegen::verbatim(LineWidthInfo.description)]]
std::optional<float> lineWidth;
// [[codegen::verbatim(StandoffDistanceInfo.description)]]
std::optional<float> standOffDistance;
// If this value is set to 'true' the field-of-views bounds values will be
// simplified on load. Bound vectors will be removed if they are the strict linear
// interpolation between the two neighboring vectors. This value is disabled on
// default
std::optional<bool> simplifyBounds;
};
#include "renderablefov_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderableFov::Documentation() {
using namespace documentation;
return {
"RenderableFieldOfView",
"newhorizons_renderable_fieldofview",
{
{
KeyBody,
new StringVerifier,
Optional::No,
"The SPICE name of the source body for which the field of view should be "
"rendered."
},
{
KeyFrame,
new StringVerifier,
Optional::No,
"The SPICE name of the source body's frame in which the field of view "
"should be rendered."
},
{
KeyInstrument,
new TableVerifier({
{
KeyInstrumentName,
new StringVerifier,
Optional::No,
"The SPICE name of the instrument that is rendered"
},
{
KeyInstrumentAberration,
new StringInListVerifier({
// Taken from SpiceManager::AberrationCorrection
"NONE",
"LT", "LT+S",
"CN", "CN+S",
"XLT", "XLT+S",
"XCN", "XCN+S"
}),
Optional::Yes,
"The aberration correction that is used for this field of view. "
"The default is 'NONE'."
}
}),
Optional::No,
"A table describing the instrument whose field of view should be "
"rendered."
},
{
KeyPotentialTargets,
new StringListVerifier,
Optional::No,
"A list of potential targets (specified as SPICE names) that the field "
"of view should be tested against."
},
{
KeyFrameConversions,
new TableVerifier({
{
DocumentationEntry::Wildcard,
new StringVerifier,
Optional::No
}
}),
Optional::Yes,
"A list of frame conversions that should be registered with the "
"SpiceManager."
},
{
LineWidthInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LineWidthInfo.description
},
{
StandoffDistanceInfo.identifier,
new DoubleVerifier,
Optional::Yes,
StandoffDistanceInfo.description
},
{
KeyBoundsSimplification,
new BoolVerifier,
Optional::Yes,
"If this value is set to 'true' the field-of-views bounds values will be "
"simplified on load. Bound vectors will be removed if they are the "
"strict linear interpolation between the two neighboring vectors. This "
"value is disabled on default."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "newhorizons_renderable_fieldofview";
return doc;
}
@@ -342,61 +248,37 @@ RenderableFov::RenderableFov(const ghoul::Dictionary& dictionary)
}
})
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableFov"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_instrument.spacecraft = dictionary.value<std::string>(KeyBody);
_instrument.referenceFrame = dictionary.value<std::string>(KeyFrame);
_instrument.name = dictionary.value<std::string>(
std::string(KeyInstrument) + "." + KeyInstrumentName
);
std::string ia = std::string(KeyInstrument) + "." + KeyInstrumentAberration;
if (dictionary.hasValue<std::string>(ia)) {
const std::string& ac = dictionary.value<std::string>(ia);
_instrument.aberrationCorrection = SpiceManager::AberrationCorrection(ac);
_instrument.spacecraft = p.body;
_instrument.referenceFrame = p.frame;
_instrument.name = p.instrument.name;
if (p.instrument.aberration.has_value()) {
_instrument.aberrationCorrection = SpiceManager::AberrationCorrection(
*p.instrument.aberration
);
}
ghoul::Dictionary pt = dictionary.value<ghoul::Dictionary>(KeyPotentialTargets);
_instrument.potentialTargets.reserve(pt.size());
for (size_t i = 1; i <= pt.size(); ++i) {
std::string target = pt.value<std::string>(std::to_string(i));
_instrument.potentialTargets.push_back(std::move(target));
}
_instrument.potentialTargets = p.potentialTargets;
if (dictionary.hasKey(KeyFrameConversions)) {
ghoul::Dictionary fc = dictionary.value<ghoul::Dictionary>(KeyFrameConversions);
for (std::string_view key : fc.keys()) {
if (p.frameConversions.has_value()) {
for (const std::pair<const std::string, std::string>& fc : *p.frameConversions) {
global::moduleEngine->module<SpacecraftInstrumentsModule>()->addFrame(
std::string(key),
fc.value<std::string>(key)
fc.first,
fc.second
);
}
}
if (dictionary.hasKey(LineWidthInfo.identifier)) {
_lineWidth = static_cast<float>(dictionary.value<double>(
LineWidthInfo.identifier
));
}
if (dictionary.hasKey(StandoffDistanceInfo.identifier)) {
_standOffDistance = static_cast<float>(dictionary.value<double>(
StandoffDistanceInfo.identifier
));
}
if (dictionary.hasKey(KeyBoundsSimplification)) {
_simplifyBounds = dictionary.value<bool>(KeyBoundsSimplification);
}
_lineWidth = p.lineWidth.value_or(_lineWidth);
addProperty(_lineWidth);
addProperty(_drawSolid);
_standOffDistance = p.standOffDistance.value_or(_standOffDistance);
addProperty(_standOffDistance);
_simplifyBounds = p.simplifyBounds.value_or(_simplifyBounds);
addProperty(_drawSolid);
addProperty(_colors.defaultStart);
addProperty(_colors.defaultEnd);

View File

@@ -279,7 +279,6 @@ void RenderablePlaneProjection::updatePlane(const Image& img, double currentTime
glm::vec3(projection[1]),
glm::vec3(projection[2]),
glm::vec3(projection[3])
};
const GLfloat vertex_data[] = {
// square of two triangles drawn within fov in target coordinates

View File

@@ -39,6 +39,7 @@
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureconversion.h>
#include <ghoul/opengl/textureunit.h>
#include <optional>
namespace {
constexpr const char* _loggerCat = "RenderablePlanetProjection";
@@ -56,8 +57,6 @@ namespace {
"boresight", "_radius", "_segments"
};
constexpr const char* KeyGeometry = "Geometry";
constexpr const char* KeyProjection = "Projection";
constexpr const char* KeyRadius = "Geometry.Radius";
constexpr const char* _mainFrame = "GALACTIC";
@@ -134,67 +133,42 @@ namespace {
"Clear Projection Buffer",
"Remove all pending projections from the buffer"
};
struct [[codegen::Dictionary(RenderablePlanetProjection)]] Parameters {
// The geometry that is used for rendering this planet
ghoul::Dictionary geometry [[codegen::reference("space_geometry_planet")]];
// Contains information about projecting onto this planet
ghoul::Dictionary projection
[[codegen::reference("newhorizons_projectioncomponent")]];
// [[codegen::verbatim(ColorTexturePathsInfo.description)]]
std::vector<std::string> colorTexturePaths;
// [[codegen::verbatim(HeightTexturePathsInfo.description)]]
std::vector<std::string> heightTexturePaths;
// [[codegen::verbatim(HeightExaggerationInfo.description)]]
std::optional<float> heightExaggeration;
// [[codegen::verbatim(MeridianShiftInfo.description)]]
std::optional<bool> meridianShift;
// [[codegen::verbatim(AmbientBrightnessInfo.description)]]
std::optional<double> ambientBrightness;
// [[codegen::verbatim(MaxProjectionsPerFrameInfo.description)]]
std::optional<int> maxProjectionsPerFrame;
};
#include "renderableplanetprojection_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderablePlanetProjection::Documentation() {
using namespace openspace::documentation;
return {
"Renderable Planet Projection",
"newhorizons_renderable_planetprojection",
{
{
KeyGeometry,
new ReferencingVerifier("space_geometry_planet"),
Optional::No,
"The geometry that is used for rendering this planet.",
},
{
KeyProjection,
new ReferencingVerifier("newhorizons_projectioncomponent"),
Optional::No,
"Contains information about projecting onto this planet.",
},
{
ColorTexturePathsInfo.identifier,
new StringListVerifier,
Optional::No,
ColorTexturePathsInfo.description
},
{
HeightTexturePathsInfo.identifier,
new StringListVerifier,
Optional::Yes,
HeightTexturePathsInfo.description
},
{
HeightExaggerationInfo.identifier,
new DoubleVerifier,
Optional::Yes,
HeightExaggerationInfo.description
},
{
MeridianShiftInfo.identifier,
new BoolVerifier,
Optional::Yes,
MeridianShiftInfo.description
},
{
AmbientBrightnessInfo.identifier,
new DoubleVerifier,
Optional::Yes,
AmbientBrightnessInfo.description
},
{
MaxProjectionsPerFrameInfo.identifier,
new DoubleVerifier,
Optional::Yes,
MaxProjectionsPerFrameInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "newhorizons_renderable_planetprojection";
return doc;
}
RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& dict)
@@ -210,43 +184,24 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary&
, _projectionsInBuffer(ProjectionsInBufferInfo, 0, 1, 32)
, _clearProjectionBuffer(ClearProjectionBufferInfo)
{
documentation::testSpecificationAndThrow(
Documentation(),
dict,
"RenderablePlanetProjection"
);
const Parameters p = codegen::bake<Parameters>(dict);
ghoul::Dictionary geometryDictionary = dict.value<ghoul::Dictionary>(KeyGeometry);
_geometry = planetgeometry::PlanetGeometry::createFromDictionary(geometryDictionary);
_projectionComponent.initialize(
identifier(),
dict.value<ghoul::Dictionary>(KeyProjection)
);
_geometry = planetgeometry::PlanetGeometry::createFromDictionary(p.geometry);
_projectionComponent.initialize(identifier(), p.projection);
_colorTexturePaths.addOption(0, NoImageText);
_colorTexturePaths.onChange([this](){ _colorTextureDirty = true; });
addProperty(_colorTexturePaths);
if (dict.hasValue<ghoul::Dictionary>(ColorTexturePathsInfo.identifier)) {
const ghoul::Dictionary& value = dict.value<ghoul::Dictionary>(
ColorTexturePathsInfo.identifier
for (const std::string& t : p.colorTexturePaths) {
_colorTexturePaths.addOption(
static_cast<int>(_colorTexturePaths.options().size()),
t
);
for (size_t i = 1; i <= value.size(); ++i) {
std::string texture = absPath(value.value<std::string>(std::to_string(i)));
_colorTexturePaths.addOption(
// as we started with 0, this works
static_cast<int>(_colorTexturePaths.options().size()),
texture
);
_colorTexturePaths = static_cast<int>(
_colorTexturePaths.options().size() - 1
);
}
}
_colorTexturePaths = static_cast<int>(
_colorTexturePaths.options().size() - 1
);
_addColorTexturePath.onChange([this]() {
if (!_addColorTexturePath.value().empty()) {
@@ -271,27 +226,15 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary&
_heightMapTexturePaths.onChange([this]() { _heightMapTextureDirty = true; });
addProperty(_heightMapTexturePaths);
if (dict.hasValue<ghoul::Dictionary>(HeightTexturePathsInfo.identifier)) {
const ghoul::Dictionary& value = dict.value<ghoul::Dictionary>(
HeightTexturePathsInfo.identifier
for (const std::string& t : p.heightTexturePaths) {
_heightMapTexturePaths.addOption(
static_cast<int>(_heightMapTexturePaths.options().size()),
t
);
for (size_t i = 1; i <= value.size(); ++i) {
std::string texture = absPath(value.value<std::string>(std::to_string(i)));
_heightMapTexturePaths.addOption(
// as we started with 0, this works
static_cast<int>(_heightMapTexturePaths.options().size()),
texture
);
_heightMapTexturePaths = static_cast<int>(
_heightMapTexturePaths.options().size() - 1
);
}
}
_heightMapTexturePaths = static_cast<int>(
_heightMapTexturePaths.options().size() - 1
);
_addHeightMapTexturePath.onChange([this]() {
if (!_addHeightMapTexturePath.value().empty()) {
_heightMapTexturePaths.addOption(
@@ -308,11 +251,12 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary&
});
addProperty(_addHeightMapTexturePath);
_meridianShift = p.meridianShift.value_or(_meridianShift);
addProperty(_meridianShift);
if (dict.hasValue<bool>(MeridianShiftInfo.identifier)) {
_meridianShift = dict.value<bool>(MeridianShiftInfo.identifier);
}
// @TODO (abock, 2021-03-26) Poking into the Geometry dictionary is not really
// optimal as we don't have local control over how the dictionary is checked. We
// should instead ask the geometry whether it has a radius or not
double radius = std::pow(10.0, 9.0);
if (dict.hasValue<double>(KeyRadius)) {
radius = dict.value<double>(KeyRadius);
@@ -322,25 +266,16 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary&
addPropertySubOwner(_geometry.get());
addPropertySubOwner(_projectionComponent);
if (dict.hasKey(HeightExaggerationInfo.identifier)) {
_heightExaggeration = static_cast<float>(
dict.value<double>(HeightExaggerationInfo.identifier)
);
}
if (dict.hasKey(MaxProjectionsPerFrameInfo.identifier)) {
_maxProjectionsPerFrame = static_cast<int>(
dict.value<double>(MaxProjectionsPerFrameInfo.identifier)
);
}
_heightExaggeration = p.heightExaggeration.value_or(_heightExaggeration);
addProperty(_heightExaggeration);
addProperty(_meridianShift);
addProperty(_ambientBrightness);
_maxProjectionsPerFrame = p.maxProjectionsPerFrame.value_or(_maxProjectionsPerFrame);
addProperty(_maxProjectionsPerFrame);
addProperty(_ambientBrightness);
addProperty(_projectionsInBuffer);
_clearProjectionBuffer.onChange([this]() {
_imageTimes.clear();
_projectionsInBuffer = static_cast<int>(_imageTimes.size());

View File

@@ -38,39 +38,29 @@
namespace {
constexpr const char* _loggerCat = "HttpSynchronization";
constexpr const char* KeyIdentifier = "Identifier";
constexpr const char* KeyVersion = "Version";
constexpr const char* TempSuffix = ".tmp";
constexpr const char* QueryKeyIdentifier = "identifier";
constexpr const char* QueryKeyFileVersion = "file_version";
constexpr const char* QueryKeyApplicationVersion = "application_version";
constexpr const int ApplicationVersion = 1;
struct [[codegen::Dictionary(HttpSynchronization)]] Parameters {
// A unique identifier for this resource
std::string identifier;
// The version of this resource
int version;
};
#include "httpsynchronization_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation HttpSynchronization::Documentation() {
using namespace openspace::documentation;
return {
"HttpSynchronization",
"http_synchronization",
{
{
KeyIdentifier,
new StringVerifier,
Optional::No,
"A unique identifier for this resource"
},
{
KeyVersion,
new IntVerifier,
Optional::No,
"The version of this resource"
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "http_synchronization";
return doc;
}
HttpSynchronization::HttpSynchronization(const ghoul::Dictionary& dict,
@@ -81,14 +71,10 @@ HttpSynchronization::HttpSynchronization(const ghoul::Dictionary& dict,
, _synchronizationRoot(std::move(synchronizationRoot))
, _synchronizationRepositories(std::move(synchronizationRepositories))
{
documentation::testSpecificationAndThrow(
Documentation(),
dict,
"HttpSynchronization"
);
const Parameters p = codegen::bake<Parameters>(dict);
_identifier = dict.value<std::string>(KeyIdentifier);
_version = static_cast<int>(dict.value<double>(KeyVersion));
_identifier = p.identifier;
_version = p.version;
}
HttpSynchronization::~HttpSynchronization() {

View File

@@ -37,6 +37,8 @@
#include <fstream>
#include <numeric>
#include <memory>
#include <optional>
#include <variant>
namespace {
constexpr const char* KeyUrl = "Url";
@@ -46,61 +48,44 @@ namespace {
constexpr const char* KeyFilename = "Filename";
constexpr const char* TempSuffix = ".tmp";
struct [[codegen::Dictionary(UrlSynchronization)]] Parameters {
// The URL or urls from where the files are downloaded. If multiple URLs are
// provided, all files will be downloaded to the same directory
std::variant<std::string, std::vector<std::string>> url;
// This optional identifier will be part of the used folder structure and, if
// provided, can be used to manually find the downloaded folder in the
// synchronization folder. If this value is not specified, 'UseHash' has to be set
// to 'true'
std::optional<std::string> identifier;
// If this value is set to 'true' and it is not overwritten by the global
// settings, the file(s) pointed to by this URLSynchronization will always be
// downloaded, thus overwriting the local files. This is useful for files that are
// updated regularly remotely and should be fetch at every startup
std::optional<bool> forceOverride [[codegen::key("override")]];
// If this value is set to 'true' (the default), the hash of the URL is appended
// to the directory name to produce a unique directory under all circumstances. If
// this is not desired, the URLSynchronization use the bare directory name alone
// if this value is 'false'. If this value is 'false', the identifier has to be
// specified
std::optional<bool> useHash;
// Optional to provide filename to override the one which is otherwise
// automatically created from the url
std::optional<std::string> filename;
};
#include "urlsynchronization_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation UrlSynchronization::Documentation() {
using namespace openspace::documentation;
return {
"Url Synchronization",
"sync_synchronization_url",
{
{
KeyUrl,
new OrVerifier({ new StringVerifier, new StringListVerifier }),
Optional::No,
"The URL or urls from where the files are downloaded. If multiple URLs "
"are provided, all files will be downloaded to the same directory."
},
{
KeyIdentifier,
new StringVerifier,
Optional::Yes,
"This optional identifier will be part of the used folder structure and, "
"if provided, can be used to manually find the downloaded folder in the "
"synchronization folder. If this value is not specified, 'UseHash' has "
"to be set to 'true'."
},
{
KeyOverride,
new BoolVerifier,
Optional::Yes,
"If this value is set to 'true' and it is not overwritten by the global "
"settings, the file(s) pointed to by this URLSynchronization will always "
"be downloaded, thus overwriting the local files. This is useful for "
"files that are updated regularly remotely and should be fetch at every "
"startup."
},
{
KeyUseHash,
new BoolVerifier,
Optional::Yes,
"If this value is set to 'true' (the default), the hash of the URL is "
"appended to the directory name to produce a unique directory under all "
"circumstances. If this is not desired, the URLSynchronization use the "
"bare directory name alone if this value is 'false'. If this value is "
"'false', the identifier has to be specified."
},
{
KeyFilename,
new StringVerifier,
Optional::Yes,
"Optional to provide filename to override the one which is otherwise "
"automatically created from the url. "
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "sync_synchronization_url";
return doc;
}
UrlSynchronization::UrlSynchronization(const ghoul::Dictionary& dict,
@@ -108,43 +93,33 @@ UrlSynchronization::UrlSynchronization(const ghoul::Dictionary& dict,
: ResourceSynchronization(dict)
, _synchronizationRoot(std::move(synchronizationRoot))
{
documentation::testSpecificationAndThrow(
Documentation(),
dict,
"UrlSynchroniztion"
);
const Parameters p = codegen::bake<Parameters>(dict);
if (dict.hasValue<std::string>(KeyUrl)) {
_urls.push_back(dict.value<std::string>(KeyUrl));
if (std::holds_alternative<std::string>(p.url)) {
_urls.push_back(std::get<std::string>(p.url));
}
else if (std::holds_alternative<std::vector<std::string>>(p.url)) {
_urls = std::get<std::vector<std::string>>(p.url);
}
else {
ghoul::Dictionary urls = dict.value<ghoul::Dictionary>(KeyUrl);
for (size_t i = 1; i <= urls.size(); ++i) {
std::string url = urls.value<std::string>(std::to_string(i));
_urls.push_back(std::move(url));
}
throw ghoul::MissingCaseException();
}
if (dict.hasValue<std::string>(KeyFilename)) {
_filename = dict.value<std::string>(KeyFilename);
}
_filename = p.filename.value_or(_filename);
bool useHash = true;
if (dict.hasValue<bool>(KeyUseHash)) {
useHash = dict.value<bool>(KeyUseHash);
}
bool useHash = p.useHash.value_or(true);
// We just merge all of the URLs together to generate a hash, it's not as stable to
// reordering URLs, but every other solution would be more error prone
std::string urlConcat = std::accumulate(_urls.begin(), _urls.end(), std::string());
size_t hash = std::hash<std::string>{}(urlConcat);
if (dict.hasValue<std::string>(KeyIdentifier)) {
std::string ident = dict.value<std::string>(KeyIdentifier);
if (p.identifier.has_value()) {
if (useHash) {
_identifier = std::move(ident) + "(" + std::to_string(hash) + ")";
_identifier = *p.identifier + "(" + std::to_string(hash) + ")";
}
else {
_identifier = std::move(ident);
_identifier = *p.identifier;
}
}
else {
@@ -154,17 +129,15 @@ UrlSynchronization::UrlSynchronization(const ghoul::Dictionary& dict,
else {
documentation::TestResult res;
res.success = false;
res.offenses.push_back({
std::string(KeyIdentifier) + "|" + KeyUseHash,
documentation::TestResult::Offense::Reason::MissingKey
});
documentation::TestResult::Offense o;
o.offender = std::string(KeyIdentifier) + "|" + KeyUseHash;
o.reason = documentation::TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
throw documentation::SpecificationError(std::move(res), "UrlSynchronization");
}
}
if (dict.hasValue<bool>(KeyOverride)) {
_forceOverride = dict.value<bool>(KeyOverride);
}
_forceOverride = p.forceOverride.value_or(_forceOverride);
}
UrlSynchronization::~UrlSynchronization() {
@@ -193,7 +166,6 @@ void UrlSynchronization::start() {
std::vector<std::unique_ptr<AsyncHttpFileDownload>> downloads;
for (const std::string& url : _urls) {
if (_filename.empty()) {
const size_t lastSlash = url.find_last_of('/');
std::string lastPartOfUrl = url.substr(lastSlash + 1);

View File

@@ -31,6 +31,7 @@
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scene.h>
#include <ghoul/logging/logmanager.h>
#include <optional>
#include <string>
namespace {
@@ -56,36 +57,26 @@ namespace {
"Property to define a custom unit descriptor to use to describe the distance "
"value. Defaults to the units SI descriptor if not specified."
};
}
struct [[codegen::Dictionary(RenderableDistanceLabel)]] Parameters {
// [[codegen::verbatim(NodeLineInfo.description)]]
std::string nodeLine;
// [[codegen::verbatim(DistanceUnitInfo.description)]]
std::optional<int> distanceUnit;
// [[codegen::verbatim(CustomUnitDescriptorInfo.description)]]
std::optional<std::string> customUnitDescriptor;
};
#include "renderabledistancelabel_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderableDistanceLabel::Documentation() {
using namespace documentation;
return {
"Renderable Distance Label",
"vislab_renderable_distance_label",
{
{
NodeLineInfo.identifier,
new StringVerifier,
Optional::No,
NodeLineInfo.description
},
{
DistanceUnitInfo.identifier,
new IntVerifier,
Optional::Yes,
DistanceUnitInfo.description
},
{
CustomUnitDescriptorInfo.identifier,
new StringVerifier,
Optional::Yes,
CustomUnitDescriptorInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "vislab_renderable_distance_label";
return doc;
}
RenderableDistanceLabel::RenderableDistanceLabel(const ghoul::Dictionary& dictionary)
@@ -94,27 +85,16 @@ RenderableDistanceLabel::RenderableDistanceLabel(const ghoul::Dictionary& dictio
, _distanceUnit(DistanceUnitInfo, 1, 0, 11)
, _customUnitDescriptor(CustomUnitDescriptorInfo)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableDistanceLabel"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
if (dictionary.hasKey(NodeLineInfo.identifier)) {
_nodelineId = dictionary.value<std::string>(NodeLineInfo.identifier);
addProperty(_nodelineId);
}
if (dictionary.hasKey(DistanceUnitInfo.identifier)) {
_distanceUnit = static_cast<int>(
dictionary.value<double>(DistanceUnitInfo.identifier)
);
addProperty(_distanceUnit);
}
if (dictionary.hasKey(CustomUnitDescriptorInfo.identifier)) {
_customUnitDescriptor =
dictionary.value<std::string>(CustomUnitDescriptorInfo.identifier);
addProperty(_customUnitDescriptor);
}
_nodelineId = p.nodeLine;
addProperty(_nodelineId);
_distanceUnit = p.distanceUnit.value_or(_distanceUnit);
addProperty(_distanceUnit);
_customUnitDescriptor = p.customUnitDescriptor.value_or(_customUnitDescriptor);
addProperty(_customUnitDescriptor);
}
void RenderableDistanceLabel::update(const UpdateData&) {

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* Copyright (c) 2014-2021 *
* *
* 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 *

View File

@@ -28,20 +28,35 @@
#include <openspace/documentation/documentation.h>
#include <openspace/util/time.h>
#include <ghoul/misc/dictionary.h>
#include <optional>
namespace {
constexpr const char* KeyDimensions = "Dimensions";
constexpr const char* KeyLowerDomainBound = "LowerDomainBound";
constexpr const char* KeyUpperDomainBound = "UpperDomainBound";
struct [[codegen::Dictionary(RawVolumeMetaData)]] Parameters {
// Specifies the number of grid cells in each dimension
glm::ivec3 dimensions;
constexpr const char* KeyMinValue = "MinValue";
constexpr const char* KeyMaxValue = "MaxValue";
// Specifies the unit used to specity the domain
std::optional<std::string> domainUnit;
constexpr const char* KeyTime = "Time";
constexpr const char* KeyDomainUnit = "DomainUnit";
constexpr const char* KeyValueUnit = "ValueUnit";
// Specifies the lower domain bounds in the model coordinate system
std::optional<glm::vec3> lowerDomainBound;
constexpr const char* KeyGridType = "GridType";
// Specifies the upper domain bounds in the model coordinate system
std::optional<glm::vec3> upperDomainBound;
// Specifies the time on the format YYYY-MM-DDTHH:MM:SS.000Z
std::optional<std::string> time;
// Specifies the unit used to specity the value
std::optional<std::string> valueUnit;
// Specifies the minimum value stored in the volume
std::optional<float> minValue;
// Specifies the maximum value stored in the volume
std::optional<float> maxValue;
};
#include "rawvolumemetadata_codegen.cpp"
} // namespace
namespace openspace::volume {
@@ -49,51 +64,53 @@ namespace openspace::volume {
RawVolumeMetadata RawVolumeMetadata::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RawVolumeMetadata"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
RawVolumeMetadata metadata;
metadata.dimensions = dictionary.value<glm::dvec3>(KeyDimensions);
metadata.dimensions = p.dimensions;
metadata.hasDomainBounds = dictionary.hasValue<glm::dvec3>(KeyLowerDomainBound) &&
dictionary.hasValue<glm::dvec3>(KeyUpperDomainBound);
metadata.hasDomainBounds =
p.lowerDomainBound.has_value() &&
p.upperDomainBound.has_value();
if (metadata.hasDomainBounds) {
metadata.lowerDomainBound = dictionary.value<glm::dvec3>(KeyLowerDomainBound);
metadata.upperDomainBound = dictionary.value<glm::dvec3>(KeyUpperDomainBound);
metadata.lowerDomainBound = *p.lowerDomainBound;
metadata.upperDomainBound = *p.upperDomainBound;
}
metadata.hasDomainUnit = static_cast<float>(
dictionary.hasValue<double>(KeyDomainUnit)
);
metadata.hasDomainUnit = p.domainUnit.has_value();
if (metadata.hasDomainUnit) {
metadata.domainUnit = dictionary.value<std::string>(KeyDomainUnit);
metadata.domainUnit = *p.domainUnit;
}
metadata.hasValueRange = dictionary.hasValue<double>(KeyMinValue) &&
dictionary.hasValue<double>(KeyMaxValue);
metadata.hasValueRange = p.minValue.has_value() && p.maxValue.has_value();
if (metadata.hasValueRange) {
metadata.minValue = static_cast<float>(dictionary.value<double>(KeyMinValue));
metadata.maxValue = static_cast<float>(dictionary.value<double>(KeyMaxValue));
metadata.minValue = *p.minValue;
metadata.maxValue = *p.maxValue;
}
metadata.hasValueUnit = static_cast<float>(dictionary.hasValue<double>(KeyValueUnit));
metadata.hasValueUnit = p.valueUnit.has_value();
if (metadata.hasValueUnit) {
metadata.valueUnit = dictionary.value<std::string>(KeyValueUnit);
metadata.valueUnit = *p.valueUnit;
}
metadata.hasTime = dictionary.hasValue<std::string>(KeyTime);
metadata.hasTime = p.time.has_value();
if (metadata.hasTime) {
std::string timeString = dictionary.value<std::string>(KeyTime);
metadata.time = Time::convertTime(timeString);
metadata.time = Time::convertTime(*p.time);
}
return metadata;
}
ghoul::Dictionary RawVolumeMetadata::dictionary() {
constexpr const char* KeyDimensions = "Dimensions";
constexpr const char* KeyLowerDomainBound = "LowerDomainBound";
constexpr const char* KeyUpperDomainBound = "UpperDomainBound";
constexpr const char* KeyMinValue = "MinValue";
constexpr const char* KeyMaxValue = "MaxValue";
constexpr const char* KeyTime = "Time";
constexpr const char* KeyDomainUnit = "DomainUnit";
constexpr const char* KeyValueUnit = "ValueUnit";
constexpr const char* KeyGridType = "GridType";
ghoul::Dictionary dict;
dict.setValue(KeyDimensions, glm::dvec3(dimensions));
dict.setValue(KeyGridType, gridTypeToString(gridType));
@@ -126,61 +143,9 @@ ghoul::Dictionary RawVolumeMetadata::dictionary() {
}
documentation::Documentation RawVolumeMetadata::Documentation() {
using namespace documentation;
return {
"RawVolumeMetadata",
"volume_rawvolumemetadata",
{
{
KeyDimensions,
new DoubleVector3Verifier,
Optional::No,
"Specifies the number of grid cells in each dimension",
},
{
KeyDomainUnit,
new StringVerifier,
Optional::Yes,
"Specifies the unit used to specity the domain",
},
{
KeyLowerDomainBound,
new DoubleVector3Verifier,
Optional::Yes,
"Specifies the lower domain bounds in the model coordinate system",
},
{
KeyUpperDomainBound,
new DoubleVector3Verifier,
Optional::Yes,
"Specifies the upper domain bounds in the model coordinate system",
},
{
KeyTime,
new StringVerifier,
Optional::Yes,
"Specifies the time on the format YYYY-MM-DDTHH:MM:SS.000Z",
},
{
KeyValueUnit,
new StringVerifier,
Optional::Yes,
"Specifies the unit used to specity the value",
},
{
KeyMinValue,
new DoubleVerifier,
Optional::Yes,
"Specifies the minimum value stored in the volume"
},
{
KeyMaxValue,
new DoubleVerifier,
Optional::Yes,
"Specifies the maximum value stored in the volume"
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "volume_rawvolumemetadata";
return doc;
}
} // namespace openspace::volume

View File

@@ -30,6 +30,7 @@
#include <openspace/documentation/verifier.h>
#include <openspace/util/time.h>
#include <openspace/util/spicemanager.h>
#include <ghoul/fmt.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/logging/logmanager.h>
@@ -37,7 +38,6 @@
#include <ghoul/lua/lua_helper.h>
#include <ghoul/misc/dictionaryluaformatter.h>
#include <ghoul/misc/defer.h>
#include <fstream>
namespace {
@@ -48,41 +48,65 @@ namespace {
constexpr const char* KeyValueFunction = "ValueFunction";
constexpr const char* KeyLowerDomainBound = "LowerDomainBound";
constexpr const char* KeyUpperDomainBound = "UpperDomainBound";
struct [[codegen::Dictionary(GenerateRawVolumeTask)]] Parameters {
// The Lua function used to compute the cell values
std::string valueFunction [[codegen::annotation("A Lua expression that returns a "
"function taking three numbers as arguments (x, y, z) and returning a "
"number")]];
// The raw volume file to export data to
std::string rawVolumeOutput [[codegen::annotation("A valid filepath")]];
// The lua dictionary file to export metadata to
std::string dictionaryOutput [[codegen::annotation("A valid filepath")]];
// The timestamp that is written to the metadata of this volume
std::string time;
// A vector representing the number of cells in each dimension
glm::ivec3 dimensions;
// A vector representing the lower bound of the domain
glm::dvec3 lowerDomainBound;
// A vector representing the upper bound of the domain
glm::dvec3 upperDomainBound;
};
#include "generaterawvolumetask_codegen.cpp"
} // namespace
namespace openspace::volume {
GenerateRawVolumeTask::GenerateRawVolumeTask(const ghoul::Dictionary& dictionary) {
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"GenerateRawVolumeTask"
);
documentation::Documentation GenerateRawVolumeTask::documentation() {
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "generate_raw_volume_task";
return doc;
}
_rawVolumeOutputPath = absPath(dictionary.value<std::string>(KeyRawVolumeOutput));
_dictionaryOutputPath = absPath(dictionary.value<std::string>(KeyDictionaryOutput));
_dimensions = glm::uvec3(dictionary.value<glm::dvec3>(KeyDimensions));
_time = dictionary.value<std::string>(KeyTime);
_valueFunctionLua = dictionary.value<std::string>(KeyValueFunction);
_lowerDomainBound = dictionary.value<glm::dvec3>(KeyLowerDomainBound);
_upperDomainBound = dictionary.value<glm::dvec3>(KeyUpperDomainBound);
GenerateRawVolumeTask::GenerateRawVolumeTask(const ghoul::Dictionary& dictionary) {
const Parameters p = codegen::bake<Parameters>(dictionary);
_rawVolumeOutputPath = absPath(p.rawVolumeOutput);
_dictionaryOutputPath = absPath(p.dictionaryOutput);
_dimensions = p.dimensions;
_time = p.time;
_valueFunctionLua = p.valueFunction;
_lowerDomainBound = p.lowerDomainBound;
_upperDomainBound = p.upperDomainBound;
}
std::string GenerateRawVolumeTask::description() {
return "Generate a raw volume with dimenstions: (" +
std::to_string(_dimensions.x) + ", " +
std::to_string(_dimensions.y) + ", " +
std::to_string(_dimensions.z) + "). " +
"For each cell, set the value by evaluating the lua function: " +
"`" + _valueFunctionLua + "`, with three arguments (x, y, z) ranging from " +
"(" + std::to_string(_lowerDomainBound.x) + ", "
+ std::to_string(_lowerDomainBound.y) + ", " +
std::to_string(_lowerDomainBound.z) + ") to (" +
std::to_string(_upperDomainBound.x) + ", " +
std::to_string(_upperDomainBound.y) + ", " +
std::to_string(_upperDomainBound.z) + ")." +
"Write raw volume data into " + _rawVolumeOutputPath +
" and dictionary with metadata to " + _dictionaryOutputPath;
return fmt::format(
"Generate a raw volume with dimenstions: ({}, {}, {}). For each cell, set the "
"value by evaluating the lua function: `{}`, with three arguments (x, y, z) "
"ranging from ({}, {}, {}) to ({}, {}, {}). Write raw volume data into {} and "
"dictionary with metadata to {}",
_dimensions.x, _dimensions.y, _dimensions.z, _valueFunctionLua,
_lowerDomainBound.x, _lowerDomainBound.y, _lowerDomainBound.z,
_upperDomainBound.x, _upperDomainBound.y, _upperDomainBound.z,
_rawVolumeOutputPath, _dictionaryOutputPath
);
}
void GenerateRawVolumeTask::perform(const Task::ProgressCallback& progressCallback) {
@@ -184,51 +208,4 @@ void GenerateRawVolumeTask::perform(const Task::ProgressCallback& progressCallba
progressCallback(1.0f);
}
documentation::Documentation GenerateRawVolumeTask::documentation() {
using namespace documentation;
return {
"GenerateRawVolumeTask",
"generate_raw_volume_task",
{
{
KeyValueFunction,
new StringAnnotationVerifier("A lua expression that returns a function "
"taking three numbers as arguments (x, y, z) and returning a number."),
Optional::No,
"The lua function used to compute the cell values",
},
{
KeyRawVolumeOutput,
new StringAnnotationVerifier("A valid filepath"),
Optional::No,
"The raw volume file to export data to",
},
{
KeyDictionaryOutput,
new StringAnnotationVerifier("A valid filepath"),
Optional::No,
"The lua dictionary file to export metadata to",
},
{
KeyDimensions,
new DoubleVector3Verifier,
Optional::No,
"A vector representing the number of cells in each dimension",
},
{
KeyLowerDomainBound,
new DoubleVector3Verifier,
Optional::No,
"A vector representing the lower bound of the domain"
},
{
KeyUpperDomainBound,
new DoubleVector3Verifier,
Optional::No,
"A vector representing the upper bound of the domain"
}
}
};
}
} // namespace openspace::volume

View File

@@ -229,18 +229,20 @@ ScreenSpaceRotation = { 0.0, 0.0, 0.0 }
RenderingMethod = "Framebuffer"
OpenGLDebugContext = {
Activate = true,
FilterIdentifier = {
{ Type = "Other", Source = "API", Identifier = 131185 },
-- Buffer performance warning: "copied/moved from VIDEO memory to HOST memory"
{ Type = "Performance", Source = "API", Identifier = 131186 },
-- API_ID_LINE_WIDTH deprecated behavior warning has been generated
{ Type = "Deprecated", Source = "API", Identifier = 7 },
-- Program/shader state performance warning: Vertex shader in program %i is being recompiled based on GL state.
{ Type = "Performance", Source = "API", Identifier = 131218 },
-- This is getting a bit wordy
{ Type = "Push group", Source = "Application", Identifier = 0 },
{ Type = "Pop group", Source = "Application", Identifier = 0 },
Activate = true,
FilterIdentifier = {
{ Type = "Other", Source = "API", Identifier = 131185 },
-- API_ID_RECOMPILE_FRAGMENT_SHADER performance warning has been generated. Fragment shader recompiled due to state change
{ Type = "Performance", Source = "API", Identifier = 2 },
-- Buffer performance warning: "copied/moved from VIDEO memory to HOST memory"
{ Type = "Performance", Source = "API", Identifier = 131186 },
-- API_ID_LINE_WIDTH deprecated behavior warning has been generated
{ Type = "Deprecated", Source = "API", Identifier = 7 },
-- Program/shader state performance warning: Vertex shader in program %i is being recompiled based on GL state.
{ Type = "Performance", Source = "API", Identifier = 131218 },
-- This is getting a bit wordy
{ Type = "Push group", Source = "Application", Identifier = 0 },
{ Type = "Pop group", Source = "Application", Identifier = 0 },
},
-- FilterSeverity = { }
}

View File

@@ -161,7 +161,6 @@ set(OPENSPACE_SOURCE
${OPENSPACE_BASE_DIR}/src/scene/sceneinitializer.cpp
${OPENSPACE_BASE_DIR}/src/scene/scenelicensewriter.cpp
${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode.cpp
${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode_doc.inl
${OPENSPACE_BASE_DIR}/src/scene/timeframe.cpp
${OPENSPACE_BASE_DIR}/src/scene/translation.cpp
${OPENSPACE_BASE_DIR}/src/scripting/lualibrary.cpp
@@ -208,6 +207,10 @@ if (APPLE)
${OPENSPACE_SOURCE}
${OPENSPACE_BASE_DIR}/src/interaction/touchbar.mm
)
set_source_files_properties(
${OPENSPACE_BASE_DIR}/src/interaction/touchbar.mm
PROPERTIES SKIP_PRECOMPILE_HEADERS ON
)
endif ()
set(OPENSPACE_HEADER
${OPENSPACE_BASE_DIR}/include/openspace/json.h

View File

@@ -140,21 +140,6 @@ namespace openspace::documentation {
const std::string DocumentationEntry::Wildcard = "*";
//std::string concatenate(const std::vector<TestResult::Offense>& offenses) {
// std::string result = "Error in specification (";
// for (const TestResult::Offense& o : offenses) {
// if (o.explanation.empty()) {
// result += fmt::format("{} ({}), ", o.offender, ghoul::to_string(o.reason));
// }
// else {
// result += fmt::format("{} ({}: {}), ", o.offender, ghoul::to_string(o.reason), o.explanation);
// }
// }
// result.pop_back();
// result.back() = ')';
// return result;
//}
SpecificationError::SpecificationError(TestResult res, std::string comp)
: ghoul::RuntimeError("Error in specification", std::move(comp))
, result(std::move(res))

View File

@@ -141,7 +141,9 @@ TestResult IntVerifier::operator()(const ghoul::Dictionary& dict,
{
if (dict.hasValue<int>(key)) {
// We have a key and the value is int, we are done
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
if (dict.hasKey(key)) {
@@ -151,23 +153,39 @@ TestResult IntVerifier::operator()(const ghoul::Dictionary& dict,
double intPart;
bool isInt = modf(value, &intPart) == 0.0;
if (isInt) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return {
false,
{ { key, TestResult::Offense::Reason::WrongType } },
{}
};
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
// If we don't have a double value, we cannot have an int value
return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -317,17 +335,26 @@ TestResult Color3Verifier::operator()(const ghoul::Dictionary& dictionary,
glm::dvec3 values = dictionary.value<glm::dvec3>(key);
if (values.x < 0.0 || values.x > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".x", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".x";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values.y < 0.0 || values.y > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".y", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".y";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values.z < 0.0 || values.z > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".z", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".z";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
return res;
@@ -345,27 +372,39 @@ TestResult Color4Verifier::operator()(const ghoul::Dictionary& dictionary,
return res;
}
std::vector<double> values = dictionary.value<std::vector<double>>(key);
if (values[0] < 0.0 || values[0] > 1.0) {
glm::dvec4 values = dictionary.value<glm::dvec4>(key);
if (values.x < 0.0 || values.x > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".x", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".x";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values[1] < 0.0 || values[1] > 1.0) {
if (values.y < 0.0 || values.y > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".y", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".y";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values[2] < 0.0 || values[2] > 1.0) {
if (values.z < 0.0 || values.z > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".z", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".z";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values[3] < 0.0 || values[3] > 1.0) {
if (values.w < 0.0 || values.w > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".a", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".a";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
return res;
}
@@ -378,7 +417,9 @@ TestResult TemplateVerifier<glm::ivec2>::operator()(const ghoul::Dictionary& dic
const std::string& key) const
{
if (dict.hasValue<glm::ivec2>(key)) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
if (dict.hasKey(key)) {
@@ -390,22 +431,38 @@ TestResult TemplateVerifier<glm::ivec2>::operator()(const ghoul::Dictionary& dic
modf(value.y, &intPart.y) == 0.0
};
if (isInt.x && isInt.y) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return {
false,
{{ key, TestResult::Offense::Reason::WrongType }},
{}
};
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -415,7 +472,9 @@ TestResult TemplateVerifier<glm::ivec3>::operator()(const ghoul::Dictionary& dic
const std::string& key) const
{
if (dict.hasValue<glm::ivec3>(key)) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
if (dict.hasKey(key)) {
@@ -428,22 +487,38 @@ TestResult TemplateVerifier<glm::ivec3>::operator()(const ghoul::Dictionary& dic
modf(value.z, &intPart.z) == 0.0
};
if (isInt.x && isInt.y && isInt.z) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return {
false,
{{ key, TestResult::Offense::Reason::WrongType }},
{}
};
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -453,7 +528,9 @@ TestResult TemplateVerifier<glm::ivec4>::operator()(const ghoul::Dictionary& dic
const std::string& key) const
{
if (dict.hasValue<glm::ivec4>(key)) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
if (dict.hasKey(key)) {
@@ -467,22 +544,38 @@ TestResult TemplateVerifier<glm::ivec4>::operator()(const ghoul::Dictionary& dic
modf(value.w, &intPart.w) == 0.0
};
if (isInt.x && isInt.y && isInt.z && isInt.w) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return {
false,
{{ key, TestResult::Offense::Reason::WrongType }},
{}
};
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -512,11 +605,22 @@ TestResult TableVerifier::operator()(const ghoul::Dictionary& dictionary,
}
else {
if (dictionary.hasKey(key)) {
return { false, { { key, TestResult::Offense::Reason::WrongType } }, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
else {
return { false, { { key, TestResult::Offense::Reason::MissingKey } }, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -565,11 +669,11 @@ TestResult ReferencingVerifier::operator()(const ghoul::Dictionary& dictionary,
);
if (it == docs.end()) {
res.offenses.push_back({
key,
TestResult::Offense::Reason::UnknownIdentifier
});
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::UnknownIdentifier;
res.offenses.push_back(o);
return res;
}
@@ -629,10 +733,18 @@ TestResult AndVerifier::operator()(const ghoul::Dictionary& dictionary,
);
if (success) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
return res;
}
}
@@ -698,10 +810,18 @@ TestResult OrVerifier::operator()(const ghoul::Dictionary& dictionary,
);
if (success) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
return res;
}
}

View File

@@ -60,7 +60,7 @@ namespace {
std::optional<std::vector<std::string>> globalCustomizationScripts;
// A list of paths that are automatically registered with the file system. If a
// key X is used in the table, it is then useable by referencing ${X} in all other
// key X is used in the table, it is then useable by referencing ${X} in all other
// configuration files or scripts
std::map<std::string, std::string> paths;
@@ -277,7 +277,7 @@ namespace {
// errors easier. This defaults to 'false'
std::optional<bool> checkOpenGLState;
// Determines whether each OpenGL call that happens should be logged using the
// Determines whether each OpenGL call that happens should be logged using the
// 'TRACE' loglevel. This will bring the rendering to a crawl but provides useful
// debugging features for the order in which OpenGL calls occur. This defaults to
// 'false'
@@ -312,7 +312,7 @@ namespace {
// bar that gives an estimate of the loading progression
std::optional<bool> showProgressbar;
};
// Values in this table describe the behavior of the loading screen that is
// Values in this table describe the behavior of the loading screen that is
// displayed while the scene graph is created and initialized
std::optional<LoadingScreen> loadingScreen;
@@ -458,7 +458,7 @@ void parseLuaState(Configuration& configuration) {
}
}
}
if (p.documentation.has_value()) {
c.documentation.path = p.documentation->path.value_or(c.documentation.path);
}

View File

@@ -63,7 +63,6 @@ void ModuleEngine::initialize(
m->initialize(configuration);
}
catch (const documentation::SpecificationError& e) {
//LFATALC(e.component, e.message);
for (const documentation::TestResult::Offense& o : e.result.offenses) {
LERRORC(e.component, o.offender + ": " + ghoul::to_string(o.reason));
}

View File

@@ -306,11 +306,10 @@ int createSingleColorImage(lua_State* L) {
// @TODO (emmbr 2020-12-18) Verify that the input dictionary is a vec3
// Would like to clean this up with a more direct use of the Verifier in the future
using namespace openspace::documentation;
const std::string& key = "color";
ghoul::Dictionary colorDict;
colorDict.setValue(key, d);
TestResult res = Color3Verifier()(colorDict, key);
documentation::TestResult res = documentation::Color3Verifier()(colorDict, key);
if (!res.success) {
return ghoul::lua::luaError(

View File

@@ -44,13 +44,6 @@
namespace {
constexpr const char* _loggerCat = "NavigationHandler";
constexpr const char* KeyAnchor = "Anchor";
constexpr const char* KeyAim = "Aim";
constexpr const char* KeyPosition = "Position";
constexpr const char* KeyUp = "Up";
constexpr const char* KeyYaw = "Yaw";
constexpr const char* KeyPitch = "Pitch";
constexpr const char* KeyReferenceFrame = "ReferenceFrame";
const double Epsilon = 1E-7;
using namespace openspace;
@@ -73,6 +66,31 @@ namespace {
"than using the mouse interaction."
};
struct [[codegen::Dictionary(NavigationHandler)]] Parameters {
// The identifier of the anchor node
std::string anchor;
// The identifier of the aim node, if used
std::optional<std::string> aim;
// The identifier of the scene graph node to use as reference frame. If not
// specified, this will be the same as the anchor
std::optional<std::string> referenceFrame;
// The position of the camera relative to the anchor node, expressed in meters in
// the specified reference frame
glm::dvec3 position;
// The up vector expressed in the coordinate system of the reference frame
std::optional<glm::dvec3> up;
// The yaw angle in radians. Positive angle means yawing camera to the right
std::optional<double> yaw;
// The pitch angle in radians. Positive angle means pitching camera upwards
std::optional<double> pitch;
};
#include "navigationhandler_codegen.cpp"
} // namespace
#include "navigationhandler_lua.inl"
@@ -80,6 +98,14 @@ namespace {
namespace openspace::interaction {
ghoul::Dictionary NavigationHandler::NavigationState::dictionary() const {
constexpr const char* KeyAnchor = "Anchor";
constexpr const char* KeyAim = "Aim";
constexpr const char* KeyPosition = "Position";
constexpr const char* KeyUp = "Up";
constexpr const char* KeyYaw = "Yaw";
constexpr const char* KeyPitch = "Pitch";
constexpr const char* KeyReferenceFrame = "ReferenceFrame";
ghoul::Dictionary cameraDict;
cameraDict.setValue(KeyPosition, position);
cameraDict.setValue(KeyAnchor, anchor);
@@ -105,36 +131,19 @@ ghoul::Dictionary NavigationHandler::NavigationState::dictionary() const {
}
NavigationHandler::NavigationState::NavigationState(const ghoul::Dictionary& dictionary) {
const bool hasAnchor = dictionary.hasValue<std::string>(KeyAnchor);
const bool hasPosition = dictionary.hasValue<glm::dvec3>(KeyPosition);
if (!hasAnchor || !hasPosition) {
throw ghoul::RuntimeError(
"Position and Anchor need to be defined for navigation dictionary."
);
}
const Parameters p = codegen::bake<Parameters>(dictionary);
anchor = dictionary.value<std::string>(KeyAnchor);
position = dictionary.value<glm::dvec3>(KeyPosition);
anchor = p.anchor;
position = p.position;
if (dictionary.hasValue<std::string>(KeyReferenceFrame)) {
referenceFrame = dictionary.value<std::string>(KeyReferenceFrame);
}
else {
referenceFrame = anchor;
}
if (dictionary.hasValue<std::string>(KeyAim)) {
aim = dictionary.value<std::string>(KeyAim);
}
referenceFrame = p.referenceFrame.value_or(anchor);
aim = p.aim.value_or(aim);
if (dictionary.hasValue<glm::dvec3>(KeyUp)) {
up = dictionary.value<glm::dvec3>(KeyUp);
if (p.up.has_value()) {
up = *p.up;
if (dictionary.hasValue<double>(KeyYaw)) {
yaw = dictionary.value<double>(KeyYaw);
}
if (dictionary.hasValue<double>(KeyPitch)) {
pitch = dictionary.value<double>(KeyPitch);
}
yaw = p.yaw.value_or(yaw);
pitch = p.pitch.value_or(pitch);
}
}
@@ -142,8 +151,7 @@ NavigationHandler::NavigationState::NavigationState(std::string anchor_, std::st
std::string referenceFrame_,
glm::dvec3 position_,
std::optional<glm::dvec3> up_,
double yaw_,
double pitch_)
double yaw_, double pitch_)
: anchor(std::move(anchor_))
, aim(std::move(aim_))
, referenceFrame(std::move(referenceFrame_))
@@ -545,60 +553,9 @@ std::vector<std::string> NavigationHandler::joystickButtonCommand(int button) co
}
documentation::Documentation NavigationHandler::NavigationState::Documentation() {
using namespace documentation;
return {
"Navigation State",
"core_navigation_state",
{
{
KeyAnchor,
new StringVerifier,
Optional::No,
"The identifier of the anchor node."
},
{
KeyAim,
new StringVerifier,
Optional::Yes,
"The identifier of the aim node, if used."
},
{
KeyReferenceFrame,
new StringVerifier,
Optional::Yes,
"The identifier of the scene graph node to use as reference frame. "
"If not specified, this will be the same as the anchor."
},
{
KeyPosition,
new DoubleVector3Verifier,
Optional::No,
"The position of the camera relative to the anchor node, "
"expressed in meters in the specified reference frame."
},
{
KeyUp,
new DoubleVector3Verifier,
Optional::Yes,
"The up vector expressed in the coordinate system of the reference frame."
},
{
KeyYaw,
new DoubleVerifier,
Optional::Yes,
"The yaw angle in radians. "
"Positive angle means yawing camera to the right."
},
{
KeyPitch,
new DoubleVerifier,
Optional::Yes,
"The pitch angle in radians. "
"Positive angle means pitching camera upwards."
},
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_navigation_state";
return doc;
}
scripting::LuaLibrary NavigationHandler::luaLibrary() {

View File

@@ -29,73 +29,47 @@
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/lua/lua_helper.h>
#include <algorithm>
#include <optional>
namespace {
constexpr const char* KeyName = "Name";
constexpr const char* KeyDescription = "Description";
constexpr const char* KeyPhases = "Phases";
constexpr const char* KeyTimeRange = "TimeRange";
struct [[codegen::Dictionary(MissionPhase)]] Parameters {
// The human readable name of this mission or mission phase that is displayed to
// the user
std::string name;
// A description of this mission or mission phase
std::optional<std::string> description;
// The time range for which this mission or mission phase is valid. If no time
// range is specified, the ranges of sub mission phases are used instead
std::optional<ghoul::Dictionary> timeRange
[[codegen::reference("core_util_timerange")]];
// The phases into which this mission or mission phase is separated
std::optional<std::vector<ghoul::Dictionary>> phases
[[codegen::reference("core_mission_mission")]];
};
#include "mission_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation MissionPhase::Documentation() {
using namespace documentation;
return {
"Missions and Mission Phases",
"core_mission_mission",
{
{
KeyName,
new StringVerifier,
Optional::No,
"The human readable name of this mission or mission phase that is "
"displayed to the user."
},
{
KeyDescription,
new StringVerifier,
Optional::Yes,
"A description of this mission or mission phase."
},
{
KeyTimeRange,
new ReferencingVerifier("core_util_timerange"),
Optional::Yes,
"The time range for which this mission or mission phase is valid. If no "
"time range is specified, the ranges of sub mission phases are used "
"instead."
},
{
KeyPhases,
new TableVerifier({
{
"*",
new ReferencingVerifier("core_mission_mission"),
Optional::Yes
}
}),
Optional::Yes,
"The phases into which this mission or mission phase is separated."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_mission_mission";
return doc;
}
MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) {
_name = dictionary.value<std::string>(KeyName);
if (dictionary.hasValue<std::string>(KeyDescription)) {
_description = dictionary.value<std::string>(KeyDescription);
}
const Parameters p = codegen::bake<Parameters>(dictionary);
if (dictionary.hasValue<ghoul::Dictionary>(KeyPhases)) {
ghoul::Dictionary childDicts = dictionary.value<ghoul::Dictionary>(KeyPhases);
// This is a nested mission phase
_subphases.reserve(childDicts.size());
for (size_t i = 0; i < childDicts.size(); ++i) {
std::string key = std::to_string(i + 1);
_subphases.emplace_back(childDicts.value<ghoul::Dictionary>(key));
_name = p.name;
_description = p.description.value_or(_description);
if (p.phases.has_value()) {
_subphases.reserve(p.phases->size());
for (const ghoul::Dictionary& phase : *p.phases) {
_subphases.emplace_back(phase);
}
// Ensure subphases are sorted
@@ -112,15 +86,14 @@ MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) {
timeRangeSubPhases.start = _subphases[0].timeRange().start;
timeRangeSubPhases.end = _subphases.back().timeRange().end;
// user may specify an overall time range. In that case expand this timerange.
if (dictionary.hasValue<ghoul::Dictionary>(KeyTimeRange)) {
ghoul::Dictionary range = dictionary.value<ghoul::Dictionary>(KeyTimeRange);
TimeRange overallTimeRange(range);
// user may specify an overall time range. In that case expand this timerange
if (p.timeRange.has_value()) {
TimeRange overallTimeRange(*p.timeRange);
if (!overallTimeRange.includes(timeRangeSubPhases)) {
throw ghoul::RuntimeError(
throw ghoul::RuntimeError(fmt::format(
"User specified time range must at least include its subphases'",
"Mission (" + _name + ")"
);
"Mission ({})", _name
));
}
_timeRange.include(overallTimeRange);
@@ -132,16 +105,14 @@ MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) {
}
}
else {
if (dictionary.hasValue<ghoul::Dictionary>(KeyTimeRange)) {
ghoul::Dictionary timeRangeDict =
dictionary.value<ghoul::Dictionary>(KeyTimeRange);
_timeRange = TimeRange(timeRangeDict); // throws exception if unable to parse
if (p.timeRange.has_value()) {
_timeRange = TimeRange(*p.timeRange); // throws exception if unable to parse
}
else {
throw ghoul::RuntimeError(
throw ghoul::RuntimeError(fmt::format(
"If there are no subphases specified, the time range has to be specified",
"Mission (" + _name + ")"
);
"Mission ({})", _name
));
}
}
}

View File

@@ -89,7 +89,7 @@ DashboardItem::DashboardItem(const ghoul::Dictionary& dictionary)
, _isEnabled(EnabledInfo, true)
{
const Parameters p = codegen::bake<Parameters>(dictionary);
setIdentifier(p.identifier);
if (p.guiName.has_value()) {
setGuiName(*p.guiName);

View File

@@ -129,7 +129,7 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary)
registerUpdateRenderBinFromOpacity();
const Parameters p = codegen::bake<Parameters>(dictionary);
if (p.tag.has_value()) {
if (std::holds_alternative<std::string>(*p.tag)) {
if (!std::get<std::string>(*p.tag).empty()) {

View File

@@ -38,11 +38,10 @@
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/textureunit.h>
#include <optional>
#include <variant>
namespace {
constexpr const char* KeyType = "Type";
constexpr const char* KeyTag = "Tag";
constexpr const std::array<const char*, 4> UniformNames = {
"Alpha", "ModelTransform", "ViewProjectionMatrix", "texture1"
};
@@ -207,109 +206,72 @@ namespace {
wrap(elevation, -glm::pi<float>(), glm::pi<float>())
);
}
struct [[codegen::Dictionary(ScreenSpaceRenderable)]] Parameters {
// The type of the Screenspace renderable that is to be created. The available
// types of Screenspace renderable depend on the configuration of the application
// and can be written to disk on application startup into the FactoryDocumentation
std::string type
[[codegen::annotation("Must name a valid Screenspace renderable")]];
// Specifies the name of this screenspace renderable. This does not have to be
// unique to the scene, but it is recommended to be
std::optional<std::string> name;
// This is the unique identifier for this screenspace renderable. It has to be
// unique amongst all existing screenspace nodes that already have been added to
// the scene. The identifier is not allowed to have any whitespace or '.' and must
// not be empty
std::optional<std::string> identifier;
// [[codegen::verbatim(EnabledInfo.description)]]
std::optional<bool> enabled;
// [[codegen::verbatim(UseRadiusAzimuthElevationInfo.description)]]
std::optional<bool> useRadiusAzimuthElevation;
// [[codegen::verbatim(FaceCameraInfo.description)]]
std::optional<bool> faceCamera;
// [[codegen::verbatim(CartesianPositionInfo.description)]]
std::optional<glm::vec3> cartesianPosition;
// [[codegen::verbatim(RadiusAzimuthElevationInfo.description)]]
std::optional<glm::vec3> radiusAzimuthElevation;
// [[codegen::verbatim(ScaleInfo.description)]]
std::optional<float> scale;
// [[codegen::verbatim(UsePerspectiveProjectionInfo.description)]]
std::optional<bool> usePerspectiveProjection;
// [codegen::verbatim(OpacityInfo.description)]]
std::optional<float> opacity [[codegen::inrange(0.f, 1.f)]];
// Defines either a single or multiple tags that apply to this
// ScreenSpaceRenderable, thus making it possible to address multiple, separate
// Renderables with a single property change
std::optional<std::variant<std::string, std::vector<std::string>>> tag;
};
#include "screenspacerenderable_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation ScreenSpaceRenderable::Documentation() {
using namespace openspace::documentation;
return {
"Screenspace Renderable",
"core_screenspacerenderable",
{
{
KeyType,
new StringAnnotationVerifier("Must name a valid Screenspace renderable"),
Optional::No,
"The type of the Screenspace renderable that is to be created. The "
"available types of Screenspace renderable depend on the configuration of"
"the application and can be written to disk on application startup into "
"the FactoryDocumentation."
},
{
KeyName,
new StringVerifier,
Optional::Yes,
"Specifies the name of this screenspace renderable. This does not have "
"to be unique to the scene, but it is recommended to be."
},
{
KeyIdentifier,
new StringVerifier,
Optional::Yes,
"This is the unique identifier for this screenspace renderable. It has "
"to be unique amongst all existing screenspace nodes that already have "
"been added to the scene. The identifier is not allowed to have any "
"whitespace or '.' and must not be empty."
},
{
EnabledInfo.identifier,
new BoolVerifier,
Optional::Yes,
EnabledInfo.description
},
{
UseRadiusAzimuthElevationInfo.identifier,
new BoolVerifier,
Optional::Yes,
UseRadiusAzimuthElevationInfo.description
},
{
FaceCameraInfo.identifier,
new BoolVerifier,
Optional::Yes,
FaceCameraInfo.description
},
{
CartesianPositionInfo.identifier,
new DoubleVector3Verifier,
Optional::Yes,
CartesianPositionInfo.description
},
{
RadiusAzimuthElevationInfo.identifier,
new DoubleVector3Verifier,
Optional::Yes,
RadiusAzimuthElevationInfo.description
},
{
ScaleInfo.identifier,
new DoubleVerifier,
Optional::Yes,
ScaleInfo.description
},
{
OpacityInfo.identifier,
new DoubleVerifier,
Optional::Yes,
OpacityInfo.description
},
{
KeyTag,
new OrVerifier({ new StringVerifier, new StringListVerifier }),
Optional::Yes,
"Defines either a single or multiple tags that apply to this "
"ScreenSpaceRenderable, thus making it possible to address multiple, "
"seprate Renderables with a single property change."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_screenspacerenderable";
return doc;
}
std::unique_ptr<ScreenSpaceRenderable> ScreenSpaceRenderable::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"ScreenSpaceRenderable"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
const std::string& renderableType = dictionary.value<std::string>(KeyType);
ScreenSpaceRenderable* ssr =
FactoryManager::ref().factory<ScreenSpaceRenderable>()->create(
renderableType,
p.type,
dictionary
);
return std::unique_ptr<ScreenSpaceRenderable>(ssr);
@@ -365,12 +327,14 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary
, _opacity(OpacityInfo, 1.f, 0.f, 1.f)
, _delete(DeleteInfo)
{
if (dictionary.hasKey(KeyIdentifier)) {
setIdentifier(dictionary.value<std::string>(KeyIdentifier));
const Parameters p = codegen::bake<Parameters>(dictionary);
if (p.identifier.has_value()) {
setIdentifier(*p.identifier);
}
if (dictionary.hasKey(KeyName)) {
setGuiName(dictionary.value<std::string>(KeyName));
if (p.name.has_value()) {
setGuiName(*p.name);
}
addProperty(_enabled);
@@ -394,62 +358,38 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary
addProperty(_opacity);
addProperty(_localRotation);
if (dictionary.hasKey(EnabledInfo.identifier)) {
_enabled = dictionary.value<bool>(EnabledInfo.identifier);
}
if (dictionary.hasKey(UseRadiusAzimuthElevationInfo.identifier)) {
_useRadiusAzimuthElevation = dictionary.value<bool>(
UseRadiusAzimuthElevationInfo.identifier
);
}
_enabled = p.enabled.value_or(_enabled);
_useRadiusAzimuthElevation =
p.useRadiusAzimuthElevation.value_or(_useRadiusAzimuthElevation);
if (_useRadiusAzimuthElevation) {
if (dictionary.hasKey(RadiusAzimuthElevationInfo.identifier)) {
_raePosition = dictionary.value<glm::dvec3>(
RadiusAzimuthElevationInfo.identifier
);
}
_raePosition = p.radiusAzimuthElevation.value_or(_raePosition);
}
else {
if (dictionary.hasKey(CartesianPositionInfo.identifier)) {
_cartesianPosition = dictionary.value<glm::dvec3>(
CartesianPositionInfo.identifier
);
_cartesianPosition = p.cartesianPosition.value_or(_cartesianPosition);
}
_scale = p.scale.value_or(_scale);
_opacity = p.opacity.value_or(_opacity);
_usePerspectiveProjection =
p.usePerspectiveProjection.value_or(_usePerspectiveProjection);
_faceCamera = p.faceCamera.value_or(_faceCamera);
if (p.tag.has_value()) {
if (std::holds_alternative<std::string>(*p.tag)) {
addTag(std::get<std::string>(*p.tag));
}
}
if (dictionary.hasKey(ScaleInfo.identifier)) {
_scale = static_cast<float>(dictionary.value<double>(ScaleInfo.identifier));
}
if (dictionary.hasKey(OpacityInfo.identifier)) {
_opacity = static_cast<float>(dictionary.value<double>(OpacityInfo.identifier));
}
if (dictionary.hasKey(UsePerspectiveProjectionInfo.identifier)) {
_usePerspectiveProjection =
dictionary.value<bool>(UsePerspectiveProjectionInfo.identifier);
}
if (dictionary.hasKey(FaceCameraInfo.identifier)) {
_faceCamera = dictionary.value<bool>(FaceCameraInfo.identifier);
}
if (dictionary.hasValue<std::string>(KeyTag)) {
std::string tagName = dictionary.value<std::string>(KeyTag);
if (!tagName.empty()) {
addTag(std::move(tagName));
}
}
else if (dictionary.hasValue<ghoul::Dictionary>(KeyTag)) {
const ghoul::Dictionary& tagNames = dictionary.value<ghoul::Dictionary>(KeyTag);
for (std::string_view key : tagNames.keys()) {
std::string tagName = tagNames.value<std::string>(key);
if (!tagName.empty()) {
addTag(std::move(tagName));
else if (std::holds_alternative<std::vector<std::string>>(*p.tag)) {
for (const std::string& t : std::get<std::vector<std::string>>(*p.tag)) {
if (!t.empty()) {
addTag(t);
}
}
}
else {
throw ghoul::MissingCaseException();
}
}
_delete.onChange([this](){

View File

@@ -32,17 +32,28 @@
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/misc/templatefactory.h>
#include <optional>
namespace {
constexpr const char* KeyType = "Type";
constexpr const char* KeyIdentifier = "Identifier";
constexpr openspace::properties::Property::PropertyInfo EnabledInfo = {
"Enabled",
"Enabled",
"Whether the light source is enabled or not"
};
struct [[codegen::Dictionary(LightSource)]] Parameters {
// The type of the light source that is described in this element. The available
// types of light sources depend on the configuration of the application and can
// be written to disk on application startup into the FactoryDocumentation
std::string type [[codegen::annotation("Must name a valid LightSource type")]];
// The identifier of the light source
std::string identifier;
// [[codegen::verbatim(EnabledInfo.description)]]
std::optional<bool> enabled;
};
#include "lightsource_codegen.cpp"
} // namespace
namespace openspace {
@@ -52,49 +63,19 @@ bool LightSource::isEnabled() const {
}
documentation::Documentation LightSource::Documentation() {
using namespace openspace::documentation;
return {
"Light Source",
"core_light_source",
{
{
KeyType,
new StringAnnotationVerifier("Must name a valid LightSource type"),
Optional::No,
"The type of the light source that is described in this element. "
"The available types of light sources depend on the configuration "
"of the application and can be written to disk on "
"application startup into the FactoryDocumentation."
},
{
KeyIdentifier,
new StringVerifier,
Optional::No,
"The identifier of the light source."
},
{
EnabledInfo.identifier,
new BoolVerifier,
Optional::Yes,
EnabledInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_light_source";
return doc;
}
std::unique_ptr<LightSource> LightSource::createFromDictionary(
const ghoul::Dictionary& dictionary)
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(Documentation(), dictionary, "LightSource");
const std::string timeFrameType = dictionary.value<std::string>(KeyType);
const Parameters p = codegen::bake<Parameters>(dictionary);
auto factory = FactoryManager::ref().factory<LightSource>();
LightSource* source = factory->create(timeFrameType, dictionary);
const std::string identifier = dictionary.value<std::string>(KeyIdentifier);
source->setIdentifier(identifier);
LightSource* source = factory->create(p.type, dictionary);
source->setIdentifier(p.identifier);
return std::unique_ptr<LightSource>(source);
}
@@ -110,10 +91,9 @@ LightSource::LightSource(const ghoul::Dictionary& dictionary)
: properties::PropertyOwner({ "LightSource" })
, _enabled(EnabledInfo, true)
{
if (dictionary.hasValue<bool>(EnabledInfo.identifier)) {
_enabled = dictionary.value<bool>(EnabledInfo.identifier);
}
const Parameters p = codegen::bake<Parameters>(dictionary);
_enabled = p.enabled.value_or(_enabled);
addProperty(_enabled);
}

View File

@@ -36,40 +36,30 @@
#include <ghoul/misc/templatefactory.h>
namespace {
constexpr const char* KeyType = "Type";
struct [[codegen::Dictionary(Rotation)]] Parameters {
// The type of the rotation that is described in this element. The available types
// of rotations depend on the configuration of the application and can be written
// to disk on application startup into the FactoryDocumentation
std::string type [[codegen::annotation("Must name a valid Rotation type")]];
};
#include "rotation_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation Rotation::Documentation() {
using namespace openspace::documentation;
return {
"Transformation Rotation",
"core_transform_rotation",
{
{
KeyType,
new StringAnnotationVerifier("Must name a valid Rotation type."),
Optional::No,
"The type of the rotation that is described in this element. The "
"available types of rotations depend on the configuration of the "
"application and can be written to disk on application startup into the "
"FactoryDocumentation."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_transform_rotation";
return doc;
}
ghoul::mm_unique_ptr<Rotation> Rotation::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(Documentation(), dictionary, "Rotation");
const Parameters p = codegen::bake<Parameters>(dictionary);
const std::string& rotationType = dictionary.value<std::string>(KeyType);
auto factory = FactoryManager::ref().factory<Rotation>();
Rotation* result = factory->create(
rotationType,
Rotation* result = FactoryManager::ref().factory<Rotation>()->create(
p.type,
dictionary,
&global::memoryManager->PersistentMemory
);
@@ -78,6 +68,8 @@ ghoul::mm_unique_ptr<Rotation> Rotation::createFromDictionary(
Rotation::Rotation() : properties::PropertyOwner({ "Rotation" }) {}
// @TODO (abock, 2021-03-25) This constructor can probably die since it doesn't do any
// above the default constructor
Rotation::Rotation(const ghoul::Dictionary&)
: properties::PropertyOwner({ "Rotation" })
{}

View File

@@ -27,6 +27,8 @@
#include <modules/base/scale/staticscale.h>
#include <modules/base/rotation/staticrotation.h>
#include <modules/base/translation/statictranslation.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/rendering/renderable.h>
@@ -38,23 +40,11 @@
#include <ghoul/misc/assert.h>
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/ghoul_gl.h>
#include "scenegraphnode_doc.inl"
#include <cmath>
#include <optional>
namespace {
constexpr const char* _loggerCat = "SceneGraphNode";
constexpr const char* KeyRenderable = "Renderable";
constexpr const char* KeyGuiName = "GUI.Name";
constexpr const char* KeyGuiPath = "GUI.Path";
constexpr const char* KeyGuiHidden = "GUI.Hidden";
constexpr const char* KeyGuiDescription = "GUI.Description";
constexpr const char* KeyTransformTranslation = "Transform.Translation";
constexpr const char* KeyTransformRotation = "Transform.Rotation";
constexpr const char* KeyTransformScale = "Transform.Scale";
constexpr const char* KeyTimeFrame = "TimeFrame";
constexpr openspace::properties::Property::PropertyInfo ComputeScreenSpaceInfo =
{
@@ -138,6 +128,90 @@ namespace {
openspace::properties::Property::Visibility::Hidden
};
struct [[codegen::Dictionary(SceneGraphNode)]] Parameters {
// The identifier of this scenegraph node. This name must be unique among all
// scene graph nodes that are loaded in a specific scene. If a duplicate is
// detected the loading of the node will fail, as will all childing that depend on
// the node. The identifier must not contain any whitespaces or '.'
std::string identifier;
// This names the parent of the currently specified scenegraph node. The parent
// must already exist in the scene graph. If not specified, the node will be
// attached to the root of the scenegraph
std::optional<std::string> parent
[[codegen::annotation(
"If specified, this must be a name for another scenegraph node"
)]];
// The renderable that is to be created for this scenegraph node. A renderable is
// a component of a scenegraph node that will lead to some visual result on the
// screen. The specifics heavily depend on the 'Type' of the renderable. If no
// Renderable is specified, this scenegraph node is an internal node and can be
// used for either group children, or apply common transformations to a group of
// children
std::optional<ghoul::Dictionary> renderable [[codegen::reference("renderable")]];
// A hard-coded bounding sphere to be used for the cases where the Renderable is
// not able to provide a reasonable bounding sphere or the calculated bounding
// sphere needs to be overwritten for some reason
std::optional<double> boundingSphere;
struct Transform {
// This node describes a translation that is applied to the scenegraph node
// and all its children. Depending on the 'Type' of the translation, this can
// either be a static translation or a time-varying one
std::optional<ghoul::Dictionary> translation
[[codegen::reference("core_transform_translation")]];
// This nodes describes a rotation that is applied to the scenegraph node and
// all its children. Depending on the 'Type' of the rotation, this can either
// be a static rotation or a time-varying one
std::optional<ghoul::Dictionary> rotation
[[codegen::reference("core_transform_rotation")]];
// This node describes a scaling that is applied to the scenegraph node and
// all its children. Depending on the 'Type' of the scaling, this can either
// be a static scaling or a time-varying one
std::optional<ghoul::Dictionary> scale
[[codegen::reference("core_transform_scaling")]];
};
// This describes a set of transformations that are applied to this scenegraph
// node and all of its children. There are only three possible values
// corresponding to a 'Translation', a 'Rotation', and a 'Scale'
std::optional<Transform> transform;
// Specifies the time frame for when this node should be active
std::optional<ghoul::Dictionary> timeFrame
[[codegen::reference("core_time_frame")]];
// A tag or list of tags that can be used to reference to a group of scenegraph
// nodes.
std::optional<std::variant<std::string, std::vector<std::string>>> tag;
struct Gui {
// An optional user-facing name for this SceneGraphNode, which does not have
// to be unique, though it is recommended, and can contain any characters
std::optional<std::string> name;
// If this value is specified, this '/' separated URI specifies the location
// of this scenegraph node in a GUI representation, for instance
// '/SolarSystem/Earth/Moon'
std::optional<std::string> path;
// A user-facing description about this scene graph node
std::optional<std::string> description;
// If this value is specified, GUI applications are incouraged to ignore this
// scenegraph node. This is most useful to trim collective lists of nodes and
// not display, for example, barycenters
std::optional<bool> hidden;
};
// Additional information that is passed to GUI applications. These are all hints
// and do not have any impact on the actual function of the scenegraph node
std::optional<Gui> gui [[codegen::key("GUI")]];
};
#include "scenegraphnode_codegen.cpp"
} // namespace
namespace openspace {
@@ -149,11 +223,7 @@ int SceneGraphNode::nextIndex = 0;
ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
openspace::documentation::testSpecificationAndThrow(
SceneGraphNode::Documentation(),
dictionary,
"SceneGraphNode"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
SceneGraphNode* n = global::memoryManager->PersistentMemory.alloc<SceneGraphNode>();
ghoul::mm_unique_ptr<SceneGraphNode> result = ghoul::mm_unique_ptr<SceneGraphNode>(n);
@@ -162,89 +232,102 @@ ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
result->index = nextIndex++;
#endif // Debugging_Core_SceneGraphNode_Indices
std::string identifier = dictionary.value<std::string>(KeyIdentifier);
result->setIdentifier(std::move(identifier));
result->setIdentifier(p.identifier);
if (dictionary.hasKey(KeyGuiName)) {
result->setGuiName(dictionary.value<std::string>(KeyGuiName));
result->_guiDisplayName = result->guiName();
result->addProperty(result->_guiDisplayName);
if (p.gui.has_value()) {
if (p.gui->name.has_value()) {
result->setGuiName(*p.gui->name);
result->_guiDisplayName = result->guiName();
result->addProperty(result->_guiDisplayName);
}
if (p.gui->description.has_value()) {
result->setDescription(*p.gui->description);
result->_guiDescription = result->description();
result->addProperty(result->_guiDescription);
}
if (p.gui->hidden.has_value()) {
result->_guiHidden = *p.gui->hidden;
result->addProperty(result->_guiHidden);
}
if (p.gui->path.has_value()) {
result->_guiPath = *p.gui->path;
result->addProperty(result->_guiPath);
}
}
if (dictionary.hasKey(KeyGuiDescription)) {
result->setDescription(dictionary.value<std::string>(KeyGuiDescription));
result->_guiDescription = result->description();
result->addProperty(result->_guiDescription);
}
if (dictionary.hasKey(KeyGuiHidden)) {
result->_guiHidden = dictionary.value<bool>(KeyGuiHidden);
result->addProperty(result->_guiHidden);
}
if (dictionary.hasKey(BoundingSphereInfo.identifier)) {
result->_boundingSphere = dictionary.value<double>(BoundingSphereInfo.identifier);
if (p.boundingSphere.has_value()) {
result->_boundingSphere = *p.boundingSphere;
result->_boundingSphere.setVisibility(properties::Property::Visibility::All);
}
if (dictionary.hasKey(KeyTransformTranslation)) {
ghoul::Dictionary translationDictionary =
dictionary.value<ghoul::Dictionary>(KeyTransformTranslation);
result->_transform.translation = Translation::createFromDictionary(
translationDictionary
);
if (result->_transform.translation == nullptr) {
LERROR(fmt::format(
"Failed to create ephemeris for SceneGraphNode '{}'", result->identifier()
if (p.transform.has_value()) {
if (p.transform->translation.has_value()) {
result->_transform.translation = Translation::createFromDictionary(
*p.transform->translation
);
// @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we
// transitioned to throwing exceptions when the construction fails
if (result->_transform.translation == nullptr) {
LERROR(fmt::format(
"Failed to create ephemeris for SceneGraphNode '{}'",
result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format(
"Successfully created ephemeris for '{}'", result->identifier()
));
return nullptr;
result->addPropertySubOwner(result->_transform.translation.get());
}
if (p.transform->rotation.has_value()) {
result->_transform.rotation = Rotation::createFromDictionary(
*p.transform->rotation
);
// @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we
// transitioned to throwing exceptions when the construction fails
if (result->_transform.rotation == nullptr) {
LERROR(fmt::format(
"Failed to create rotation for SceneGraphNode '{}'",
result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format(
"Successfully created rotation for '{}'", result->identifier()
));
result->addPropertySubOwner(result->_transform.rotation.get());
}
if (p.transform->scale.has_value()) {
result->_transform.scale = Scale::createFromDictionary(*p.transform->scale);
// @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we
// transitioned to throwing exceptions when the construction fails
if (result->_transform.scale == nullptr) {
LERROR(fmt::format(
"Failed to create scale for SceneGraphNode '{}'",
result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format(
"Successfully created scale for '{}'", result->identifier()
));
result->addPropertySubOwner(result->_transform.scale.get());
}
LDEBUG(fmt::format(
"Successfully created ephemeris for '{}'", result->identifier()
));
}
if (result->_transform.translation) {
result->addPropertySubOwner(result->_transform.translation.get());
}
if (dictionary.hasKey(KeyTransformRotation)) {
ghoul::Dictionary rotationDictionary =
dictionary.value<ghoul::Dictionary>(KeyTransformRotation);
result->_transform.rotation = Rotation::createFromDictionary(rotationDictionary);
if (result->_transform.rotation == nullptr) {
LERROR(fmt::format(
"Failed to create rotation for SceneGraphNode '{}'", result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format(
"Successfully created rotation for '{}'", result->identifier()
));
}
if (result->_transform.rotation) {
result->addPropertySubOwner(result->_transform.rotation.get());
}
if (p.timeFrame.has_value()) {
result->_timeFrame = TimeFrame::createFromDictionary(*p.timeFrame);
if (dictionary.hasKey(KeyTransformScale)) {
ghoul::Dictionary scaleDictionary =
dictionary.value<ghoul::Dictionary>(KeyTransformScale);
result->_transform.scale = Scale::createFromDictionary(scaleDictionary);
if (result->_transform.scale == nullptr) {
LERROR(fmt::format(
"Failed to create scale for SceneGraphNode '{}'", result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format("Successfully created scale for '{}'", result->identifier()));
}
if (result->_transform.scale) {
result->addPropertySubOwner(result->_transform.scale.get());
}
if (dictionary.hasKey(KeyTimeFrame)) {
ghoul::Dictionary timeFrameDictionary =
dictionary.value<ghoul::Dictionary>(KeyTimeFrame);
result->_timeFrame = TimeFrame::createFromDictionary(timeFrameDictionary);
// @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we
// transitioned to throwing exceptions when the construction fails
if (result->_timeFrame == nullptr) {
LERROR(fmt::format(
"Failed to create time frame for SceneGraphNode '{}'",
@@ -252,30 +335,26 @@ ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
));
return nullptr;
}
result->addPropertySubOwner(result->_timeFrame.get());
LDEBUG(fmt::format(
"Successfully created time frame for '{}'",
result->identifier()
"Successfully created time frame for '{}'", result->identifier()
));
result->addPropertySubOwner(result->_timeFrame.get());
}
// We initialize the renderable last as it probably has the most dependencies
if (dictionary.hasValue<ghoul::Dictionary>(KeyRenderable)) {
ghoul::Dictionary renderableDictionary =
dictionary.value<ghoul::Dictionary>(KeyRenderable);
result->_renderable = Renderable::createFromDictionary(renderableDictionary);
if (p.renderable.has_value()) {
result->_renderable = Renderable::createFromDictionary(*p.renderable);
ghoul_assert(result->_renderable, "Failed to create Renderable");
result->addPropertySubOwner(result->_renderable.get());
LDEBUG(fmt::format(
"Successfully created renderable for '{}'", result->identifier()
));
// If the renderable child has a larger bounding sphere, we allow it tooverride
// If the renderable child has a larger bounding sphere, we allow it to override
if (result->_renderable->boundingSphere() > result->_boundingSphere) {
result->_boundingSphere = result->_renderable->boundingSphere();
if (dictionary.hasKey(BoundingSphereInfo.identifier)) {
if (p.boundingSphere.has_value()) {
LWARNING(fmt::format(
"The specified property 'BoundingSphere' for '{}' was overwritten "
"by a child renderable",
@@ -285,37 +364,34 @@ ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
}
}
if (dictionary.hasKey(KeyTag)) {
if (dictionary.hasValue<std::string>(KeyTag)) {
std::string tagName = dictionary.value<std::string>(KeyTag);
if (!tagName.empty()) {
result->addTag(std::move(tagName));
}
if (p.tag.has_value()) {
if (std::holds_alternative<std::string>(*p.tag)) {
result->addTag(std::get<std::string>(*p.tag));
}
else if (dictionary.hasValue<ghoul::Dictionary>(KeyTag)) {
ghoul::Dictionary tagNames = dictionary.value<ghoul::Dictionary>(KeyTag);
std::string tagName;
for (std::string_view key : tagNames.keys()) {
tagName = tagNames.value<std::string>(key);
if (!tagName.empty()) {
result->addTag(std::move(tagName));
else if (std::holds_alternative<std::vector<std::string>>(*p.tag)) {
for (const std::string& tag : std::get<std::vector<std::string>>(*p.tag)) {
if (!tag.empty()) {
result->addTag(tag);
}
}
}
else {
throw ghoul::MissingCaseException();
}
}
if (dictionary.hasKey(KeyGuiPath)) {
result->_guiPath = dictionary.value<std::string>(KeyGuiPath);
result->addProperty(result->_guiPath);
}
LDEBUG(fmt::format("Successfully created SceneGraphNode '{}'", result->identifier()));
result->_lastScreenSpaceUpdateTime = std::chrono::high_resolution_clock::now();
return result;
}
documentation::Documentation SceneGraphNode::Documentation() {
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_scene_node";
return doc;
}
SceneGraphNode::SceneGraphNode()
: properties::PropertyOwner({ "" })
, _guiHidden(GuiHiddenInfo)

View File

@@ -1,145 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* *
* 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 <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
namespace openspace {
documentation::Documentation SceneGraphNode::Documentation() {
using namespace documentation;
return {
"Scenegraph Node",
"core_scene_node",
{
{
"Identifier",
new StringVerifier,
Optional::No,
"The identifier of this scenegraph node. This name must be unique among all "
"scene graph nodes that are loaded in a specific scene. If a duplicate is "
"detected the loading of the node will fail, as will all childing that "
"depend on the node. The identifier must not contain any whitespaces or '.'."
},
{
"Parent",
new StringAnnotationVerifier(
"If specified, this must be a name for another scenegraph node"
),
Optional::Yes,
"This names the parent of the currently specified scenegraph node. The "
"parent must already exist in the scene graph. If not specified, the node "
"will be attached to the root of the scenegraph.",
},
{
"Renderable",
new ReferencingVerifier("renderable"),
Optional::Yes,
"The renderable that is to be created for this scenegraph node. A renderable "
"is a component of a scenegraph node that will lead to some visual result on "
"the screen. The specifics heavily depend on the 'Type' of the renderable. "
"If no Renderable is specified, this scenegraph node is an internal node and "
"can be used for either group children, or apply common transformations to a "
"group of children."
},
{
"Transform",
new TableVerifier({
{
"Translation",
new ReferencingVerifier("core_transform_translation"),
Optional::Yes,
"This node describes a translation that is applied to the scenegraph "
"node and all its children. Depending on the 'Type' of the "
"translation, this can either be a static translation or a "
"time-varying one."
},
{
"Rotation",
new ReferencingVerifier("core_transform_rotation"),
Optional::Yes,
"This nodes describes a rotation that is applied to the scenegraph "
"node and all its children. Depending on the 'Type' of the rotation, "
"this can either be a static rotation or a time-varying one."
},
{
"Scale",
new ReferencingVerifier("core_transform_scaling"),
Optional::Yes,
"This node describes a scaling that is applied to the scenegraph "
"node and all its children. Depending on the 'Type' of the scaling, "
"this can either be a static scaling or a time-varying one."
}
}),
Optional::Yes,
"This describes a set of transformations that are applied to this scenegraph "
"node and all of its children. There are only three possible values "
"corresponding to a 'Translation', a 'Rotation', and a 'Scale'."
},
{
"TimeFrame",
new ReferencingVerifier("core_time_frame"),
Optional::Yes,
"Specifies the time frame for when this node should be active."
},
{
"GUI",
new TableVerifier({
{
"Name",
new StringVerifier,
Optional::Yes,
"An optional user-facing name for this SceneGraphNode, which does "
"not have to be unique, though it is recommended, and can contain "
"any characters."
},
{
"Path",
new StringVerifier,
Optional::Yes,
"If this value is specified, this '/' separated URI specifies the "
"location of this scenegraph node in a GUI representation, for "
"instance '/SolarSystem/Earth/Moon'."
},
{
"Hidden",
new BoolVerifier,
Optional::Yes,
"If this value is specified, GUI applications are incouraged to "
"ignore this scenegraph node. This is most useful to trim collective "
"lists of nodes and not display, for example, barycenters."
}
}),
Optional::Yes,
"Additional information that is passed to GUI applications. These are all "
"hints and do not have any impact on the actual function of the scenegraph "
"node."
},
}
};
}
} // namespace openspace

View File

@@ -34,45 +34,33 @@
#include <ghoul/misc/templatefactory.h>
namespace {
const char* KeyType = "Type";
struct [[codegen::Dictionary(Translation)]] Parameters {
// The type of translation that is described in this element. The available types
// of translations depend on the configuration of the application and can be
// written to disk on application startup into the FactoryDocumentation
std::string type [[codegen::annotation("Must name a valid Translation type")]];
};
#include "translation_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation Translation::Documentation() {
using namespace documentation;
return {
"Transformation Translation",
"core_transform_translation",
{
{
KeyType,
new StringAnnotationVerifier("Must name a valid Translation type"),
Optional::No,
"The type of translation that is described in this element. "
"The available types of translations depend on the "
"configuration of the application and can be written to disk "
"on application startup into the FactoryDocumentation."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_transform_translation";
return doc;
}
ghoul::mm_unique_ptr<Translation> Translation::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(Documentation(), dictionary, "Translation");
const Parameters p = codegen::bake<Parameters>(dictionary);
const std::string& translationType = dictionary.value<std::string>(KeyType);
ghoul::TemplateFactory<Translation>* factory
= FactoryManager::ref().factory<Translation>();
Translation* result = factory->create(
translationType,
Translation* result = FactoryManager::ref().factory<Translation>()->create(
p.type,
dictionary,
&global::memoryManager->PersistentMemory
);
result->setIdentifier("Translation");
return ghoul::mm_unique_ptr<Translation>(result);
}

View File

@@ -33,68 +33,51 @@
#include "scriptscheduler_lua.inl"
namespace {
constexpr const char* KeyTime = "Time";
constexpr const char* KeyForwardScript = "ForwardScript";
constexpr const char* KeyBackwardScript = "BackwardScript";
constexpr const char* KeyUniversalScript = "Script";
struct [[codegen::Dictionary(ScheduledScript)]] Parameters {
// The time at which, when the in game time passes it, the two scripts will
// be executed. If the traversal is forwards (towards + infinity), the
// ForwardScript will be executed, otherwise the BackwardScript will be
// executed instead
std::string time;
// The Lua script that will be executed when the specified time is passed
// independent of its direction. This script will be executed before the
// specific scripts if both versions are specified
std::optional<std::string> script;
// The Lua script that is executed when OpenSpace passes the time in a
// forward direction
std::optional<std::string> forwardScript;
// The Lua script that is executed when OpenSpace passes the time in a
// backward direction
std::optional<std::string> backwardScript;
};
#include "scriptscheduler_codegen.cpp"
} // namespace
namespace openspace::scripting {
documentation::Documentation ScriptScheduler::Documentation() {
using namespace openspace::documentation;
using TimeVerifier = StringVerifier;
using LuaScriptVerifier = StringVerifier;
return {
"Scheduled Scripts",
"core_scheduledscript",
{
{
"*",
new TableVerifier({
{
KeyTime,
new TimeVerifier,
Optional::No,
"The time at which, when the in game time passes it, the two "
"scripts will be executed. If the traversal is forwards (towards "
"+ infinity), the ForwardScript will be executed, otherwise the "
"BackwardScript will be executed instead."
},
{
KeyUniversalScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that will be executed when the specified time is "
"passed independent of its direction. This script will be "
"executed before the specific scripts if both versions are "
"specified"
},
{
KeyForwardScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that is executed when OpenSpace passes the time "
"in a forward direction."
},
{
KeyBackwardScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that is executed when OpenSpace passes the time "
"in a backward direction."
}
}),
Optional::No
}
}
};
// @TODO (abock, 2021-03-25) This is not really correct. This function currently
// returns the documentation for the ScheduledScript, not for the ScriptScheduler
// itself. This should be cleaned up a bit
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_scheduledscript";
return doc;
}
using namespace openspace::interaction;
ScriptScheduler::ScheduledScript::ScheduledScript(const ghoul::Dictionary& dict) {
const Parameters p = codegen::bake<Parameters>(dict);
time = Time::convertTime(p.time);
forwardScript = p.forwardScript.value_or(forwardScript);
backwardScript = p.backwardScript.value_or(backwardScript);
universalScript = p.script.value_or(universalScript);
}
void ScriptScheduler::loadScripts(std::vector<ScheduledScript> scheduledScripts) {
// Sort scripts by time; use a stable_sort as the user might have had an intention
// specifying multiple scripts for the same time in a specific order

View File

@@ -38,35 +38,20 @@ int loadFile(lua_State* L) {
return ghoul::lua::luaError(L, "filepath string is empty");
}
ghoul::Dictionary scriptsDict = ghoul::lua::loadDictionaryFromFile(fileName, L);
ghoul::Dictionary scriptsDict;
scriptsDict.setValue("Scripts", ghoul::lua::loadDictionaryFromFile(fileName, L));
documentation::testSpecificationAndThrow(
scripting::ScriptScheduler::Documentation(),
scriptsDict,
"ScriptScheduler"
);
std::vector<scripting::ScriptScheduler::ScheduledScript> scripts;
for (int i = 1; i <= scriptsDict.size(); ++i) {
for (size_t i = 1; i <= scriptsDict.size(); ++i) {
ghoul::Dictionary d = scriptsDict.value<ghoul::Dictionary>(std::to_string(i));
scripting::ScriptScheduler::ScheduledScript script;
constexpr const char* KeyTime = "Time";
if (d.hasValue<std::string>(KeyTime)) {
script.time = Time::convertTime(d.value<std::string>(KeyTime));
}
constexpr const char* KeyForwardScript = "ForwardScript";
if (d.hasValue<std::string>(KeyForwardScript)) {
script.forwardScript = d.value<std::string>(KeyForwardScript);
}
constexpr const char* KeyBackwardScript = "BackwardScript";
if (d.hasValue<std::string>(KeyBackwardScript)) {
script.backwardScript = d.value<std::string>(KeyBackwardScript);
}
constexpr const char* KeyUniversalScript = "Script";
if (d.hasValue<std::string>(KeyUniversalScript)) {
script.universalScript = d.value<std::string>(KeyUniversalScript);
}
scripting::ScriptScheduler::ScheduledScript script =
scripting::ScriptScheduler::ScheduledScript(d);
scripts.push_back(script);
}

View File

@@ -30,67 +30,41 @@
#include <ghoul/misc/templatefactory.h>
namespace {
constexpr const char* KeyType = "Type";
constexpr const char* KeyName = "Name";
struct [[codegen::Dictionary(ResourceSynchronization)]] Parameters {
// This key specifies the type of ResourceSyncrhonization that gets created. It
// has to be one of the valid ResourceSyncrhonizations that are available for
// creation (see the FactoryDocumentation for a list of possible
// ResourceSyncrhonizations), which depends on the configration of the application
std::string type
[[codegen::annotation("A ResourceSynchronization created by a factory")]];
// A user readable name of this synchronization
std::string name;
};
#include "resourcesynchronization_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation ResourceSynchronization::Documentation() {
using namespace openspace::documentation;
return {
"ResourceSynchronization",
"resourceSynchronization",
{
{
KeyType,
new StringAnnotationVerifier(
"A valid ResourceSyncrhonization created by a factory"
),
Optional::No,
"This key specifies the type of ResourceSyncrhonization that gets "
"created. It has to be one of the valid ResourceSyncrhonizations that "
"are available for creation (see the FactoryDocumentation for a list of "
"possible ResourceSyncrhonizations), which depends on the configration "
"of the application"
},
{
KeyName,
new StringVerifier,
Optional::No,
"A user readable name of this synchronization"
},
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "resourceSynchronization";
return doc;
}
std::unique_ptr<ResourceSynchronization> ResourceSynchronization::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"ResourceSynchronization"
);
const std::string& synchronizationType = dictionary.value<std::string>(KeyType);
const Parameters p = codegen::bake<Parameters>(dictionary);
auto factory = FactoryManager::ref().factory<ResourceSynchronization>();
ghoul_assert(factory, "ResourceSynchronization factory did not exist");
ResourceSynchronization* sync = factory->create(synchronizationType, dictionary);
ResourceSynchronization* sync = factory->create(p.type, dictionary);
sync->_name = p.name;
return std::unique_ptr<ResourceSynchronization>(sync);
}
ResourceSynchronization::ResourceSynchronization(const ghoul::Dictionary& dictionary) {
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"ResourceSynchronization"
);
_name = dictionary.value<std::string>(KeyName);
}
ResourceSynchronization::ResourceSynchronization(const ghoul::Dictionary&) {}
ResourceSynchronization::State ResourceSynchronization::state() const {
return _state;
@@ -111,14 +85,14 @@ bool ResourceSynchronization::isSyncing() const {
ResourceSynchronization::CallbackHandle
ResourceSynchronization::addStateChangeCallback(StateChangeCallback cb)
{
std::lock_guard<std::mutex> guard(_callbackMutex);
std::lock_guard guard(_callbackMutex);
CallbackHandle callbackId = _nextCallbackId++;
_stateChangeCallbacks[callbackId] = std::move(cb);
return callbackId;
}
void ResourceSynchronization::removeStateChangeCallback(CallbackHandle id) {
std::lock_guard<std::mutex> guard(_callbackMutex);
std::lock_guard guard(_callbackMutex);
_stateChangeCallbacks.erase(id);
}

View File

@@ -586,7 +586,7 @@ std::string SpiceManager::dateFromEphemerisTime(double ephemerisTime, const char
ephemerisTime, format
));
}
return std::string(Buffer);
}

View File

@@ -3,7 +3,7 @@
"""
OpenSpace
Copyright (c) 2014-2020
Copyright (c) 2014-2021
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
@@ -59,7 +59,7 @@ import os
import re
import sys
current_year = '2020'
current_year = '2021'
is_strict_mode = False
is_silent_mode = False
@@ -659,19 +659,23 @@ if not is_silent_mode:
check_files(
[basePath + 'src/**/*.cpp'],
[],
[basePath + 'src/**/*_codegen.cpp'],
'openspace_core',
check_source_file
)
check_files(
[basePath + 'apps/**/*.cpp'],
[basePath + 'apps/**/ext/**/*.cpp'],
[basePath + 'apps/**/ext/**/*.cpp', basePath + 'apps/**/*_codegen.cpp'],
'openspace_app',
check_source_file
)
check_files(
[basePath + 'modules/**/*.cpp'],
[basePath + 'modules/**/ext/**/*.cpp', basePath + 'modules/**/node_modules/**/*.cpp'],
[
basePath + 'modules/**/ext/**/*.cpp',
basePath + 'modules/**/node_modules/**/*.cpp',
basePath + 'modules/**/*_codegen.cpp'
],
'openspace_module',
check_source_file
)