From 775d9751ddf8194bb7abd3188acd2f5386e37f38 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Tue, 9 Mar 2021 11:26:37 +0100 Subject: [PATCH 01/28] Apply mesh transform in shader including normals and tangents * Update Ghoul * Loading several models have not yet been updated and will not work with this commit --- ext/ghoul | 2 +- modules/base/shaders/model_vs.glsl | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 2180f32860..5b79e1b455 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 2180f32860131b08831f9d4c2be5839b5aa48672 +Subproject commit 5b79e1b455d5df0ec1ea06f0b0abe4553de433d2 diff --git a/modules/base/shaders/model_vs.glsl b/modules/base/shaders/model_vs.glsl index df55892597..1a0e9cac14 100644 --- a/modules/base/shaders/model_vs.glsl +++ b/modules/base/shaders/model_vs.glsl @@ -40,9 +40,11 @@ out mat3 TBN; uniform mat4 modelViewTransform; uniform mat4 projectionTransform; uniform mat4 normalTransform; +uniform mat4 meshTransform; +uniform mat4 meshNormalTransform; void main() { - vs_positionCameraSpace = modelViewTransform * in_position; + vs_positionCameraSpace = modelViewTransform * (meshTransform * in_position); vec4 positionClipSpace = projectionTransform * vs_positionCameraSpace; vec4 positionScreenSpace = z_normalization(positionClipSpace); @@ -50,11 +52,11 @@ void main() { vs_st = in_st; vs_screenSpaceDepth = positionScreenSpace.w; - vs_normalViewSpace = normalize(mat3(normalTransform) * in_normal); + vs_normalViewSpace = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_normal)); // TBN matrix for normal mapping - vec3 T = normalize(vec3(modelViewTransform * vec4(in_tangent, 0.0))); - vec3 N = normalize(vec3(modelViewTransform * vec4(in_normal, 0.0))); + vec3 T = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_tangent)); + vec3 N = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_normal)); // Re-orthogonalize T with respect to N T = normalize(T - dot(T, N) * N); From e850e7a1b7debe949e227158815f13ef5bb9cc2f Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Wed, 10 Mar 2021 17:15:56 +0100 Subject: [PATCH 02/28] Add DateTime verifier --- include/openspace/documentation/verifier.h | 11 +++++ src/documentation/verifier.cpp | 53 ++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/include/openspace/documentation/verifier.h b/include/openspace/documentation/verifier.h index 0126a804f7..86b2ed1faa 100644 --- a/include/openspace/documentation/verifier.h +++ b/include/openspace/documentation/verifier.h @@ -180,6 +180,17 @@ struct DirectoryVerifier : public StringVerifier { std::string type() const override; }; +/** + * A Verifier that checks whether a given key inside a ghoul::Dictionary is a string and + * a valid date time + */ +struct DateTimeVerifier : public StringVerifier { + TestResult operator()(const ghoul::Dictionary& dict, + const std::string& key) const override; + + std::string type() const override; +}; + /** * A Verifier that checks whether a given key inside a ghoul::Dictionary is another * ghoul::Dictionary. The constructor takes a list of DocumentationEntry%s, which are used diff --git a/src/documentation/verifier.cpp b/src/documentation/verifier.cpp index ed326b1910..751bb0ee9b 100644 --- a/src/documentation/verifier.cpp +++ b/src/documentation/verifier.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include namespace openspace::documentation { @@ -226,6 +228,57 @@ std::string DirectoryVerifier::type() const { return "Directory"; } +TestResult DateTimeVerifier::operator()(const ghoul::Dictionary& dict, + const std::string& key) const +{ + TestResult res = StringVerifier::operator()(dict, key); + if (!res.success) { + return res; + } + + std::string dateTime = dict.value(key); + std::string format = "%Y %m %d %H:%M:%S"; // YYYY MM DD hh:mm:ss + + std::tm t = {}; + std::istringstream ss(dateTime); + ss >> std::get_time(&t, format.c_str()); + + // first check format (automatically checks if valid time) + if (ss.fail()) { + res.success = false; + TestResult::Offense off; + off.offender = key; + off.reason = TestResult::Offense::Reason::Verification; + off.explanation = "Not a valid format"; + res.offenses.push_back(off); + } + // then check if valid date + else { + // normalize e.g. 29/02/2013 would become 01/03/2013 + std::tm t_copy(t); + time_t when = mktime(&t_copy); + std::tm *norm = localtime(&when); + + // validate (is the normalized date still the same?): + if (norm->tm_mday != t.tm_mday && + norm->tm_mon != t.tm_mon && + norm->tm_year != t.tm_year) + { + res.success = false; + TestResult::Offense off; + off.offender = key; + off.reason = TestResult::Offense::Reason::Verification; + off.explanation = "Not a valid date"; + res.offenses.push_back(off); + } + } + return res; +} + +std::string DateTimeVerifier::type() const { + return "Date and time"; +} + TestResult Color3Verifier::operator()(const ghoul::Dictionary& dictionary, const std::string& key) const { From aebe6c12175abf6aeea1157ea275fa78e6537eb4 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Fri, 12 Mar 2021 09:55:23 +0100 Subject: [PATCH 03/28] Add codegen for renderablemodel --- modules/base/rendering/renderablemodel.cpp | 299 +++++++++------------ src/documentation/verifier.cpp | 2 +- support/coding/codegen | 2 +- 3 files changed, 125 insertions(+), 178 deletions(-) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index e72cd9f986..9ed0159885 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -40,13 +40,11 @@ #include #include #include +#include namespace { constexpr const char* _loggerCat = "RenderableModel"; - constexpr const char* ProgramName = "ModelProgram"; - constexpr const char* KeyGeomModelFile = "GeometryFile"; - constexpr const char* KeyForceRenderInvisible = "ForceRenderInvisible"; constexpr const int DefaultBlending = 0; constexpr const int AdditiveBlending = 1; @@ -126,7 +124,7 @@ namespace { }; constexpr openspace::properties::Property::PropertyInfo BlendingOptionInfo = { - "BledingOption", + "BlendingOption", "Blending Options", "Debug option for blending colors." }; @@ -136,106 +134,64 @@ namespace { "Enable Opacity Blending", "Enable Opacity Blending." }; + + struct [[codegen::Dictionary(RenderableModel)]] Parameters { + // The file or files that should be loaded in this RenderableModel. The file can + // contain filesystem tokens or can be specified relatively to the + // location of the .mod file. + // This specifies the model that is rendered by the Renderable. + std::variant> geometryFile; + + // Set if invisible parts (parts with no textures or materials) of the model + // should be forced to render or not. + std::optional forceRenderInvisible; + + // The date and time that the model animation should start. + // In format 'YYYY MM DD hh:mm:ss'. + std::optional animationStartTime [[codegen::dateTime()]]; + + // [[codegen::verbatim(AmbientIntensityInfo.description)]] + std::optional ambientIntensity; + + // [[codegen::verbatim(DiffuseIntensityInfo.description)]] + std::optional diffuseIntensity; + + // [[codegen::verbatim(SpecularIntensityInfo.description)]] + std::optional specularIntensity; + + // [[codegen::verbatim(ShadingInfo.description)]] + std::optional performShading; + + // [[codegen::verbatim(DisableFaceCullingInfo.description)]] + std::optional disableFaceCulling; + + // [[codegen::verbatim(ModelTransformInfo.description)]] + std::optional modelTransform; + + // [[codegen::verbatim(RotationVecInfo.description)]] + std::optional rotationVector; + + // [[codegen::verbatim(LightSourcesInfo.description)]] + std::optional> lightSources [[codegen::reference("core_light_source")]]; + + // [[codegen::verbatim(DisableDepthTestInfo.description)]] + std::optional disableDepthTest; + + // [[codegen::verbatim(BlendingOptionInfo.description)]] + std::optional blendingOption; + + // [[codegen::verbatim(EnableOpacityBlendingInfo.description)]] + std::optional enableOpacityBlending; + }; +#include "renderablemodel_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderableModel::Documentation() { - using namespace documentation; - return { - "RenderableModel", - "base_renderable_model", - { - { - KeyGeomModelFile, - new OrVerifier({ new StringVerifier, new StringListVerifier }), - Optional::No, - "The file or files that should be loaded in this RenderableModel. The file can " - "contain filesystem tokens or can be specified relatively to the " - "location of the .mod file. " - "This specifies the model that is rendered by the Renderable." - }, - { - KeyForceRenderInvisible, - new BoolVerifier, - Optional::Yes, - "Set if invisible parts (parts with no textures or materials) of the model " - "should be forced to render or not." - }, - { - AmbientIntensityInfo.identifier, - new DoubleVerifier, - Optional::Yes, - AmbientIntensityInfo.description - }, - { - DiffuseIntensityInfo.identifier, - new DoubleVerifier, - Optional::Yes, - DiffuseIntensityInfo.description - }, - { - SpecularIntensityInfo.identifier, - new DoubleVerifier, - Optional::Yes, - SpecularIntensityInfo.description - }, - { - ShadingInfo.identifier, - new BoolVerifier, - Optional::Yes, - ShadingInfo.description - }, - { - DisableFaceCullingInfo.identifier, - new BoolVerifier, - Optional::Yes, - DisableFaceCullingInfo.description - }, - { - ModelTransformInfo.identifier, - new DoubleMatrix3Verifier, - Optional::Yes, - ModelTransformInfo.description - }, - { - RotationVecInfo.identifier, - new DoubleVector3Verifier, - Optional::Yes, - RotationVecInfo.description - }, - { - LightSourcesInfo.identifier, - new TableVerifier({ - { - "*", - new ReferencingVerifier("core_light_source"), - Optional::Yes - } - }), - Optional::Yes, - LightSourcesInfo.description - }, - { - DisableDepthTestInfo.identifier, - new BoolVerifier, - Optional::Yes, - DisableDepthTestInfo.description - }, - { - BlendingOptionInfo.identifier, - new StringVerifier, - Optional::Yes, - BlendingOptionInfo.description - }, - { - EnableOpacityBlendingInfo.identifier, - new BoolVerifier, - Optional::Yes, - EnableOpacityBlendingInfo.description - }, - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "base_renderable_model"; + return doc; } RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) @@ -260,17 +216,13 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) ) , _lightSourcePropertyOwner({ "LightSources", "Light Sources" }) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderableModel" - ); + const Parameters p = codegen::bake(dictionary); addProperty(_opacity); registerUpdateRenderBinFromOpacity(); - if (dictionary.hasKey(KeyForceRenderInvisible)) { - _forceRenderInvisible = dictionary.value(KeyForceRenderInvisible); + if (p.forceRenderInvisible.has_value()) { + _forceRenderInvisible = *p.forceRenderInvisible; if (!_forceRenderInvisible) { // Asset file have specifically said to not render invisible parts, @@ -279,88 +231,87 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } } - if (dictionary.hasKey(KeyGeomModelFile)) { + if (std::holds_alternative(p.geometryFile)) { + // Handle single file std::string file; + file = absPath(std::get(p.geometryFile)); + _geometry = ghoul::io::ModelReader::ref().loadModel( + file, + ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible), + ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped) + ); + } + else if (std::holds_alternative>(p.geometryFile)){ + LWARNING("Loading a model with several files is deprecated and will be " + "removed in a future release TESTING" + ); + /* + //TODO: update to use new codegen stuff + std::string file; + ghoul::Dictionary fileDictionary = dictionary.value( + KeyGeomModelFile + ); + std::vector> geometries; - if (dictionary.hasValue(KeyGeomModelFile)) { - // Handle single file - file = absPath(dictionary.value(KeyGeomModelFile)); - _geometry = ghoul::io::ModelReader::ref().loadModel( + for (std::string_view k : fileDictionary.keys()) { + // Handle each file + file = absPath(fileDictionary.value(k)); + geometries.push_back(ghoul::io::ModelReader::ref().loadModel( file, ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible), ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped) - ); + )); } - else if (dictionary.hasValue(KeyGeomModelFile)) { - LWARNING("Loading a model with several files is deprecated and will be " - "removed in a future release" - ); - ghoul::Dictionary fileDictionary = dictionary.value( - KeyGeomModelFile - ); - std::vector> geometries; + if (!geometries.empty()) { + std::unique_ptr combinedGeometry = + std::move(geometries[0]); - for (std::string_view k : fileDictionary.keys()) { - // Handle each file - file = absPath(fileDictionary.value(k)); - geometries.push_back(ghoul::io::ModelReader::ref().loadModel( - file, - ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible), - ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped) - )); - } - - if (!geometries.empty()) { - std::unique_ptr combinedGeometry = - std::move(geometries[0]); - - // Combine all models into one ModelGeometry - for (unsigned int i = 1; i < geometries.size(); ++i) { - for (ghoul::io::ModelMesh& mesh : geometries[i]->meshes()) { - combinedGeometry->meshes().push_back( - std::move(mesh) - ); - } - - for (ghoul::modelgeometry::ModelGeometry::TextureEntry& texture : - geometries[i]->textureStorage()) - { - combinedGeometry->textureStorage().push_back( - std::move(texture) - ); - } + // Combine all models into one ModelGeometry + for (unsigned int i = 1; i < geometries.size(); ++i) { + for (ghoul::io::ModelMesh& mesh : geometries[i]->meshes()) { + combinedGeometry->meshes().push_back( + std::move(mesh) + ); + } + + for (ghoul::modelgeometry::ModelGeometry::TextureEntry& texture : + geometries[i]->textureStorage()) + { + combinedGeometry->textureStorage().push_back( + std::move(texture) + ); } - _geometry = std::move(combinedGeometry); - _geometry->calculateBoundingRadius(); } - } + _geometry = std::move(combinedGeometry); + _geometry->calculateBoundingRadius(); + }*/ } - if (dictionary.hasKey(ModelTransformInfo.identifier)) { - _modelTransform = dictionary.value(ModelTransformInfo.identifier); + if (p.modelTransform.has_value()) { + _modelTransform = *p.modelTransform; } - if (dictionary.hasKey(AmbientIntensityInfo.identifier)) { - _ambientIntensity = dictionary.value(AmbientIntensityInfo.identifier); + if (p.ambientIntensity.has_value()) { + _ambientIntensity = *p.ambientIntensity; } - if (dictionary.hasKey(DiffuseIntensityInfo.identifier)) { - _diffuseIntensity = dictionary.value(DiffuseIntensityInfo.identifier); + if (p.diffuseIntensity.has_value()) { + _diffuseIntensity = *p.diffuseIntensity; } - if (dictionary.hasKey(SpecularIntensityInfo.identifier)) { - _specularIntensity = dictionary.value(SpecularIntensityInfo.identifier); + if (p.specularIntensity.has_value()) { + _specularIntensity = *p.specularIntensity; } - if (dictionary.hasKey(ShadingInfo.identifier)) { - _performShading = dictionary.value(ShadingInfo.identifier); + if (p.performShading.has_value()) { + _performShading = *p.performShading; } - if (dictionary.hasKey(DisableDepthTestInfo.identifier)) { - _disableDepthTest = dictionary.value(DisableDepthTestInfo.identifier); + if (p.disableDepthTest.has_value()) { + _disableDepthTest = *p.disableDepthTest; } - if (dictionary.hasKey(DisableFaceCullingInfo.identifier)) { - _disableFaceCulling = dictionary.value(DisableFaceCullingInfo.identifier); + if (p.disableFaceCulling.has_value()) { + _disableFaceCulling = *p.disableFaceCulling; } if (dictionary.hasKey(LightSourcesInfo.identifier)) { @@ -391,8 +342,8 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) }); - if (dictionary.hasKey(RotationVecInfo.identifier)) { - _rotationVec = dictionary.value(RotationVecInfo.identifier); + if (p.rotationVector.has_value()) { + _rotationVec = *p.rotationVector; } _blendingFuncOption.addOption(DefaultBlending, "Default"); @@ -403,17 +354,13 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) addProperty(_blendingFuncOption); - if (dictionary.hasKey(BlendingOptionInfo.identifier)) { - const std::string blendingOpt = dictionary.value( - BlendingOptionInfo.identifier - ); + if (p.blendingOption.has_value()) { + const std::string blendingOpt = *p.blendingOption; _blendingFuncOption.set(BlendingMapping[blendingOpt]); } - if (dictionary.hasKey(DisableDepthTestInfo.identifier)) { - _enableOpacityBlending = dictionary.value( - EnableOpacityBlendingInfo.identifier - ); + if (p.enableOpacityBlending.has_value()) { + _enableOpacityBlending = *p.enableOpacityBlending; } addProperty(_enableOpacityBlending); diff --git a/src/documentation/verifier.cpp b/src/documentation/verifier.cpp index 751bb0ee9b..2e2e4f00d0 100644 --- a/src/documentation/verifier.cpp +++ b/src/documentation/verifier.cpp @@ -249,7 +249,7 @@ TestResult DateTimeVerifier::operator()(const ghoul::Dictionary& dict, TestResult::Offense off; off.offender = key; off.reason = TestResult::Offense::Reason::Verification; - off.explanation = "Not a valid format"; + off.explanation = "Not a valid format, should be: YYYY MM DD hh:mm:ss"; res.offenses.push_back(off); } // then check if valid date diff --git a/support/coding/codegen b/support/coding/codegen index 1ca72c0202..37b4ff6f5a 160000 --- a/support/coding/codegen +++ b/support/coding/codegen @@ -1 +1 @@ -Subproject commit 1ca72c0202e3bd4b61510f84797db131591c8ca3 +Subproject commit 37b4ff6f5a7d62fdacded6da12ef6d455cda9241 From 323e9b2fb5f5272a588c8966648ef6db8aab513d Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Tue, 16 Mar 2021 09:05:32 +0100 Subject: [PATCH 04/28] Update ghoul and add model animation --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 19 ++++++++++++++++++- modules/base/rendering/renderablemodel.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 5b79e1b455..9b2f195271 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 5b79e1b455d5df0ec1ea06f0b0abe4553de433d2 +Subproject commit 9b2f19527130fff598269324923a7c5d14b9ba1b diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 9ed0159885..56aefb946a 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -150,6 +150,10 @@ namespace { // In format 'YYYY MM DD hh:mm:ss'. std::optional animationStartTime [[codegen::dateTime()]]; + // The time scale for the animation relative to seconds. + // Ex if animation is in milli seconds then AnimationTimeScale = 1000 + std::optional animationTimeScale; + // [[codegen::verbatim(AmbientIntensityInfo.description)]] std::optional ambientIntensity; @@ -231,6 +235,10 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } } + if (p.animationStartTime.has_value()) { + _animationStart = *p.animationStartTime; + } + if (std::holds_alternative(p.geometryFile)) { // Handle single file std::string file; @@ -288,6 +296,10 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) }*/ } + if (p.animationTimeScale.has_value()) { + _geometry->setTimeScale(*p.animationTimeScale); + } + if (p.modelTransform.has_value()) { _modelTransform = *p.modelTransform; } @@ -516,11 +528,16 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) { _program->deactivate(); } -void RenderableModel::update(const UpdateData&) { +void RenderableModel::update(const UpdateData& data) { if (_program->isDirty()) { _program->rebuildFromFile(); ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); } + + double realtiveTime = data.time.j2000Seconds() - data.time.convertTime(_animationStart); + if (_geometry->hasAnimation()) { + _geometry->update(realtiveTime); + } } } // namespace openspace diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index 42dbf8828d..da96c8a1da 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -73,6 +73,7 @@ private: std::unique_ptr _geometry; bool _forceRenderInvisible = false; bool _notifyInvisibleDropped = true; + std::string _animationStart; properties::FloatProperty _ambientIntensity; From 3201be5cd63a0ecb009ca73df1de818528e917db Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Thu, 18 Mar 2021 09:34:38 +0100 Subject: [PATCH 05/28] Add loop from start mode for animation --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 70 ++++++++++++++++++++-- modules/base/rendering/renderablemodel.h | 7 +++ 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index bb643bb1d5..3bd220b9fc 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit bb643bb1d578a8cf471476d7a555d5170cb105c9 +Subproject commit 3bd220b9fc3d8f05af79c1c5c7594755499b77be diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 56aefb946a..5c0bc62f3f 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -150,9 +150,25 @@ namespace { // In format 'YYYY MM DD hh:mm:ss'. std::optional animationStartTime [[codegen::dateTime()]]; + enum class TimeUnit { + Millisecond, + Second + }; + // The time scale for the animation relative to seconds. - // Ex if animation is in milli seconds then AnimationTimeScale = 1000 - std::optional animationTimeScale; + // Ex, if animation is in milliseconds then AnimationTimeScale = 0.001 or + // AnimationTimeScale = "Millisecond", default is "Second" + std::optional> animationTimeScale; + + enum class AnimationMode { + Once, + LoopFromStart, + Bounce + }; + + // The mode of how the animation should be played back. + // Default is animation is played back once at the start time + std::optional animationMode; // [[codegen::verbatim(AmbientIntensityInfo.description)]] std::optional ambientIntensity; @@ -176,7 +192,7 @@ namespace { std::optional rotationVector; // [[codegen::verbatim(LightSourcesInfo.description)]] - std::optional> lightSources [[codegen::reference("core_light_source")]]; + std::optional> lightSources [[codegen::reference("core_light_source")]]; // [[codegen::verbatim(DisableDepthTestInfo.description)]] std::optional disableDepthTest; @@ -297,7 +313,37 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } if (p.animationTimeScale.has_value()) { - _geometry->setTimeScale(*p.animationTimeScale); + if (std::holds_alternative(*p.animationTimeScale)) { + _geometry->setTimeScale(std::get(*p.animationTimeScale)); + } + else if (std::holds_alternative(*p.animationTimeScale)) { + Parameters::TimeUnit timeUnit = + std::get(*p.animationTimeScale); + + switch (timeUnit) { + case Parameters::TimeUnit::Millisecond: + _geometry->setTimeScale(0.001); + break; + case Parameters::TimeUnit::Second: + _geometry->setTimeScale(1.0); + break; + } + } + } + + if (p.animationMode.has_value()) { + switch (*p.animationMode) { + case Parameters::AnimationMode::LoopFromStart: + _animationMode = AnimationMode::LoopFromStart; + break; + case Parameters::AnimationMode::Bounce: + _animationMode = AnimationMode::Bounce; + break; + case Parameters::AnimationMode::Once: + default: + _animationMode = AnimationMode::Once; + break; + } } if (p.modelTransform.has_value()) { @@ -534,8 +580,22 @@ void RenderableModel::update(const UpdateData& data) { ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); } - double realtiveTime = data.time.j2000Seconds() - data.time.convertTime(_animationStart); if (_geometry->hasAnimation()) { + double realtiveTime; + switch (_animationMode) { + case AnimationMode::LoopFromStart: + realtiveTime = std::fmod( + data.time.j2000Seconds() - data.time.convertTime(_animationStart), + _geometry->animationDuration() + ); + break; + case AnimationMode::Once: + default: + realtiveTime = + data.time.j2000Seconds() - data.time.convertTime(_animationStart); + break; + } + _geometry->update(realtiveTime); } } diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index da96c8a1da..7ef6d4a977 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -70,10 +70,17 @@ public: static documentation::Documentation Documentation(); private: + enum class AnimationMode { + Once = 0, + LoopFromStart, + Bounce + }; + std::unique_ptr _geometry; bool _forceRenderInvisible = false; bool _notifyInvisibleDropped = true; std::string _animationStart; + AnimationMode _animationMode; properties::FloatProperty _ambientIntensity; From c1fb7d39e76e5263114d4b2c07911b67b803bbd3 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Thu, 18 Mar 2021 09:40:30 +0100 Subject: [PATCH 06/28] Remove "" from codegen comments --- modules/base/rendering/renderablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 5c0bc62f3f..3da3695999 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -157,7 +157,7 @@ namespace { // The time scale for the animation relative to seconds. // Ex, if animation is in milliseconds then AnimationTimeScale = 0.001 or - // AnimationTimeScale = "Millisecond", default is "Second" + // AnimationTimeScale = Millisecond, default is Second std::optional> animationTimeScale; enum class AnimationMode { From 205374fee2bc38d69742c571ae4ebb94bbc98573 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Thu, 18 Mar 2021 15:20:23 +0100 Subject: [PATCH 07/28] Use codegen in ModelProjection and make use of new dictionary --- modules/base/rendering/renderablemodel.cpp | 12 +- .../rendering/renderablemodelprojection.cpp | 165 ++++++++---------- 2 files changed, 79 insertions(+), 98 deletions(-) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 3da3695999..2a387cbd9d 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -372,14 +372,12 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) _disableFaceCulling = *p.disableFaceCulling; } - if (dictionary.hasKey(LightSourcesInfo.identifier)) { - const ghoul::Dictionary& lsDictionary = - dictionary.value(LightSourcesInfo.identifier); + if (p.lightSources.has_value()) { + std::vector lightsources = *p.lightSources; - for (std::string_view k : lsDictionary.keys()) { - std::unique_ptr lightSource = LightSource::createFromDictionary( - lsDictionary.value(k) - ); + for (const ghoul::Dictionary& lsDictionary : lightsources) { + std::unique_ptr lightSource = + LightSource::createFromDictionary(lsDictionary); _lightSourcePropertyOwner.addPropertySubOwner(lightSource.get()); _lightSources.push_back(std::move(lightSource)); } diff --git a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp index d91c07fe59..eb3fdf4cd2 100644 --- a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp @@ -42,6 +42,7 @@ #include #include #include +#include namespace { constexpr const char* _loggerCat = "RenderableModelProjection"; @@ -73,131 +74,113 @@ namespace { "location to the Sun. If this value is disabled, shading is disabled and the " "entire model is rendered brightly." }; + + struct [[codegen::Dictionary(RenderableModelProjection)]] Parameters { + // The file or files that should be loaded in this RenderableModel. The file can + // contain filesystem tokens or can be specified relatively to the + // location of the .mod file. + // This specifies the model that is rendered by the Renderable. + std::variant> geometryFile; + + // Contains information about projecting onto this planet. + ghoul::Dictionary projection [[codegen::reference("newhorizons_projectioncomponent")]]; + + // [[codegen::verbatim(PerformShadingInfo.description)]] + std::optional performShading; + + // The radius of the bounding sphere of this object. This has to be a + // radius that is larger than anything that is rendered by it. It has to + // be at least as big as the convex hull of the object. The default value + // is 10e9 meters. + std::optional boundingSphereRadius; + }; +#include "renderablemodelprojection_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderableModelProjection::Documentation() { - using namespace documentation; - - return { - "Renderable Model Projection", - "newhorizons_renderable_modelprojection", - { - { - KeyGeomModelFile, - new OrVerifier({ new StringVerifier, new StringListVerifier }), - Optional::No, - "The file or files that are used for rendering of this model" - }, - { - keyProjection, - new ReferencingVerifier("newhorizons_projectioncomponent"), - Optional::No, - "Contains information about projecting onto this planet." - }, - { - PerformShadingInfo.identifier, - new BoolVerifier, - Optional::Yes, - PerformShadingInfo.description - }, - { - keyBoundingSphereRadius, - new DoubleVerifier, - Optional::Yes, - "The radius of the bounding sphere of this object. This has to be a " - "radius that is larger than anything that is rendered by it. It has to " - "be at least as big as the convex hull of the object. The default value " - "is 10e9 meters." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "newhorizons_renderable_modelprojection"; + return doc; } RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _performShading(PerformShadingInfo, true) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderableModelProjection" - ); + const Parameters p = codegen::bake(dictionary); - if (dictionary.hasKey(KeyGeomModelFile)) { + if (std::holds_alternative(p.geometryFile)) { + // Handle single file std::string file; + file = absPath(std::get(p.geometryFile)); + _geometry = ghoul::io::ModelReader::ref().loadModel( + file, + ghoul::io::ModelReader::ForceRenderInvisible::No, + ghoul::io::ModelReader::NotifyInvisibleDropped::Yes + ); + } + else if (std::holds_alternative>(p.geometryFile)) { + LWARNING("Loading a model with several files is deprecated and will be " + "removed in a future release, TESTING" + ); + /* + ghoul::Dictionary fileDictionary = dictionary.value( + KeyGeomModelFile + ); + std::vector> geometries; - if (dictionary.hasValue(KeyGeomModelFile)) { - // Handle single file - file = absPath(dictionary.value(KeyGeomModelFile)); - _geometry = ghoul::io::ModelReader::ref().loadModel( + for (std::string_view k : fileDictionary.keys()) { + // Handle each file + file = absPath(fileDictionary.value(k)); + geometries.push_back(ghoul::io::ModelReader::ref().loadModel( file, ghoul::io::ModelReader::ForceRenderInvisible::No, ghoul::io::ModelReader::NotifyInvisibleDropped::Yes - ); + )); } - else if (dictionary.hasValue(KeyGeomModelFile)) { - LWARNING("Loading a model with several files is deprecated and will be " - "removed in a future release" - ); - ghoul::Dictionary fileDictionary = dictionary.value( - KeyGeomModelFile - ); - std::vector> geometries; + if (!geometries.empty()) { + std::unique_ptr combinedGeometry = + std::move(geometries[0]); - for (std::string_view k : fileDictionary.keys()) { - // Handle each file - file = absPath(fileDictionary.value(k)); - geometries.push_back(ghoul::io::ModelReader::ref().loadModel( - file, - ghoul::io::ModelReader::ForceRenderInvisible::No, - ghoul::io::ModelReader::NotifyInvisibleDropped::Yes - )); - } - - if (!geometries.empty()) { - std::unique_ptr combinedGeometry = - std::move(geometries[0]); - - // Combine all models into one ModelGeometry - for (unsigned int i = 1; i < geometries.size(); ++i) { - for (ghoul::io::ModelMesh& mesh : geometries[i]->meshes()) { - combinedGeometry->meshes().push_back( - std::move(mesh) - ); - } - - for (ghoul::modelgeometry::ModelGeometry::TextureEntry& texture : - geometries[i]->textureStorage()) - { - combinedGeometry->textureStorage().push_back( - std::move(texture) - ); - } + // Combine all models into one ModelGeometry + for (unsigned int i = 1; i < geometries.size(); ++i) { + for (ghoul::io::ModelMesh& mesh : geometries[i]->meshes()) { + combinedGeometry->meshes().push_back( + std::move(mesh) + ); + } + + for (ghoul::modelgeometry::ModelGeometry::TextureEntry& texture : + geometries[i]->textureStorage()) + { + combinedGeometry->textureStorage().push_back( + std::move(texture) + ); } - _geometry = std::move(combinedGeometry); - _geometry->calculateBoundingRadius(); } - } + _geometry = std::move(combinedGeometry); + _geometry->calculateBoundingRadius(); + }*/ } addPropertySubOwner(_projectionComponent); _projectionComponent.initialize( identifier(), - dictionary.value(keyProjection) + p.projection ); double boundingSphereRadius = 1.0e9; - if (dictionary.hasValue(keyBoundingSphereRadius)) { - boundingSphereRadius = dictionary.value(keyBoundingSphereRadius); + if (p.boundingSphereRadius.has_value()) { + boundingSphereRadius = *p.boundingSphereRadius; } setBoundingSphere(boundingSphereRadius); - if (dictionary.hasValue(PerformShadingInfo.identifier)) { - _performShading = dictionary.value(PerformShadingInfo.identifier); + if (p.performShading.has_value()) { + _performShading = *p.performShading; } addProperty(_performShading); From d534b7dbafe7b02103ffe906c52f3083cbd0d69c Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Thu, 18 Mar 2021 15:21:45 +0100 Subject: [PATCH 08/28] Apply transforms to ModelProjection --- .../shaders/renderableModelProjection_vs.glsl | 6 ++++-- .../spacecraftinstruments/shaders/renderableModel_vs.glsl | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/spacecraftinstruments/shaders/renderableModelProjection_vs.glsl b/modules/spacecraftinstruments/shaders/renderableModelProjection_vs.glsl index 80a73c107e..5d9ed84f35 100644 --- a/modules/spacecraftinstruments/shaders/renderableModelProjection_vs.glsl +++ b/modules/spacecraftinstruments/shaders/renderableModelProjection_vs.glsl @@ -37,13 +37,15 @@ out vec4 vs_ndc; uniform mat4 ProjectorMatrix; uniform mat4 ModelTransform; +uniform mat4 meshTransform; +uniform mat4 meshNormalTransform; uniform vec3 boresight; void main() { - vec4 raw_pos = psc_to_meter(in_position, vec2(1.0, 0.0)); + vec4 raw_pos = psc_to_meter((meshTransform * in_position), vec2(1.0, 0.0)); vs_position = ProjectorMatrix * ModelTransform * raw_pos; - vs_normal = normalize(ModelTransform * vec4(in_normal,0)); + vs_normal = normalize(ModelTransform * meshNormalTransform * vec4(in_normal,0)); vs_ndc = vs_position / vs_position.w; //match clipping plane diff --git a/modules/spacecraftinstruments/shaders/renderableModel_vs.glsl b/modules/spacecraftinstruments/shaders/renderableModel_vs.glsl index 4a7fff09a5..388b589732 100644 --- a/modules/spacecraftinstruments/shaders/renderableModel_vs.glsl +++ b/modules/spacecraftinstruments/shaders/renderableModel_vs.glsl @@ -39,10 +39,12 @@ uniform mat4 modelViewTransform; uniform mat4 projectionTransform; uniform vec3 cameraDirectionWorldSpace; uniform float _magnification; +uniform mat4 meshTransform; +uniform mat4 meshNormalTransform; void main() { - vec4 position = in_position; // Position already in homogenous coordinates + vec4 position = meshTransform * in_position; // Position already in homogenous coordinates position.xyz *= pow(10, _magnification); vs_positionCameraSpace = modelViewTransform * position; vec4 positionClipSpace = projectionTransform * vs_positionCameraSpace; @@ -52,5 +54,5 @@ void main() { gl_Position = vs_positionScreenSpace; // The normal transform should be the transposed inverse of the model transform? - vs_normalViewSpace = normalize(mat3(modelViewTransform) * in_normal); + vs_normalViewSpace = normalize(mat3(modelViewTransform) * (mat3(meshNormalTransform) * in_normal)); } From 4eb7c8abcee5fc7f75ce221efde28c805b1139a0 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Thu, 18 Mar 2021 16:08:38 +0100 Subject: [PATCH 09/28] Add animation mode to infinetly loop --- modules/base/rendering/renderablemodel.cpp | 14 ++++++++++++++ modules/base/rendering/renderablemodel.h | 1 + 2 files changed, 15 insertions(+) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 2a387cbd9d..1c3033ced4 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -163,6 +163,7 @@ namespace { enum class AnimationMode { Once, LoopFromStart, + LoopInfinitely, Bounce }; @@ -336,6 +337,9 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) case Parameters::AnimationMode::LoopFromStart: _animationMode = AnimationMode::LoopFromStart; break; + case Parameters::AnimationMode::LoopInfinitely: + _animationMode = AnimationMode::LoopInfinitely; + break; case Parameters::AnimationMode::Bounce: _animationMode = AnimationMode::Bounce; break; @@ -587,6 +591,16 @@ void RenderableModel::update(const UpdateData& data) { _geometry->animationDuration() ); break; + case AnimationMode::LoopInfinitely: + realtiveTime = std::fmod( + data.time.j2000Seconds() - data.time.convertTime(_animationStart), + _geometry->animationDuration() + ); + + if (realtiveTime < 0) { + realtiveTime += _geometry->animationDuration(); + } + break; case AnimationMode::Once: default: realtiveTime = diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index 7ef6d4a977..a8d8ea49f9 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -73,6 +73,7 @@ private: enum class AnimationMode { Once = 0, LoopFromStart, + LoopInfinitely, Bounce }; From fb4cb67ef64fd0bdca3b61e7dba0d540e04d8701 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Fri, 19 Mar 2021 09:54:47 +0100 Subject: [PATCH 10/28] Add property to enable/disable animation --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 28 ++++++++++++++++++++++ modules/base/rendering/renderablemodel.h | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index 3bd220b9fc..d7db2ad6be 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 3bd220b9fc3d8f05af79c1c5c7594755499b77be +Subproject commit d7db2ad6be22de836b6d7a14b756577dd91d983d diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 1c3033ced4..b2fa4390be 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -60,6 +60,12 @@ namespace { { "Color Adding", ColorAddingBlending } }; + constexpr openspace::properties::Property::PropertyInfo enableAnimationInfo = { + "EnableAnimation", + "Enable Animation", + "Enable Animation" + }; + constexpr const std::array UniformNames = { "opacity", "nLightSources", "lightDirectionsViewSpace", "lightIntensities", "modelViewTransform", "normalTransform", "projectionTransform", @@ -146,6 +152,9 @@ namespace { // should be forced to render or not. std::optional forceRenderInvisible; + // [[codegen::verbatim(enableAnimationInfo.description)]] + std::optional enableAnimation; + // The date and time that the model animation should start. // In format 'YYYY MM DD hh:mm:ss'. std::optional animationStartTime [[codegen::dateTime()]]; @@ -217,6 +226,7 @@ documentation::Documentation RenderableModel::Documentation() { RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) : Renderable(dictionary) + , _enableAnimation(enableAnimationInfo, false) , _ambientIntensity(AmbientIntensityInfo, 0.2f, 0.f, 1.f) , _diffuseIntensity(DiffuseIntensityInfo, 1.f, 0.f, 1.f) , _specularIntensity(SpecularIntensityInfo, 1.f, 0.f, 1.f) @@ -313,6 +323,11 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) }*/ } + if (p.enableAnimation.has_value()) { + _enableAnimation = *p.enableAnimation; + _geometry->enableAnimation(_enableAnimation.value()); + } + if (p.animationTimeScale.has_value()) { if (std::holds_alternative(*p.animationTimeScale)) { _geometry->setTimeScale(std::get(*p.animationTimeScale)); @@ -387,6 +402,10 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } } + if (_geometry->hasAnimation()) { + addProperty(_enableAnimation); + } + addPropertySubOwner(_lightSourcePropertyOwner); addProperty(_ambientIntensity); addProperty(_diffuseIntensity); @@ -401,6 +420,15 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) _modelTransform = glm::mat4_cast(glm::quat(glm::radians(_rotationVec.value()))); }); + _enableAnimation.onChange([this]() { + if (_enableAnimation.value() && !_geometry->hasAnimation()) { + LWARNING("Attempting to enable animation for a model that does not have any"); + _enableAnimation = false; + } + + _geometry->enableAnimation(_enableAnimation.value()); + }); + if (p.rotationVector.has_value()) { _rotationVec = *p.rotationVector; diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index a8d8ea49f9..225285f58c 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -82,6 +82,7 @@ private: bool _notifyInvisibleDropped = true; std::string _animationStart; AnimationMode _animationMode; + properties::BoolProperty _enableAnimation; properties::FloatProperty _ambientIntensity; From f1d66b1a74436da39e6bee1674194100607ecec1 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Fri, 19 Mar 2021 09:55:50 +0100 Subject: [PATCH 11/28] Add animation mode bounce --- modules/base/rendering/renderablemodel.cpp | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index b2fa4390be..db0669b10c 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -612,30 +612,29 @@ void RenderableModel::update(const UpdateData& data) { if (_geometry->hasAnimation()) { double realtiveTime; + double now = data.time.j2000Seconds(); + double startTime = data.time.convertTime(_animationStart); + double duration = _geometry->animationDuration(); + switch (_animationMode) { case AnimationMode::LoopFromStart: - realtiveTime = std::fmod( - data.time.j2000Seconds() - data.time.convertTime(_animationStart), - _geometry->animationDuration() - ); + realtiveTime = std::fmod(now - startTime, duration); break; case AnimationMode::LoopInfinitely: - realtiveTime = std::fmod( - data.time.j2000Seconds() - data.time.convertTime(_animationStart), - _geometry->animationDuration() - ); - + realtiveTime = std::fmod(now - startTime, duration); if (realtiveTime < 0) { - realtiveTime += _geometry->animationDuration(); + realtiveTime += duration; } break; + case AnimationMode::Bounce: + realtiveTime = + duration - abs(fmod(now - startTime, 2 * duration) - duration); + break; case AnimationMode::Once: default: - realtiveTime = - data.time.j2000Seconds() - data.time.convertTime(_animationStart); + realtiveTime = now - startTime; break; } - _geometry->update(realtiveTime); } } From 0a9af8f0eda46e85e70628a18293934d55132e86 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Mon, 22 Mar 2021 10:01:16 +0100 Subject: [PATCH 12/28] Add error handling --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 48 +++++++++++++++++----- modules/base/rendering/renderablemodel.h | 2 +- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index d7db2ad6be..b2b7331fb4 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit d7db2ad6be22de836b6d7a14b756577dd91d983d +Subproject commit b2b7331fb4f4a0cc322fa89cea8bf48d6c2b54ce diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index db0669b10c..4d5749c149 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -262,10 +262,6 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } } - if (p.animationStartTime.has_value()) { - _animationStart = *p.animationStartTime; - } - if (std::holds_alternative(p.geometryFile)) { // Handle single file std::string file; @@ -323,12 +319,32 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) }*/ } + if (p.animationStartTime.has_value()) { + if (!_geometry->hasAnimation()) { + LWARNING("Animation start time given to model without animation"); + } + _animationStart = *p.animationStartTime; + _enableAnimation = true; + } + if (p.enableAnimation.has_value()) { - _enableAnimation = *p.enableAnimation; - _geometry->enableAnimation(_enableAnimation.value()); + if (!_geometry->hasAnimation()) { + LWARNING("Attempting to enable animation for a model that does not have any"); + } + else if (*p.enableAnimation &&_animationStart == "") { + LWARNING("Cannot enable animation without a given start time"); + } + else { + _enableAnimation = *p.enableAnimation; + _geometry->enableAnimation(_enableAnimation.value()); + } } if (p.animationTimeScale.has_value()) { + if (!_geometry->hasAnimation()) { + LWARNING("Animation time scale given to model without animation"); + } + if (std::holds_alternative(*p.animationTimeScale)) { _geometry->setTimeScale(std::get(*p.animationTimeScale)); } @@ -348,6 +364,10 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } if (p.animationMode.has_value()) { + if (!_geometry->hasAnimation()) { + LWARNING("Animation mode given to model without animation"); + } + switch (*p.animationMode) { case Parameters::AnimationMode::LoopFromStart: _animationMode = AnimationMode::LoopFromStart; @@ -421,12 +441,16 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) }); _enableAnimation.onChange([this]() { - if (_enableAnimation.value() && !_geometry->hasAnimation()) { + if (!_geometry->hasAnimation()) { LWARNING("Attempting to enable animation for a model that does not have any"); + } + else if (_enableAnimation.value() && _animationStart == "") { + LWARNING("Cannot enable animation without a given start time"); _enableAnimation = false; } - - _geometry->enableAnimation(_enableAnimation.value()); + else { + _geometry->enableAnimation(_enableAnimation.value()); + } }); @@ -461,6 +485,10 @@ bool RenderableModel::isReady() const { void RenderableModel::initialize() { ZoneScoped + if (_geometry->hasAnimation() && _enableAnimation.value() && _animationStart == "") { + LWARNING("Model with animation not given any start time"); + } + for (const std::unique_ptr& ls : _lightSources) { ls->initialize(); } @@ -610,7 +638,7 @@ void RenderableModel::update(const UpdateData& data) { ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); } - if (_geometry->hasAnimation()) { + if (_geometry->hasAnimation() && _animationStart != "") { double realtiveTime; double now = data.time.j2000Seconds(); double startTime = data.time.convertTime(_animationStart); diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index 225285f58c..ca674fd3ca 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -80,7 +80,7 @@ private: std::unique_ptr _geometry; bool _forceRenderInvisible = false; bool _notifyInvisibleDropped = true; - std::string _animationStart; + std::string _animationStart = ""; AnimationMode _animationMode; properties::BoolProperty _enableAnimation; From 7ae3922132be94f1f48dad0a6629eb20f5e55ff7 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Tue, 23 Mar 2021 13:42:05 +0100 Subject: [PATCH 13/28] Add BounceFromStart and BounceInfinitely animation modes --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 21 +++++++++++++++++---- modules/base/rendering/renderablemodel.h | 3 ++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index b2b7331fb4..bb3686b175 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit b2b7331fb4f4a0cc322fa89cea8bf48d6c2b54ce +Subproject commit bb3686b1757688bca658cdf51604b393efdd5d26 diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 4d5749c149..4c0cace2d8 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -173,7 +173,8 @@ namespace { Once, LoopFromStart, LoopInfinitely, - Bounce + BounceFromStart, + BounceInfinitely }; // The mode of how the animation should be played back. @@ -375,8 +376,11 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) case Parameters::AnimationMode::LoopInfinitely: _animationMode = AnimationMode::LoopInfinitely; break; - case Parameters::AnimationMode::Bounce: - _animationMode = AnimationMode::Bounce; + case Parameters::AnimationMode::BounceFromStart: + _animationMode = AnimationMode::BounceFromStart; + break; + case Parameters::AnimationMode::BounceInfinitely: + _animationMode = AnimationMode::BounceInfinitely; break; case Parameters::AnimationMode::Once: default: @@ -654,10 +658,19 @@ void RenderableModel::update(const UpdateData& data) { realtiveTime += duration; } break; - case AnimationMode::Bounce: + case AnimationMode::BounceFromStart: realtiveTime = duration - abs(fmod(now - startTime, 2 * duration) - duration); break; + case AnimationMode::BounceInfinitely: { + double modulo = fmod(now - startTime, 2 * duration); + if (modulo < 0) { + modulo += 2 * duration; + } + realtiveTime = + duration - abs(modulo - duration); + break; + } case AnimationMode::Once: default: realtiveTime = now - startTime; diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index ca674fd3ca..167158b4fd 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -74,7 +74,8 @@ private: Once = 0, LoopFromStart, LoopInfinitely, - Bounce + BounceFromStart, + BounceInfinitely }; std::unique_ptr _geometry; From aa5394e88c9d73c5a0a0448a6dbdd1a0b076b53f Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Tue, 23 Mar 2021 18:02:06 +0100 Subject: [PATCH 14/28] Add info message when an animation exists for a model --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index bb3686b175..bc904e36c2 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit bb3686b1757688bca658cdf51604b393efdd5d26 +Subproject commit bc904e36c25de93a0bfbafd5e746e335e06c80e7 diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 4c0cace2d8..4d991aa8e9 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -492,6 +492,11 @@ void RenderableModel::initialize() { if (_geometry->hasAnimation() && _enableAnimation.value() && _animationStart == "") { LWARNING("Model with animation not given any start time"); } + else if (_geometry->hasAnimation() && !_enableAnimation.value()) { + LINFO("Model with deactivated animation was found. " + "The animation could be activated by entering a start time in the asset file" + ); + } for (const std::unique_ptr& ls : _lightSources) { ls->initialize(); From 3a976ba0b0fa35cde8de5bb233fc327771fbf841 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Wed, 24 Mar 2021 10:47:40 +0100 Subject: [PATCH 15/28] Add model scale that can be set in asset file for each model * Add cm and dm as units in DistanceUnits * Scale the model according to the set unit --- include/openspace/util/distanceconstants.h | 6 ++ include/openspace/util/distanceconversion.h | 97 +++++++++++++++++-- .../base/dashboard/dashboarditemdistance.cpp | 4 +- .../base/dashboard/dashboarditemvelocity.cpp | 4 +- modules/base/rendering/renderablemodel.cpp | 51 +++++++++- modules/base/rendering/renderablemodel.h | 3 + 6 files changed, 148 insertions(+), 17 deletions(-) diff --git a/include/openspace/util/distanceconstants.h b/include/openspace/util/distanceconstants.h index 695e463324..4184263485 100644 --- a/include/openspace/util/distanceconstants.h +++ b/include/openspace/util/distanceconstants.h @@ -35,6 +35,12 @@ namespace openspace::distanceconstants { constexpr double LightHour = LightDay / 24; constexpr double AstronomicalUnit = 1.495978707E11; constexpr double Parsec = 3.0856776E16; + + constexpr double Inch = 0.0254; + constexpr double Foot = 0.3048; + constexpr double Yard = 0.9144; + constexpr double Chain = 20.1168; + constexpr double Mile = 1609.344; } // openspace::distanceconstants #endif // __OPENSPACE_CORE___DISTANCECONSTANTS___H__ diff --git a/include/openspace/util/distanceconversion.h b/include/openspace/util/distanceconversion.h index 79258ba131..9cc496e5ef 100644 --- a/include/openspace/util/distanceconversion.h +++ b/include/openspace/util/distanceconversion.h @@ -39,6 +39,8 @@ enum class DistanceUnit { Nanometer = 0, Micrometer, Millimeter, + Centimeter, + Decimeter, Meter, Kilometer, AU, @@ -66,6 +68,8 @@ enum class DistanceUnit { constexpr const char* DistanceUnitNanometer = "nanometer"; constexpr const char* DistanceUnitMicrometer = "micrometer"; constexpr const char* DistanceUnitMillimeter = "millimeter"; +constexpr const char* DistanceUnitCentimeter = "centimeter"; +constexpr const char* DistanceUnitDecimeter = "decimeter"; constexpr const char* DistanceUnitMeter = "meter"; constexpr const char* DistanceUnitKilometer = "km"; constexpr const char* DistanceUnitAU = "AU"; @@ -91,6 +95,8 @@ constexpr const char* DistanceUnitLeague = "league"; constexpr const char* DistanceUnitNanometers = "nanometers"; constexpr const char* DistanceUnitMicrometers = "micrometers"; constexpr const char* DistanceUnitMillimeters = "millimeters"; +constexpr const char* DistanceUnitCentimeters = "centimeters"; +constexpr const char* DistanceUnitDecimeters = "decimeters"; constexpr const char* DistanceUnitMeters = "meters"; constexpr const char* DistanceUnitKilometers = "km"; constexpr const char* DistanceUnitAUs = "AU"; @@ -114,18 +120,20 @@ constexpr const char* DistanceUnitLeagues = "leagues"; constexpr const std::array(DistanceUnit::League) + 1> DistanceUnits = { DistanceUnit::Nanometer, DistanceUnit::Micrometer, DistanceUnit::Millimeter, - DistanceUnit::Meter, DistanceUnit::Kilometer, DistanceUnit::AU, - DistanceUnit::Lighthour, DistanceUnit::Lightday, DistanceUnit::Lightmonth, - DistanceUnit::Lightyear, DistanceUnit::Parsec, DistanceUnit::Kiloparsec, - DistanceUnit::Megaparsec, DistanceUnit::Gigaparsec, DistanceUnit::Thou, - DistanceUnit::Inch, DistanceUnit::Foot, DistanceUnit::Yard, DistanceUnit::Chain, - DistanceUnit::Furlong, DistanceUnit::Mile, DistanceUnit::League + DistanceUnit::Centimeter, DistanceUnit::Decimeter, DistanceUnit::Meter, + DistanceUnit::Kilometer, DistanceUnit::AU, DistanceUnit::Lighthour, + DistanceUnit::Lightday, DistanceUnit::Lightmonth, DistanceUnit::Lightyear, + DistanceUnit::Parsec, DistanceUnit::Kiloparsec, DistanceUnit::Megaparsec, + DistanceUnit::Gigaparsec, DistanceUnit::Thou, DistanceUnit::Inch, + DistanceUnit::Foot, DistanceUnit::Yard, DistanceUnit::Chain, DistanceUnit::Furlong, + DistanceUnit::Mile, DistanceUnit::League }; constexpr const std::array(DistanceUnit::League) + 1> DistanceUnitNamesSingular = { DistanceUnitNanometer, DistanceUnitMicrometer, DistanceUnitMillimeter, - DistanceUnitMeter, DistanceUnitKilometer, DistanceUnitAU, DistanceUnitLighthour, + DistanceUnitCentimeter, DistanceUnitDecimeter, DistanceUnitMeter, + DistanceUnitKilometer, DistanceUnitAU, DistanceUnitLighthour, DistanceUnitLightday, DistanceUnitLightmonth, DistanceUnitLightyear, DistanceUnitParsec, DistanceUnitKiloparsec, DistanceUnitMegaparsec, DistanceUnitGigaparsec, DistanceUnitThou, DistanceUnitInch, DistanceUnitFoot, @@ -136,7 +144,8 @@ DistanceUnitNamesSingular = { constexpr const std::array(DistanceUnit::League) + 1> DistanceUnitNamesPlural = { DistanceUnitNanometers, DistanceUnitMicrometers, DistanceUnitMillimeters, - DistanceUnitMeters, DistanceUnitKilometers, DistanceUnitAUs, DistanceUnitLighthours, + DistanceUnitCentimeters, DistanceUnitDecimeters, DistanceUnitMeters, + DistanceUnitKilometers, DistanceUnitAUs, DistanceUnitLighthours, DistanceUnitLightdays, DistanceUnitLightmonths, DistanceUnitLightyears, DistanceUnitParsecs, DistanceUnitKiloparsecs, DistanceUnitMegaparsecs, DistanceUnitGigaparsecs, DistanceUnitThous, DistanceUnitInches, DistanceUnitFeet, @@ -168,6 +177,8 @@ constexpr const char* nameForDistanceUnit(DistanceUnit unit, bool pluralForm = f case DistanceUnit::Nanometer: case DistanceUnit::Micrometer: case DistanceUnit::Millimeter: + case DistanceUnit::Centimeter: + case DistanceUnit::Decimeter: case DistanceUnit::Meter: case DistanceUnit::Kilometer: case DistanceUnit::AU: @@ -232,7 +243,7 @@ constexpr DistanceUnit distanceUnitFromString(const char* unitName) { std::pair simplifyDistance(double meters, bool forceSingularForm = false); -constexpr double convertDistance(double meters, DistanceUnit requestedUnit) { +constexpr double convertMeters(double meters, DistanceUnit requestedUnit) { switch (requestedUnit) { case DistanceUnit::Nanometer: return meters / 1e-9; @@ -240,6 +251,10 @@ constexpr double convertDistance(double meters, DistanceUnit requestedUnit) { return meters / 1e-6; case DistanceUnit::Millimeter: return meters / 1e-3; + case DistanceUnit::Centimeter: + return meters / 1e-2; + case DistanceUnit::Decimeter: + return meters / 1e-1; case DistanceUnit::Meter: return meters; case DistanceUnit::Kilometer: @@ -262,7 +277,6 @@ constexpr double convertDistance(double meters, DistanceUnit requestedUnit) { return meters / (1e6 * distanceconstants::Parsec); case DistanceUnit::Gigaparsec: return meters / (1e9 * distanceconstants::Parsec); - // Such wow, such coefficients case DistanceUnit::Thou: return (meters * 1000.0 / 25.4) * 1000.0; // m -> mm -> inch -> thou case DistanceUnit::Inch: @@ -289,6 +303,69 @@ constexpr double convertDistance(double meters, DistanceUnit requestedUnit) { } } +constexpr double toMeter(DistanceUnit unit) { + switch (unit) { + case DistanceUnit::Nanometer: + return 1e-9; + case DistanceUnit::Micrometer: + return 1e-6; + case DistanceUnit::Millimeter: + return 1e-3; + case DistanceUnit::Centimeter: + return 1e-2; + case DistanceUnit::Decimeter: + return 1e-1; + case DistanceUnit::Meter: + return 1.0; + case DistanceUnit::Kilometer: + return 1000.0; + case DistanceUnit::AU: + return distanceconstants::AstronomicalUnit; + case DistanceUnit::Lighthour: + return distanceconstants::LightHour; + case DistanceUnit::Lightday: + return distanceconstants::LightDay; + case DistanceUnit::Lightmonth: + return distanceconstants::LightMonth; + case DistanceUnit::Lightyear: + return distanceconstants::LightYear; + case DistanceUnit::Parsec: + return distanceconstants::Parsec; + case DistanceUnit::Kiloparsec: + return 1e3 * distanceconstants::Parsec; + case DistanceUnit::Megaparsec: + return 1e6 * distanceconstants::Parsec; + case DistanceUnit::Gigaparsec: + return 1e9 * distanceconstants::Parsec; + case DistanceUnit::Thou: + return 1e-3 * distanceconstants::Inch; + case DistanceUnit::Inch: + return distanceconstants::Inch; + case DistanceUnit::Foot: + return distanceconstants::Foot; + case DistanceUnit::Yard: + return distanceconstants::Yard; + case DistanceUnit::Chain: + return distanceconstants::Chain; + case DistanceUnit::Furlong: + return 10.0 * distanceconstants::Chain; + case DistanceUnit::Mile: + return distanceconstants::Mile; + case DistanceUnit::League: + return 3.0 * distanceconstants::Mile; + default: + throw ghoul::MissingCaseException(); + } +} + +constexpr double convertUnit(DistanceUnit fromUnit, DistanceUnit toUnit) { + return convertMeters(toMeter(fromUnit), toUnit); +} + +constexpr double convertDistance(double distance, DistanceUnit fromUnit, DistanceUnit toUnit) { + return distance * convertUnit(fromUnit, toUnit); +} + float convertMasPerYearToMeterPerSecond(float masPerYear, float parallax); } // namespace openspace diff --git a/modules/base/dashboard/dashboarditemdistance.cpp b/modules/base/dashboard/dashboarditemdistance.cpp index bcca68c879..bfb364ad2d 100644 --- a/modules/base/dashboard/dashboarditemdistance.cpp +++ b/modules/base/dashboard/dashboarditemdistance.cpp @@ -368,7 +368,7 @@ void DashboardItemDistance::render(glm::vec2& penPosition) { } else { const DistanceUnit unit = static_cast(_requestedUnit.value()); - const double convertedD = convertDistance(d, unit); + const double convertedD = convertMeters(d, unit); dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) }; } @@ -399,7 +399,7 @@ glm::vec2 DashboardItemDistance::size() const { } else { DistanceUnit unit = static_cast(_requestedUnit.value()); - double convertedD = convertDistance(d, unit); + double convertedD = convertMeters(d, unit); dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) }; } diff --git a/modules/base/dashboard/dashboarditemvelocity.cpp b/modules/base/dashboard/dashboarditemvelocity.cpp index d71e4c585d..587e83f4f3 100644 --- a/modules/base/dashboard/dashboarditemvelocity.cpp +++ b/modules/base/dashboard/dashboarditemvelocity.cpp @@ -133,7 +133,7 @@ void DashboardItemVelocity::render(glm::vec2& penPosition) { } else { const DistanceUnit unit = static_cast(_requestedUnit.value()); - const double convertedD = convertDistance(speedPerSecond, unit); + const double convertedD = convertMeters(speedPerSecond, unit); dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) }; } @@ -159,7 +159,7 @@ glm::vec2 DashboardItemVelocity::size() const { } else { DistanceUnit unit = static_cast(_requestedUnit.value()); - double convertedD = convertDistance(d, unit); + double convertedD = convertMeters(d, unit); dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) }; } diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 4d991aa8e9..47f0b45af6 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -148,6 +148,20 @@ namespace { // This specifies the model that is rendered by the Renderable. std::variant> geometryFile; + enum class ScaleUnit { + Nanometer [[codegen::key("nm")]], + Micrometer [[codegen::key("um")]], + Millimeter [[codegen::key("mm")]], + Centimeter [[codegen::key("cm")]], + Decimeter [[codegen::key("dm")]], + Meter [[codegen::key("m")]], + Kilometer [[codegen::key("km")]] + }; + + // The scale of the model. For example if the model is in centimeters + // then ModelScale=cm + std::optional modelScale; + // Set if invisible parts (parts with no textures or materials) of the model // should be forced to render or not. std::optional forceRenderInvisible; @@ -320,6 +334,38 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) }*/ } + if (p.modelScale.has_value()) { + Parameters::ScaleUnit scaleUnit = *p.modelScale; + + switch (scaleUnit) { + case Parameters::ScaleUnit::Nanometer: + _modelScale = DistanceUnit::Nanometer; + break; + case Parameters::ScaleUnit::Micrometer: + _modelScale = DistanceUnit::Micrometer; + break; + case Parameters::ScaleUnit::Millimeter: + _modelScale = DistanceUnit::Millimeter; + break; + case Parameters::ScaleUnit::Centimeter: + _modelScale = DistanceUnit::Centimeter; + break; + case Parameters::ScaleUnit::Decimeter: + _modelScale = DistanceUnit::Decimeter; + break; + case Parameters::ScaleUnit::Meter: + _modelScale = DistanceUnit::Meter; + break; + case Parameters::ScaleUnit::Kilometer: + _modelScale = DistanceUnit::Kilometer; + break; + default: + throw ghoul::MissingCaseException(); + } + + _scaleVector = glm::dvec3(convertUnit(_modelScale, DistanceUnit::Meter)); + } + if (p.animationStartTime.has_value()) { if (!_geometry->hasAnimation()) { LWARNING("Animation start time given to model without animation"); @@ -546,9 +592,8 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) { const glm::dmat4 modelTransform = glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation glm::dmat4(data.modelTransform.rotation) * // Spice rotation - glm::scale( - glm::dmat4(_modelTransform.value()), glm::dvec3(data.modelTransform.scale) - ); + glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) * + glm::scale(glm::dmat4(_modelTransform.value()), _scaleVector); // Model scale unit const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform; diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index 167158b4fd..e0c6449e7b 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,8 @@ private: }; std::unique_ptr _geometry; + DistanceUnit _modelScale; + glm::dvec3 _scaleVector = glm::dvec3(1.0, 1.0, 1.0); bool _forceRenderInvisible = false; bool _notifyInvisibleDropped = true; std::string _animationStart = ""; From 2873cdb826e4eb0d566587d0ad3a241b1152e249 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Wed, 24 Mar 2021 12:06:34 +0100 Subject: [PATCH 16/28] Use convertTime for time scale of animation --- modules/base/rendering/renderablemodel.cpp | 54 ++++++++++++++-------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 47f0b45af6..c3e113f39c 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -149,13 +150,13 @@ namespace { std::variant> geometryFile; enum class ScaleUnit { - Nanometer [[codegen::key("nm")]], - Micrometer [[codegen::key("um")]], - Millimeter [[codegen::key("mm")]], - Centimeter [[codegen::key("cm")]], - Decimeter [[codegen::key("dm")]], - Meter [[codegen::key("m")]], - Kilometer [[codegen::key("km")]] + Nanometer, + Micrometer, + Millimeter, + Centimeter, + Decimeter, + Meter, + Kilometer }; // The scale of the model. For example if the model is in centimeters @@ -173,15 +174,18 @@ namespace { // In format 'YYYY MM DD hh:mm:ss'. std::optional animationStartTime [[codegen::dateTime()]]; - enum class TimeUnit { + enum class AnimationTimeUnit { + Nanosecond, + Microsecond, Millisecond, - Second + Second, + Minute }; // The time scale for the animation relative to seconds. // Ex, if animation is in milliseconds then AnimationTimeScale = 0.001 or // AnimationTimeScale = Millisecond, default is Second - std::optional> animationTimeScale; + std::optional> animationTimeScale; enum class AnimationMode { Once, @@ -395,18 +399,32 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) if (std::holds_alternative(*p.animationTimeScale)) { _geometry->setTimeScale(std::get(*p.animationTimeScale)); } - else if (std::holds_alternative(*p.animationTimeScale)) { - Parameters::TimeUnit timeUnit = - std::get(*p.animationTimeScale); + else if (std::holds_alternative(*p.animationTimeScale)) { + Parameters::AnimationTimeUnit animationTimeUnit = + std::get(*p.animationTimeScale); + TimeUnit timeUnit; - switch (timeUnit) { - case Parameters::TimeUnit::Millisecond: - _geometry->setTimeScale(0.001); + switch (animationTimeUnit) { + case Parameters::AnimationTimeUnit::Nanosecond: + timeUnit = TimeUnit::Nanosecond; break; - case Parameters::TimeUnit::Second: - _geometry->setTimeScale(1.0); + case Parameters::AnimationTimeUnit::Microsecond: + timeUnit = TimeUnit::Microsecond; break; + case Parameters::AnimationTimeUnit::Millisecond: + timeUnit = TimeUnit::Millisecond; + break; + case Parameters::AnimationTimeUnit::Second: + timeUnit = TimeUnit::Second; + break; + case Parameters::AnimationTimeUnit::Minute: + timeUnit = TimeUnit::Minute; + break; + default: + throw ghoul::MissingCaseException(); } + + _geometry->setTimeScale(convertTime(1.0, timeUnit, TimeUnit::Second)); } } From 1bfa7bf5f5e7a8ad058eb469c729cd1c909413d4 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Wed, 24 Mar 2021 17:05:18 +0100 Subject: [PATCH 17/28] Remove support to combine several geometries * And make use of file verifier for the model file --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 70 +++---------------- .../rendering/renderablemodelprojection.cpp | 65 +++-------------- 3 files changed, 22 insertions(+), 115 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index bc904e36c2..4ab350ef59 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit bc904e36c25de93a0bfbafd5e746e335e06c80e7 +Subproject commit 4ab350ef594f6fa1b4d83d1e2cdea215a41bbde6 diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index c3e113f39c..15f9149982 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include namespace { @@ -147,7 +148,7 @@ namespace { // contain filesystem tokens or can be specified relatively to the // location of the .mod file. // This specifies the model that is rendered by the Renderable. - std::variant> geometryFile; + std::filesystem::path geometryFile; enum class ScaleUnit { Nanometer, @@ -281,62 +282,14 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } } - if (std::holds_alternative(p.geometryFile)) { - // Handle single file - std::string file; - file = absPath(std::get(p.geometryFile)); - _geometry = ghoul::io::ModelReader::ref().loadModel( - file, - ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible), - ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped) - ); - } - else if (std::holds_alternative>(p.geometryFile)){ - LWARNING("Loading a model with several files is deprecated and will be " - "removed in a future release TESTING" - ); - /* - //TODO: update to use new codegen stuff - std::string file; - ghoul::Dictionary fileDictionary = dictionary.value( - KeyGeomModelFile - ); - std::vector> geometries; - - for (std::string_view k : fileDictionary.keys()) { - // Handle each file - file = absPath(fileDictionary.value(k)); - geometries.push_back(ghoul::io::ModelReader::ref().loadModel( - file, - ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible), - ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped) - )); - } - - if (!geometries.empty()) { - std::unique_ptr combinedGeometry = - std::move(geometries[0]); - - // Combine all models into one ModelGeometry - for (unsigned int i = 1; i < geometries.size(); ++i) { - for (ghoul::io::ModelMesh& mesh : geometries[i]->meshes()) { - combinedGeometry->meshes().push_back( - std::move(mesh) - ); - } - - for (ghoul::modelgeometry::ModelGeometry::TextureEntry& texture : - geometries[i]->textureStorage()) - { - combinedGeometry->textureStorage().push_back( - std::move(texture) - ); - } - } - _geometry = std::move(combinedGeometry); - _geometry->calculateBoundingRadius(); - }*/ - } + // Import Model from file + std::string file; + file = absPath(p.geometryFile.string()); + _geometry = ghoul::io::ModelReader::ref().loadModel( + file, + ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible), + ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped) + ); if (p.modelScale.has_value()) { Parameters::ScaleUnit scaleUnit = *p.modelScale; @@ -395,8 +348,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) if (!_geometry->hasAnimation()) { LWARNING("Animation time scale given to model without animation"); } - - if (std::holds_alternative(*p.animationTimeScale)) { + else if (std::holds_alternative(*p.animationTimeScale)) { _geometry->setTimeScale(std::get(*p.animationTimeScale)); } else if (std::holds_alternative(*p.animationTimeScale)) { diff --git a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp index eb3fdf4cd2..e969b7fcb2 100644 --- a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include namespace { @@ -80,7 +81,7 @@ namespace { // contain filesystem tokens or can be specified relatively to the // location of the .mod file. // This specifies the model that is rendered by the Renderable. - std::variant> geometryFile; + std::filesystem::path geometryFile; // Contains information about projecting onto this planet. ghoul::Dictionary projection [[codegen::reference("newhorizons_projectioncomponent")]]; @@ -111,60 +112,14 @@ RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& di { const Parameters p = codegen::bake(dictionary); - if (std::holds_alternative(p.geometryFile)) { - // Handle single file - std::string file; - file = absPath(std::get(p.geometryFile)); - _geometry = ghoul::io::ModelReader::ref().loadModel( - file, - ghoul::io::ModelReader::ForceRenderInvisible::No, - ghoul::io::ModelReader::NotifyInvisibleDropped::Yes - ); - } - else if (std::holds_alternative>(p.geometryFile)) { - LWARNING("Loading a model with several files is deprecated and will be " - "removed in a future release, TESTING" - ); - /* - ghoul::Dictionary fileDictionary = dictionary.value( - KeyGeomModelFile - ); - std::vector> geometries; - - for (std::string_view k : fileDictionary.keys()) { - // Handle each file - file = absPath(fileDictionary.value(k)); - geometries.push_back(ghoul::io::ModelReader::ref().loadModel( - file, - ghoul::io::ModelReader::ForceRenderInvisible::No, - ghoul::io::ModelReader::NotifyInvisibleDropped::Yes - )); - } - - if (!geometries.empty()) { - std::unique_ptr combinedGeometry = - std::move(geometries[0]); - - // Combine all models into one ModelGeometry - for (unsigned int i = 1; i < geometries.size(); ++i) { - for (ghoul::io::ModelMesh& mesh : geometries[i]->meshes()) { - combinedGeometry->meshes().push_back( - std::move(mesh) - ); - } - - for (ghoul::modelgeometry::ModelGeometry::TextureEntry& texture : - geometries[i]->textureStorage()) - { - combinedGeometry->textureStorage().push_back( - std::move(texture) - ); - } - } - _geometry = std::move(combinedGeometry); - _geometry->calculateBoundingRadius(); - }*/ - } + // Import Model from file + std::string file; + file = absPath(p.geometryFile.string()); + _geometry = ghoul::io::ModelReader::ref().loadModel( + file, + ghoul::io::ModelReader::ForceRenderInvisible::No, + ghoul::io::ModelReader::NotifyInvisibleDropped::Yes + ); addPropertySubOwner(_projectionComponent); From 9bfc87958bdb3de6b10d06e97417fe2b518d05f9 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Thu, 25 Mar 2021 11:19:54 +0100 Subject: [PATCH 18/28] Add tests for distance conversion --- include/openspace/util/distanceconversion.h | 21 +-- tests/CMakeLists.txt | 1 + tests/test_distanceconversion.cpp | 186 ++++++++++++++++++++ 3 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 tests/test_distanceconversion.cpp diff --git a/include/openspace/util/distanceconversion.h b/include/openspace/util/distanceconversion.h index 9cc496e5ef..252948c720 100644 --- a/include/openspace/util/distanceconversion.h +++ b/include/openspace/util/distanceconversion.h @@ -278,26 +278,21 @@ constexpr double convertMeters(double meters, DistanceUnit requestedUnit) { case DistanceUnit::Gigaparsec: return meters / (1e9 * distanceconstants::Parsec); case DistanceUnit::Thou: - return (meters * 1000.0 / 25.4) * 1000.0; // m -> mm -> inch -> thou + return meters / (1e-3 * distanceconstants::Inch); case DistanceUnit::Inch: - return (meters * 1000.0 / 25.4); // m -> mm -> inch + return meters / distanceconstants::Inch; case DistanceUnit::Foot: - return (meters * 1000.0 / 25.4) / 12.0; // m -> mm -> inch -> feet + return meters / distanceconstants::Foot; case DistanceUnit::Yard: - // m -> mm -> inch -> feet -> yard - return (meters * 1000.0 / 25.4) / 12.0 / 3.0; + return meters / distanceconstants::Yard; case DistanceUnit::Chain: - // m -> mm -> inch -> feet -> yard -> chain - return (meters * 1000.0 / 25.4) / 12.0 / 3.0 / 22.0; + return meters / distanceconstants::Chain; case DistanceUnit::Furlong: - // m -> mm -> inch -> feet -> yard -> chain -> furlong - return (meters * 1000.0 / 25.4) / 12.0 / 3.0 / 22.0 / 10.0; + return meters / (10.0 * distanceconstants::Chain); case DistanceUnit::Mile: - // m -> mm -> inch -> feet -> yard -> chain -> furlong -> mile - return (meters * 1000.0 / 25.4) / 12.0 / 3.0 / 22.0 / 10.0 / 8.0; + return meters / distanceconstants::Mile; case DistanceUnit::League: - // m -> mm -> inch -> feet -> yard -> chain -> furlong -> mile -> league - return (meters * 1000.0 / 25.4) / 12.0 / 3.0 / 22.0 / 10.0 / 8.0 / 3.0; + return meters / (3.0 * distanceconstants::Mile); default: throw ghoul::MissingCaseException(); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 522b4509c8..eee5ab79fa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable( test_assetloader.cpp test_concurrentjobmanager.cpp test_concurrentqueue.cpp + test_distanceconversion.cpp test_documentation.cpp test_iswamanager.cpp test_latlonpatch.cpp diff --git a/tests/test_distanceconversion.cpp b/tests/test_distanceconversion.cpp new file mode 100644 index 0000000000..7187196a83 --- /dev/null +++ b/tests/test_distanceconversion.cpp @@ -0,0 +1,186 @@ +/***************************************************************************************** + * * + * 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 "catch2/catch.hpp" + +#include +#include + +using namespace openspace; + +TEST_CASE("DistanceConversion: Convert to meters", "[distanceconversion]") { + const double unit = 1.0; + double res; + + res = convertDistance(unit, DistanceUnit::Nanometer, DistanceUnit::Meter); + REQUIRE(res == Approx(1e-9)); + + res = convertDistance(unit, DistanceUnit::Micrometer, DistanceUnit::Meter); + REQUIRE(res == Approx(1e-6)); + + res = convertDistance(unit, DistanceUnit::Millimeter, DistanceUnit::Meter); + REQUIRE(res == Approx(1e-3)); + + res = convertDistance(unit, DistanceUnit::Centimeter, DistanceUnit::Meter); + REQUIRE(res == Approx(1e-2)); + + res = convertDistance(unit, DistanceUnit::Decimeter, DistanceUnit::Meter); + REQUIRE(res == Approx(1e-1)); + + res = convertDistance(unit, DistanceUnit::Meter, DistanceUnit::Meter); + REQUIRE(res == Approx(1.0)); + + res = convertDistance(unit, DistanceUnit::Kilometer, DistanceUnit::Meter); + REQUIRE(res == Approx(1000.0)); + + res = convertDistance(unit, DistanceUnit::AU, DistanceUnit::Meter); + REQUIRE(res == Approx(openspace::distanceconstants::AstronomicalUnit)); + + res = convertDistance(unit, DistanceUnit::Lighthour, DistanceUnit::Meter); + REQUIRE(res == Approx(openspace::distanceconstants::LightHour)); + + res = convertDistance(unit, DistanceUnit::Lightday, DistanceUnit::Meter); + REQUIRE(res == Approx(openspace::distanceconstants::LightDay)); + + res = convertDistance(unit, DistanceUnit::Lightmonth, DistanceUnit::Meter); + REQUIRE(res == Approx(openspace::distanceconstants::LightMonth)); + + res = convertDistance(unit, DistanceUnit::Lightyear, DistanceUnit::Meter); + REQUIRE(res == Approx(openspace::distanceconstants::LightYear)); + + res = convertDistance(unit, DistanceUnit::Parsec, DistanceUnit::Meter); + REQUIRE(res == Approx(openspace::distanceconstants::Parsec)); + + res = convertDistance(unit, DistanceUnit::Kiloparsec, DistanceUnit::Meter); + REQUIRE(res == Approx(1e3 * distanceconstants::Parsec)); + + res = convertDistance(unit, DistanceUnit::Megaparsec, DistanceUnit::Meter); + REQUIRE(res == Approx(1e6 * distanceconstants::Parsec)); + + res = convertDistance(unit, DistanceUnit::Gigaparsec, DistanceUnit::Meter); + REQUIRE(res == Approx(1e9 * distanceconstants::Parsec)); + + res = convertDistance(unit, DistanceUnit::Thou, DistanceUnit::Meter); + REQUIRE(res == Approx(1e-3 * distanceconstants::Inch)); + + res = convertDistance(unit, DistanceUnit::Inch, DistanceUnit::Meter); + REQUIRE(res == Approx(distanceconstants::Inch)); + + res = convertDistance(unit, DistanceUnit::Foot, DistanceUnit::Meter); + REQUIRE(res == Approx(distanceconstants::Foot)); + + res = convertDistance(unit, DistanceUnit::Yard, DistanceUnit::Meter); + REQUIRE(res == Approx(distanceconstants::Yard)); + + res = convertDistance(unit, DistanceUnit::Chain, DistanceUnit::Meter); + REQUIRE(res == Approx(distanceconstants::Chain)); + + res = convertDistance(unit, DistanceUnit::Furlong, DistanceUnit::Meter); + REQUIRE(res == Approx(10.0 * distanceconstants::Chain)); + + res = convertDistance(unit, DistanceUnit::Mile, DistanceUnit::Meter); + REQUIRE(res == Approx(distanceconstants::Mile)); + + res = convertDistance(unit, DistanceUnit::League, DistanceUnit::Meter); + REQUIRE(res == Approx(3.0 * distanceconstants::Mile)); +} + +TEST_CASE("DistanceConversion: Convert from meters", "[distanceconversion]") { + const double meters = 1.0; + double res; + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Nanometer); + REQUIRE(res == Approx(meters / 1e-9)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Micrometer); + REQUIRE(res == Approx(meters / 1e-6)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Millimeter); + REQUIRE(res == Approx(meters / 1e-3)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Centimeter); + REQUIRE(res == Approx(meters / 1e-2)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Decimeter); + REQUIRE(res == Approx(meters / 1e-1)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Meter); + REQUIRE(res == Approx(1.0)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Kilometer); + REQUIRE(res == Approx(meters / 1000.0)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::AU); + REQUIRE(res == Approx(meters / openspace::distanceconstants::AstronomicalUnit)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Lighthour); + REQUIRE(res == Approx(meters / openspace::distanceconstants::LightHour)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Lightday); + REQUIRE(res == Approx(meters / openspace::distanceconstants::LightDay)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Lightmonth); + REQUIRE(res == Approx(meters / openspace::distanceconstants::LightMonth)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Lightyear); + REQUIRE(res == Approx(meters / openspace::distanceconstants::LightYear)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Parsec); + REQUIRE(res == Approx(meters / openspace::distanceconstants::Parsec)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Kiloparsec); + REQUIRE(res == Approx(meters / (1e3 * distanceconstants::Parsec))); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Megaparsec); + REQUIRE(res == Approx(meters / (1e6 * distanceconstants::Parsec))); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Gigaparsec); + REQUIRE(res == Approx(meters / (1e9 * distanceconstants::Parsec))); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Thou); + REQUIRE(res == Approx(meters / (1e-3 * distanceconstants::Inch))); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Inch); + REQUIRE(res == Approx(meters / distanceconstants::Inch)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Foot); + REQUIRE(res == Approx(meters / distanceconstants::Foot)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Yard); + REQUIRE(res == Approx(meters / distanceconstants::Yard)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Chain); + REQUIRE(res == Approx(meters / distanceconstants::Chain)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Furlong); + REQUIRE(res == Approx(meters / (10.0 * distanceconstants::Chain))); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Mile); + REQUIRE(res == Approx(meters / distanceconstants::Mile)); + + res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::League); + REQUIRE(res == Approx(meters / (3.0 * distanceconstants::Mile))); +} + + From a7ef1d5f1667a86ac6a54e51e759defdd4957996 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Thu, 25 Mar 2021 13:18:21 +0100 Subject: [PATCH 19/28] Switch to numerical numbers in distances test --- tests/test_distanceconversion.cpp | 68 +++++++++++++++---------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/test_distanceconversion.cpp b/tests/test_distanceconversion.cpp index 7187196a83..306a3cc294 100644 --- a/tests/test_distanceconversion.cpp +++ b/tests/test_distanceconversion.cpp @@ -55,55 +55,55 @@ TEST_CASE("DistanceConversion: Convert to meters", "[distanceconversion]") { REQUIRE(res == Approx(1000.0)); res = convertDistance(unit, DistanceUnit::AU, DistanceUnit::Meter); - REQUIRE(res == Approx(openspace::distanceconstants::AstronomicalUnit)); + REQUIRE(res == Approx(1.495978707E11)); res = convertDistance(unit, DistanceUnit::Lighthour, DistanceUnit::Meter); - REQUIRE(res == Approx(openspace::distanceconstants::LightHour)); + REQUIRE(res == Approx(1.0799921E12)); res = convertDistance(unit, DistanceUnit::Lightday, DistanceUnit::Meter); - REQUIRE(res == Approx(openspace::distanceconstants::LightDay)); + REQUIRE(res == Approx(2.591981E13)); res = convertDistance(unit, DistanceUnit::Lightmonth, DistanceUnit::Meter); - REQUIRE(res == Approx(openspace::distanceconstants::LightMonth)); + REQUIRE(res == Approx(7.8839421E14)); res = convertDistance(unit, DistanceUnit::Lightyear, DistanceUnit::Meter); - REQUIRE(res == Approx(openspace::distanceconstants::LightYear)); + REQUIRE(res == Approx(9.4607304725808E15)); res = convertDistance(unit, DistanceUnit::Parsec, DistanceUnit::Meter); - REQUIRE(res == Approx(openspace::distanceconstants::Parsec)); + REQUIRE(res == Approx(3.0856776E16)); res = convertDistance(unit, DistanceUnit::Kiloparsec, DistanceUnit::Meter); - REQUIRE(res == Approx(1e3 * distanceconstants::Parsec)); + REQUIRE(res == Approx(1e3 * 3.0856776E16)); res = convertDistance(unit, DistanceUnit::Megaparsec, DistanceUnit::Meter); - REQUIRE(res == Approx(1e6 * distanceconstants::Parsec)); + REQUIRE(res == Approx(1e6 * 3.0856776E16)); res = convertDistance(unit, DistanceUnit::Gigaparsec, DistanceUnit::Meter); - REQUIRE(res == Approx(1e9 * distanceconstants::Parsec)); + REQUIRE(res == Approx(1e9 * 3.0856776E16)); res = convertDistance(unit, DistanceUnit::Thou, DistanceUnit::Meter); - REQUIRE(res == Approx(1e-3 * distanceconstants::Inch)); + REQUIRE(res == Approx(1e-3 * 0.0254)); res = convertDistance(unit, DistanceUnit::Inch, DistanceUnit::Meter); - REQUIRE(res == Approx(distanceconstants::Inch)); + REQUIRE(res == Approx(0.0254)); res = convertDistance(unit, DistanceUnit::Foot, DistanceUnit::Meter); - REQUIRE(res == Approx(distanceconstants::Foot)); + REQUIRE(res == Approx(0.3048)); res = convertDistance(unit, DistanceUnit::Yard, DistanceUnit::Meter); - REQUIRE(res == Approx(distanceconstants::Yard)); + REQUIRE(res == Approx(0.9144)); res = convertDistance(unit, DistanceUnit::Chain, DistanceUnit::Meter); - REQUIRE(res == Approx(distanceconstants::Chain)); + REQUIRE(res == Approx(20.1168)); res = convertDistance(unit, DistanceUnit::Furlong, DistanceUnit::Meter); - REQUIRE(res == Approx(10.0 * distanceconstants::Chain)); + REQUIRE(res == Approx(10.0 * 20.1168)); res = convertDistance(unit, DistanceUnit::Mile, DistanceUnit::Meter); - REQUIRE(res == Approx(distanceconstants::Mile)); + REQUIRE(res == Approx(1609.344)); res = convertDistance(unit, DistanceUnit::League, DistanceUnit::Meter); - REQUIRE(res == Approx(3.0 * distanceconstants::Mile)); + REQUIRE(res == Approx(3.0 * 1609.344)); } TEST_CASE("DistanceConversion: Convert from meters", "[distanceconversion]") { @@ -132,55 +132,55 @@ TEST_CASE("DistanceConversion: Convert from meters", "[distanceconversion]") { REQUIRE(res == Approx(meters / 1000.0)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::AU); - REQUIRE(res == Approx(meters / openspace::distanceconstants::AstronomicalUnit)); + REQUIRE(res == Approx(meters / 1.495978707E11)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Lighthour); - REQUIRE(res == Approx(meters / openspace::distanceconstants::LightHour)); + REQUIRE(res == Approx(meters / 1.0799921E12)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Lightday); - REQUIRE(res == Approx(meters / openspace::distanceconstants::LightDay)); + REQUIRE(res == Approx(meters / 2.591981E13)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Lightmonth); - REQUIRE(res == Approx(meters / openspace::distanceconstants::LightMonth)); + REQUIRE(res == Approx(meters / 7.8839421E14)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Lightyear); - REQUIRE(res == Approx(meters / openspace::distanceconstants::LightYear)); + REQUIRE(res == Approx(meters / 9.4607304725808E15)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Parsec); - REQUIRE(res == Approx(meters / openspace::distanceconstants::Parsec)); + REQUIRE(res == Approx(meters / 3.0856776E16)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Kiloparsec); - REQUIRE(res == Approx(meters / (1e3 * distanceconstants::Parsec))); + REQUIRE(res == Approx(meters / (1e3 * 3.0856776E16))); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Megaparsec); - REQUIRE(res == Approx(meters / (1e6 * distanceconstants::Parsec))); + REQUIRE(res == Approx(meters / (1e6 * 3.0856776E16))); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Gigaparsec); - REQUIRE(res == Approx(meters / (1e9 * distanceconstants::Parsec))); + REQUIRE(res == Approx(meters / (1e9 * 3.0856776E16))); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Thou); - REQUIRE(res == Approx(meters / (1e-3 * distanceconstants::Inch))); + REQUIRE(res == Approx(meters / (1e-3 * 0.0254))); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Inch); - REQUIRE(res == Approx(meters / distanceconstants::Inch)); + REQUIRE(res == Approx(meters / 0.0254)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Foot); - REQUIRE(res == Approx(meters / distanceconstants::Foot)); + REQUIRE(res == Approx(meters / 0.3048)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Yard); - REQUIRE(res == Approx(meters / distanceconstants::Yard)); + REQUIRE(res == Approx(meters / 0.9144)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Chain); - REQUIRE(res == Approx(meters / distanceconstants::Chain)); + REQUIRE(res == Approx(meters / 20.1168)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Furlong); - REQUIRE(res == Approx(meters / (10.0 * distanceconstants::Chain))); + REQUIRE(res == Approx(meters / (10.0 * 20.1168))); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::Mile); - REQUIRE(res == Approx(meters / distanceconstants::Mile)); + REQUIRE(res == Approx(meters / 1609.344)); res = convertDistance(meters, DistanceUnit::Meter, DistanceUnit::League); - REQUIRE(res == Approx(meters / (3.0 * distanceconstants::Mile))); + REQUIRE(res == Approx(meters / (3.0 * 1609.344))); } From 1f89cc370cf1a2b8147e4dee3d2ec83e98ac6e98 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Fri, 26 Mar 2021 10:42:58 +0100 Subject: [PATCH 20/28] Add cross conversion tests for distance conversions --- tests/test_distanceconversion.cpp | 72 +++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/test_distanceconversion.cpp b/tests/test_distanceconversion.cpp index 306a3cc294..8f2a94e14a 100644 --- a/tests/test_distanceconversion.cpp +++ b/tests/test_distanceconversion.cpp @@ -183,4 +183,76 @@ TEST_CASE("DistanceConversion: Convert from meters", "[distanceconversion]") { REQUIRE(res == Approx(meters / (3.0 * 1609.344))); } +TEST_CASE("DistanceConversion: Cross convertions", "[distanceconversion]") { + const double unit = 1.0; + double res; + res = convertDistance(unit, DistanceUnit::Nanometer, DistanceUnit::Kilometer); + REQUIRE(res == Approx(1e-12)); + + res = convertDistance(unit, DistanceUnit::Micrometer, DistanceUnit::Decimeter); + REQUIRE(res == Approx(1e-5)); + + res = convertDistance(unit, DistanceUnit::Millimeter, DistanceUnit::Nanometer); + REQUIRE(res == Approx(1e6)); + + res = convertDistance(unit, DistanceUnit::Centimeter, DistanceUnit::Micrometer); + REQUIRE(res == Approx(1e4)); + + res = convertDistance(unit, DistanceUnit::Decimeter, DistanceUnit::Millimeter); + REQUIRE(res == Approx(1e2)); + + res = convertDistance(unit, DistanceUnit::Kilometer, DistanceUnit::Centimeter); + REQUIRE(res == Approx(1e5)); + + res = convertDistance(unit, DistanceUnit::AU, DistanceUnit::Parsec); + REQUIRE(res == Approx(4.84813681e-6)); + + res = convertDistance(unit, DistanceUnit::Lighthour, DistanceUnit::Lightmonth); + REQUIRE(res == Approx(1.36986305e-3)); + + res = convertDistance(unit, DistanceUnit::Lightday, DistanceUnit::Kiloparsec); + REQUIRE(res == Approx(8.40003829e-7)); + + res = convertDistance(unit, DistanceUnit::Lightmonth, DistanceUnit::Lightday); + REQUIRE(res == Approx(30.4166662487)); + + res = convertDistance(unit, DistanceUnit::Lightyear, DistanceUnit::Gigaparsec); + REQUIRE(res == Approx(3.0660139e-10)); + + res = convertDistance(unit, DistanceUnit::Parsec, DistanceUnit::Lightyear); + REQUIRE(res == Approx(3.26156379673)); + + res = convertDistance(unit, DistanceUnit::Kiloparsec, DistanceUnit::AU); + REQUIRE(res == Approx(2.06264806E8)); + + res = convertDistance(unit, DistanceUnit::Megaparsec, DistanceUnit::Lighthour); + REQUIRE(res == Approx(2.85712978826E10)); + + res = convertDistance(unit, DistanceUnit::Gigaparsec, DistanceUnit::Megaparsec); + REQUIRE(res == Approx(1e3)); + + res = convertDistance(unit, DistanceUnit::Thou, DistanceUnit::Yard); + REQUIRE(res == Approx(2.77777778e-5)); + + res = convertDistance(unit, DistanceUnit::Inch, DistanceUnit::Foot); + REQUIRE(res == Approx(8.33333333e-2)); + + res = convertDistance(unit, DistanceUnit::Foot, DistanceUnit::Mile); + REQUIRE(res == Approx(1.89393939e-4)); + + res = convertDistance(unit, DistanceUnit::Yard, DistanceUnit::Chain); + REQUIRE(res == Approx(4.54545455e-2)); + + res = convertDistance(unit, DistanceUnit::Chain, DistanceUnit::League); + REQUIRE(res == Approx(4.16666666e-3)); + + res = convertDistance(unit, DistanceUnit::Furlong, DistanceUnit::Thou); + REQUIRE(res == Approx(7.92E6)); + + res = convertDistance(unit, DistanceUnit::Mile, DistanceUnit::Inch); + REQUIRE(res == Approx(6.3360E4)); + + res = convertDistance(unit, DistanceUnit::League, DistanceUnit::Furlong); + REQUIRE(res == Approx(24.0)); +} From 2feff86181b6f220737a1e48bf6db41ca6a3182a Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Mon, 29 Mar 2021 11:37:23 +0200 Subject: [PATCH 21/28] Clean up --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 42 +++++++++---------- .../rendering/renderablemodelprojection.cpp | 3 +- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index c9314d1b94..a49caf0d5a 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit c9314d1b94c365ccd598fcf7ba1608c1a310491a +Subproject commit a49caf0d5adadaa105882d7787e6d2686f1f050c diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 15f9149982..c2c6fef101 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -161,7 +161,7 @@ namespace { }; // The scale of the model. For example if the model is in centimeters - // then ModelScale=cm + // then ModelScale = Centimeter std::optional modelScale; // Set if invisible parts (parts with no textures or materials) of the model @@ -283,8 +283,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } // Import Model from file - std::string file; - file = absPath(p.geometryFile.string()); + std::string file = absPath(p.geometryFile.string()); _geometry = ghoul::io::ModelReader::ref().loadModel( file, ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible), @@ -386,22 +385,22 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } switch (*p.animationMode) { - case Parameters::AnimationMode::LoopFromStart: - _animationMode = AnimationMode::LoopFromStart; - break; - case Parameters::AnimationMode::LoopInfinitely: - _animationMode = AnimationMode::LoopInfinitely; - break; - case Parameters::AnimationMode::BounceFromStart: - _animationMode = AnimationMode::BounceFromStart; - break; - case Parameters::AnimationMode::BounceInfinitely: - _animationMode = AnimationMode::BounceInfinitely; - break; - case Parameters::AnimationMode::Once: - default: - _animationMode = AnimationMode::Once; - break; + case Parameters::AnimationMode::LoopFromStart: + _animationMode = AnimationMode::LoopFromStart; + break; + case Parameters::AnimationMode::LoopInfinitely: + _animationMode = AnimationMode::LoopInfinitely; + break; + case Parameters::AnimationMode::BounceFromStart: + _animationMode = AnimationMode::BounceFromStart; + break; + case Parameters::AnimationMode::BounceInfinitely: + _animationMode = AnimationMode::BounceInfinitely; + break; + case Parameters::AnimationMode::Once: + default: + _animationMode = AnimationMode::Once; + break; } } @@ -510,7 +509,7 @@ void RenderableModel::initialize() { } else if (_geometry->hasAnimation() && !_enableAnimation.value()) { LINFO("Model with deactivated animation was found. " - "The animation could be activated by entering a start time in the asset file" + "The animation can be activated by entering a start time in the asset file" ); } @@ -687,8 +686,7 @@ void RenderableModel::update(const UpdateData& data) { if (modulo < 0) { modulo += 2 * duration; } - realtiveTime = - duration - abs(modulo - duration); + realtiveTime = duration - abs(modulo - duration); break; } case AnimationMode::Once: diff --git a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp index e969b7fcb2..66cb67b0d8 100644 --- a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp @@ -113,8 +113,7 @@ RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& di const Parameters p = codegen::bake(dictionary); // Import Model from file - std::string file; - file = absPath(p.geometryFile.string()); + std::string file = absPath(p.geometryFile.string()); _geometry = ghoul::io::ModelReader::ref().loadModel( file, ghoul::io::ModelReader::ForceRenderInvisible::No, From 1596bbb81372d0e49c4145d89e3fe41dec2174a5 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Tue, 30 Mar 2021 17:13:38 +0200 Subject: [PATCH 22/28] Add possibility to give model scale as a number --- modules/base/rendering/renderablemodel.cpp | 63 ++++++++++++---------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index c2c6fef101..d04221c4cc 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -161,8 +161,8 @@ namespace { }; // The scale of the model. For example if the model is in centimeters - // then ModelScale = Centimeter - std::optional modelScale; + // then ModelScale = Centimeter or ModelScale = 0.01 + std::optional> modelScale; // Set if invisible parts (parts with no textures or materials) of the model // should be forced to render or not. @@ -291,35 +291,40 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) ); if (p.modelScale.has_value()) { - Parameters::ScaleUnit scaleUnit = *p.modelScale; + if (std::holds_alternative(*p.modelScale)) { + Parameters::ScaleUnit scaleUnit = + std::get(*p.modelScale); - switch (scaleUnit) { - case Parameters::ScaleUnit::Nanometer: - _modelScale = DistanceUnit::Nanometer; - break; - case Parameters::ScaleUnit::Micrometer: - _modelScale = DistanceUnit::Micrometer; - break; - case Parameters::ScaleUnit::Millimeter: - _modelScale = DistanceUnit::Millimeter; - break; - case Parameters::ScaleUnit::Centimeter: - _modelScale = DistanceUnit::Centimeter; - break; - case Parameters::ScaleUnit::Decimeter: - _modelScale = DistanceUnit::Decimeter; - break; - case Parameters::ScaleUnit::Meter: - _modelScale = DistanceUnit::Meter; - break; - case Parameters::ScaleUnit::Kilometer: - _modelScale = DistanceUnit::Kilometer; - break; - default: - throw ghoul::MissingCaseException(); + switch (scaleUnit) { + case Parameters::ScaleUnit::Nanometer: + _modelScale = DistanceUnit::Nanometer; + break; + case Parameters::ScaleUnit::Micrometer: + _modelScale = DistanceUnit::Micrometer; + break; + case Parameters::ScaleUnit::Millimeter: + _modelScale = DistanceUnit::Millimeter; + break; + case Parameters::ScaleUnit::Centimeter: + _modelScale = DistanceUnit::Centimeter; + break; + case Parameters::ScaleUnit::Decimeter: + _modelScale = DistanceUnit::Decimeter; + break; + case Parameters::ScaleUnit::Meter: + _modelScale = DistanceUnit::Meter; + break; + case Parameters::ScaleUnit::Kilometer: + _modelScale = DistanceUnit::Kilometer; + break; + default: + throw ghoul::MissingCaseException(); + } + _scaleVector = glm::dvec3(convertUnit(_modelScale, DistanceUnit::Meter)); + } + else if (std::holds_alternative(*p.modelScale)) { + _scaleVector = glm::dvec3(std::get(*p.modelScale)); } - - _scaleVector = glm::dvec3(convertUnit(_modelScale, DistanceUnit::Meter)); } if (p.animationStartTime.has_value()) { From e91d833c659d4147e10640aa18dea86503a3f875 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Wed, 31 Mar 2021 14:19:27 +0200 Subject: [PATCH 23/28] Optimize model rendering * Don't render when too far away, same as RenderableGlobe --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 164 +++++++++++---------- 2 files changed, 90 insertions(+), 76 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index fc69a527bc..091b97ee2d 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit fc69a527bcb1ff139dd9b7d9656f26050df8dc6b +Subproject commit 091b97ee2d7bcab999857474bbaf6353b14d2d92 diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index d04221c4cc..587bc33655 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -332,7 +332,6 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) LWARNING("Animation start time given to model without animation"); } _animationStart = *p.animationStartTime; - _enableAnimation = true; } if (p.enableAnimation.has_value()) { @@ -541,7 +540,7 @@ void RenderableModel::initializeGL() { _geometry->initialize(); _geometry->calculateBoundingRadius(); - setBoundingSphere(glm::sqrt(_geometry->boundingRadius())); + setBoundingSphere(glm::sqrt(_geometry->boundingRadius()) * _scaleVector.x); } void RenderableModel::deinitializeGL() { @@ -558,73 +557,87 @@ void RenderableModel::deinitializeGL() { } void RenderableModel::render(const RenderData& data, RendererTasks&) { - _program->activate(); + const double distanceToCamera = glm::distance( + data.camera.positionVec3(), + data.modelTransform.translation + ); - _program->setUniform(_uniformCache.opacity, _opacity); + // This distance will be enough to render the globe as one pixel if the field of + // view is 'fov' radians and the screen resolution is 'res' pixels. + //constexpr double fov = 2 * glm::pi() / 6; // 60 degrees + //constexpr double tfov = tan(fov / 2.0); // doesn't work unfortunately + constexpr double tfov = 0.5773502691896257; + constexpr int res = 2880; + const double maxDistance = res * boundingSphere()/ tfov; - // Model transform and view transform needs to be in double precision - const glm::dmat4 modelTransform = - glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation - glm::dmat4(data.modelTransform.rotation) * // Spice rotation - glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) * - glm::scale(glm::dmat4(_modelTransform.value()), _scaleVector); // Model scale unit - const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * - modelTransform; + if (distanceToCamera < maxDistance) { + _program->activate(); - int nLightSources = 0; - _lightIntensitiesBuffer.resize(_lightSources.size()); - _lightDirectionsViewSpaceBuffer.resize(_lightSources.size()); - for (const std::unique_ptr& lightSource : _lightSources) { - if (!lightSource->isEnabled()) { - continue; + _program->setUniform(_uniformCache.opacity, _opacity); + + // Model transform and view transform needs to be in double precision + const glm::dmat4 modelTransform = + glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation + glm::dmat4(data.modelTransform.rotation) * // Spice rotation + glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) * + glm::scale(glm::dmat4(_modelTransform.value()), _scaleVector); // Model scale unit + const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * + modelTransform; + + int nLightSources = 0; + _lightIntensitiesBuffer.resize(_lightSources.size()); + _lightDirectionsViewSpaceBuffer.resize(_lightSources.size()); + for (const std::unique_ptr& lightSource : _lightSources) { + if (!lightSource->isEnabled()) { + continue; + } + _lightIntensitiesBuffer[nLightSources] = lightSource->intensity(); + _lightDirectionsViewSpaceBuffer[nLightSources] = + lightSource->directionViewSpace(data); + + ++nLightSources; } - _lightIntensitiesBuffer[nLightSources] = lightSource->intensity(); - _lightDirectionsViewSpaceBuffer[nLightSources] = - lightSource->directionViewSpace(data); - ++nLightSources; - } + _program->setUniform( + _uniformCache.nLightSources, + nLightSources + ); + _program->setUniform( + _uniformCache.lightIntensities, + _lightIntensitiesBuffer + ); + _program->setUniform( + _uniformCache.lightDirectionsViewSpace, + _lightDirectionsViewSpaceBuffer + ); + _program->setUniform( + _uniformCache.modelViewTransform, + glm::mat4(modelViewTransform) + ); - _program->setUniform( - _uniformCache.nLightSources, - nLightSources - ); - _program->setUniform( - _uniformCache.lightIntensities, - _lightIntensitiesBuffer - ); - _program->setUniform( - _uniformCache.lightDirectionsViewSpace, - _lightDirectionsViewSpaceBuffer - ); - _program->setUniform( - _uniformCache.modelViewTransform, - glm::mat4(modelViewTransform) - ); + glm::dmat4 normalTransform = glm::transpose(glm::inverse(modelViewTransform)); - glm::dmat4 normalTransform = glm::transpose(glm::inverse(modelViewTransform)); + _program->setUniform( + _uniformCache.normalTransform, + glm::mat4(normalTransform) + ); - _program->setUniform( - _uniformCache.normalTransform, - glm::mat4(normalTransform) - ); + _program->setUniform( + _uniformCache.projectionTransform, + data.camera.projectionMatrix() + ); + _program->setUniform(_uniformCache.ambientIntensity, _ambientIntensity); + _program->setUniform(_uniformCache.diffuseIntensity, _diffuseIntensity); + _program->setUniform(_uniformCache.specularIntensity, _specularIntensity); + _program->setUniform(_uniformCache.performShading, _performShading); + _program->setUniform(_uniformCache.opacityBlending, _enableOpacityBlending); - _program->setUniform( - _uniformCache.projectionTransform, - data.camera.projectionMatrix() - ); - _program->setUniform(_uniformCache.ambientIntensity, _ambientIntensity); - _program->setUniform(_uniformCache.diffuseIntensity, _diffuseIntensity); - _program->setUniform(_uniformCache.specularIntensity, _specularIntensity); - _program->setUniform(_uniformCache.performShading, _performShading); - _program->setUniform(_uniformCache.opacityBlending, _enableOpacityBlending); + if (_disableFaceCulling) { + glDisable(GL_CULL_FACE); + } - if (_disableFaceCulling) { - glDisable(GL_CULL_FACE); - } - - glEnablei(GL_BLEND, 0); - switch (_blendingFuncOption) { + glEnablei(GL_BLEND, 0); + switch (_blendingFuncOption) { case DefaultBlending: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; @@ -640,24 +653,25 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) { case ColorAddingBlending: glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR); break; - }; + }; - if (_disableDepthTest) { - glDisable(GL_DEPTH_TEST); + if (_disableDepthTest) { + glDisable(GL_DEPTH_TEST); + } + + _geometry->render(*_program); + if (_disableFaceCulling) { + glEnable(GL_CULL_FACE); + } + + global::renderEngine->openglStateCache().resetBlendState(); + + if (_disableDepthTest) { + glEnable(GL_DEPTH_TEST); + } + + _program->deactivate(); } - - _geometry->render(*_program); - if (_disableFaceCulling) { - glEnable(GL_CULL_FACE); - } - - global::renderEngine->openglStateCache().resetBlendState(); - - if (_disableDepthTest) { - glEnable(GL_DEPTH_TEST); - } - - _program->deactivate(); } void RenderableModel::update(const UpdateData& data) { From 3ec01fa25e7d83c84ebd368f863003b1fa202a31 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Wed, 7 Apr 2021 17:25:58 +0200 Subject: [PATCH 24/28] Address PR comments --- ext/ghoul | 2 +- modules/base/rendering/renderablemodel.cpp | 177 ++++++++++-------- modules/base/rendering/renderablemodel.h | 5 +- .../rendering/renderablemodelprojection.cpp | 9 +- src/documentation/verifier.cpp | 4 +- support/coding/codegen | 2 +- 6 files changed, 103 insertions(+), 96 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 091b97ee2d..8e3a9928f1 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 091b97ee2d7bcab999857474bbaf6353b14d2d92 +Subproject commit 8e3a9928f1919730e08b3a1808711e9a4b14ed8c diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 587bc33655..dac0c50678 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -62,10 +62,10 @@ namespace { { "Color Adding", ColorAddingBlending } }; - constexpr openspace::properties::Property::PropertyInfo enableAnimationInfo = { + constexpr openspace::properties::Property::PropertyInfo EnableAnimationInfo = { "EnableAnimation", "Enable Animation", - "Enable Animation" + "Enable or disable the animation for the model if it has any" }; constexpr const std::array UniformNames = { @@ -145,9 +145,8 @@ namespace { struct [[codegen::Dictionary(RenderableModel)]] Parameters { // The file or files that should be loaded in this RenderableModel. The file can - // contain filesystem tokens or can be specified relatively to the - // location of the .mod file. - // This specifies the model that is rendered by the Renderable. + // contain filesystem tokens. This specifies the model that is rendered by + // the Renderable. std::filesystem::path geometryFile; enum class ScaleUnit { @@ -162,18 +161,18 @@ namespace { // The scale of the model. For example if the model is in centimeters // then ModelScale = Centimeter or ModelScale = 0.01 - std::optional> modelScale; + std::optional> modelScale; // Set if invisible parts (parts with no textures or materials) of the model // should be forced to render or not. std::optional forceRenderInvisible; - // [[codegen::verbatim(enableAnimationInfo.description)]] + // [[codegen::verbatim(EnableAnimationInfo.description)]] std::optional enableAnimation; // The date and time that the model animation should start. // In format 'YYYY MM DD hh:mm:ss'. - std::optional animationStartTime [[codegen::dateTime()]]; + std::optional animationStartTime [[codegen::datetime()]]; enum class AnimationTimeUnit { Nanosecond, @@ -222,7 +221,8 @@ namespace { std::optional rotationVector; // [[codegen::verbatim(LightSourcesInfo.description)]] - std::optional> lightSources [[codegen::reference("core_light_source")]]; + std::optional> lightSources + [[codegen::reference("core_light_source")]]; // [[codegen::verbatim(DisableDepthTestInfo.description)]] std::optional disableDepthTest; @@ -246,7 +246,7 @@ documentation::Documentation RenderableModel::Documentation() { RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) : Renderable(dictionary) - , _enableAnimation(enableAnimationInfo, false) + , _enableAnimation(EnableAnimationInfo, false) , _ambientIntensity(AmbientIntensityInfo, 0.2f, 0.f, 1.f) , _diffuseIntensity(DiffuseIntensityInfo, 1.f, 0.f, 1.f) , _specularIntensity(SpecularIntensityInfo, 1.f, 0.f, 1.f) @@ -294,36 +294,40 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) if (std::holds_alternative(*p.modelScale)) { Parameters::ScaleUnit scaleUnit = std::get(*p.modelScale); + DistanceUnit distanceUnit; switch (scaleUnit) { case Parameters::ScaleUnit::Nanometer: - _modelScale = DistanceUnit::Nanometer; + distanceUnit = DistanceUnit::Nanometer; break; case Parameters::ScaleUnit::Micrometer: - _modelScale = DistanceUnit::Micrometer; + distanceUnit = DistanceUnit::Micrometer; break; case Parameters::ScaleUnit::Millimeter: - _modelScale = DistanceUnit::Millimeter; + distanceUnit = DistanceUnit::Millimeter; break; case Parameters::ScaleUnit::Centimeter: - _modelScale = DistanceUnit::Centimeter; + distanceUnit = DistanceUnit::Centimeter; break; case Parameters::ScaleUnit::Decimeter: - _modelScale = DistanceUnit::Decimeter; + distanceUnit = DistanceUnit::Decimeter; break; case Parameters::ScaleUnit::Meter: - _modelScale = DistanceUnit::Meter; + distanceUnit = DistanceUnit::Meter; break; case Parameters::ScaleUnit::Kilometer: - _modelScale = DistanceUnit::Kilometer; + distanceUnit = DistanceUnit::Kilometer; break; default: throw ghoul::MissingCaseException(); } - _scaleVector = glm::dvec3(convertUnit(_modelScale, DistanceUnit::Meter)); + _modelScale = convertUnit(distanceUnit, DistanceUnit::Meter); } - else if (std::holds_alternative(*p.modelScale)) { - _scaleVector = glm::dvec3(std::get(*p.modelScale)); + else if (std::holds_alternative(*p.modelScale)) { + _modelScale = std::get(*p.modelScale); + } + else { + throw ghoul::MissingCaseException(); } } @@ -338,12 +342,12 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) if (!_geometry->hasAnimation()) { LWARNING("Attempting to enable animation for a model that does not have any"); } - else if (*p.enableAnimation &&_animationStart == "") { + else if (*p.enableAnimation && _animationStart.empty()) { LWARNING("Cannot enable animation without a given start time"); } else { _enableAnimation = *p.enableAnimation; - _geometry->enableAnimation(_enableAnimation.value()); + _geometry->enableAnimation(_enableAnimation); } } @@ -381,6 +385,9 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) _geometry->setTimeScale(convertTime(1.0, timeUnit, TimeUnit::Second)); } + else { + throw ghoul::MissingCaseException(); + } } if (p.animationMode.has_value()) { @@ -402,9 +409,10 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) _animationMode = AnimationMode::BounceInfinitely; break; case Parameters::AnimationMode::Once: - default: _animationMode = AnimationMode::Once; break; + default: + throw ghoul::MissingCaseException(); } } @@ -412,27 +420,12 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) _modelTransform = *p.modelTransform; } - if (p.ambientIntensity.has_value()) { - _ambientIntensity = *p.ambientIntensity; - } - if (p.diffuseIntensity.has_value()) { - _diffuseIntensity = *p.diffuseIntensity; - } - if (p.specularIntensity.has_value()) { - _specularIntensity = *p.specularIntensity; - } - - if (p.performShading.has_value()) { - _performShading = *p.performShading; - } - - if (p.disableDepthTest.has_value()) { - _disableDepthTest = *p.disableDepthTest; - } - - if (p.disableFaceCulling.has_value()) { - _disableFaceCulling = *p.disableFaceCulling; - } + _ambientIntensity = p.ambientIntensity.value_or(_ambientIntensity); + _diffuseIntensity = p.diffuseIntensity.value_or(_diffuseIntensity); + _specularIntensity = p.specularIntensity.value_or(_specularIntensity); + _performShading = p.performShading.value_or(_performShading); + _disableDepthTest = p.disableDepthTest.value_or(_disableDepthTest); + _disableFaceCulling = p.disableFaceCulling.value_or(_disableFaceCulling); if (p.lightSources.has_value()) { std::vector lightsources = *p.lightSources; @@ -467,16 +460,15 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) if (!_geometry->hasAnimation()) { LWARNING("Attempting to enable animation for a model that does not have any"); } - else if (_enableAnimation.value() && _animationStart == "") { + else if (_enableAnimation && _animationStart.empty()) { LWARNING("Cannot enable animation without a given start time"); _enableAnimation = false; } else { - _geometry->enableAnimation(_enableAnimation.value()); + _geometry->enableAnimation(_enableAnimation); } }); - if (p.rotationVector.has_value()) { _rotationVec = *p.rotationVector; } @@ -494,9 +486,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) _blendingFuncOption.set(BlendingMapping[blendingOpt]); } - if (p.enableOpacityBlending.has_value()) { - _enableOpacityBlending = *p.enableOpacityBlending; - } + _enableOpacityBlending = p.enableOpacityBlending.value_or(_enableOpacityBlending); addProperty(_enableOpacityBlending); } @@ -508,10 +498,10 @@ bool RenderableModel::isReady() const { void RenderableModel::initialize() { ZoneScoped - if (_geometry->hasAnimation() && _enableAnimation.value() && _animationStart == "") { + if (_geometry->hasAnimation() && _enableAnimation && _animationStart.empty()) { LWARNING("Model with animation not given any start time"); } - else if (_geometry->hasAnimation() && !_enableAnimation.value()) { + else if (_geometry->hasAnimation() && !_enableAnimation) { LINFO("Model with deactivated animation was found. " "The animation can be activated by entering a start time in the asset file" ); @@ -540,7 +530,7 @@ void RenderableModel::initializeGL() { _geometry->initialize(); _geometry->calculateBoundingRadius(); - setBoundingSphere(glm::sqrt(_geometry->boundingRadius()) * _scaleVector.x); + setBoundingSphere(glm::sqrt(_geometry->boundingRadius()) * _modelScale); } void RenderableModel::deinitializeGL() { @@ -568,7 +558,7 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) { //constexpr double tfov = tan(fov / 2.0); // doesn't work unfortunately constexpr double tfov = 0.5773502691896257; constexpr int res = 2880; - const double maxDistance = res * boundingSphere()/ tfov; + const double maxDistance = res * boundingSphere() / tfov; if (distanceToCamera < maxDistance) { _program->activate(); @@ -580,7 +570,10 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) { glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation glm::dmat4(data.modelTransform.rotation) * // Spice rotation glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) * - glm::scale(glm::dmat4(_modelTransform.value()), _scaleVector); // Model scale unit + glm::scale( + glm::dmat4(_modelTransform.value()), + glm::dvec3(_modelScale) // Model scale unit + ); const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform; @@ -638,21 +631,21 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) { glEnablei(GL_BLEND, 0); switch (_blendingFuncOption) { - case DefaultBlending: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - case AdditiveBlending: - glBlendFunc(GL_ONE, GL_ONE); - break; - case PointsAndLinesBlending: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - case PolygonBlending: - glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE); - break; - case ColorAddingBlending: - glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR); - break; + case DefaultBlending: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case AdditiveBlending: + glBlendFunc(GL_ONE, GL_ONE); + break; + case PointsAndLinesBlending: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case PolygonBlending: + glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE); + break; + case ColorAddingBlending: + glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR); + break; }; if (_disableDepthTest) { @@ -680,40 +673,60 @@ void RenderableModel::update(const UpdateData& data) { ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); } - if (_geometry->hasAnimation() && _animationStart != "") { - double realtiveTime; + if (_geometry->hasAnimation() && !_animationStart.empty()) { + double relativeTime; double now = data.time.j2000Seconds(); double startTime = data.time.convertTime(_animationStart); double duration = _geometry->animationDuration(); switch (_animationMode) { case AnimationMode::LoopFromStart: - realtiveTime = std::fmod(now - startTime, duration); + // Start looping from the start time + // s//// + relativeTime = std::fmod(now - startTime, duration); break; case AnimationMode::LoopInfinitely: - realtiveTime = std::fmod(now - startTime, duration); - if (realtiveTime < 0) { - realtiveTime += duration; + // Loop both before and after the start time where the model is + // in the initial position at the start time. std::fmod is not a + // true modulo function, it just calculates the remainder of the division + // which can be negative. To make it true modulo it is bumped up to + // possitive values when it is negative. + // //s// + relativeTime = std::fmod(now - startTime, duration); + if (relativeTime < 0.0) { + relativeTime += duration; } break; case AnimationMode::BounceFromStart: - realtiveTime = + // Bounce from the start position. Bounce means to do the animation + // and when it ends, play the animation in reverse to make sure the model + // goes back to its initial position bofore starting again. Avoids a + // visible jump from the last position to the first position when loop + // starts again + // s/\/\/\/\ + relativeTime = duration - abs(fmod(now - startTime, 2 * duration) - duration); break; case AnimationMode::BounceInfinitely: { + // Bounce both before and after the start time where the model is + // in the initial position at the start time + // /\/\s/\/\ double modulo = fmod(now - startTime, 2 * duration); - if (modulo < 0) { + if (modulo < 0.0) { modulo += 2 * duration; } - realtiveTime = duration - abs(modulo - duration); + relativeTime = duration - abs(modulo - duration); break; } case AnimationMode::Once: - default: - realtiveTime = now - startTime; + // Play animation once starting from the start time + // s/ + relativeTime = now - startTime; break; + default: + throw ghoul::MissingCaseException(); } - _geometry->update(realtiveTime); + _geometry->update(relativeTime); } } diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index e0c6449e7b..58874a1988 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -80,11 +80,10 @@ private: }; std::unique_ptr _geometry; - DistanceUnit _modelScale; - glm::dvec3 _scaleVector = glm::dvec3(1.0, 1.0, 1.0); + double _modelScale = 1.0; bool _forceRenderInvisible = false; bool _notifyInvisibleDropped = true; - std::string _animationStart = ""; + std::string _animationStart; AnimationMode _animationMode; properties::BoolProperty _enableAnimation; diff --git a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp index 66cb67b0d8..d5e30753d1 100644 --- a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp @@ -127,15 +127,10 @@ RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& di p.projection ); - double boundingSphereRadius = 1.0e9; - if (p.boundingSphereRadius.has_value()) { - boundingSphereRadius = *p.boundingSphereRadius; - } + double boundingSphereRadius = p.boundingSphereRadius.value_or(1.0e9); setBoundingSphere(boundingSphereRadius); - if (p.performShading.has_value()) { - _performShading = *p.performShading; - } + _performShading = p.performShading.value_or(_performShading); addProperty(_performShading); } diff --git a/src/documentation/verifier.cpp b/src/documentation/verifier.cpp index eebcc3c6f4..a4a9c20d9b 100644 --- a/src/documentation/verifier.cpp +++ b/src/documentation/verifier.cpp @@ -274,7 +274,7 @@ std::string DirectoryVerifier::type() const { } TestResult DateTimeVerifier::operator()(const ghoul::Dictionary& dict, - const std::string& key) const + const std::string& key) const { TestResult res = StringVerifier::operator()(dict, key); if (!res.success) { @@ -302,7 +302,7 @@ TestResult DateTimeVerifier::operator()(const ghoul::Dictionary& dict, // normalize e.g. 29/02/2013 would become 01/03/2013 std::tm t_copy(t); time_t when = mktime(&t_copy); - std::tm *norm = localtime(&when); + std::tm* norm = localtime(&when); // validate (is the normalized date still the same?): if (norm->tm_mday != t.tm_mday && diff --git a/support/coding/codegen b/support/coding/codegen index 73fb9593db..2994ef0c18 160000 --- a/support/coding/codegen +++ b/support/coding/codegen @@ -1 +1 @@ -Subproject commit 73fb9593dbcef6ec7dcef8c63bd92178146cd0b8 +Subproject commit 2994ef0c186ad53aecf100e4a3827f1f551cc396 From 2b1c0c567d88adf2c36c4b456225407fb63a0c83 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Thu, 8 Apr 2021 17:15:58 +0200 Subject: [PATCH 25/28] Update ISS model scale --- .../scene/solarsystem/planets/earth/satellites/misc/iss.asset | 1 + ext/ghoul | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/data/assets/scene/solarsystem/planets/earth/satellites/misc/iss.asset b/data/assets/scene/solarsystem/planets/earth/satellites/misc/iss.asset index 4d3ae8016b..a21b3bd1a2 100644 --- a/data/assets/scene/solarsystem/planets/earth/satellites/misc/iss.asset +++ b/data/assets/scene/solarsystem/planets/earth/satellites/misc/iss.asset @@ -60,6 +60,7 @@ local initializeAndAddNodes = function() Renderable = { Type = "RenderableModel", GeometryFile = models .. "/ISS.fbx", + ModelScale = "Centimeter", LightSources = { { Type = "SceneGraphLightSource", diff --git a/ext/ghoul b/ext/ghoul index 8e3a9928f1..6ef01a676c 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 8e3a9928f1919730e08b3a1808711e9a4b14ed8c +Subproject commit 6ef01a676c10da0613b1f59cccc893966bfc60ec From 4a97a212976248b99ef35020c7c6060a9ee647bd Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Wed, 14 Apr 2021 17:12:46 +0200 Subject: [PATCH 26/28] Address PR comments --- modules/base/rendering/renderablemodel.cpp | 17 +++++++++++------ modules/base/rendering/renderablemodel.h | 2 +- .../rendering/renderablemodelprojection.cpp | 1 - tests/test_distanceconversion.cpp | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index dac0c50678..275b392765 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -282,7 +282,6 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } } - // Import Model from file std::string file = absPath(p.geometryFile.string()); _geometry = ghoul::io::ModelReader::ref().loadModel( file, @@ -552,10 +551,9 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) { data.modelTransform.translation ); - // This distance will be enough to render the globe as one pixel if the field of + // This distance will be enough to render the model as one pixel if the field of // view is 'fov' radians and the screen resolution is 'res' pixels. - //constexpr double fov = 2 * glm::pi() / 6; // 60 degrees - //constexpr double tfov = tan(fov / 2.0); // doesn't work unfortunately + // Formula from RenderableGlobe constexpr double tfov = 0.5773502691896257; constexpr int res = 2880; const double maxDistance = res * boundingSphere() / tfov; @@ -679,6 +677,13 @@ void RenderableModel::update(const UpdateData& data) { double startTime = data.time.convertTime(_animationStart); double duration = _geometry->animationDuration(); + // The animation works in a time range 0 to duration where 0 in the animation is + // the given _animationStart time in OpenSpace. The time in OpenSpace then has to + // be converted to the animation time range, so the animation knows which + // keyframes it should interpolate between for each frame. The conversion is + // done in different ways depending on the animation mode. + // Explanation: s indicates start time, / indicates animation is played once forwards, + // \ indicates animation is played once backwards, time moves to the right. switch (_animationMode) { case AnimationMode::LoopFromStart: // Start looping from the start time @@ -690,7 +695,7 @@ void RenderableModel::update(const UpdateData& data) { // in the initial position at the start time. std::fmod is not a // true modulo function, it just calculates the remainder of the division // which can be negative. To make it true modulo it is bumped up to - // possitive values when it is negative. + // positive values when it is negative // //s// relativeTime = std::fmod(now - startTime, duration); if (relativeTime < 0.0) { @@ -700,7 +705,7 @@ void RenderableModel::update(const UpdateData& data) { case AnimationMode::BounceFromStart: // Bounce from the start position. Bounce means to do the animation // and when it ends, play the animation in reverse to make sure the model - // goes back to its initial position bofore starting again. Avoids a + // goes back to its initial position before starting again. Avoids a // visible jump from the last position to the first position when loop // starts again // s/\/\/\/\ diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index 58874a1988..d1d2cbc9fa 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -84,7 +84,7 @@ private: bool _forceRenderInvisible = false; bool _notifyInvisibleDropped = true; std::string _animationStart; - AnimationMode _animationMode; + AnimationMode _animationMode = AnimationMode::Once; properties::BoolProperty _enableAnimation; properties::FloatProperty _ambientIntensity; diff --git a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp index d5e30753d1..376e7c509d 100644 --- a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp @@ -112,7 +112,6 @@ RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& di { const Parameters p = codegen::bake(dictionary); - // Import Model from file std::string file = absPath(p.geometryFile.string()); _geometry = ghoul::io::ModelReader::ref().loadModel( file, diff --git a/tests/test_distanceconversion.cpp b/tests/test_distanceconversion.cpp index 8f2a94e14a..56e277c36e 100644 --- a/tests/test_distanceconversion.cpp +++ b/tests/test_distanceconversion.cpp @@ -183,7 +183,7 @@ TEST_CASE("DistanceConversion: Convert from meters", "[distanceconversion]") { REQUIRE(res == Approx(meters / (3.0 * 1609.344))); } -TEST_CASE("DistanceConversion: Cross convertions", "[distanceconversion]") { +TEST_CASE("DistanceConversion: Cross conversion", "[distanceconversion]") { const double unit = 1.0; double res; From a541721d335c4af8ffd8f54e550ad1f9004fce32 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Fri, 16 Apr 2021 15:51:31 +0200 Subject: [PATCH 27/28] Address PR comments and tweak animation mode Once --- modules/base/rendering/renderablemodel.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 275b392765..a13fccfaa7 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -134,7 +134,8 @@ namespace { constexpr openspace::properties::Property::PropertyInfo BlendingOptionInfo = { "BlendingOption", "Blending Options", - "Debug option for blending colors." + "Changes the blending function used to calculate the colors of the model with " + "respect to the opacity." }; constexpr openspace::properties::Property::PropertyInfo EnableOpacityBlendingInfo = { @@ -196,7 +197,9 @@ namespace { }; // The mode of how the animation should be played back. - // Default is animation is played back once at the start time + // Default is animation is played back once at the start time. + // For a more detailed description see: + // http://wiki.openspaceproject.com/docs/builders/model-animation std::optional animationMode; // [[codegen::verbatim(AmbientIntensityInfo.description)]] @@ -724,9 +727,13 @@ void RenderableModel::update(const UpdateData& data) { break; } case AnimationMode::Once: - // Play animation once starting from the start time + // Play animation once starting from the start time and stay at the + // animation's last position when animation is over // s/ relativeTime = now - startTime; + if (relativeTime > duration) { + relativeTime = duration; + } break; default: throw ghoul::MissingCaseException(); From 1267874a06bf865a882dcc56251c1bbb4004e310 Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Wed, 21 Apr 2021 10:39:19 +0200 Subject: [PATCH 28/28] Update Ghoul and codegen --- ext/ghoul | 2 +- support/coding/codegen | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 4db5e42c95..23b9673bfc 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 4db5e42c95fc9735aa87903e514c5616da1080bc +Subproject commit 23b9673bfc2e92cba4d4aa05c808aa833b150f8a diff --git a/support/coding/codegen b/support/coding/codegen index 8a061bf7cf..4b06f1ad45 160000 --- a/support/coding/codegen +++ b/support/coding/codegen @@ -1 +1 @@ -Subproject commit 8a061bf7cf8e835048982725328aa809a7778bfd +Subproject commit 4b06f1ad4586289f0f1523915e11b1c81fe21202