From 1b3df16c28cc72dda4fbee1148dfc1af9d39465e Mon Sep 17 00:00:00 2001 From: Emil Axelsson Date: Wed, 11 Jul 2018 10:42:06 +0200 Subject: [PATCH] Feature/time frame (#642) * First implementation of time frames * Add TimeFrameUnion. Only show solar system 1850-2150. * Consider dependencies when determining whether time frame is active * Code review fixes. --- .../missions/newhorizons/transforms.asset | 7 +- .../scene/solarsystem/sun/transforms.asset | 5 + include/openspace/scene/scenegraphnode.h | 5 + include/openspace/scene/timeframe.h | 58 +++++++ modules/base/CMakeLists.txt | 4 + modules/base/basemodule.cpp | 11 ++ modules/base/timeframe/timeframeinterval.cpp | 160 ++++++++++++++++++ modules/base/timeframe/timeframeinterval.h | 57 +++++++ modules/base/timeframe/timeframeunion.cpp | 101 +++++++++++ modules/base/timeframe/timeframeunion.h | 50 ++++++ src/CMakeLists.txt | 2 + src/documentation/core_registration.cpp | 2 + src/engine/openspaceengine.cpp | 5 + src/scene/scenegraphnode.cpp | 74 ++++++-- src/scene/scenegraphnode_doc.inl | 6 + src/scene/timeframe.cpp | 78 +++++++++ 16 files changed, 605 insertions(+), 20 deletions(-) create mode 100644 include/openspace/scene/timeframe.h create mode 100644 modules/base/timeframe/timeframeinterval.cpp create mode 100644 modules/base/timeframe/timeframeinterval.h create mode 100644 modules/base/timeframe/timeframeunion.cpp create mode 100644 modules/base/timeframe/timeframeunion.h create mode 100644 src/scene/timeframe.cpp diff --git a/data/assets/scene/solarsystem/missions/newhorizons/transforms.asset b/data/assets/scene/solarsystem/missions/newhorizons/transforms.asset index 26c54961f6..6efefa3590 100644 --- a/data/assets/scene/solarsystem/missions/newhorizons/transforms.asset +++ b/data/assets/scene/solarsystem/missions/newhorizons/transforms.asset @@ -2,11 +2,14 @@ local assetHelper = asset.require('util/asset_helper') local sunTransforms = asset.require('scene/solarsystem/sun/transforms') local kernels = asset.require('./kernels') - - local PlutoBarycenter = { Identifier = "PlutoBarycenter", Parent = sunTransforms.SolarSystemBarycenter.Identifier, + TimeFrame = { + Type = "TimeFrameInterval", + Start = "2015-JAN-01", + End = "2015-AUG-01" + }, Transform = { Translation = { Type = "SpiceTranslation", diff --git a/data/assets/scene/solarsystem/sun/transforms.asset b/data/assets/scene/solarsystem/sun/transforms.asset index d30fef8ae6..cd59ad6fbf 100644 --- a/data/assets/scene/solarsystem/sun/transforms.asset +++ b/data/assets/scene/solarsystem/sun/transforms.asset @@ -6,6 +6,11 @@ asset.require("spice/base") -- Barycenter of the solar system, expressed in the Galactic frame local SolarSystemBarycenter = { Identifier = "SolarSystemBarycenter", + TimeFrame = { -- Using Spice kernels for 1850-2150 + Type = "TimeFrameInterval", + Start = "1850-JAN-01", + End = "2150-JAN-01" + }, GUI = { Name = "Solar System Barycenter", Path = "/Solar System", diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index 731171dd2c..37b14e9ad4 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -48,6 +48,8 @@ class Scale; class Scene; struct UpdateData; struct SurfacePositionHandle; +class TimeFrame; +class Time; namespace documentation { struct Documentation; } @@ -120,6 +122,7 @@ public: glm::dmat4 modelTransform() const; glm::dmat4 inverseModelTransform() const; double worldScale() const; + bool isTimeFrameActive(const Time& time) const; SceneGraphNode* parent() const; std::vector children() const; @@ -168,6 +171,8 @@ private: std::unique_ptr scale; } _transform; + std::unique_ptr _timeFrame; + // Cached transform data glm::dvec3 _worldPositionCached; glm::dmat3 _worldRotationCached; diff --git a/include/openspace/scene/timeframe.h b/include/openspace/scene/timeframe.h new file mode 100644 index 0000000000..9d709fb9f4 --- /dev/null +++ b/include/openspace/scene/timeframe.h @@ -0,0 +1,58 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_CORE___TIMEFRAME___H__ +#define __OPENSPACE_CORE___TIMEFRAME___H__ + +#include + +#include +#include + +namespace ghoul { class Dictionary; } + +namespace openspace { + +class Time; + +namespace documentation { struct Documentation; } + +class TimeFrame : public properties::PropertyOwner { +public: + static std::unique_ptr createFromDictionary( + const ghoul::Dictionary& dictionary); + + TimeFrame(); + virtual ~TimeFrame() = default; + + virtual bool initialize(); + + virtual bool isActive(const Time& time) const = 0; + + static documentation::Documentation Documentation(); +}; + +} // namespace openspace + +#endif // __OPENSPACE_CORE___TIMEFRAME___H__ diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 8b5a977773..dbf81a6445 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -56,6 +56,8 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rotation/staticrotation.h ${CMAKE_CURRENT_SOURCE_DIR}/scale/luascale.h ${CMAKE_CURRENT_SOURCE_DIR}/scale/staticscale.h + ${CMAKE_CURRENT_SOURCE_DIR}/timeframe/timeframeinterval.h + ${CMAKE_CURRENT_SOURCE_DIR}/timeframe/timeframeunion.h ) source_group("Header Files" FILES ${HEADER_FILES}) @@ -91,6 +93,8 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rotation/staticrotation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/scale/luascale.cpp ${CMAKE_CURRENT_SOURCE_DIR}/scale/staticscale.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/timeframe/timeframeinterval.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/timeframe/timeframeunion.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index e5a08a3f31..f3c0d11d0e 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -53,6 +53,8 @@ #include #include #include +#include +#include #include #include #include @@ -134,6 +136,12 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) { fScale->registerClass("LuaScale"); fScale->registerClass("StaticScale"); + auto fTimeFrame = FactoryManager::ref().factory(); + ghoul_assert(fTimeFrame, "Scale factory was not created"); + + fTimeFrame->registerClass("TimeFrameInterval"); + fTimeFrame->registerClass("TimeFrameUnion"); + auto fGeometry = FactoryManager::ref().factory(); ghoul_assert(fGeometry, "Model geometry factory was not created"); fGeometry->registerClass("MultiModelGeometry"); @@ -175,6 +183,9 @@ std::vector BaseModule::documentations() const { LuaTranslation::Documentation(), StaticTranslation::Documentation(), + TimeFrameInterval::Documentation(), + TimeFrameUnion::Documentation(), + modelgeometry::ModelGeometry::Documentation(), }; } diff --git a/modules/base/timeframe/timeframeinterval.cpp b/modules/base/timeframe/timeframeinterval.cpp new file mode 100644 index 0000000000..345b5785d0 --- /dev/null +++ b/modules/base/timeframe/timeframeinterval.cpp @@ -0,0 +1,160 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include + +namespace { + constexpr const openspace::properties::Property::PropertyInfo HasStartInfo = { + "HasStart", + "Has Start", + "If enabled, this TimeFrame will be inactive before the Start" + }; + + constexpr const openspace::properties::Property::PropertyInfo StartInfo = { + "Start", + "Start", + "Specifies the time when this TimeFrame becomes active" + }; + + constexpr const openspace::properties::Property::PropertyInfo HasEndInfo = { + "HasEnd", + "Has End", + "If enabled, this TimeFrame will be inactive after the End" + }; + + constexpr const openspace::properties::Property::PropertyInfo EndInfo = { + "End", + "End", + "Specifies the time when this TimeFrame becomes inactive" + }; +} // namespace + +namespace openspace { + +documentation::Documentation TimeFrameInterval::Documentation() { + using namespace openspace::documentation; + return { + "Time Frame Interval", + "base_time_frame_interval", + { + { + HasStartInfo.identifier, + new BoolVerifier, + Optional::Yes, + HasStartInfo.description + }, + { + StartInfo.identifier, + new OrVerifier{ + new DoubleVerifier, + new StringVerifier, + }, + Optional::Yes, + StartInfo.description + }, + { + HasEndInfo.identifier, + new BoolVerifier, + Optional::Yes, + HasEndInfo.description + }, + { + EndInfo.identifier, + new OrVerifier{ + new DoubleVerifier, + new StringVerifier, + }, + Optional::Yes, + EndInfo.description + }, + } + }; +} + +bool TimeFrameInterval::isActive(const Time& time) const { + if (_hasStart && time.j2000Seconds() < _start ) { + return false; + } + if (_hasEnd && time.j2000Seconds() >= _end ) { + return false; + } + return true; +} + +TimeFrameInterval::TimeFrameInterval() + : _hasStart(HasStartInfo, false) + , _start(StartInfo, 0, 0, 1E9) + , _hasEnd(HasEndInfo, false) + , _end(EndInfo, 0, 0, 1E9) +{ + addProperty(_hasStart); + addProperty(_start); + addProperty(_hasEnd); + addProperty(_end); +} + +TimeFrameInterval::TimeFrameInterval(const ghoul::Dictionary& dictionary) + : TimeFrame() + , _hasStart(HasStartInfo, false) + , _start(StartInfo, 0, 0, 1E9) + , _hasEnd(HasEndInfo, false) + , _end(EndInfo, 0, 0, 1E9) +{ + addProperty(_hasStart); + addProperty(_start); + addProperty(_hasEnd); + addProperty(_end); + + documentation::testSpecificationAndThrow(Documentation(), + dictionary, + "TimeFrameInterval"); + + if (dictionary.hasValue(StartInfo.identifier)) { + _start = SpiceManager::ref().ephemerisTimeFromDate( + dictionary.value(StartInfo.identifier) + ); + _hasStart = true; + } else if (dictionary.hasValue(StartInfo.identifier)) { + _start = dictionary.value(StartInfo.identifier); + _hasStart = true; + } + + if (dictionary.hasValue(EndInfo.identifier)) { + _end = SpiceManager::ref().ephemerisTimeFromDate( + dictionary.value(EndInfo.identifier) + ); + _hasEnd = true; + } + else if (dictionary.hasValue(EndInfo.identifier)) { + _end = dictionary.value(EndInfo.identifier); + _hasEnd = true; + } +} + +} // namespace openspace diff --git a/modules/base/timeframe/timeframeinterval.h b/modules/base/timeframe/timeframeinterval.h new file mode 100644 index 0000000000..375266c7e0 --- /dev/null +++ b/modules/base/timeframe/timeframeinterval.h @@ -0,0 +1,57 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_BASE___TIMEFRAMEINTERVAL___H__ +#define __OPENSPACE_MODULE_BASE___TIMEFRAMEINTERVAL___H__ + +#include + +#include +#include + +namespace openspace { + +class Time; + +namespace documentation { struct Documentation; } + +class TimeFrameInterval : public TimeFrame { +public: + TimeFrameInterval(); + TimeFrameInterval(const ghoul::Dictionary& dictionary); + bool isActive(const Time&) const override; + + static documentation::Documentation Documentation(); + +private: + properties::BoolProperty _hasStart; + properties::DoubleProperty _start; + + properties::BoolProperty _hasEnd; + properties::DoubleProperty _end; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___TIMEFRAMEINTERVAL___H__ diff --git a/modules/base/timeframe/timeframeunion.cpp b/modules/base/timeframe/timeframeunion.cpp new file mode 100644 index 0000000000..9b53041764 --- /dev/null +++ b/modules/base/timeframe/timeframeunion.cpp @@ -0,0 +1,101 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +namespace { + constexpr const openspace::properties::Property::PropertyInfo TimeFramesInfo = { + "TimeFrames", + "Time Frames", + "A vector of time frames to combine into one." + "The time frame is active when any of the contained time frames are, " + "but not in gaps between contained time frames." + }; +} // namespace + +namespace openspace { + +documentation::Documentation TimeFrameUnion::Documentation() { + using namespace openspace::documentation; + return { + "Time Frame Union", + "base_time_frame_union", + { + { + TimeFramesInfo.identifier, + new TableVerifier({ + { + "*", + new ReferencingVerifier("core_time_frame"), + Optional::Yes + } + }), + Optional::No, + TimeFramesInfo.description + }, + } + }; +} + +bool TimeFrameUnion::isActive(const Time& time) const { + for (const auto& tf : _timeFrames) { + if (tf->isActive(time)) { + return true; + } + } + return false; +} + +TimeFrameUnion::TimeFrameUnion() {} + +TimeFrameUnion::TimeFrameUnion(const ghoul::Dictionary& dictionary) + : TimeFrame() +{ + documentation::testSpecificationAndThrow(Documentation(), + dictionary, + "TimeFrameUnion"); + + ghoul::Dictionary frames = + dictionary.value(TimeFramesInfo.identifier); + + for (const std::string& k : frames.keys()) { + const ghoul::Dictionary& subDictionary = frames.value(k); + _timeFrames.push_back(TimeFrame::createFromDictionary(subDictionary)); + TimeFrame& subFrame = *_timeFrames.back(); + subFrame.setIdentifier(k); + subFrame.setGuiName(k); + subFrame.setDescription(k); + addPropertySubOwner(*_timeFrames.back()); + } + + +} + +} // namespace openspace diff --git a/modules/base/timeframe/timeframeunion.h b/modules/base/timeframe/timeframeunion.h new file mode 100644 index 0000000000..59fd402c67 --- /dev/null +++ b/modules/base/timeframe/timeframeunion.h @@ -0,0 +1,50 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_BASE___TIMEFRAMEUNION___H__ +#define __OPENSPACE_MODULE_BASE___TIMEFRAMEUNION___H__ + +#include + +namespace openspace { + +class Time; + +namespace documentation { struct Documentation; } + +class TimeFrameUnion : public TimeFrame { +public: + TimeFrameUnion(); + TimeFrameUnion(const ghoul::Dictionary& dictionary); + bool isActive(const Time&) const override; + + static documentation::Documentation Documentation(); + +private: + std::vector> _timeFrames; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___TIMEFRAMEUNION___H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 941295bef0..cf90fc4418 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -154,6 +154,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/scene/scenelicensewriter.cpp ${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode.cpp ${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode_doc.inl + ${OPENSPACE_BASE_DIR}/src/scene/timeframe.cpp ${OPENSPACE_BASE_DIR}/src/scripting/lualibrary.cpp ${OPENSPACE_BASE_DIR}/src/scripting/scriptengine.cpp ${OPENSPACE_BASE_DIR}/src/scripting/scriptengine_lua.inl @@ -332,6 +333,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/scene/scenelicense.h ${OPENSPACE_BASE_DIR}/include/openspace/scene/scenelicensewriter.h ${OPENSPACE_BASE_DIR}/include/openspace/scene/scenegraphnode.h + ${OPENSPACE_BASE_DIR}/include/openspace/scene/timeframe.h ${OPENSPACE_BASE_DIR}/include/openspace/scripting/lualibrary.h ${OPENSPACE_BASE_DIR}/include/openspace/scripting/scriptengine.h ${OPENSPACE_BASE_DIR}/include/openspace/scripting/scriptscheduler.h diff --git a/src/documentation/core_registration.cpp b/src/documentation/core_registration.cpp index f45004eb1c..b4264226bf 100644 --- a/src/documentation/core_registration.cpp +++ b/src/documentation/core_registration.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ void registerCoreClasses(documentation::DocumentationEngine& engine) { engine.addDocumentation(ScreenSpaceRenderable::Documentation()); engine.addDocumentation(TimeRange::Documentation()); engine.addDocumentation(Translation::Documentation()); + engine.addDocumentation(TimeFrame::Documentation()); } void registerCoreClasses(scripting::ScriptEngine& engine) { diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index e604c4c07e..9af9583576 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -202,6 +203,10 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName, std::make_unique>(), "Scale" ); + FactoryManager::ref().addFactory( + std::make_unique>(), + "TimeFrame" + ); FactoryManager::ref().addFactory( std::make_unique>(), "Task" diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index bb9688dbe8..4847c87372 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,8 @@ namespace { constexpr const char* KeyTransformTranslation = "Transform.Translation"; constexpr const char* KeyTransformRotation = "Transform.Rotation"; constexpr const char* KeyTransformScale = "Transform.Scale"; + + constexpr const char* KeyTimeFrame = "TimeFrame"; } // namespace namespace openspace { @@ -119,6 +122,24 @@ std::unique_ptr SceneGraphNode::createFromDictionary( LDEBUG(fmt::format("Successfully created scale for '{}'", result->identifier())); } + if (dictionary.hasKey(KeyTimeFrame)) { + ghoul::Dictionary timeFrameDictionary; + dictionary.getValue(KeyTimeFrame, timeFrameDictionary); + result->_timeFrame = TimeFrame::createFromDictionary(timeFrameDictionary); + if (result->_timeFrame == nullptr) { + LERROR(fmt::format( + "Failed to create time frame for SceneGraphNode '{}'", + result->identifier() + )); + return nullptr; + } + result->addPropertySubOwner(result->_timeFrame.get()); + LDEBUG(fmt::format( + "Successfully created time frame for '{}'", + result->identifier() + )); + } + // We initialize the renderable last as it probably has the most dependencies if (dictionary.hasValue(KeyRenderable)) { ghoul::Dictionary renderableDictionary; @@ -240,6 +261,10 @@ void SceneGraphNode::update(const UpdateData& data) { if (s != State::Initialized && _state != State::GLInitialized) { return; } + if (!isTimeFrameActive(data.time)) { + return; + } + if (_transform.translation) { if (data.doPerformanceMeasurement) { glFinish(); @@ -351,7 +376,9 @@ void SceneGraphNode::render(const RenderData& data, RendererTasks& tasks) { { _worldPositionCached, _worldRotationCached, _worldScaleCached } }; - //_performanceRecord.renderTime = 0; + if (!isTimeFrameActive(data.time)) { + return; + } bool visible = _renderable && _renderable->isVisible() && @@ -359,26 +386,23 @@ void SceneGraphNode::render(const RenderData& data, RendererTasks& tasks) { _renderable->isEnabled() && _renderable->matchesRenderBinMask(data.renderBinMask); - if (visible) { - if (data.doPerformanceMeasurement) { - glFinish(); - auto start = std::chrono::high_resolution_clock::now(); - - _renderable->render(newData, tasks); - - glFinish(); - auto end = std::chrono::high_resolution_clock::now(); - _performanceRecord.renderTime = (end - start).count(); - } - else { - _renderable->render(newData, tasks); - } + if (!visible) { + return; } - // evaluate all the children, tail-recursive function(?) + if (data.doPerformanceMeasurement) { + glFinish(); + auto start = std::chrono::high_resolution_clock::now(); - //for (SceneGraphNode* child : _children) - // child->render(newData); + _renderable->render(newData, tasks); + + glFinish(); + auto end = std::chrono::high_resolution_clock::now(); + _performanceRecord.renderTime = (end - start).count(); + } + else { + _renderable->render(newData, tasks); + } } void SceneGraphNode::setParent(SceneGraphNode& parent) { @@ -589,6 +613,20 @@ glm::dvec3 SceneGraphNode::calculateWorldPosition() const { } } +bool SceneGraphNode::isTimeFrameActive(const Time& time) const { + for (const auto& dep : _dependencies) { + if (!dep->isTimeFrameActive(time)) { + return false; + } + } + + if (_parent && !_parent->isTimeFrameActive(time)) { + return false; + } + + return !_timeFrame || _timeFrame->isActive(time); +} + glm::dmat3 SceneGraphNode::calculateWorldRotation() const { // recursive up the hierarchy if there are parents available if (_parent) { diff --git a/src/scene/scenegraphnode_doc.inl b/src/scene/scenegraphnode_doc.inl index 938d2322b2..d5dec95d3d 100644 --- a/src/scene/scenegraphnode_doc.inl +++ b/src/scene/scenegraphnode_doc.inl @@ -98,6 +98,12 @@ documentation::Documentation SceneGraphNode::Documentation() { "node and all of its children. There are only three possible values " "corresponding to a 'Translation', a 'Rotation', and a 'Scale'." }, + { + "TimeFrame", + new ReferencingVerifier("core_time_frame"), + Optional::Yes, + "Specifies the time frame for when this node should be active." + }, { "GUI", new TableVerifier({ diff --git a/src/scene/timeframe.cpp b/src/scene/timeframe.cpp new file mode 100644 index 0000000000..b1e8f30c66 --- /dev/null +++ b/src/scene/timeframe.cpp @@ -0,0 +1,78 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* KeyType = "Type"; +} // namespace + +namespace openspace { + +documentation::Documentation TimeFrame::Documentation() { + using namespace openspace::documentation; + + return { + "Time Frame", + "core_time_frame", + { + { + KeyType, + new StringAnnotationVerifier("Must name a valid TimeFrame type"), + Optional::No, + "The type of the time frame that is described in this element. " + "The available types of scaling depend on the configuration " + "of the application and can be written to disk on " + "application startup into the FactoryDocumentation." + } + } + }; +} + +std::unique_ptr TimeFrame::createFromDictionary(const ghoul::Dictionary& dictionary) { + documentation::testSpecificationAndThrow(Documentation(), dictionary, "TimeFrame"); + + const std::string timeFrameType = dictionary.value(KeyType); + + auto factory = FactoryManager::ref().factory(); + std::unique_ptr result = factory->create(timeFrameType, dictionary); + result->setIdentifier("TimeFrame"); + return result; +} + +TimeFrame::TimeFrame() : properties::PropertyOwner({ "TimeFrame" }) {} + +bool TimeFrame::initialize() { + return true; +} + +} // namespace openspace