From 46f005fa58b808e56450b6084ee9d429673206e2 Mon Sep 17 00:00:00 2001 From: Erik Broberg Date: Mon, 22 Aug 2016 11:53:59 -0400 Subject: [PATCH] Add Mission related classes. Add basic mission phase viz. Move TimeRange to separate file --- data/scene/osirisrex.scene | 3 +- .../osirisrex/osirisrex/osirisrex.mission | 30 +++ modules/newhorizons/CMakeLists.txt | 7 +- modules/newhorizons/util/imagesequencer.h | 2 + modules/newhorizons/util/missionmanager.cpp | 227 ++++++++++++++++++ modules/newhorizons/util/missionmanager.h | 135 +++++++++++ .../util/missionphasesequencer.cpp | 227 ++++++++++++++++++ modules/newhorizons/util/sequenceparser.h | 37 +-- modules/newhorizons/util/timerange.h | 71 ++++++ src/rendering/renderengine.cpp | 83 ++++--- 10 files changed, 756 insertions(+), 66 deletions(-) create mode 100644 data/scene/osirisrex/osirisrex/osirisrex.mission create mode 100644 modules/newhorizons/util/missionmanager.cpp create mode 100644 modules/newhorizons/util/missionmanager.h create mode 100644 modules/newhorizons/util/missionphasesequencer.cpp create mode 100644 modules/newhorizons/util/timerange.h diff --git a/data/scene/osirisrex.scene b/data/scene/osirisrex.scene index a8ef1810a1..70e8ae54f8 100644 --- a/data/scene/osirisrex.scene +++ b/data/scene/osirisrex.scene @@ -144,7 +144,7 @@ function preInitialization() -- openspace.time.setTime("2018-12-20T22:47:00.00") --openspace.time.setTime("2019-05-25T03:57:55.00") - openspace.time.setTime("2016-09-08T23:05:00.00") + openspace.time.setTime("2016-09-06T23:05:00.00") openspace.time.setDeltaTime(0) end @@ -169,6 +169,7 @@ function postInitialization() openspace.setPropertyValue("OsirisRex.renderable.modelrotation", {90.0, 0.0, 0.0}) openspace.printInfo("Done setting default values") + openspace.loadMission("${OPENSPACE_DATA}/scene/osirisrex/osirisrex/osirisrex.mission") openspace.resetCameraDirection() end diff --git a/data/scene/osirisrex/osirisrex/osirisrex.mission b/data/scene/osirisrex/osirisrex/osirisrex.mission new file mode 100644 index 0000000000..e9e2c3055b --- /dev/null +++ b/data/scene/osirisrex/osirisrex/osirisrex.mission @@ -0,0 +1,30 @@ +return { + Name = "OSIRIS-REx", + Phases = { + { + Name = "Pre Launch", + Phases = { + { + Name = "Planning", + StartTime = "2016 SEP 06 23:05:05", + EndTime = "2016 SEP 07 23:05:00", + }, + { + Name = "Pre check", + StartTime = "2016 SEP 07 23:05:05", + EndTime = "2016 SEP 08 23:05:00", + } + }, + }, + { + Name = "Launch", + StartTime = "2016 SEP 08 23:05:05", + EndTime = "2016 OCT 08 23:05:00", + }, + { + Name = "Here we go", + StartTime = "2016 OCT 08 23:05:05", + EndTime = "2016 NOV 08 23:05:00", + }, + } +} \ No newline at end of file diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index be3314becd..301ad5794b 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -24,7 +24,7 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) -set(HEADER_FILES +set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecrawlingline.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefov.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneprojection.h @@ -32,10 +32,12 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableshadowcylinder.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodelprojection.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/writeToTexture.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/timerange.h ${CMAKE_CURRENT_SOURCE_DIR}/util/decoder.h ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.h ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumenttimesparser.h ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/missionmanager.h ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.h ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.h ${CMAKE_CURRENT_SOURCE_DIR}/util/projectioncomponent.h @@ -43,6 +45,8 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/util/sequenceparser.h ${CMAKE_CURRENT_SOURCE_DIR}/util/targetdecoder.h ) + + source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES @@ -56,6 +60,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumenttimesparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/missionmanager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/projectioncomponent.cpp diff --git a/modules/newhorizons/util/imagesequencer.h b/modules/newhorizons/util/imagesequencer.h index 25c34a271e..39afeb0ac5 100644 --- a/modules/newhorizons/util/imagesequencer.h +++ b/modules/newhorizons/util/imagesequencer.h @@ -33,8 +33,10 @@ #include #include +#include #include + namespace openspace { /** * The ImageSequencer singleton main function is to manage the timekeeping and diff --git a/modules/newhorizons/util/missionmanager.cpp b/modules/newhorizons/util/missionmanager.cpp new file mode 100644 index 0000000000..174c03e6c5 --- /dev/null +++ b/modules/newhorizons/util/missionmanager.cpp @@ -0,0 +1,227 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include +#include +#include +#include + + + +namespace { + const std::string _loggerCat = "MissionPhaseSequencer"; +} + + + + + +namespace openspace { + +MissionPhase::MissionPhase(const ghoul::Dictionary& dict) { + const auto byPhaseStartTime = [](const MissionPhase& a, const MissionPhase& b)->bool{ + return a.timeRange().start < b.timeRange().start; + }; + + _name = dict.value("Name"); + ghoul::Dictionary childDicts; + if (dict.getValue("Phases", childDicts)) { + // This is a nested mission phase + size_t numSubPhases = childDicts.size(); + _subphases.resize(numSubPhases); + for (size_t i = 0; i < numSubPhases; ++i) { + std::string key = std::to_string(i + 1); + _subphases[i] = MissionPhase(childDicts.value(key)); + } + + std::stable_sort(_subphases.begin(), _subphases.end(), byPhaseStartTime); + + // The subphases will have a total time phases + TimeRange timeRangeSubPhases; + timeRangeSubPhases.start = _subphases[0].timeRange().start; + timeRangeSubPhases.end = _subphases.back().timeRange().end; + + // user may specify an overall time range. In that case expand this timerange. + TimeRange overallTimeRange; + try { + overallTimeRange = parseTimeRange(dict); + ghoul_assert(overallTimeRange.includes(timeRangeSubPhases), + "User specified time range must at least include its subphases'"); + _timeRange.include(overallTimeRange); + } + catch (...) { + // Its OK to not specify an overall time range, the time range for the + // subphases will simply be used. + _timeRange.include(timeRangeSubPhases); + } + } + else { + _timeRange = parseTimeRange(dict); + } +}; + +TimeRange MissionPhase::parseTimeRange(const ghoul::Dictionary& dict) { + std::string startTimeStr; + std::string endTimeStr; + bool success = true; + success &= dict.getValue("StartTime", startTimeStr); + success &= dict.getValue("EndTime", endTimeStr); + + if (!success) { + // Had to do this because ghoul::Dictionary::value<>(std::string key) throws + // uncatchable xtree error on my AMNH windwos machine/ eb) + throw "meh"; + } + // Parse to date + TimeRange timeRange; + timeRange.start = SpiceManager::ref().ephemerisTimeFromDate(startTimeStr); + timeRange.end = SpiceManager::ref().ephemerisTimeFromDate(endTimeStr); + return timeRange; +} + + + + + +Mission::Mission(std::string filepath) + : MissionPhase(readDictFromFile(filepath)) + , _filepath(filepath) +{ + +} + +ghoul::Dictionary Mission::readDictFromFile(std::string filepath) { + filepath = absPath(filepath); + LINFO("Reading mission phases fomr file: " << filepath); + if (!FileSys.fileExists(filepath)) + throw ghoul::FileNotFoundError(filepath, "Mission file path"); + + ghoul::Dictionary missionDict; + try { + ghoul::lua::loadDictionaryFromFile(filepath, missionDict); + return missionDict; + } + catch (ghoul::RuntimeError& e) { + LWARNING("Unable to load mission phases"); + LWARNING(e.message); + } + return {}; +} + + + + +MissionManager* MissionManager::_instance = nullptr; + +MissionManager& MissionManager::ref() { + assert(_instance != nullptr); + return *_instance; +} + +void MissionManager::initialize() { + assert(_instance == nullptr); + _instance = new MissionManager; + OsEng.scriptEngine().addLibrary(MissionManager::luaLibrary()); +} + +void MissionManager::deinitialize() { + delete _instance; + _instance = nullptr; +} + +void MissionManager::setCurrentMission(const std::string missionName) { + auto it = _missionMap.find(missionName); + if (it == _missionMap.end()) { + LWARNING("Mission with name \"" << missionName << "\" has not been loaded!"); + } + else { + _currentMissionIter = it; + } +} + +void MissionManager::loadMission(const std::string& filepath) { + Mission mission(filepath); + _missionMap[mission.name()] = mission; + if (_missionMap.size() == 1) { + setCurrentMission(mission.name()); + } +} + +const Mission& MissionManager::currentMission() { + if (_currentMissionIter == _missionMap.end()) { + LWARNING("No current mission has been specified. returning dummy mission"); + } + return _currentMissionIter->second; +} + +namespace luascriptfunctions { + int loadMission(lua_State* L) { + using ghoul::lua::luaTypeToString; + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + std::string missionFileName = luaL_checkstring(L, -1); + if (missionFileName.empty()) { + return luaL_error(L, "filepath string is empty"); + } + MissionManager::ref().loadMission(missionFileName); + } + + int setCurrentMission(lua_State* L) { + using ghoul::lua::luaTypeToString; + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + std::string missionName = luaL_checkstring(L, -1); + if (missionName.empty()) { + return luaL_error(L, "mission name string is empty"); + } + MissionManager::ref().setCurrentMission(missionName); + } +} // namespace luascriptfunction + +scripting::LuaLibrary MissionManager::luaLibrary() { + return{ + "", + { + { + "loadMission", + &luascriptfunctions::loadMission, + "string", + "Load mission phases from file" + }, + { + "setCurrentMission", + &luascriptfunctions::setCurrentMission, + "string", + "Set the currnet mission" + }, + } + }; +} + +} // namespace openspace diff --git a/modules/newhorizons/util/missionmanager.h b/modules/newhorizons/util/missionmanager.h new file mode 100644 index 0000000000..f8a6f0a11f --- /dev/null +++ b/modules/newhorizons/util/missionmanager.h @@ -0,0 +1,135 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __MISSIONPHASEEQUENCER_H__ +#define __MISSIONPHASEEQUENCER_H__ + + +#include +#include +#include +#include + +#include +#include + + +namespace openspace { + + +/** +* Used to represent a named period of time within a mission. Allows nested phases, i.e. +* phases within phases. Designed for WORM usage (Write Once, Read Multiple), and therefor +* has only accessors. +*/ +class MissionPhase { +public: + MissionPhase() {}; + MissionPhase(const ghoul::Dictionary& dict); + + const std::string& name() const { return _name; } + + const TimeRange timeRange() const { return _timeRange; }; + + /** + * Returns all subphases sorted by start time + */ + const std::vector& phases() const { return _subphases; } + + /** + * Returns the i:th subphase, sorted by start time + */ + const MissionPhase& phase(size_t i) const { return _subphases[i]; } + +protected: + + static TimeRange parseTimeRange(const ghoul::Dictionary& dict); + + std::string _name; + TimeRange _timeRange; + std::vector _subphases; +}; + + + +class Mission : public MissionPhase { +public: + Mission() {}; + Mission(std::string filename); + +private: + static ghoul::Dictionary readDictFromFile(std::string filepath); + std::string _filepath; +}; + +/** +* Singleton class keeping track of space missions. +*/ +class MissionManager { +public: + + static MissionManager& ref(); + + static void initialize(); + static void deinitialize(); + + /** + * Reads a mission from file and maps the mission name to the Mission object. If + * this is the first mission to be loaded, the mission will also be set as the + * current active mission. + */ + void loadMission(const std::string& fileName); + + /** + * Sets the mission with the name as the current mission. The current + * mission is what is return by `currentMission()`. + */ + void setCurrentMission(const std::string missionName); + + /** + * Returns the latest mission specified to `setCurrentMission()`. If no mission has + * been specified, the first mission loaded will be returned. If no mission has been + * loaded, a warning will be printed and a dummy mission will be returned. + */ + const Mission& currentMission(); + + +private: + + static scripting::LuaLibrary luaLibrary(); + static MissionManager* _instance; + + typedef std::unordered_map MissionMap; + MissionMap _missionMap; + MissionMap::iterator _currentMissionIter; + + // Singleton + MissionManager() : _currentMissionIter(_missionMap.end()) { }; +}; + +} // namespace openspace + + +#endif // __MISSIONPHASEEQUENCER_H__ + diff --git a/modules/newhorizons/util/missionphasesequencer.cpp b/modules/newhorizons/util/missionphasesequencer.cpp new file mode 100644 index 0000000000..174c03e6c5 --- /dev/null +++ b/modules/newhorizons/util/missionphasesequencer.cpp @@ -0,0 +1,227 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include +#include +#include +#include + + + +namespace { + const std::string _loggerCat = "MissionPhaseSequencer"; +} + + + + + +namespace openspace { + +MissionPhase::MissionPhase(const ghoul::Dictionary& dict) { + const auto byPhaseStartTime = [](const MissionPhase& a, const MissionPhase& b)->bool{ + return a.timeRange().start < b.timeRange().start; + }; + + _name = dict.value("Name"); + ghoul::Dictionary childDicts; + if (dict.getValue("Phases", childDicts)) { + // This is a nested mission phase + size_t numSubPhases = childDicts.size(); + _subphases.resize(numSubPhases); + for (size_t i = 0; i < numSubPhases; ++i) { + std::string key = std::to_string(i + 1); + _subphases[i] = MissionPhase(childDicts.value(key)); + } + + std::stable_sort(_subphases.begin(), _subphases.end(), byPhaseStartTime); + + // The subphases will have a total time phases + TimeRange timeRangeSubPhases; + timeRangeSubPhases.start = _subphases[0].timeRange().start; + timeRangeSubPhases.end = _subphases.back().timeRange().end; + + // user may specify an overall time range. In that case expand this timerange. + TimeRange overallTimeRange; + try { + overallTimeRange = parseTimeRange(dict); + ghoul_assert(overallTimeRange.includes(timeRangeSubPhases), + "User specified time range must at least include its subphases'"); + _timeRange.include(overallTimeRange); + } + catch (...) { + // Its OK to not specify an overall time range, the time range for the + // subphases will simply be used. + _timeRange.include(timeRangeSubPhases); + } + } + else { + _timeRange = parseTimeRange(dict); + } +}; + +TimeRange MissionPhase::parseTimeRange(const ghoul::Dictionary& dict) { + std::string startTimeStr; + std::string endTimeStr; + bool success = true; + success &= dict.getValue("StartTime", startTimeStr); + success &= dict.getValue("EndTime", endTimeStr); + + if (!success) { + // Had to do this because ghoul::Dictionary::value<>(std::string key) throws + // uncatchable xtree error on my AMNH windwos machine/ eb) + throw "meh"; + } + // Parse to date + TimeRange timeRange; + timeRange.start = SpiceManager::ref().ephemerisTimeFromDate(startTimeStr); + timeRange.end = SpiceManager::ref().ephemerisTimeFromDate(endTimeStr); + return timeRange; +} + + + + + +Mission::Mission(std::string filepath) + : MissionPhase(readDictFromFile(filepath)) + , _filepath(filepath) +{ + +} + +ghoul::Dictionary Mission::readDictFromFile(std::string filepath) { + filepath = absPath(filepath); + LINFO("Reading mission phases fomr file: " << filepath); + if (!FileSys.fileExists(filepath)) + throw ghoul::FileNotFoundError(filepath, "Mission file path"); + + ghoul::Dictionary missionDict; + try { + ghoul::lua::loadDictionaryFromFile(filepath, missionDict); + return missionDict; + } + catch (ghoul::RuntimeError& e) { + LWARNING("Unable to load mission phases"); + LWARNING(e.message); + } + return {}; +} + + + + +MissionManager* MissionManager::_instance = nullptr; + +MissionManager& MissionManager::ref() { + assert(_instance != nullptr); + return *_instance; +} + +void MissionManager::initialize() { + assert(_instance == nullptr); + _instance = new MissionManager; + OsEng.scriptEngine().addLibrary(MissionManager::luaLibrary()); +} + +void MissionManager::deinitialize() { + delete _instance; + _instance = nullptr; +} + +void MissionManager::setCurrentMission(const std::string missionName) { + auto it = _missionMap.find(missionName); + if (it == _missionMap.end()) { + LWARNING("Mission with name \"" << missionName << "\" has not been loaded!"); + } + else { + _currentMissionIter = it; + } +} + +void MissionManager::loadMission(const std::string& filepath) { + Mission mission(filepath); + _missionMap[mission.name()] = mission; + if (_missionMap.size() == 1) { + setCurrentMission(mission.name()); + } +} + +const Mission& MissionManager::currentMission() { + if (_currentMissionIter == _missionMap.end()) { + LWARNING("No current mission has been specified. returning dummy mission"); + } + return _currentMissionIter->second; +} + +namespace luascriptfunctions { + int loadMission(lua_State* L) { + using ghoul::lua::luaTypeToString; + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + std::string missionFileName = luaL_checkstring(L, -1); + if (missionFileName.empty()) { + return luaL_error(L, "filepath string is empty"); + } + MissionManager::ref().loadMission(missionFileName); + } + + int setCurrentMission(lua_State* L) { + using ghoul::lua::luaTypeToString; + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + std::string missionName = luaL_checkstring(L, -1); + if (missionName.empty()) { + return luaL_error(L, "mission name string is empty"); + } + MissionManager::ref().setCurrentMission(missionName); + } +} // namespace luascriptfunction + +scripting::LuaLibrary MissionManager::luaLibrary() { + return{ + "", + { + { + "loadMission", + &luascriptfunctions::loadMission, + "string", + "Load mission phases from file" + }, + { + "setCurrentMission", + &luascriptfunctions::setCurrentMission, + "string", + "Set the currnet mission" + }, + } + }; +} + +} // namespace openspace diff --git a/modules/newhorizons/util/sequenceparser.h b/modules/newhorizons/util/sequenceparser.h index 5a9bafa10f..7c715ca416 100644 --- a/modules/newhorizons/util/sequenceparser.h +++ b/modules/newhorizons/util/sequenceparser.h @@ -26,6 +26,7 @@ #define __SEQUENCEPARSER_H__ #include +#include #include #include @@ -35,42 +36,6 @@ namespace openspace { class Decoder; - -struct TimeRange { - - TimeRange() : start(DBL_MAX), end(-DBL_MAX) { }; - TimeRange(double startTime, double endTime) : start(startTime) , end(endTime) { }; - - void include(double val){ - if (start > val) start = val; - if (end < val) end = val; - }; - - void include(const TimeRange& other) { - if (other.start < start) start = other.start; - if (other.end > end) end = other.end; - } - - double duration() const { - return end - start; - } - - bool isDefined() const { - return start <= end; - } - - bool inRange(double min, double max){ - return (min >= start && max <= end); - } - - bool includes(double val) const { - return (start <= val && val <= end); - } - - double start; - double end; -}; - struct Image { TimeRange timeRange; std::string path; diff --git a/modules/newhorizons/util/timerange.h b/modules/newhorizons/util/timerange.h new file mode 100644 index 0000000000..20b2031327 --- /dev/null +++ b/modules/newhorizons/util/timerange.h @@ -0,0 +1,71 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2016 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ + +#ifndef __TIMERANGE_H__ +#define __TIMERANGE_H__ + +namespace openspace { + +struct TimeRange { + + TimeRange() : start(DBL_MAX), end(-DBL_MAX) { }; + TimeRange(double startTime, double endTime) : start(startTime) , end(endTime) { }; + + void include(double val){ + if (start > val) start = val; + if (end < val) end = val; + }; + + void include(const TimeRange& other) { + if (other.start < start) start = other.start; + if (other.end > end) end = other.end; + } + + double duration() const { + return end - start; + } + + bool isDefined() const { + return start <= end; + } + + bool inRange(double min, double max){ + return (min >= start && max <= end); + } + + bool includes(double val) const { + return (start <= val && val <= end); + } + + bool includes(const TimeRange& o) const { + return start <= o.start && o.end <= end; + } + + double start; + double end; +}; + +} // namespace openspace + +#endif //__TIMERANGE_H__ diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index ecf1e734cc..ffacbd360b 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -26,6 +26,7 @@ #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED #include +#include #endif #include @@ -150,6 +151,10 @@ bool RenderEngine::deinitialize() { screenspacerenderable->deinitialize(); } +#ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED + MissionManager::deinitialize(); +#endif + _sceneGraph->clearSceneGraph(); return true; } @@ -218,6 +223,10 @@ bool RenderEngine::initialize() { ghoul::io::TextureReader::ref().addReader(std::make_shared()); +#ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED + MissionManager::initialize(); +#endif + return true; } @@ -390,7 +399,7 @@ void RenderEngine::postSynchronizationPreDraw() { } for (auto screenspacerenderable : _screenSpaceRenderables) { - screenspacerenderable->update(); + screenspacerenderable->update(); } //Allow focus node to update camera (enables camera-following) //FIX LATER: THIS CAUSES MASTER NODE TO BE ONE FRAME AHEAD OF SLAVES @@ -1358,45 +1367,64 @@ void RenderEngine::renderInformation() { } } - struct MissionPhase { - std::string name; - TimeRange timeRange; - }; + const Mission& mission = MissionManager::ref().currentMission(); - std::vector missionPhases = { - { "test phase 1",{ 526647968.0, 526647968.0 + 60.0*15.0} }, - { "anotyer test phase (2)",{ 526647968.0 + 5000, 600000000.0} }, - { "The last phase - 3",{ 600000000.0, 700000000.0 } }, - }; - - if (missionPhases.size() > 0) { - glm::vec4 activeMissionColor(0.0, 0.6, 1.0, 1); + if (mission.phases().size() > 0) { glm::vec4 inactiveColor(0.3, 0.3, 0.3, 1); + glm::vec4 nextMissionColor(0.3, 0.4, 0.7, 1); + glm::vec4 activeMissionColor(0.3, 0.6, 1.0, 1); - RenderFontCr(*_fontInfo, - penPosition, - activeMissionColor, - "Mission Phases:" - ); - - for (const MissionPhase& missionPhase : missionPhases) { - if (missionPhase.timeRange.includes(currentTime)) { - double remaining = missionPhase.timeRange.end - currentTime; - float t = static_cast(1.0 - remaining / missionPhase.timeRange.duration()); + size_t nextMissionPhaseIndex = -1; + size_t currentMissionPhaseIndex = -1; + + for (size_t i = 0; i < mission.phases().size(); i++) { + if (mission.phase(i).timeRange().includes(currentTime)) { + RenderFontCr(*_fontInfo, penPosition, activeMissionColor, "Current Mission Phase Progress:" ); + double remaining = mission.phase(i).timeRange().end - currentTime; + float t = static_cast(1.0 - remaining / mission.phase(i).timeRange().duration()); std::string progress = progressToStr(25, t); RenderFontCr(*_fontInfo, penPosition, activeMissionColor, - "%s %s %.1f %%", - missionPhase.name.c_str(), progress.c_str(), t * 100 + "%s %.1f %%", + progress.c_str(), t * 100 + ); + currentMissionPhaseIndex = i; + break; + } + else if (mission.phase(i).timeRange().start > currentTime) { + RenderFontCr(*_fontInfo, penPosition, nextMissionColor, "Next Mission Phase:"); + if (i > 0) { + double remaining = mission.phase(i).timeRange().start - currentTime; + double duration = mission.phase(i).timeRange().start - mission.phase(i - 1).timeRange().end; + float t = static_cast(1.0 - remaining / duration); + std::string progress = progressToStr(25, t); + RenderFontCr(*_fontInfo, + penPosition, + nextMissionColor, + "%s %.1f %%", + progress.c_str(), t * 100 + ); + } + nextMissionPhaseIndex = i; + break; + } + } + + for (size_t i = 0; i < mission.phases().size(); i++) { + if (i == currentMissionPhaseIndex || i == nextMissionPhaseIndex) { + RenderFontCr(*_fontInfo, + penPosition, + currentMissionPhaseIndex != -1 ? activeMissionColor : nextMissionColor, + mission.phase(i).name().c_str() ); } else { - RenderFontCr(*_fontInfo, penPosition, inactiveColor, missionPhase.name.c_str()); + RenderFontCr(*_fontInfo, penPosition, inactiveColor, mission.phase(i).name().c_str()); } } RenderFontCr(*_fontInfo, penPosition, inactiveColor, " " ); - } + } double remaining = openspace::ImageSequencer::ref().getNextCaptureTime() - currentTime; float t = static_cast(1.0 - remaining / openspace::ImageSequencer::ref().getIntervalLength()); @@ -1409,7 +1437,6 @@ void RenderEngine::renderInformation() { glm::vec4 active(0.6, 1, 0.00, 1); glm::vec4 brigther_active(0.9, 1, 0.75, 1); - if (remaining > 0) { std::string progress = progressToStr(25, t);