diff --git a/apps/OpenSpace/ext/launcher/src/profile/timedialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/timedialog.cpp index e9f518bc81..efbcf4293c 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/timedialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/timedialog.cpp @@ -50,7 +50,7 @@ TimeDialog::TimeDialog(QWidget* parent, std::optional* _timeData = **_time; if (_timeData.type == Profile::Time::Type::Relative) { if (_timeData.value == "") { - _timeData.value = "now"; + _timeData.value = "0d"; } _relativeEdit->setSelection(0, _relativeEdit->text().length()); } @@ -60,7 +60,7 @@ TimeDialog::TimeDialog(QWidget* parent, std::optional* } else { _timeData.type = Profile::Time::Type::Relative; - _timeData.value = "now"; + _timeData.value = "0d"; } _initializedAsAbsolute = (_timeData.type == Profile::Time::Type::Absolute); enableAccordingToType(static_cast(_timeData.type)); @@ -114,7 +114,7 @@ void TimeDialog::enableAccordingToType(int idx) { if (comboIdx == Profile::Time::Type::Relative) { _relativeEdit->setText("Relative Time:"); if (_initializedAsAbsolute) { - _relativeEdit->setText("now"); + _relativeEdit->setText("0d"); } else { _relativeEdit->setText(QString::fromStdString(_timeData.value)); diff --git a/apps/OpenSpace/ext/sgct b/apps/OpenSpace/ext/sgct index d89724510f..3f2bbf3ebb 160000 --- a/apps/OpenSpace/ext/sgct +++ b/apps/OpenSpace/ext/sgct @@ -1 +1 @@ -Subproject commit d89724510f70a5b19dd93f481e4fb6493e67721c +Subproject commit 3f2bbf3ebb1e1ef23af25731c182a711b4102814 diff --git a/data/assets/actions/toggle_trail.asset b/data/assets/actions/toggle_trail.asset new file mode 100644 index 0000000000..d3c5848d6c --- /dev/null +++ b/data/assets/actions/toggle_trail.asset @@ -0,0 +1,105 @@ +local toggle_trail = { + Identifier = "os.toggle_trail", + Name = "Toggle Trail", + Command = [[ + local node + if is_declared("args") then + node = args.Node + else + node = openspace.navigation.getNavigationState().Anchor + end + + local trail + if openspace.hasSceneGraphNode(node .. "Trail") then + trail = node .. "Trail" + elseif openspace.hasSceneGraphNode(node .. "_trail") then + trail = node .. "_trail" + else + -- No trail found, so nothing more to do here + return + end + + local visibility + if is_declared("args") then + if args.Transition == "Approaching" then + visibility = false + elseif args.Transition == "Exiting" then + visibility = true + else + return + end + else + visibility = not openspace.getPropertyValue("Scene." .. trail .. ".Renderable.Enabled") + end + + openspace.setPropertyValueSingle("Scene." .. trail .. ".Renderable.Enabled", visibility) + ]], + Documentation = [[Toggles the visibility of the associated trail of a scene graph node. + This action takes optional arguments to 1) determine which trail to hide (as the + 'Node') and 2) the transition direction (as 'After' and 'Before').]], + GuiPath = "/Trails", + IsLocal = true +} +asset.export("toggle_trail", toggle_trail.Identifier) + +local hide_trail = { + Identifier = "os.hide_trail", + Name = "Hide Trail", + Command = [[ + local node + if is_declared("args") then + node = args.Node + else + node = openspace.navigation.getNavigationState().Anchor + end + + if openspace.hasSceneGraphNode(node .. "Trail") then + openspace.setPropertyValue("Scene." .. node .. "Trail.Renderable.Enabled", false) + elseif openspace.hasSceneGraphNode(node .. "_trail") then + openspace.setPropertyValue("Scene." .. node .. "_trail.Renderable.Enabled", false) + end + ]], + Documentation = [[Hides the associated trail of a scene graph node. This action takes an + optional argument to determine whose trail to hide. If no argument is provided, the + current focus node is used instead]], + GuiPath = "/Trails", + IsLocal = true +} +asset.export("hide_trail", hide_trail.Identifier) + +local show_trail = { + Identifier = "os.show_trail", + Name = "Show Trail", + Command = [[ + local node + if is_declared("args") then + node = args.Node + else + node = openspace.navigation.getNavigationState().Anchor + end + + if openspace.hasSceneGraphNode(node .. "Trail") then + openspace.setPropertyValue("Scene." .. node .. "Trail.Renderable.Enabled", true) + elseif openspace.hasSceneGraphNode(node .. "_trail") then + openspace.setPropertyValue("Scene." .. node .. "_trail.Renderable.Enabled", true) + end + ]], + Documentation = [[Shows the associated trail of a scene graph node. This action takes an + optional argument to determine whose trail to hide. If no argument is provided, the + current focus node is used instead]], + GuiPath = "/Trails", + IsLocal = true +} +asset.export("show_trail", show_trail.Identifier) + +asset.onInitialize(function() + openspace.action.registerAction(toggle_trail) + openspace.action.registerAction(show_trail) + openspace.action.registerAction(hide_trail) +end) + +asset.onDeinitialize(function() + openspace.action.removeAction(toggle_trail.Identifier) + openspace.action.removeAction(show_trail.Identifier) + openspace.action.removeAction(hide_trail.Identifier) +end) diff --git a/data/assets/events/toggle_trail.asset b/data/assets/events/toggle_trail.asset new file mode 100644 index 0000000000..2e397ed7d8 --- /dev/null +++ b/data/assets/events/toggle_trail.asset @@ -0,0 +1,27 @@ +local action = asset.require('actions/toggle_trail') + +asset.onInitialize(function() + openspace.event.registerEventAction( + "CameraFocusTransition", + action.show_trail, + { Transition = "Exiting" } + ); + openspace.event.registerEventAction( + "CameraFocusTransition", + action.hide_trail, + { Transition = "Approaching" } + ); +end) + +asset.onDeinitialize(function() + openspace.event.unregisterEventAction( + "CameraFocusTransition", + action.show_trail, + { Transition = "Exiting" } + ); + openspace.event.unregisterEventAction( + "CameraFocusTransition", + action.hide_trail, + { Transition = "Approaching" } + ); +end) diff --git a/data/assets/examples/approachevents.asset b/data/assets/examples/approachevents.asset new file mode 100644 index 0000000000..47ddc3281c --- /dev/null +++ b/data/assets/examples/approachevents.asset @@ -0,0 +1,68 @@ +local assetHelper = asset.require('util/asset_helper') +local sunTransforms = asset.require('scene/solarsystem/sun/transforms') +local transforms = asset.require('scene/solarsystem/planets/earth/transforms') + +local generic_action = { + Identifier = "os.example.generic", + Name = "Generic Example", + Command = [[ + openspace.printInfo("Node: " .. args.Node) + openspace.printInfo("Transition: " .. args.Transition) + ]], + Documentation = "Prints the argument information for camera transitions to the log", + GuiPath = "/Examples/Events", + IsLocal = true +} + +local model = asset.syncedResource({ + Name = "Animated Box", + Type = "HttpSynchronization", + Identifier = "animated_box", + Version = 1 +}) + +local obj = { + Identifier = "ExampleEventModel", + Parent = transforms.EarthCenter.Identifier, + Transform = { + Translation = { + Type = "StaticTranslation", + Position = { 0.0, 11E7, 0.0 } + } + }, + Renderable = { + Type = "RenderableModel", + GeometryFile = model .. "/BoxAnimated.glb", + ModelScale = 1.0, + LightSources = { + { + Type = "SceneGraphLightSource", + Identifier = "Sun", + Node = sunTransforms.SolarSystemBarycenter.Identifier, + Intensity = 1.0 + } + }, + PerformShading = true, + DisableFaceCulling = true + }, + InteractionSphere = 1000.0, + OnApproach = { "os.example.generic" }, + OnReach = { "os.example.generic" }, + OnRecede = { "os.example.generic" }, + OnExit = { "os.example.generic" }, + GUI = { + Name = "Example Event Model", + Path = "/Example", + Description = "", + } +} + +asset.onInitialize(function() + openspace.action.registerAction(generic_action) + openspace.addSceneGraphNode(obj) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(obj.Identifier) + openspace.action.removeAction(generic_action.Identifier) +end) diff --git a/ext/ghoul b/ext/ghoul index 4b64684acd..874300a089 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 4b64684acd728069e80948f9a4767b30baf268a3 +Subproject commit 874300a08923cf366a487825c93d6309d016fbcd diff --git a/include/openspace/documentation/verifier.inl b/include/openspace/documentation/verifier.inl index 7363b325ec..6b86ff0918 100644 --- a/include/openspace/documentation/verifier.inl +++ b/include/openspace/documentation/verifier.inl @@ -175,7 +175,7 @@ TestResult OperatorVerifier::operator()(const ghoul::Dictionary& di o.offender = key; o.reason = TestResult::Offense::Reason::Verification; r.offenses.push_back(o); - return r; + return r; } } else { @@ -290,7 +290,7 @@ TestResult NotInListVerifier::operator()(const ghoul::Dictionary& dict, o.offender = key; o.reason = TestResult::Offense::Reason::Verification; r.offenses.push_back(o); - return r; + return r; } } else { @@ -346,7 +346,7 @@ TestResult InRangeVerifier::operator()(const ghoul::Dictionary& dict, o.offender = key; o.reason = TestResult::Offense::Reason::WrongType; r.offenses.push_back(o); - return r; + return r; } } else { @@ -363,7 +363,7 @@ TestResult InRangeVerifier::operator()(const ghoul::Dictionary& dict, o.offender = key; o.reason = TestResult::Offense::Reason::Verification; r.offenses.push_back(o); - return r; + return r; } } else { @@ -405,7 +405,7 @@ TestResult NotInRangeVerifier::operator()(const ghoul::Dictionary& dict, o.offender = key; o.reason = TestResult::Offense::Reason::WrongType; r.offenses.push_back(o); - return r; + return r; } } else { @@ -419,7 +419,7 @@ TestResult NotInRangeVerifier::operator()(const ghoul::Dictionary& dict, o.offender = key; o.reason = TestResult::Offense::Reason::Verification; r.offenses.push_back(o); - return r; + return r; } else { return { true, {}, {} }; diff --git a/include/openspace/engine/configuration.h b/include/openspace/engine/configuration.h index 9748891b38..cb4303c5bb 100644 --- a/include/openspace/engine/configuration.h +++ b/include/openspace/engine/configuration.h @@ -89,6 +89,7 @@ struct Configuration { bool isCheckingOpenGLState = false; bool isLoggingOpenGLCalls = false; + bool isPrintingEvents = false; float shutdownCountdown = 0.f; diff --git a/include/openspace/engine/globals.h b/include/openspace/engine/globals.h index 0055117b3d..38b7d37915 100644 --- a/include/openspace/engine/globals.h +++ b/include/openspace/engine/globals.h @@ -36,6 +36,7 @@ namespace openspace { class Dashboard; class DeferredcasterManager; class DownloadManager; +class EventEngine; class LuaConsole; class MemoryManager; class MissionManager; @@ -74,6 +75,7 @@ inline ghoul::fontrendering::FontManager* fontManager; inline Dashboard* dashboard; inline DeferredcasterManager* deferredcasterManager; inline DownloadManager* downloadManager; +inline EventEngine* eventEngine; inline LuaConsole* luaConsole; inline MemoryManager* memoryManager; inline MissionManager* missionManager; diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index aaadc9cd18..5b48f40b2e 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -26,6 +26,7 @@ #define __OPENSPACE_CORE___OPENSPACEENGINE___H__ #include +#include #include #include #include @@ -117,6 +118,8 @@ private: std::string generateFilePath(std::string openspaceRelativePath); void resetPropertyChangeFlagsOfSubowners(openspace::properties::PropertyOwner* po); + properties::BoolProperty _printEvents; + std::unique_ptr _scene; std::unique_ptr _assetManager; bool _shouldAbortLoading = false; diff --git a/include/openspace/events/event.h b/include/openspace/events/event.h new file mode 100644 index 0000000000..201814da55 --- /dev/null +++ b/include/openspace/events/event.h @@ -0,0 +1,395 @@ +/***************************************************************************************** + * * + * 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. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_CORE___EVENT___H__ +#define __OPENSPACE_CORE___EVENT___H__ + +#include +#include +#include + +namespace openspace { + namespace properties { class Property; } + + class Camera; + class Layer; + class Profile; + class SceneGraphNode; + class ScreenSpaceRenderable; + class Time; +} // namespace openspace + +namespace openspace::events { + +struct Event { + // Steps to add a new event type: + // 1. Add a new entry into this enum list + // 2. Create a new subclass of Event in this file with a constructor that sets the + // Event's `type` to this new enum entry + // 3. In the cpp file, add a new `log` message that takes the new type as an argument + // and that prints something useful when the log is encountered and the user wants + // to see all events. + // 4. Add a new case into the logAllEvents function that handles the new enum entry + // 5. If the new event type has any parameters it takes in its constructor, go into + // the `toParameter` function and add a case label for the new enum type and + // return a dictionary with these parameters. This dictionary is passed to actions + // if they are triggered by events + // 6. Add the new enum entry into the `toString` and `fromString` methods + enum class Type { + SceneGraphNodeAdded, + SceneGraphNodeRemoved, + ParallelConnection, + ProfileLoadingFinished, + ApplicationShutdown, + ScreenSpaceRenderableAdded, + ScreenSpaceRenderableRemoved, + CameraFocusTransition, + TimeOfInterestReached, + MissionEventReached, + PlanetEclipsed, + InterpolationFinished, + FocusNodeChanged, + LayerAdded, + LayerRemoved, + SessionRecordingPlayback, + Custom + }; + constexpr explicit Event(Type type_) : type(type_) {} + + const Type type; + const Event* next = nullptr; +}; + +template +T* asType(Event* e) { + ghoul_assert(e->type == T::Type, "Wrong type requested, check 'isType'"); + return static_cast(e); +} + +template +bool isType(Event* e) { + return e->type == T::Type; +} + +std::string_view toString(Event::Type type); +Event::Type fromString(std::string_view str); + +ghoul::Dictionary toParameter(const Event& e); + +void logAllEvents(const Event* e); + +// +// Events +// + +/** + * This event is created whenever a new scene graph node is added to the system. By the + * time this event is signalled, the scene graph node has already been created and added + * to the scene. + * + * \param Node The identifier of the node that was added + */ +struct EventSceneGraphNodeAdded : public Event { + static const Type Type = Event::Type::SceneGraphNodeAdded; + + explicit EventSceneGraphNodeAdded(const SceneGraphNode* node_); + const tstring node; +}; + +/** + * This event is created whenever a scene graph node was removed. By the time this event + * is signalled, the scene graph node has already been removed. + * + * \param Node The identifier of that node that was removed + */ +struct EventSceneGraphNodeRemoved : public Event { + static const Type Type = Event::Type::SceneGraphNodeRemoved; + + explicit EventSceneGraphNodeRemoved(const SceneGraphNode* node_); + const tstring node; +}; + +/** + * This event is created whenever something in the parallel connection subsystem changes. + * The new state is sent as an argument with this event. + * + * \param State The new state of the parallel connection system; is one of `Established`, + * `Lost`, `HostshipGained`, or `HostshipLost` + */ +struct EventParallelConnection : public Event { + static const Type Type = Event::Type::ParallelConnection; + + enum class State : uint8_t { + Established, + Lost, + HostshipGained, + HostshipLost + }; + explicit EventParallelConnection(State state_); + State state; +}; + +/** + * This event is created when the loading of a profile is finished. This is emitted + * regardless of whether it is the initial profile, or any subsequent profile is loaded. + */ +struct EventProfileLoadingFinished : public Event { + static const Type Type = Event::Type::ProfileLoadingFinished; + + EventProfileLoadingFinished(); +}; + +/** + * This event is created whenever some information about the application shutdown sequence + * changes. This can either be that the seqeuence started, was aborted, or is finished, + * which means that OpenSpace is just about the shutdown. + * + * \param State The next state of the application shutdown sequence; is one of `Started`, + * `Aborted`, or `Finished` + */ +struct EventApplicationShutdown : public Event { + static const Type Type = Event::Type::ApplicationShutdown; + + enum class State : uint8_t { + Started, + Aborted, + Finished + }; + + explicit EventApplicationShutdown(State state_); + const State state; +}; + +/** + * This event is created when a new screenspace renderable has been created. By the time + * this event is craeted, the screenspace renderable is already registered and available. + * + * \param Renderable The identifier of the new screenspace renderable that was just added + * to the system + */ +struct EventScreenSpaceRenderableAdded : public Event { + static const Type Type = Event::Type::ScreenSpaceRenderableAdded; + + explicit EventScreenSpaceRenderableAdded(const ScreenSpaceRenderable* renderable_); + const tstring renderable; +}; + +/** + * This event is created when a screenspace renderable has been removed from the system. + * When this event is created, the screenspace renderable has already been removed and is + * no longer available + * + * \param Renderable The identifier of the screenspace renderable that was removed + */ +struct EventScreenSpaceRenderableRemoved : public Event { + static const Type Type = Event::Type::ScreenSpaceRenderableRemoved; + + explicit EventScreenSpaceRenderableRemoved(const ScreenSpaceRenderable* renderable_); + const tstring renderable; +}; + +/** + * This event is created when the camera transitions between different interaction sphere + * distances. Right now, only movement relative to camera's focus node is considered. + * Each scene graph node has an interaction sphere radius that serves as the reference + * distance for all spheres. +``` +Diagram of events for a camera moving from right-to-left. Interaction sphere is 'O' in +middle, and ')' are spherical boundaries. The approach factor, reach factor, and +interaction sphere radius are all taken from the current focus node. + +|<------------------->| Approach factor * Interaction sphere + |<------>| Reach Factor * Interaction sphere + +( ( O ) ) +^ ^ ^ ^ +Exiting Receding Reaching Approaching +``` + * + * \param Node The name of the node the camera is transitioning relative to. Currently is + * always the same as the camera's focus node + * \param Transition The transition type that the camera just finished; is one of + * `Approaching`, `Reaching`, `Receding`, or `Exiting` + */ +struct EventCameraFocusTransition : public Event { + static const Type Type = Event::Type::CameraFocusTransition; + + enum class Transition { + Approaching, + Reaching, + Receding, + Exiting + }; + + EventCameraFocusTransition(const Camera* camera_, const SceneGraphNode* node_, + Transition transition_); + + const Camera* camera = nullptr; + const tstring node; + const Transition transition; +}; + + +/** + * This event is created with a specific time of interest is reached. This event is + * currently unused. + */ +struct EventTimeOfInterestReached : public Event { + static const Type Type = Event::Type::TimeOfInterestReached; + + EventTimeOfInterestReached(const Time* time_, const Camera* camera_); + const Time* time = nullptr; + const Camera* camera = nullptr; +}; + + +/** + * This event is created when the end of a mission phase is reached. This event is + * currently unused. + */ +struct EventMissionEventReached : public Event { + static const Type Type = Event::Type::MissionEventReached; + + // Not sure which kind of parameters we want to pass here + EventMissionEventReached(); +}; + +/** + * This event is created when a planet is eclipsed by a moon or a different planet. This + * event is currently unused. + * + * \param Eclipsee The identifier of the scene graph node that is eclipsed by another + * object + * \param Eclipser The identifier of the scene graph node that is eclipsing the other + * object + */ +struct EventPlanetEclipsed : public Event { + static const Type Type = Event::Type::PlanetEclipsed; + + EventPlanetEclipsed(const SceneGraphNode* eclipsee_, const SceneGraphNode* eclipser_); + const tstring eclipsee; + const tstring eclipser; +}; + +/** + * This event is created when the interpolation of a property value is finished. If the + * interpolation time of a property change is 0s, this event is not fired + * + * \param Property The URI of the property whose interpolation has finished + */ +struct EventInterpolationFinished : public Event { + static const Type Type = Event::Type::InterpolationFinished; + + EventInterpolationFinished(const properties::Property* property_); + const tstring property; +}; + +/** + * This event is created when the camera changes focus nodes. Even if the camera position + * is interpolated, the node change happens instantaneously and the event is fired at the + * same time. + * + * \param OldNode The identifier of the scene graph node which was the old focus node + * \param NewNode The identifier of the scene graph node that is the new focus node + */ +struct EventFocusNodeChanged : public Event { + static const Type Type = Event::Type::FocusNodeChanged; + + EventFocusNodeChanged(const SceneGraphNode* oldNode_, const SceneGraphNode* newNode_); + const tstring oldNode; + const tstring newNode; +}; + +/** + * This event is created when a layer is added to to a globe. + * + * \param Globe The identifier of the globe to which the layer is added + * \param Group The identifier of the layer group to which the layer is added + * \param Layer The identifier of the layer that was added + */ +struct EventLayerAdded : public Event { + static const Type Type = Event::Type::LayerAdded; + + explicit EventLayerAdded(std::string_view node_, std::string_view layerGroup_, + std::string_view layer_); + const tstring node; + const tstring layerGroup; + const tstring layer; +}; + +/** + * This event is created when a layer is removed from a globe. + * + * \param Globe The identifier of the globe from which the layer is removed + * \param Group The identifier of the layer group from which the layer is removed + * \param Layer The identifier of the layer that was removed + */ +struct EventLayerRemoved : public Event { + static const Type Type = Event::Type::LayerRemoved; + + explicit EventLayerRemoved(std::string_view node_, std::string_view layerGroup_, + std::string_view layer_); + const tstring node; + const tstring layerGroup; + const tstring layer; +}; + +/** + * This event is created when something regarding a session recording playback changes. + * The event contains information about the new state of the session recording subsystem. + * + * \param State The new state of the session recording; one of `Started`, `Paused`, + * `Resumed`, `Finished` + */ +struct EventSessionRecordingPlayback : public Event { + static const Type Type = Event::Type::SessionRecordingPlayback; + + enum class State { + Started, + Paused, + Resumed, + Finished + }; + + EventSessionRecordingPlayback(State state_); + const State state; +}; + +/** + * A custom event type that can be used in a pinch when no explicit event type is + * available. This should only be used in special circumstances and it should be + * transitioned to a specific event type, if it is deemed to be useful. + */ +struct CustomEvent : public Event { + static const Type Type = Event::Type::Custom; + + CustomEvent(std::string_view subtype_, const void* payload_); + + const tstring subtype; + const void* payload = nullptr; +}; + +} // namespace openspace::events + +#endif // __OPENSPACE_CORE___EVENT___H__ diff --git a/include/openspace/events/eventengine.h b/include/openspace/events/eventengine.h new file mode 100644 index 0000000000..4e98175572 --- /dev/null +++ b/include/openspace/events/eventengine.h @@ -0,0 +1,125 @@ +/***************************************************************************************** + * * + * 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. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_CORE___EVENTENGINE___H__ +#define __OPENSPACE_CORE___EVENTENGINE___H__ + +#include +#include +#include +#include + +namespace openspace { + +namespace events { struct Event; } + +class EventEngine { +public: + /** + * This function returns the first event stored in the EventEngine, or \c nullptr if + * no event exists. To navigate the full list of events, you can access the returned + * Event's next function. If the end of the list is reached, the next pointer will be + * a nullptr + * + * \return The first event stored in the EventEngine or nullptr if no event exists + */ + events::Event* firstEvent() const; + + /** + * Publish a new event of type T by providing optional arguments Args to the Event's + * constructor. An example of usage is + * engine.publishEvent("a", 2.0); which would call the + * constructor of \c MyEvent with a const char* and \c double parameter. + * + * \param args The arguments that are passed to the constructor of T + * \tparam T The subclass of Event that is to be published + */ + template + void publishEvent(Args&&... args); + + /** + * This function cleans up the memory for all published events.After this function + * has been called, no previously published events are valid any longer. This means + * that pointers retrieved from events before this call must be kept beyond this call. + */ + void postFrameCleanup(); + + /** + * Registers a new action for a specific event type. + * + * \param type The type for which a new action is registered + * \param actionIdentifier The identifier of the action that will be triggered the + * identifier must not exist at this moment, but must exist by the time the + * event is encountered next + * \param filter If the filter is provided, it describes the event parameters that are + * checked and only if an event passes the filter, the corresponding action is + * triggered + */ + void registerEventAction(events::Event::Type type, std::string identifier, + std::optional filter = std::nullopt); + + /** + * Removing registration for a type/action combination. + * + * \param type The type of the action that should be unregistered + * \param actionIdentifier The identifier of the action that should be unregistered + * \param filter The optional filter applied to the event-action combination + */ + void unregisterEventAction(events::Event::Type type, + const std::string& identifier, + std::optional filter = std::nullopt); + + /** + * Triggers all actions that are registered for events that are in the current event + * queue + */ + void triggerActions() const; + + static scripting::LuaLibrary luaLibrary(); + +private: + /// The storage space in which Events are stored + ghoul::MemoryPool<4096> _memory; + /// The first event in the chain of events stored in the memory pool + events::Event* _firstEvent = nullptr; + /// The last event in the chain of events stored in the memory pool + events::Event* _lastEvent = nullptr; + + struct ActionInfo { + std::string action; + std::optional filter; + }; + std::unordered_map> _eventActions; + +#ifdef _DEBUG + /// Stores the total number of events during this frame for debugging purposes + static uint64_t nEvents; +#endif // _DEBUG +}; + +} // namespace openspace + +#include "eventengine.inl" + +#endif // __OPENSPACE_CORE___EVENTENGINE___H__ diff --git a/include/openspace/events/eventengine.inl b/include/openspace/events/eventengine.inl new file mode 100644 index 0000000000..019540b48b --- /dev/null +++ b/include/openspace/events/eventengine.inl @@ -0,0 +1,51 @@ +/***************************************************************************************** + * * + * 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 + +namespace openspace { + +template +void EventEngine::publishEvent(Args&&... args) { + static_assert( + std::is_base_of::value, + "T must be a subclass of Event" + ); + + T* e = _memory.alloc(args...); + if (!_firstEvent) { + _firstEvent = e; + _lastEvent = e; + } + else { + _lastEvent->next = e; + _lastEvent = e; + } + +#ifdef _DEBUG + nEvents++; +#endif // _DEBUG +} + +} // namespace openspace diff --git a/include/openspace/navigation/navigationhandler.h b/include/openspace/navigation/navigationhandler.h index b25a14dfe5..8738c04caa 100644 --- a/include/openspace/navigation/navigationhandler.h +++ b/include/openspace/navigation/navigationhandler.h @@ -140,6 +140,7 @@ public: private: void applyNavigationState(const NavigationState& ns); + void updateCameraTransitions(); bool _playbackModeEnabled = false; @@ -147,6 +148,10 @@ private: Camera* _camera = nullptr; std::function _playbackEndCallback; + inline static const double InteractionHystersis = 0.0125; + bool _inAnchorApproachSphere = false; + bool _inAnchorReachSphere = false; + OrbitalNavigator _orbitalNavigator; KeyframeNavigator _keyframeNavigator; PathNavigator _pathNavigator; diff --git a/include/openspace/navigation/orbitalnavigator.h b/include/openspace/navigation/orbitalnavigator.h index b2b6c8556e..c964e1eef9 100644 --- a/include/openspace/navigation/orbitalnavigator.h +++ b/include/openspace/navigation/orbitalnavigator.h @@ -371,10 +371,10 @@ private: /** * Orbit the current anchor node, in a right-bound orbit, by updating the position - * and global rotation of the camera. - * + * and global rotation of the camera. + * * Used for IdleBehavior::Behavior::Orbit - * + * * \param deltaTime The time step to use for the motion. Controls the rotation angle * \param position The position of the camera. Will be changed by the function * \param globalRotation The camera's global rotation. Will be changed by the function @@ -384,15 +384,15 @@ private: glm::dquat& globalRotation, double speedScale); /** - * Orbit the current anchor node, by adding a rotation around the given axis. For - * example, when the axis is the north vector, the camera will stay on the current - * latitude band. Note that this creates a rolling motion if the camera's forward - * vector coincides with the axis, and should be used with care. - * + * Orbit the current anchor node, by adding a rotation around the given axis. For + * example, when the axis is the north vector, the camera will stay on the current + * latitude band. Note that this creates a rolling motion if the camera's forward + * vector coincides with the axis, and should be used with care. + * * Used for: * IdleBehavior::Behavior::OrbitAtConstantLat ( axis = north = z-axis ) and * IdleBehavior::Behavior::OrbitAroundUp ( axis = up = y-axis ) - * + * * \param axis The axis to arbit around, given in model coordinates of the anchor * \param deltaTime The time step to use for the motion. Controls the rotation angle * \param position The position of the camera. Will be changed by the function diff --git a/include/openspace/navigation/path.h b/include/openspace/navigation/path.h index 7a1a33bc3e..baec1ad261 100644 --- a/include/openspace/navigation/path.h +++ b/include/openspace/navigation/path.h @@ -31,8 +31,8 @@ #include #include -namespace openspace { - struct CameraPose; +namespace openspace { + struct CameraPose; } // namespace openspace namespace openspace::interaction { @@ -44,10 +44,10 @@ public: Linear, ZoomOutOverview, AvoidCollisionWithLookAt // @TODO (2021-08-13, emmbr) This type right now leads - // to rapid rotations, but is useful in specific - // scenarios, e.g. close to surfaces. Later we want to + // to rapid rotations, but is useful in specific + // scenarios, e.g. close to surfaces. Later we want to // remove it, and create a curve type that looks nicely - // at the targets when moving, avoids collisions and + // at the targets when moving, avoids collisions and // doesn't introduce sudden large changes in rotation }; @@ -63,21 +63,21 @@ public: double pathLength() const; /** - * Return a vector of positions corresponding to the control points of the path's + * Return a vector of positions corresponding to the control points of the path's * spline curve */ std::vector controlPoints() const; /** * Take a step along the current path, corresponding to the delta time step \p dt, and - * return the resulting camera pose. The \p speedScale is a factor that will be + * return the resulting camera pose. The \p speedScale is a factor that will be * multiplied with the traversal speed */ CameraPose traversePath(double dt, float speedScale = 1.f); /** - * Return the identifer of the node that is the current appropriate anchor node, of - * the start and end waypoint's reference node. Dtermined based on how far along the + * Return the identifer of the node that is the current appropriate anchor node, of + * the start and end waypoint's reference node. Dtermined based on how far along the * path we have traveled */ std::string currentAnchor() const; @@ -94,7 +94,7 @@ public: private: /** - * Interpolate between the paths start and end rotation using the approach that + * Interpolate between the paths start and end rotation using the approach that * corresponds to the path's curve type. The interpolation parameter \p t is the * same as for the position interpolation, i.e. the relative traveled in distance * along the path, in [0, 1] @@ -107,14 +107,14 @@ private: glm::dquat easedSlerpRotation(double t) const; /** - * Compute the interpolated rotation quaternion using an approach that first - * interpolates to look at the start node, and then the end node, before + * Compute the interpolated rotation quaternion using an approach that first + * interpolates to look at the start node, and then the end node, before * interpolating to the end rotation */ glm::dquat lookAtTargetsRotation(double t) const; /** - * Evaluate the current traversal speed along the path, based on the currently + * Evaluate the current traversal speed along the path, based on the currently * traveled distance. The final speed will be scaled to match the desired duration * for the path (which might have been specified by the user) */ @@ -134,9 +134,9 @@ private: }; -// Create a path of the given type based on an instruction given as a dictionary. -// See top of cpp file for documentation on keys and values for the dictionary. -// Returns the created path. +// Create a path of the given type based on an instruction given as a dictionary. +// See top of cpp file for documentation on keys and values for the dictionary. +// Returns the created path. Path createPathFromDictionary(const ghoul::Dictionary& dictionary, Path::Type type); } // namespace openspace::interaction diff --git a/include/openspace/navigation/pathcurve.h b/include/openspace/navigation/pathcurve.h index e00da37583..ed0a4c5991 100644 --- a/include/openspace/navigation/pathcurve.h +++ b/include/openspace/navigation/pathcurve.h @@ -46,9 +46,9 @@ public: glm::dvec3 positionAt(double relativeDistance) const; /** - * Get the intorlatied position along the spline, based on the given curve parameter - * u in range [0, 1]. A curve parameter of 0 returns the start position and 1 the end - * position. Note that u does not correspond to the relatively traveled distance. + * Get the intorlatied position along the spline, based on the given curve parameter + * u in range [0, 1]. A curve parameter of 0 returns the start position and 1 the end + * position. Note that u does not correspond to the relatively traveled distance. */ virtual glm::dvec3 interpolate(double u) const; diff --git a/include/openspace/navigation/pathcurves/avoidcollisioncurve.h b/include/openspace/navigation/pathcurves/avoidcollisioncurve.h index 5cd95ddd5a..b48a0529a7 100644 --- a/include/openspace/navigation/pathcurves/avoidcollisioncurve.h +++ b/include/openspace/navigation/pathcurves/avoidcollisioncurve.h @@ -45,4 +45,4 @@ private: } // namespace openspace::interaction -#endif // __OPENSPACE_MODULE_AUTONAVIGATION___AVOIDCOLLISIONCURVE___H__ +#endif // __OPENSPACE_CORE___AVOIDCOLLISIONCURVE___H__ diff --git a/include/openspace/navigation/waypoint.h b/include/openspace/navigation/waypoint.h index 79506318e5..09970bb261 100644 --- a/include/openspace/navigation/waypoint.h +++ b/include/openspace/navigation/waypoint.h @@ -53,7 +53,8 @@ public: private: CameraPose _pose; std::string _nodeIdentifier; - double _validBoundingSphere = 0.0; // to be able to handle nodes with faulty bounding spheres + // to be able to handle nodes with faulty bounding spheres + double _validBoundingSphere = 0.0; }; } // namespace openspace::interaction diff --git a/include/openspace/properties/selectionproperty.h b/include/openspace/properties/selectionproperty.h index b17f580a52..f8d4b59ba8 100644 --- a/include/openspace/properties/selectionproperty.h +++ b/include/openspace/properties/selectionproperty.h @@ -114,7 +114,8 @@ public: using TemplateProperty>::operator=; protected: - std::set fromLuaConversion(lua_State* state, bool& success) const override; + std::set fromLuaConversion(lua_State* state, + bool& success) const override; void toLuaConversion(lua_State* state) const override; diff --git a/include/openspace/scene/rotation.h b/include/openspace/scene/rotation.h index 28b2ec8f5c..531036a558 100644 --- a/include/openspace/scene/rotation.h +++ b/include/openspace/scene/rotation.h @@ -51,7 +51,7 @@ public: const glm::dmat3& matrix() const; virtual glm::dmat3 matrix(const UpdateData& time) const = 0; - void update(const UpdateData& data); + virtual void update(const UpdateData& data); static documentation::Documentation Documentation(); diff --git a/include/openspace/scene/scene.h b/include/openspace/scene/scene.h index e199f1f425..4ebabe66f2 100644 --- a/include/openspace/scene/scene.h +++ b/include/openspace/scene/scene.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,15 @@ namespace openspace { namespace documentation { struct Documentation; } namespace scripting { struct LuaLibrary; } +enum class PropertyValueType { + Boolean = 0, + Float, + String, + Table, + Nil +}; +using ProfilePropertyLua = std::variant; + class SceneInitializer; // Notifications: @@ -246,6 +256,59 @@ public: void setPropertiesFromProfile(const Profile& p); private: + /** + * Accepts string version of a property value from a profile, converts it to the + * appropriate type, and then pushes the value onto the lua state. + * + * \param L the lua state to push value to + * \param value string representation of the value with which to set property + */ + void propertyPushProfileValueToLua(ghoul::lua::LuaState& L, const std::string& value); + + /** + * Accepts string version of a property value from a profile, and processes it + * according to the data type of the value + * + * \param L the lua state to (eventually) push to + * \param value string representation of the value with which to set property + * \param didPushToLua Bool reference that represents the lua push state at the end + * of this function call. This will be set to true if the value (e.g. table) + * has already been pushed to the lua stack + * \return The ProfilePropertyLua variant type translated from string representation + */ + ProfilePropertyLua propertyProcessValue(ghoul::lua::LuaState& L, + const std::string& value); + + /** + * Accepts string version of a property value from a profile, and returns the + * supported data types that can be pushed to a lua state. Currently, the full + * range of possible lua values is not supported. + * + * \param value string representation of the value with which to set property + */ + PropertyValueType propertyValueType(const std::string& value); + + /** + * Accepts string version of a property value from a profile, and adds it to a vector + * which will later be used to push as a lua table containing values of type T + * + * \param L the lua state to (eventually) push to + * \param value string representation of the value with which to set property + * \param table the std::vector container which has elements of type T for a lua table + */ + template + void processPropertyValueTableEntries(ghoul::lua::LuaState& L, + const std::string& value, std::vector& table); + + /** + * Handles a lua table entry, creating a vector of the correct variable type based + * on the profile string, and pushes this vector to the lua stack. + * + * \param L the lua state to (eventually) push to + * \param value string representation of the value with which to set property + */ + void handlePropertyLuaTableEntry(ghoul::lua::LuaState& L, const std::string& value); + /** * Update dependencies. */ @@ -260,8 +323,9 @@ private: bool _dirtyNodeRegistry = false; SceneGraphNode _rootDummy; std::unique_ptr _initializer; - + std::string _profilePropertyName; std::vector _interestingTimes; + bool _valueIsTable = false; std::mutex _programUpdateLock; std::set _programsToUpdate; @@ -279,16 +343,6 @@ private: ghoul::MemoryPool<4096> _memoryPool; }; -/** - * Accepts string version of a property value from a profile, converts it to the - * appropriate type, and then pushes the value onto the lua state. - * - * \param L the lua state to push value to - * \param value string representation of the value with which to set property - */ -void propertyPushValueFromProfileToLuaState(ghoul::lua::LuaState& L, - const std::string& value); - } // namespace openspace #endif // __OPENSPACE_CORE___SCENE___H__ diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index d83fda6315..48aae21f1c 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -128,9 +128,17 @@ public: SceneGraphNode* parent() const; std::vector children() const; + const std::vector& onApproachAction() const; + const std::vector& onReachAction() const; + const std::vector& onRecedeAction() const; + const std::vector& onExitAction() const; + double boundingSphere() const; double interactionSphere() const; + double reachFactor() const; + double approachFactor() const; + SceneGraphNode* childNode(const std::string& identifier); const Renderable* renderable() const; @@ -155,6 +163,11 @@ private: std::vector _dependentNodes; Scene* _scene = nullptr; + std::vector _onApproachAction; + std::vector _onReachAction; + std::vector _onRecedeAction; + std::vector _onExitAction; + // If this value is 'true' GUIs are asked to hide this node from collections, as it // might be a node that is not very interesting (for example barycenters) properties::BoolProperty _guiHidden; @@ -183,6 +196,8 @@ private: properties::DoubleProperty _boundingSphere; properties::DoubleProperty _interactionSphere; + properties::DoubleProperty _approachFactor; + properties::DoubleProperty _reachFactor; properties::BoolProperty _computeScreenSpaceValues; properties::IVec2Property _screenSpacePosition; properties::BoolProperty _screenVisibility; diff --git a/include/openspace/scene/translation.h b/include/openspace/scene/translation.h index 074b90bbb2..2753456f3e 100644 --- a/include/openspace/scene/translation.h +++ b/include/openspace/scene/translation.h @@ -50,8 +50,8 @@ public: virtual ~Translation() = default; virtual bool initialize(); + virtual void update(const UpdateData& data); glm::dvec3 position() const; - void update(const UpdateData& data); virtual glm::dvec3 position(const UpdateData& data) const = 0; diff --git a/include/openspace/util/distanceconversion.h b/include/openspace/util/distanceconversion.h index 252948c720..1c1176c5f0 100644 --- a/include/openspace/util/distanceconversion.h +++ b/include/openspace/util/distanceconversion.h @@ -357,7 +357,9 @@ constexpr double convertUnit(DistanceUnit fromUnit, DistanceUnit toUnit) { return convertMeters(toMeter(fromUnit), toUnit); } -constexpr double convertDistance(double distance, DistanceUnit fromUnit, DistanceUnit toUnit) { +constexpr double convertDistance(double distance, DistanceUnit fromUnit, + DistanceUnit toUnit) +{ return distance * convertUnit(fromUnit, toUnit); } diff --git a/include/openspace/util/memorymanager.h b/include/openspace/util/memorymanager.h index e6f8771d9e..bf8121a342 100644 --- a/include/openspace/util/memorymanager.h +++ b/include/openspace/util/memorymanager.h @@ -31,11 +31,11 @@ namespace openspace { class MemoryManager { public: - ghoul::MemoryPool<8 * 1024 * 1024, false> PersistentMemory; + ghoul::MemoryPool<8 * 1024 * 1024> PersistentMemory; // This should be replaced with a std::pmr::memory_resource wrapper around our own // Memory pool so that we can get a high-water mark out of it - ghoul::MemoryPool<100 * 4096, false> TemporaryMemory; + ghoul::MemoryPool<100 * 4096, false, true> TemporaryMemory; }; } // namespace openspace diff --git a/include/openspace/util/tstring.h b/include/openspace/util/tstring.h new file mode 100644 index 0000000000..01390c49fd --- /dev/null +++ b/include/openspace/util/tstring.h @@ -0,0 +1,69 @@ +/***************************************************************************************** + * * + * 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. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_CORE___TSTRING___H__ +#define __OPENSPACE_CORE___TSTRING___H__ + +#include +#include + +namespace openspace { + +/** + * This string is a temporary string that is generated using the temporary memory + * storage. This means that under no circumstances must an instance of a tstring be kept + * across frame boundaries as the temporary storage is reset at between frames. In + * exchange, the allocation of these objects is extreme fast and with barely any overhead + * associated with it. The memory accessed through a tstring object shall never be + * released manually. + */ +using tstring = std::string_view; + +/** + * Allocate and create a temporary string from the passed std::string. + * + * \param str The string to be copied into a newly allocated tstring + * \return The copy of the str as a temporary string + */ +tstring temporaryString(const std::string& str); + +/** + * Allocate and create a temporary string from the passed std::string_view. + * + * \param str The string to be copied into a newly allocated tstring + * \return The copy of the str as a temporary string + */ +tstring temporaryString(std::string_view str); + +/** + * Allocate and create a temporary string from the passed char array. + * + * \param str The string to be copied into a newly allocated tstring + * \return The copy of the str as a temporary string + */ +tstring temporaryString(const char str[]); + +} // namespace openspace + +#endif // __OPENSPACE_CORE___TSTRING___H__ diff --git a/modules/atmosphere/rendering/atmospheredeferredcaster.cpp b/modules/atmosphere/rendering/atmospheredeferredcaster.cpp index 8bc8f1061c..dc29181555 100644 --- a/modules/atmosphere/rendering/atmospheredeferredcaster.cpp +++ b/modules/atmosphere/rendering/atmospheredeferredcaster.cpp @@ -498,7 +498,7 @@ void AtmosphereDeferredcaster::setParameters(float atmosphereRadius, float plane float averageGroundReflectance, float groundRadianceEmission, float rayleighHeightScale, bool enableOzone, - float ozoneHeightScale, float mieHeightScale, + float ozoneHeightScale, float mieHeightScale, float miePhaseConstant, float sunRadiance, glm::vec3 rayScatteringCoefficients, glm::vec3 ozoneExtinctionCoefficients, @@ -529,7 +529,7 @@ void AtmosphereDeferredcaster::setHardShadows(bool enabled) { void AtmosphereDeferredcaster::calculateTransmittance() { ZoneScoped - + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, @@ -566,7 +566,7 @@ void AtmosphereDeferredcaster::calculateTransmittance() { GLuint AtmosphereDeferredcaster::calculateDeltaE() { ZoneScoped - + GLuint deltaE = createTexture(_deltaETableSize, "DeltaE"); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaE, 0); glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y); @@ -595,7 +595,7 @@ GLuint AtmosphereDeferredcaster::calculateDeltaE() { std::pair AtmosphereDeferredcaster::calculateDeltaS() { ZoneScoped - + GLuint deltaSRayleigh = createTexture(_textureSize, "DeltaS Rayleigh", 3); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaSRayleigh, 0); GLuint deltaSMie = createTexture(_textureSize, "DeltaS Mie", 3); @@ -649,7 +649,7 @@ std::pair AtmosphereDeferredcaster::calculateDeltaS() { void AtmosphereDeferredcaster::calculateIrradiance() { ZoneScoped - + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, @@ -678,7 +678,7 @@ void AtmosphereDeferredcaster::calculateInscattering(GLuint deltaSRayleigh, GLuint deltaSMie) { ZoneScoped - + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, @@ -726,7 +726,7 @@ void AtmosphereDeferredcaster::calculateDeltaJ(int scatteringOrder, GLuint deltaSRayleigh, GLuint deltaSMie) { ZoneScoped - + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaJ, 0); glViewport(0, 0, _textureSize.x, _textureSize.y); program.activate(); @@ -745,7 +745,7 @@ void AtmosphereDeferredcaster::calculateDeltaJ(int scatteringOrder, deltaSRayleighUnit.activate(); glBindTexture(GL_TEXTURE_3D, deltaSRayleigh); program.setUniform("deltaSRTexture", deltaSRayleighUnit); - + ghoul::opengl::TextureUnit deltaSMieUnit; deltaSMieUnit.activate(); glBindTexture(GL_TEXTURE_3D, deltaSMie); @@ -784,7 +784,7 @@ void AtmosphereDeferredcaster::calculateDeltaE(int scatteringOrder, GLuint deltaSMie) { ZoneScoped - + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaE, 0); glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y); program.activate(); @@ -823,7 +823,7 @@ void AtmosphereDeferredcaster::calculateDeltaS(int scatteringOrder, GLuint deltaSRayleigh, GLuint deltaJ) { ZoneScoped - + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaSRayleigh, 0); glViewport(0, 0, _textureSize.x, _textureSize.y); program.activate(); @@ -863,7 +863,7 @@ void AtmosphereDeferredcaster::calculateIrradiance(int scatteringOrder, GLuint deltaE) { ZoneScoped - + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, @@ -895,7 +895,7 @@ void AtmosphereDeferredcaster::calculateInscattering(int scatteringOrder, { ZoneScoped - + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, diff --git a/modules/atmosphere/rendering/atmospheredeferredcaster.h b/modules/atmosphere/rendering/atmospheredeferredcaster.h index 6836352c0a..1f3608e2d6 100644 --- a/modules/atmosphere/rendering/atmospheredeferredcaster.h +++ b/modules/atmosphere/rendering/atmospheredeferredcaster.h @@ -114,9 +114,9 @@ private: UniformCache(cullAtmosphere, Rg, Rt, groundRadianceEmission, HR, betaRayleigh, HM, betaMieExtinction, mieG, sunRadiance, ozoneLayerEnabled, HO, betaOzoneExtinction, - SAMPLES_R, SAMPLES_MU, SAMPLES_MU_S, SAMPLES_NU, inverseModelTransformMatrix, + SAMPLES_R, SAMPLES_MU, SAMPLES_MU_S, SAMPLES_NU, inverseModelTransformMatrix, modelTransformMatrix, projectionToModelTransform, viewToWorldMatrix, - camPosObj, sunDirectionObj, hardShadows, transmittanceTexture, irradianceTexture, + camPosObj, sunDirectionObj, hardShadows, transmittanceTexture, irradianceTexture, inscatterTexture) _uniformCache; ghoul::opengl::TextureUnit _transmittanceTableTextureUnit; diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 6f00faa4b5..8f236ec978 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -649,8 +649,8 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) { // 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::translate(glm::dmat4(1.0), data.modelTransform.translation) * + glm::dmat4(data.modelTransform.rotation) * glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) * glm::scale( glm::dmat4(_modelTransform.value()), @@ -770,8 +770,9 @@ void RenderableModel::update(const UpdateData& data) { // 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. + // 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 diff --git a/modules/base/rendering/renderableprism.cpp b/modules/base/rendering/renderableprism.cpp index c14798a604..65bb509a68 100644 --- a/modules/base/rendering/renderableprism.cpp +++ b/modules/base/rendering/renderableprism.cpp @@ -329,7 +329,12 @@ void RenderablePrism::render(const RenderData& data, RendererTasks&) { glLineWidth(_lineWidth); glBindVertexArray(_vaoId); - glDrawElements(GL_LINE_LOOP, static_cast(_indexArray.size()), GL_UNSIGNED_BYTE, nullptr); + glDrawElements( + GL_LINE_LOOP, + static_cast(_indexArray.size()), + GL_UNSIGNED_BYTE, + nullptr + ); glBindVertexArray(0); global::renderEngine->openglStateCache().resetLineState(); diff --git a/modules/base/rendering/renderabletrailorbit.cpp b/modules/base/rendering/renderabletrailorbit.cpp index fffd3effd8..c4a20f212f 100644 --- a/modules/base/rendering/renderabletrailorbit.cpp +++ b/modules/base/rendering/renderabletrailorbit.cpp @@ -28,6 +28,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include + #include #include #include diff --git a/modules/debugging/debuggingmodule_lua.inl b/modules/debugging/debuggingmodule_lua.inl index 2f90b92376..c5dba570e0 100644 --- a/modules/debugging/debuggingmodule_lua.inl +++ b/modules/debugging/debuggingmodule_lua.inl @@ -151,7 +151,7 @@ int renderCameraPath(lua_State* L) { }; auto addDirectionLine = [addPoint, addLineBetweenPoints] - (const std::string& pointId, const CameraPose& p, + (const std::string& pointId, const CameraPose& p, double lineLength) { const glm::dvec3 dir = glm::normalize(p.rotation * glm::dvec3(0.0, 0.0, -1.0)); @@ -291,7 +291,7 @@ int removePathControlPoints(lua_State* L) { int addCartesianAxes(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, { 1, 2 }, "lua::addCartesianAxes"); - auto [nodeIdentifier, scale] = + auto [nodeIdentifier, scale] = ghoul::lua::values>(L); SceneGraphNode* n = global::renderEngine->scene()->sceneGraphNode(nodeIdentifier); diff --git a/modules/exoplanets/exoplanetsmodule_lua.inl b/modules/exoplanets/exoplanetsmodule_lua.inl index 4a2128b9dc..385f43aefa 100644 --- a/modules/exoplanets/exoplanetsmodule_lua.inl +++ b/modules/exoplanets/exoplanetsmodule_lua.inl @@ -557,7 +557,7 @@ void createExoplanetSystem(const std::string& starName) { int addExoplanetSystem(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::addExoplanetSystem"); - std::variant v = + std::variant v = ghoul::lua::value>(L); if (std::holds_alternative(v)) { diff --git a/modules/gaia/tasks/constructoctreetask.cpp b/modules/gaia/tasks/constructoctreetask.cpp index 1239a65960..19bfc8d463 100644 --- a/modules/gaia/tasks/constructoctreetask.cpp +++ b/modules/gaia/tasks/constructoctreetask.cpp @@ -462,7 +462,7 @@ void ConstructOctreeTask::constructOctreeFromFolder( } } } - + std::vector filterValues; auto writeThreads = std::vector(8); diff --git a/modules/gaia/tasks/readfitstask.cpp b/modules/gaia/tasks/readfitstask.cpp index a23d128073..720abd9f66 100644 --- a/modules/gaia/tasks/readfitstask.cpp +++ b/modules/gaia/tasks/readfitstask.cpp @@ -193,7 +193,7 @@ void ReadFitsTask::readAllFitsFilesFromFolder(const Task::ProgressCallback&) { } } } - + size_t nInputFiles = allInputFiles.size(); LINFO("Files to read: " + std::to_string(nInputFiles)); diff --git a/modules/gaia/tasks/readspecktask.cpp b/modules/gaia/tasks/readspecktask.cpp index 7a0b45917e..4ba384ab43 100644 --- a/modules/gaia/tasks/readspecktask.cpp +++ b/modules/gaia/tasks/readspecktask.cpp @@ -39,7 +39,7 @@ namespace { struct [[codegen::Dictionary(ReadSpeckTask)]] Parameters { // The path to the SPECK file that are to be read std::string inFilePath; - + // The path to the file to export raw VBO data to std::string outFilePath; }; diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index f2d103a4b6..f4fd899794 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -406,7 +406,7 @@ scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const { { // @TODO (2021-06-23, emmbr) Combine with the above function when the camera // paths work really well close to surfaces - "flyToGeo", + "flyToGeo", &globebrowsing::luascriptfunctions::flyToGeo, {}, "[string], number, number, number [, bool, number]", diff --git a/modules/globebrowsing/globebrowsingmodule_lua.inl b/modules/globebrowsing/globebrowsingmodule_lua.inl index 394f768118..e94f6beb81 100644 --- a/modules/globebrowsing/globebrowsingmodule_lua.inl +++ b/modules/globebrowsing/globebrowsingmodule_lua.inl @@ -301,9 +301,8 @@ int flyToGeo(lua_State* L) { altitude ); - using namespace std::string_literals; ghoul::Dictionary instruction; - instruction.setValue("TargetType", "Node"s); + instruction.setValue("TargetType", std::string("Node")); instruction.setValue("Target", n->identifier()); instruction.setValue("Position", positionModelCoords); diff --git a/modules/globebrowsing/src/globerotation.cpp b/modules/globebrowsing/src/globerotation.cpp index 08f5eae9b8..ddfa96ae77 100644 --- a/modules/globebrowsing/src/globerotation.cpp +++ b/modules/globebrowsing/src/globerotation.cpp @@ -174,6 +174,15 @@ glm::vec3 GlobeRotation::computeSurfacePosition(double latitude, double longitud ); } +void GlobeRotation::update(const UpdateData& data) { + if (_useHeightmap) { + // If we use the heightmap, we have to compute the height every frame + setUpdateVariables(); + } + + Rotation::update(data); +} + glm::dmat3 GlobeRotation::matrix(const UpdateData&) const { if (!_globeNode) { // @TODO(abock): The const cast should be removed on a redesign of the rotation @@ -184,11 +193,6 @@ glm::dmat3 GlobeRotation::matrix(const UpdateData&) const { _matrixIsDirty = true; } - if (_useHeightmap) { - // If we use the heightmap, we have to compute the height every frame - _matrixIsDirty = true; - } - if (!_matrixIsDirty) { return _matrix; } diff --git a/modules/globebrowsing/src/globerotation.h b/modules/globebrowsing/src/globerotation.h index a1b30a67e6..bd1960dd35 100644 --- a/modules/globebrowsing/src/globerotation.h +++ b/modules/globebrowsing/src/globerotation.h @@ -39,6 +39,7 @@ class GlobeRotation : public Rotation { public: GlobeRotation(const ghoul::Dictionary& dictionary); + void update(const UpdateData& data) override; glm::dmat3 matrix(const UpdateData& data) const override; static documentation::Documentation Documentation(); diff --git a/modules/globebrowsing/src/globetranslation.cpp b/modules/globebrowsing/src/globetranslation.cpp index 79c23906fd..12e6f4f883 100644 --- a/modules/globebrowsing/src/globetranslation.cpp +++ b/modules/globebrowsing/src/globetranslation.cpp @@ -105,7 +105,7 @@ GlobeTranslation::GlobeTranslation(const ghoul::Dictionary& dictionary) : _globe(GlobeInfo) , _latitude(LatitudeInfo, 0.0, -90.0, 90.0) , _longitude(LongitudeInfo, 0.0, -180.0, 180.0) - , _altitude(AltitudeInfo, 0.0, 0.0, 1e12) + , _altitude(AltitudeInfo, 0.0, -1e12, 1e12) , _useHeightmap(UseHeightmapInfo, false) { const Parameters p = codegen::bake(dictionary); @@ -121,7 +121,8 @@ GlobeTranslation::GlobeTranslation(const ghoul::Dictionary& dictionary) addProperty(_longitude); _altitude = p.altitude.value_or(_altitude); - _altitude.setExponent(8.f); + // @TODO (emmbr) uncomment when ranges with negative values are supported + //_altitude.setExponent(8.f); _altitude.onChange([this]() { setUpdateVariables(); }); addProperty(_altitude); @@ -152,6 +153,15 @@ void GlobeTranslation::setUpdateVariables() { requireUpdate(); } +void GlobeTranslation::update(const UpdateData& data) { + if (_useHeightmap) { + // If we use the heightmap, we have to compute the height every frame + setUpdateVariables(); + } + + Translation::update(data); +} + glm::dvec3 GlobeTranslation::position(const UpdateData&) const { if (!_attachedNode) { // @TODO(abock): The const cast should be removed on a redesign of the translation @@ -162,11 +172,6 @@ glm::dvec3 GlobeTranslation::position(const UpdateData&) const { _positionIsDirty = true; } - if (_useHeightmap) { - // If we use the heightmap, we have to compute the height every frame - _positionIsDirty = true; - } - if (!_positionIsDirty) { return _position; } diff --git a/modules/globebrowsing/src/globetranslation.h b/modules/globebrowsing/src/globetranslation.h index 85e5945174..530e0bec49 100644 --- a/modules/globebrowsing/src/globetranslation.h +++ b/modules/globebrowsing/src/globetranslation.h @@ -39,6 +39,7 @@ class GlobeTranslation : public Translation { public: GlobeTranslation(const ghoul::Dictionary& dictionary); + void update(const UpdateData& data) override; glm::dvec3 position(const UpdateData& data) const override; static documentation::Documentation Documentation(); diff --git a/modules/globebrowsing/src/layergroup.cpp b/modules/globebrowsing/src/layergroup.cpp index dc67b2e62b..3254049cb1 100644 --- a/modules/globebrowsing/src/layergroup.cpp +++ b/modules/globebrowsing/src/layergroup.cpp @@ -26,6 +26,9 @@ #include #include +#include +#include +#include #include #include @@ -125,17 +128,18 @@ Layer* LayerGroup::addLayer(const ghoul::Dictionary& layerDict) { } if (!layerDict.hasValue("Identifier")) { - LERROR("'Identifier' must be specified for layer."); + LERROR("'Identifier' must be specified for layer"); return nullptr; } - std::unique_ptr layer = std::make_unique(_groupId, layerDict, *this); - layer->onChange(_onChangeCallback); - if (hasPropertySubOwner(layer->identifier())) { - LINFO("Layer with identifier " + layer->identifier() + " already exists."); + std::string identifier = layerDict.value("Identifier"); + if (hasPropertySubOwner(identifier)) { + LINFO("Layer with identifier '" + identifier + "' already exists"); _levelBlendingEnabled.setVisibility(properties::Property::Visibility::User); return nullptr; } + std::unique_ptr layer = std::make_unique(_groupId, layerDict, *this); + layer->onChange(_onChangeCallback); Layer* ptr = layer.get(); _layers.push_back(std::move(layer)); update(); @@ -144,6 +148,17 @@ Layer* LayerGroup::addLayer(const ghoul::Dictionary& layerDict) { } addPropertySubOwner(ptr); _levelBlendingEnabled.setVisibility(properties::Property::Visibility::User); + + properties::PropertyOwner* layerGroup = ptr->owner(); + properties::PropertyOwner* layerManager = layerGroup->owner(); + properties::PropertyOwner* globe = layerManager->owner(); + properties::PropertyOwner* sceneGraphNode = globe->owner(); + + global::eventEngine->publishEvent( + sceneGraphNode->identifier(), + layerGroup->identifier(), + ptr->identifier() + ); return ptr; } @@ -159,6 +174,15 @@ void LayerGroup::deleteLayer(const std::string& layerName) { removePropertySubOwner(it->get()); (*it)->deinitialize(); _layers.erase(it); + properties::PropertyOwner* layerGroup = it->get()->owner(); + properties::PropertyOwner* layerManager = layerGroup->owner(); + properties::PropertyOwner* globe = layerManager->owner(); + properties::PropertyOwner* sceneGraphNode = globe->owner(); + global::eventEngine->publishEvent( + sceneGraphNode->identifier(), + layerGroup->identifier(), + it->get()->identifier() + ); update(); if (_onChangeCallback) { _onChangeCallback(nullptr); diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index b36fab95eb..f194eb4be6 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -1209,8 +1209,8 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&, if (global::sessionRecording->isSavingFramesDuringPlayback()) { // If our tile cache is very full, we assume we need to adjust the level of detail // dynamically to not keep rendering frames with unavailable data - // After certain number of iterations(_debugProperties.dynamicLodIterationCount) of - // unavailable/available data in a row, we assume that a change could be made. + // After certain number of iterations(_debugProperties.dynamicLodIterationCount) + // of unavailable/available data in a row, we assume that a change could be made. const int iterCount = _debugProperties.dynamicLodIterationCount; const bool exceededIterations = static_cast(_iterationsOfUnavailableData) > iterCount; diff --git a/modules/globebrowsing/src/ringscomponent.cpp b/modules/globebrowsing/src/ringscomponent.cpp index b04cf026ca..f8c2ff1f1d 100644 --- a/modules/globebrowsing/src/ringscomponent.cpp +++ b/modules/globebrowsing/src/ringscomponent.cpp @@ -64,7 +64,8 @@ namespace { "modelViewProjectionMatrix", "textureOffset", "colorFilterValue", "nightFactor", "sunPosition", "sunPositionObj", "camPositionObj", "ringTextureFwrd", "ringTextureBckwrd", "ringTextureUnlit", "ringTextureColor", - "ringTextureTransparency", "shadowMatrix", "shadowMapTexture", "zFightingPercentage" + "ringTextureTransparency", "shadowMatrix", "shadowMapTexture", + "zFightingPercentage" }; constexpr const std::array GeomUniformNames = { @@ -413,7 +414,10 @@ void RingsComponent::draw(const RenderData& data, RenderPass renderPass, modelViewProjectionTransform ); _shader->setUniform(_uniformCacheAdvancedRings.textureOffset, _offset); - _shader->setUniform(_uniformCacheAdvancedRings.colorFilterValue, _colorFilter); + _shader->setUniform( + _uniformCacheAdvancedRings.colorFilterValue, + _colorFilter + ); _shader->setUniform(_uniformCacheAdvancedRings.nightFactor, _nightFactor); _shader->setUniform(_uniformCacheAdvancedRings.sunPosition, _sunPosition); @@ -496,7 +500,10 @@ void RingsComponent::draw(const RenderData& data, RenderPass renderPass, ghoul::opengl::TextureUnit shadowMapUnit; shadowMapUnit.activate(); glBindTexture(GL_TEXTURE_2D, shadowData.shadowDepthTexture); - _shader->setUniform(_uniformCacheAdvancedRings.shadowMapTexture, shadowMapUnit); + _shader->setUniform( + _uniformCacheAdvancedRings.shadowMapTexture, + shadowMapUnit + ); glEnable(GL_DEPTH_TEST); glEnablei(GL_BLEND, 0); diff --git a/modules/globebrowsing/src/shadowcomponent.cpp b/modules/globebrowsing/src/shadowcomponent.cpp index 71daef9b4a..4266192eae 100644 --- a/modules/globebrowsing/src/shadowcomponent.cpp +++ b/modules/globebrowsing/src/shadowcomponent.cpp @@ -171,7 +171,7 @@ ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary) return; } ghoul::Dictionary d = dictionary.value("Shadows"); - + const Parameters p = codegen::bake(d); addProperty(_enabled); diff --git a/modules/globebrowsing/src/tileprovider.cpp b/modules/globebrowsing/src/tileprovider.cpp index 0f743ec592..0e21fc43e2 100644 --- a/modules/globebrowsing/src/tileprovider.cpp +++ b/modules/globebrowsing/src/tileprovider.cpp @@ -868,7 +868,7 @@ TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary) useFixedTime = dictionary.value(temporal::UseFixedTimeInfo.identifier); } addProperty(useFixedTime); - + if (dictionary.hasValue(temporal::FixedTimeInfo.identifier)) { fixedTime = dictionary.value(temporal::FixedTimeInfo.identifier); } @@ -891,7 +891,9 @@ bool initialize(TileProvider& tp) { ghoul_assert(!tp.isInitialized, "TileProvider can only be initialized once."); - if (TileProvider::NumTileProviders > std::numeric_limits::max() - 1) { + if (TileProvider::NumTileProviders > + static_cast(std::numeric_limits::max()) - 1) + { LERRORC( "TileProvider", "Number of tile providers exceeds 65535. Something will break soon" diff --git a/modules/imgui/include/guiactioncomponent.h b/modules/imgui/include/guiactioncomponent.h index 40e708c093..1395ff6c89 100644 --- a/modules/imgui/include/guiactioncomponent.h +++ b/modules/imgui/include/guiactioncomponent.h @@ -22,8 +22,8 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __OPENSPACE_MODULE_IMGUI___GUISHORTCUTSCOMPONENT___H__ -#define __OPENSPACE_MODULE_IMGUI___GUISHORTCUTSCOMPONENT___H__ +#ifndef __OPENSPACE_MODULE_IMGUI___GUIACTIONCOMPONENT___H__ +#define __OPENSPACE_MODULE_IMGUI___GUIACTIONCOMPONENT___H__ #include @@ -38,4 +38,4 @@ public: } // namespace openspace::gui -#endif // __OPENSPACE_MODULE_IMGUI___GUISHORTCUTSCOMPONENT___H__ +#endif // __OPENSPACE_MODULE_IMGUI___GUIACTIONCOMPONENT___H__ diff --git a/modules/imgui/src/renderproperties.cpp b/modules/imgui/src/renderproperties.cpp index 71f566af29..620745e581 100644 --- a/modules/imgui/src/renderproperties.cpp +++ b/modules/imgui/src/renderproperties.cpp @@ -270,7 +270,10 @@ void renderStringProperty(Property* prop, const std::string& ownerName, void renderListProperty(const std::string& name, const std::string& fullIdentifier, const std::string& stringValue, IsRegularProperty isRegular) { - ghoul_assert(stringValue.size() > 2, "an empty list should have the string value '[]'"); + ghoul_assert( + stringValue.size() > 2, + "an empty list should have the string value '[]'" + ); // Remove brackets from the string value std::string value = stringValue.substr(1, stringValue.size() - 2); diff --git a/modules/space/rendering/renderablestars.cpp b/modules/space/rendering/renderablestars.cpp index 3b42e10193..f8aa080ae6 100644 --- a/modules/space/rendering/renderablestars.cpp +++ b/modules/space/rendering/renderablestars.cpp @@ -529,35 +529,35 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) _dataMapping.bvColor = p.dataMapping.bv.value_or(""); _dataMapping.bvColor.onChange([this]() { _dataIsDirty = true; }); _dataMappingContainer.addProperty(_dataMapping.bvColor); - + _dataMapping.luminance = p.dataMapping.luminance.value_or(""); _dataMapping.luminance.onChange([this]() { _dataIsDirty = true; }); _dataMappingContainer.addProperty(_dataMapping.luminance); - + _dataMapping.absoluteMagnitude = p.dataMapping.absoluteMagnitude.value_or(""); _dataMapping.absoluteMagnitude.onChange([this]() { _dataIsDirty = true; }); _dataMappingContainer.addProperty(_dataMapping.absoluteMagnitude); - + _dataMapping.apparentMagnitude = p.dataMapping.apparentMagnitude.value_or(""); _dataMapping.apparentMagnitude.onChange([this]() { _dataIsDirty = true; }); _dataMappingContainer.addProperty(_dataMapping.apparentMagnitude); - + _dataMapping.vx = p.dataMapping.vx.value_or(""); _dataMapping.vx.onChange([this]() { _dataIsDirty = true; }); _dataMappingContainer.addProperty(_dataMapping.vx); - + _dataMapping.vy = p.dataMapping.vy.value_or(""); _dataMapping.vy.onChange([this]() { _dataIsDirty = true; }); _dataMappingContainer.addProperty(_dataMapping.vy); - + _dataMapping.vz = p.dataMapping.vz.value_or(""); _dataMapping.vz.onChange([this]() { _dataIsDirty = true; }); _dataMappingContainer.addProperty(_dataMapping.vz); - + _dataMapping.speed = p.dataMapping.speed.value_or(""); _dataMapping.speed.onChange([this]() { _dataIsDirty = true; }); _dataMappingContainer.addProperty(_dataMapping.speed); - + addPropertySubOwner(_dataMappingContainer); _speckFile = p.speckFile.string(); diff --git a/modules/space/speckloader.cpp b/modules/space/speckloader.cpp index 4bf46d86cf..d4165fa3b6 100644 --- a/modules/space/speckloader.cpp +++ b/modules/space/speckloader.cpp @@ -172,7 +172,7 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) if (startsWith(line, "datavar")) { // each datavar line is following the form: // datavar - // with being the index of the data variable + // with being the index of the data variable std::stringstream str(line); std::string dummy; @@ -197,7 +197,7 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) std::stringstream str(line); std::string dummy; str >> dummy >> res.textureDataIndex; - + continue; } @@ -216,8 +216,8 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) std::stringstream str(line); std::string dummy; str >> dummy >> res.orientationDataIndex; - - // Ok.. this is kind of weird. Speck unfortunately doesn't tell us in the + + // Ok.. this is kind of weird. Speck unfortunately doesn't tell us in the // specification how many values a datavar has. Usually this is 1 value per // datavar, unless it is a polygon orientation thing. Now, the datavar name // for these can be anything (have seen 'orientation' and 'ori' before, so we @@ -268,7 +268,7 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) return lhs.index < rhs.index; } ); - + std::sort( res.textures.begin(), res.textures.end(), [](const Dataset::Texture& lhs, const Dataset::Texture& rhs) { @@ -292,7 +292,7 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) if (line.back() == '\r') { line = line.substr(0, line.length() - 1); } - + strip(line); if (line.empty()) { @@ -357,7 +357,7 @@ std::optional loadCachedFile(std::filesystem::path path) { if (!file.good()) { return std::nullopt; } - + Dataset result; int8_t fileVersion; @@ -394,7 +394,7 @@ std::optional loadCachedFile(std::filesystem::path path) { result.textures.resize(nTextures); for (int i = 0; i < nTextures; i += 1) { Dataset::Texture tex; - + int16_t idx; file.read(reinterpret_cast(&idx), sizeof(int16_t)); tex.index = idx; @@ -784,7 +784,7 @@ ColorMap loadFile(std::filesystem::path path, SkipAllZeroLines) { if (nColorLines == -1) { // This is the first time we get this far, it will have to be the first number // meaning that it is the number of color values - + str >> nColorLines; res.entries.reserve(nColorLines); } diff --git a/modules/spacecraftinstruments/rendering/renderablefov.cpp b/modules/spacecraftinstruments/rendering/renderablefov.cpp index 9435d3b3bb..8a4971bc32 100644 --- a/modules/spacecraftinstruments/rendering/renderablefov.cpp +++ b/modules/spacecraftinstruments/rendering/renderablefov.cpp @@ -254,7 +254,7 @@ RenderableFov::RenderableFov(const ghoul::Dictionary& dictionary) _alwaysDrawFov = p.alwaysDrawFov.value_or(_alwaysDrawFov); addProperty(_alwaysDrawFov); - + _simplifyBounds = p.simplifyBounds.value_or(_simplifyBounds); addProperty(_colors.defaultStart); diff --git a/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp b/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp index 6be4be39c5..72845c2ecb 100644 --- a/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp @@ -50,7 +50,7 @@ namespace { }; constexpr const std::array FboUniformNames = { - "projectionTexture", "ProjectorMatrix", "ModelTransform", + "projectionTexture", "ProjectorMatrix", "ModelTransform", "boresight", "radius", "segments" }; diff --git a/modules/spacecraftinstruments/util/imagesequencer.h b/modules/spacecraftinstruments/util/imagesequencer.h index 9e258310ca..7c837c741c 100644 --- a/modules/spacecraftinstruments/util/imagesequencer.h +++ b/modules/spacecraftinstruments/util/imagesequencer.h @@ -42,9 +42,9 @@ class SequenceParser; * large image data-sets across all openspace renderable instances, both for past and * future unmanned-spacecraft missions. To load the instance with data the client must * provide a parser inherited from the abstract base class SequenceParser. Hence, there is - * no restriction imposed on data input, whether its data in the form of existing images or - * in the form of a planned observation schedule. Notably, in order for the sequencer to - * function the client must provide or write a parser that fills the ImageSequencers + * no restriction imposed on data input, whether its data in the form of existing images + * or in the form of a planned observation schedule. Notably, in order for the sequencer + * to function the client must provide or write a parser that fills the ImageSequencers * private members. * * \see SequenceParser @@ -136,7 +136,7 @@ private: void sortData(); /** - * This handles any types of ambiguities between the data and SPICE calls. This map is + * This handles any types of ambiguities between the data and SPICE calls. This map is * composed of a key that is a string in the data to be translated and a Decoder that * holds the corresponding translation provided through as asset. * \see Decoder @@ -179,7 +179,7 @@ private: std::string _defaultCaptureImage; std::map _latestImages; - + // if no data, no run bool _hasData = false; }; diff --git a/modules/spacecraftinstruments/util/projectioncomponent.cpp b/modules/spacecraftinstruments/util/projectioncomponent.cpp index b51cf27352..729abeb98b 100644 --- a/modules/spacecraftinstruments/util/projectioncomponent.cpp +++ b/modules/spacecraftinstruments/util/projectioncomponent.cpp @@ -109,8 +109,8 @@ namespace { InstrumentTimes [[codegen::key("instrument-times")]], ImageAndInstrumentTimes [[codegen::key("image-and-instrument-times")]] }; - // This value determines which type of sequencer is used for generating image - // schedules. The 'playbook' is using a custom format designed by the New Horizons + // This value determines which type of sequencer is used for generating image + // schedules. The 'playbook' is using a custom format designed by the New Horizons // team, the 'image-sequence' uses lbl files from a directory, and the 'hybrid' // uses both methods std::optional sequenceType; @@ -128,7 +128,7 @@ namespace { // SPICE integer std::string target [[codegen::annotation("A SPICE name of the observed object")]]; - // The aberration correction that is supposed to be used for the projection. The + // The aberration correction that is supposed to be used for the projection. The // values for the correction correspond to the SPICE definition as described in // ftp://naif.jpl.nasa.gov/pub/naif/toolkit_docs/IDL/cspice/spkezr_c.html std::string aberration [[codegen::inlist("NONE", "LT", "LT+S", "CN", "CN+S", @@ -137,18 +137,18 @@ namespace { // The list of potential targets that are involved with the image projection std::optional> potentialTargets; - // Determines whether the object requires a self-shadowing algorithm. This is - // necessary if the object is concave and might cast a shadow on itself during + // Determines whether the object requires a self-shadowing algorithm. This is + // necessary if the object is concave and might cast a shadow on itself during // presentation. The default value is 'false' std::optional textureMap; - // Determines whether the object requires a self-shadowing algorithm. This is - // necessary if the object is concave and might cast a shadow on itself during + // Determines whether the object requires a self-shadowing algorithm. This is + // necessary if the object is concave and might cast a shadow on itself during // presentation. The default value is 'false' std::optional shadowMap; - // Sets the desired aspect ratio of the projected texture. This might be necessary - // as planets usually have 2x1 aspect ratios, whereas this does not hold for + // Sets the desired aspect ratio of the projected texture. This might be necessary + // as planets usually have 2x1 aspect ratios, whereas this does not hold for // non-planet objects (comets, asteroids, etc). The default value is '1.0' std::optional aspectRatio; diff --git a/modules/statemachine/statemachinemodule.cpp b/modules/statemachine/statemachinemodule.cpp index c8b306ba6f..2a52bfd0d4 100644 --- a/modules/statemachine/statemachinemodule.cpp +++ b/modules/statemachine/statemachinemodule.cpp @@ -194,7 +194,8 @@ scripting::LuaLibrary StateMachineModule::luaLibrary() const { &luascriptfunctions::printCurrentStateInfo, {}, "", - "Prints information about the current state and possible transitions to the log." + "Prints information about the current state and possible transitions to the " + "log." }, { "saveToDotFile", diff --git a/modules/sync/syncs/urlsynchronization.cpp b/modules/sync/syncs/urlsynchronization.cpp index f0c418b02c..2f1fac6b81 100644 --- a/modules/sync/syncs/urlsynchronization.cpp +++ b/modules/sync/syncs/urlsynchronization.cpp @@ -49,22 +49,22 @@ namespace { // provided, all files will be downloaded to the same directory std::variant> url; - // This optional identifier will be part of the used folder structure and, if + // This optional identifier will be part of the used folder structure and, if // provided, can be used to manually find the downloaded folder in the // synchronization folder. If this value is not specified, 'UseHash' has to be set // to 'true' std::optional identifier; // If this value is set to 'true' and it is not overwritten by the global - // settings, the file(s) pointed to by this URLSynchronization will always be - // downloaded, thus overwriting the local files. This is useful for files that are + // settings, the file(s) pointed to by this URLSynchronization will always be + // downloaded, thus overwriting the local files. This is useful for files that are // updated regularly remotely and should be fetch at every startup std::optional forceOverride [[codegen::key("override")]]; // If this value is set to 'true' (the default), the hash of the URL is appended - // to the directory name to produce a unique directory under all circumstances. If + // to the directory name to produce a unique directory under all circumstances. If // this is not desired, the URLSynchronization use the bare directory name alone - // if this value is 'false'. If this value is 'false', the identifier has to be + // if this value is 'false'. If this value is 'false', the identifier has to be // specified std::optional useHash; diff --git a/modules/touch/ext/levmarq.cpp b/modules/touch/ext/levmarq.cpp index ad3be17530..351485f566 100644 --- a/modules/touch/ext/levmarq.cpp +++ b/modules/touch/ext/levmarq.cpp @@ -67,7 +67,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, void (*grad)(double *, double *, int, void *, LMstat*), void *fdata, LMstat *lmstat) { - int x, i, j, it, nit, ill; + int x, j, it, nit, ill; bool verbose; std::string data; double lambda, up, down, mult, weight, err, newerr, derr, target_derr; @@ -86,7 +86,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, defer { // deallocate the arrays - for (i = 0; i < npar; i++) { + for (int i = 0; i < npar; i++) { delete[] h[i]; delete[] ch[i]; } @@ -109,7 +109,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, if (verbose) { std::ostringstream qs, gs, ds, ps, oss; - for (i = 0; i < npar; ++i) { + for (int i = 0; i < npar; ++i) { qs << "q" << i; gs << "g" << i; ds << "d" << i; @@ -119,7 +119,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, ds << ","; } } - for (i = 0; i < ny; ++i) { + for (int i = 0; i < ny; ++i) { for (j = 0; j < 2; ++j) { std::string s = (j == 0) ? "x" : "y"; ps << "p" << i << s; @@ -153,7 +153,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, } // calculate the approximation to the Hessian and the "derivative" d - for (i = 0; i < npar; i++) { + for (int i = 0; i < npar; i++) { d[i] = 0; for (j = 0; j <= i; j++) { h[i][j] = 0; @@ -164,7 +164,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, weight = 1 / dysq[x]; // for weighted least-squares } grad(g, par, x, fdata, lmstat); - for (i = 0; i < npar; i++) { + for (int i = 0; i < npar; i++) { d[i] += (0.0 - func(par, x, fdata, lmstat)) * g[i] * weight; //(y[x] - func(par, x, fdata)) * g[i] * weight; for (j = 0; j <= i; j++) { h[i][j] += g[i] * g[j] * weight; @@ -175,13 +175,13 @@ bool levmarq(int npar, double *par, int ny, double *dysq, mult = 1 + lambda; ill = 1; // ill-conditioned? while (ill && (it < nit)) { - for (i = 0; i < npar; i++) { + for (int i = 0; i < npar; i++) { h[i][i] = h[i][i] * mult; } ill = cholesky_decomp(npar, ch, h); if (!ill) { solve_axb_cholesky(npar, ch, delta, d); - for (i = 0; i < npar; i++) { + for (int i = 0; i < npar; i++) { newpar[i] = par[i] + delta[i]; } lmstat->pos.clear(); @@ -197,7 +197,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, printf("\n");*/ std::ostringstream gString, qString, dString, pString, os; - for (i = 0; i < npar; ++i) { + for (int i = 0; i < npar; ++i) { gString << g[i]; qString << par[i]; dString << d[i]; @@ -207,7 +207,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, dString << ","; } } - for (i = 0; i < ny; ++i) { + for (int i = 0; i < ny; ++i) { pString << lmstat->pos.at(i).x << "," << lmstat->pos.at(i).y; if (i + 1 < ny) { pString << ","; @@ -222,7 +222,7 @@ bool levmarq(int npar, double *par, int ny, double *dysq, it++; } } - for (i = 0; i < npar; i++) { + for (int i = 0; i < npar; i++) { par[i] = newpar[i]; } err = newerr; diff --git a/modules/volume/rendering/renderabletimevaryingvolume.cpp b/modules/volume/rendering/renderabletimevaryingvolume.cpp index cd748315cb..363719f835 100644 --- a/modules/volume/rendering/renderabletimevaryingvolume.cpp +++ b/modules/volume/rendering/renderabletimevaryingvolume.cpp @@ -134,7 +134,7 @@ namespace { // actual time. The default value is 0 std::optional secondsBefore; - // Specifies the number of seconds to show the the last timestep after its + // Specifies the number of seconds to show the the last timestep after its // actual time float secondsAfter; diff --git a/modules/volume/tasks/generaterawvolumetask.cpp b/modules/volume/tasks/generaterawvolumetask.cpp index 109703d6f6..771d875782 100644 --- a/modules/volume/tasks/generaterawvolumetask.cpp +++ b/modules/volume/tasks/generaterawvolumetask.cpp @@ -93,7 +93,7 @@ std::string GenerateRawVolumeTask::description() { "value by evaluating the lua function: `{}`, with three arguments (x, y, z) " "ranging from ({}, {}, {}) to ({}, {}, {}). Write raw volume data into {} and " "dictionary with metadata to {}", - _dimensions.x, _dimensions.y, _dimensions.z, _valueFunctionLua, + _dimensions.x, _dimensions.y, _dimensions.z, _valueFunctionLua, _lowerDomainBound.x, _lowerDomainBound.y, _lowerDomainBound.z, _upperDomainBound.x, _upperDomainBound.y, _upperDomainBound.z, _rawVolumeOutputPath, _dictionaryOutputPath diff --git a/openspace.cfg b/openspace.cfg index 6828473069..9be037a435 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -222,6 +222,7 @@ LoadingScreen = { } CheckOpenGLState = false LogEachOpenGLCall = false +PrintEvents = false ShutdownCountdown = 3 ScreenshotUseDate = true diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 187628bdce..9fe2d9fbdd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,6 +43,9 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/engine/openspaceengine_lua.inl ${OPENSPACE_BASE_DIR}/src/engine/syncengine.cpp ${OPENSPACE_BASE_DIR}/src/engine/virtualpropertymanager.cpp + ${OPENSPACE_BASE_DIR}/src/events/event.cpp + ${OPENSPACE_BASE_DIR}/src/events/eventengine.cpp + ${OPENSPACE_BASE_DIR}/src/events/eventengine_lua.inl ${OPENSPACE_BASE_DIR}/src/interaction/actionmanager.cpp ${OPENSPACE_BASE_DIR}/src/interaction/actionmanager_lua.inl ${OPENSPACE_BASE_DIR}/src/interaction/camerainteractionstates.cpp @@ -176,6 +179,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/util/spicemanager_lua.inl ${OPENSPACE_BASE_DIR}/src/util/syncbuffer.cpp ${OPENSPACE_BASE_DIR}/src/util/synchronizationwatcher.cpp + ${OPENSPACE_BASE_DIR}/src/util/tstring.cpp ${OPENSPACE_BASE_DIR}/src/util/histogram.cpp ${OPENSPACE_BASE_DIR}/src/util/task.cpp ${OPENSPACE_BASE_DIR}/src/util/taskloader.cpp @@ -223,6 +227,9 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/engine/syncengine.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/virtualpropertymanager.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/windowdelegate.h + ${OPENSPACE_BASE_DIR}/include/openspace/events/event.h + ${OPENSPACE_BASE_DIR}/include/openspace/events/eventengine.h + ${OPENSPACE_BASE_DIR}/include/openspace/events/eventengine.inl ${OPENSPACE_BASE_DIR}/include/openspace/interaction/action.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/actionmanager.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/delayedvariable.h @@ -381,6 +388,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/util/timemanager.h ${OPENSPACE_BASE_DIR}/include/openspace/util/timerange.h ${OPENSPACE_BASE_DIR}/include/openspace/util/touch.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/tstring.h ${OPENSPACE_BASE_DIR}/include/openspace/util/universalhelpers.h ${OPENSPACE_BASE_DIR}/include/openspace/util/updatestructures.h ${OPENSPACE_BASE_DIR}/include/openspace/util/versionchecker.h diff --git a/src/documentation/core_registration.cpp b/src/documentation/core_registration.cpp index 0e604f34e5..f9d5d63c20 100644 --- a/src/documentation/core_registration.cpp +++ b/src/documentation/core_registration.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -77,13 +78,14 @@ void registerCoreClasses(documentation::DocumentationEngine& engine) { // documentation version. void registerCoreClasses(scripting::ScriptEngine& engine) { engine.addLibrary(Dashboard::luaLibrary()); + engine.addLibrary(EventEngine::luaLibrary()); engine.addLibrary(MissionManager::luaLibrary()); engine.addLibrary(ModuleEngine::luaLibrary()); engine.addLibrary(OpenSpaceEngine::luaLibrary()); engine.addLibrary(ParallelPeer::luaLibrary()); + engine.addLibrary(Profile::luaLibrary()); engine.addLibrary(RenderEngine::luaLibrary()); engine.addLibrary(SpiceManager::luaLibrary()); - engine.addLibrary(Profile::luaLibrary()); engine.addLibrary(Scene::luaLibrary()); engine.addLibrary(Time::luaLibrary()); engine.addLibrary(interaction::ActionManager::luaLibrary()); diff --git a/src/documentation/verifier.cpp b/src/documentation/verifier.cpp index 22ffdec0a6..857764daa6 100644 --- a/src/documentation/verifier.cpp +++ b/src/documentation/verifier.cpp @@ -462,7 +462,7 @@ TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dic o.offender = key; o.reason = TestResult::Offense::Reason::MissingKey; res.offenses.push_back(o); - return res; + return res; } } } @@ -498,7 +498,7 @@ TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dic o.offender = key; o.reason = TestResult::Offense::Reason::WrongType; res.offenses.push_back(o); - return res; + return res; } } else { @@ -508,7 +508,7 @@ TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dic o.offender = key; o.reason = TestResult::Offense::Reason::WrongType; res.offenses.push_back(o); - return res; + return res; } } else { @@ -565,7 +565,7 @@ TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dic o.offender = key; o.reason = TestResult::Offense::Reason::WrongType; res.offenses.push_back(o); - return res; + return res; } } else { @@ -575,7 +575,7 @@ TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dic o.offender = key; o.reason = TestResult::Offense::Reason::MissingKey; res.offenses.push_back(o); - return res; + return res; } } } diff --git a/src/engine/configuration.cpp b/src/engine/configuration.cpp index 454ee1f6a3..681315b554 100644 --- a/src/engine/configuration.cpp +++ b/src/engine/configuration.cpp @@ -295,6 +295,11 @@ namespace { // 'false' std::optional logEachOpenGLCall; + // Determines whether events are printed as debug messages to the console each + // frame. If this value is set it determines the default value of the property of + // the OpenSpaceEngine with the same name + std::optional printEvents; + // This value determines whether the initialization of the scene graph should // occur multithreaded, that is, whether multiple scene graph nodes should // initialize in parallel. The only use for this value is to disable it for @@ -391,6 +396,7 @@ void parseLuaState(Configuration& configuration) { p.useMultithreadedInitialization.value_or(c.useMultithreadedInitialization); c.isCheckingOpenGLState = p.checkOpenGLState.value_or(c.isCheckingOpenGLState); c.isLoggingOpenGLCalls = p.logEachOpenGLCall.value_or(c.isLoggingOpenGLCalls); + c.isPrintingEvents = p.printEvents.value_or(c.isPrintingEvents); c.shutdownCountdown = p.shutdownCountdown.value_or(c.shutdownCountdown); c.shouldUseScreenshotDate = p.screenshotUseDate.value_or(c.shouldUseScreenshotDate); if (p.onScreenTextScaling.has_value()) { diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index cc8429fa84..d523ae24fd 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -173,9 +173,10 @@ std::shared_ptr DownloadManager::downloadFile( CURL* curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // NOLINT + curl_easy_setopt(curl, CURLOPT_USERAGENT, "OpenSpace"); // NOLINT curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // NOLINT curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); // NOLINT - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); // NOLINT + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeData); // NOLINT if (timeout_secs) { curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout_secs); // NOLINT } @@ -204,7 +205,11 @@ std::shared_ptr DownloadManager::downloadFile( future->isFinished = true; } else { - future->errorMessage = curl_easy_strerror(res); + long rescode; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rescode); + future->errorMessage = fmt::format( + "{}. HTTP code: {}", curl_easy_strerror(res), rescode + ); } if (finishedCb) { diff --git a/src/engine/globals.cpp b/src/engine/globals.cpp index a048db90ba..464518bd12 100644 --- a/src/engine/globals.cpp +++ b/src/engine/globals.cpp @@ -24,14 +24,15 @@ #include -#include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -71,12 +72,13 @@ namespace { // in some random global randoms #ifdef WIN32 constexpr const int TotalSize = + sizeof(MemoryManager) + + sizeof(EventEngine) + sizeof(ghoul::fontrendering::FontManager) + sizeof(Dashboard) + sizeof(DeferredcasterManager) + sizeof(DownloadManager) + sizeof(LuaConsole) + - sizeof(MemoryManager) + sizeof(MissionManager) + sizeof(ModuleEngine) + sizeof(OpenSpaceEngine) + @@ -119,6 +121,22 @@ void create() { std::byte* currentPos = DataStorage.data(); #endif // WIN32 +#ifdef WIN32 + memoryManager = new (currentPos) MemoryManager; + ghoul_assert(memoryManager, "No memoryManager"); + currentPos += sizeof(MemoryManager); +#else // ^^^ WIN32 / !WIN32 vvv + memoryManager = new MemoryManager; +#endif // WIN32 + +#ifdef WIN32 + eventEngine = new (currentPos) EventEngine; + ghoul_assert(eventEngine, "No eventEngine"); + currentPos += sizeof(EventEngine); +#else // ^^^ WIN32 / !WIN32 vvv + downloadManager = new EventEngine; +#endif // WIN32 + #ifdef WIN32 fontManager = new (currentPos) ghoul::fontrendering::FontManager({ 1536, 1536, 1 }); ghoul_assert(fontManager, "No fontManager"); @@ -159,14 +177,6 @@ void create() { luaConsole = new LuaConsole; #endif // WIN32 -#ifdef WIN32 - memoryManager = new (currentPos) MemoryManager; - ghoul_assert(memoryManager, "No memoryManager"); - currentPos += sizeof(MemoryManager); -#else // ^^^ WIN32 / !WIN32 vvv - memoryManager = new MemoryManager; -#endif // WIN32 - #ifdef WIN32 missionManager = new (currentPos) MissionManager; ghoul_assert(missionManager, "No missionManager"); @@ -574,13 +584,6 @@ void destroy() { delete missionManager; #endif // WIN32 - LDEBUGC("Globals", "Destroying 'MemoryManager'"); -#ifdef WIN32 - memoryManager->~MemoryManager(); -#else // ^^^ WIN32 / !WIN32 vvv - delete memoryManager; -#endif // WIN32 - LDEBUGC("Globals", "Destroying 'LuaConsole'"); #ifdef WIN32 luaConsole->~LuaConsole(); @@ -616,6 +619,20 @@ void destroy() { delete fontManager; #endif // WIN32 + LDEBUGC("Globals", "Destroying 'EventEngine'"); +#ifdef WIN32 + eventEngine->~EventEngine(); +#else // ^^^ WIN32 / !WIN32 vvv + delete eventEngine; +#endif // WIN32 + + LDEBUGC("Globals", "Destroying 'MemoryManager'"); +#ifdef WIN32 + memoryManager->~MemoryManager(); +#else // ^^^ WIN32 / !WIN32 vvv + delete memoryManager; +#endif // WIN32 + callback::destroy(); } diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 355c9ab671..ae7f8c0469 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include @@ -53,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -101,13 +104,22 @@ namespace { template overloaded(Ts...)->overloaded; constexpr const char* _loggerCat = "OpenSpaceEngine"; + + openspace::properties::Property::PropertyInfo PrintEventsInfo = { + "PrintEvents", + "Print Events", + "If this is enabled, all events that are propagated through the system are " + "printed to the log." + }; } // namespace namespace openspace { class Scene; -OpenSpaceEngine::OpenSpaceEngine() { +OpenSpaceEngine::OpenSpaceEngine() + : _printEvents(PrintEventsInfo, false) +{ FactoryManager::initialize(); FactoryManager::ref().addFactory( std::make_unique>(), @@ -186,6 +198,8 @@ void OpenSpaceEngine::initialize() { global::initialize(); + _printEvents = global::configuration->isPrintingEvents; + const std::string versionCheckUrl = global::configuration->versionCheckUrl; if (!versionCheckUrl.empty()) { global::versionChecker->requestLatestVersion(versionCheckUrl); @@ -895,13 +909,16 @@ void OpenSpaceEngine::deinitialize() { TransformationManager::deinitialize(); SpiceManager::deinitialize(); + if (_printEvents) { + events::Event* e = global::eventEngine->firstEvent(); + events::logAllEvents(e); + } + ghoul::fontrendering::FontRenderer::deinitialize(); ghoul::logging::LogManager::deinitialize(); LTRACE("deinitialize(end)"); - - LTRACE("OpenSpaceEngine::deinitialize(end)"); } @@ -1101,6 +1118,7 @@ void OpenSpaceEngine::preSynchronization() { resetPropertyChangeFlagsOfSubowners(global::rootPropertyOwner); _hasScheduledAssetLoading = false; _scheduledAssetPathToLoad.clear(); + global::eventEngine->publishEvent(); } else if (_isRenderingFirstFrame) { global::profile->ignoreUpdates = true; @@ -1185,6 +1203,9 @@ void OpenSpaceEngine::postSynchronizationPreDraw() { if (_shutdown.inShutdown) { if (_shutdown.timer <= 0.f) { + global::eventEngine->publishEvent( + events::EventApplicationShutdown::State::Finished + ); global::windowDelegate->terminate(); return; } @@ -1310,6 +1331,23 @@ void OpenSpaceEngine::postDraw() { _isRenderingFirstFrame = false; } + // + // Handle events + // + const events::Event* e = global::eventEngine->firstEvent(); + if (_printEvents) { + events::logAllEvents(e); + } + global::eventEngine->triggerActions(); + while (e) { + // @TODO (abock, 2021-08-25) Need to send all events to a topic to be sent out to + // others + + e = e->next; + } + + + global::eventEngine->postFrameCleanup(); global::memoryManager->PersistentMemory.housekeeping(); LTRACE("OpenSpaceEngine::postDraw(end)"); @@ -1543,15 +1581,24 @@ void OpenSpaceEngine::toggleShutdownMode() { if (_shutdown.inShutdown) { // If we are already in shutdown mode, we want to disable it _shutdown.inShutdown = false; + global::eventEngine->publishEvent( + events::EventApplicationShutdown::State::Aborted + ); } else { // Else, we have to enable it _shutdown.timer = _shutdown.waitTime; _shutdown.inShutdown = true; + global::eventEngine->publishEvent( + events::EventApplicationShutdown::State::Started + ); } } void setCameraFromProfile(const Profile& p) { + if (!p.camera.has_value()) { + throw ghoul::RuntimeError("No 'camera' entry exists in the startup profile"); + } std::visit( overloaded{ [](const Profile::CameraNavState& navStateProfile) { diff --git a/src/events/event.cpp b/src/events/event.cpp new file mode 100644 index 0000000000..ad314e99b8 --- /dev/null +++ b/src/events/event.cpp @@ -0,0 +1,595 @@ +/***************************************************************************************** + * * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char _loggerCat[] = "EventInfo"; +} // namespace + +using namespace std::string_literals; + +namespace openspace::events { + +void log(int i, const EventSceneGraphNodeAdded& e) { + ghoul_assert(e.type == EventSceneGraphNodeAdded::Type, "Wrong type"); + LINFO(fmt::format("[{}] SceneGraphNodeAdded: {}", i, e.node)); +} + +void log(int i, const EventSceneGraphNodeRemoved& e) { + ghoul_assert(e.type == EventSceneGraphNodeRemoved::Type, "Wrong type"); + LINFO(fmt::format("[{}] SceneGraphNodeRemoved: {}", i, e.node)); +} + +void log(int i, const EventParallelConnection& e) { + ghoul_assert(e.type == EventParallelConnection::Type, "Wrong type"); + std::string_view state = [](EventParallelConnection::State s) { + switch (s) { + case EventParallelConnection::State::Established: return "Established"; + case EventParallelConnection::State::Lost: return "Lost"; + case EventParallelConnection::State::HostshipGained: return "HostshipGained"; + case EventParallelConnection::State::HostshipLost: return "HostshipLost"; + default: throw ghoul::MissingCaseException(); + } + }(e.state); + LINFO(fmt::format("[{}] ParallelConnection ({})", i, state)); +} + +void log(int i, [[ maybe_unused ]] const EventProfileLoadingFinished& e) { + ghoul_assert(e.type == EventProfileLoadingFinished::Type, "Wrong type"); + LINFO(fmt::format("[{}] ProfileLoadingFinished", i)); +} + +void log(int i, const EventApplicationShutdown& e) { + ghoul_assert(e.type == EventApplicationShutdown::Type, "Wrong type"); + std::string t = [](EventApplicationShutdown::State state) { + switch (state) { + case EventApplicationShutdown::State::Started: return "started"; + case EventApplicationShutdown::State::Aborted: return "aborted"; + case EventApplicationShutdown::State::Finished: return "finished"; + default: throw ghoul::MissingCaseException(); + } + }(e.state); + LINFO(fmt::format("[{}] ApplicationShutdown", i)); +} + +void log(int i, const EventScreenSpaceRenderableAdded& e) { + ghoul_assert(e.type == EventScreenSpaceRenderableAdded::Type, "Wrong type"); + LINFO(fmt::format("[{}] ScreenSpaceRenderableAdded: {}", i, e.renderable)); +} + +void log(int i, const EventScreenSpaceRenderableRemoved& e) { + ghoul_assert(e.type == EventScreenSpaceRenderableRemoved::Type, "Wrong type"); + LINFO(fmt::format("[{}] ScreenSpaceRenderableRemoved: {}", i, e.renderable)); +} + +void log(int i, const EventCameraFocusTransition& e) { + ghoul_assert(e.type == EventCameraFocusTransition::Type, "Wrong type"); + std::string_view t = [](EventCameraFocusTransition::Transition transition) { + switch (transition) { + case EventCameraFocusTransition::Transition::Approaching: + return "Approaching"; + case EventCameraFocusTransition::Transition::Reaching: + return "Reaching"; + case EventCameraFocusTransition::Transition::Receding: + return "Receding"; + case EventCameraFocusTransition::Transition::Exiting: + return "Exiting"; + default: throw ghoul::MissingCaseException(); + } + }(e.transition); + + LINFO(fmt::format( + "[{}] CameraTransition: {}, {} ({})", + i, reinterpret_cast(e.camera), e.node, t + )); +} + +void log(int i, const EventTimeOfInterestReached& e) { + ghoul_assert(e.type == EventTimeOfInterestReached::Type, "Wrong type"); + LINFO(fmt::format( + "[{}] TimeOfInterestReached: {}, {}", + i, e.time->UTC(), reinterpret_cast(e.camera) + )); +} + +void log(int i, [[ maybe_unused ]] const EventMissionEventReached& e) { + ghoul_assert(e.type == EventMissionEventReached::Type, "Wrong type"); + LINFO(fmt::format("[{}] MissionEventReached", i)); +} + +void log(int i, const EventPlanetEclipsed& e) { + ghoul_assert(e.type == EventPlanetEclipsed::Type, "Wrong type"); + LINFO(fmt::format("[{}] PlanetEclipsed: {} -> {}", i, e.eclipsee, e.eclipser)); +} + +void log(int i, [[ maybe_unused ]] const EventInterpolationFinished& e) { + ghoul_assert(e.type == EventInterpolationFinished::Type, "Wrong type"); + LINFO(fmt::format("[{}] InterpolationFinished", i)); +} + +void log(int i, const EventFocusNodeChanged& e) { + ghoul_assert(e.type == EventFocusNodeChanged::Type, "Wrong type"); + LINFO(fmt::format("[{}] FocusNodeChanged: {} -> {}", i, e.oldNode, e.newNode)); +} + +void log(int i, const EventLayerAdded& e) { + ghoul_assert(e.type == EventLayerAdded::Type, "Wrong type"); + LINFO(fmt::format("[{}] LayerAdded: {}", i, e.layer)); +} + +void log(int i, const EventLayerRemoved& e) { + ghoul_assert(e.type == EventLayerRemoved::Type, "Wrong type"); + LINFO(fmt::format("[{}] LayerRemoved: {}", i, e.layer)); +} + +void log(int i, const EventSessionRecordingPlayback& e) { + ghoul_assert(e.type == EventSessionRecordingPlayback::Type, "Wrong type"); + + std::string_view state = [](EventSessionRecordingPlayback::State s) { + switch (s) { + case EventSessionRecordingPlayback::State::Started: return "Started"; + case EventSessionRecordingPlayback::State::Paused: return "Paused"; + case EventSessionRecordingPlayback::State::Resumed: return "Resumed"; + case EventSessionRecordingPlayback::State::Finished: return "Finished"; + default: throw ghoul::MissingCaseException(); + } + }(e.state); + + LINFO(fmt::format("[{}] SessionRecordingPlayback: {}", i, state)); +} + +void log(int i, const CustomEvent& e) { + ghoul_assert(e.type == CustomEvent::Type, "Wrong type"); + LINFO(fmt::format("[{}] CustomEvent: {} ({})", i, e.subtype, e.payload)); +} + +std::string_view toString(Event::Type type) { + switch (type) { + case Event::Type::SceneGraphNodeAdded: return "SceneGraphNodeAdded"; + case Event::Type::SceneGraphNodeRemoved: return "SceneGraphNodeRemoved"; + case Event::Type::ParallelConnection: return "ParallelConnection"; + case Event::Type::ProfileLoadingFinished: return "ProfileLoadingFinished"; + case Event::Type::ApplicationShutdown: return "ApplicationShutdown"; + case Event::Type::ScreenSpaceRenderableAdded: return "ScreenSpaceRenderableAdded"; + case Event::Type::ScreenSpaceRenderableRemoved: + return "ScreenSpaceRenderableRemoved"; + case Event::Type::CameraFocusTransition: return "CameraFocusTransition"; + case Event::Type::TimeOfInterestReached: return "TimeOfInterestReached"; + case Event::Type::MissionEventReached: return "MissionEventReached"; + case Event::Type::PlanetEclipsed: return "PlanetEclipsed"; + case Event::Type::InterpolationFinished: return "InterpolationFinished"; + case Event::Type::FocusNodeChanged: return "FocusNodeChanged"; + case Event::Type::LayerAdded: return "LayerAdded"; + case Event::Type::LayerRemoved: return "LayerRemoved"; + case Event::Type::SessionRecordingPlayback: return "SessionRecordingPlayback"; + case Event::Type::Custom: return "Custom"; + default: + throw ghoul::MissingCaseException(); + } +} + +Event::Type fromString(std::string_view str) { + if (str == "SceneGraphNodeAdded") { + return Event::Type::SceneGraphNodeAdded; + } + else if (str == "SceneGraphNodeRemoved") { + return Event::Type::SceneGraphNodeRemoved; + } + else if (str == "ParallelConnection") { + return Event::Type::ParallelConnection; + } + else if (str == "ProfileLoadingFinished") { + return Event::Type::ProfileLoadingFinished; + } + else if (str == "ApplicationShutdown") { + return Event::Type::ApplicationShutdown; + } + else if (str == "ScreenSpaceRenderableAdded") { + return Event::Type::ScreenSpaceRenderableAdded; + } + else if (str == "ScreenSpaceRenderableRemoved") { + return Event::Type::ScreenSpaceRenderableRemoved; + } + else if (str == "CameraFocusTransition") { + return Event::Type::CameraFocusTransition; + } + else if (str == "TimeOfInterestReached") { + return Event::Type::TimeOfInterestReached; + } + else if (str == "MissionEventReached") { + return Event::Type::MissionEventReached; + } + else if (str == "PlanetEclipsed") { + return Event::Type::PlanetEclipsed; + } + else if (str == "InterpolationFinished") { + return Event::Type::InterpolationFinished; + } + else if (str == "FocusNodeChanged") { + return Event::Type::FocusNodeChanged; + } + else if (str == "LayerAdded") { + return Event::Type::LayerAdded; + } + else if (str == "LayerRemoved") { + return Event::Type::LayerRemoved; + } + else if (str == "SessionRecordingPlayback") { + return Event::Type::SessionRecordingPlayback; + } + else if (str == "Custom") { + return Event::Type::Custom; + } + + throw ghoul::RuntimeError(fmt::format("Unknown event type '{}'", str)); +} + +ghoul::Dictionary toParameter(const Event& e) { + ghoul::Dictionary d; + switch (e.type) { + case Event::Type::SceneGraphNodeAdded: + d.setValue( + "Node", + std::string(static_cast(e).node) + ); + break; + case Event::Type::SceneGraphNodeRemoved: + d.setValue( + "Node", + std::string(static_cast(e).node) + ); + break; + case Event::Type::ParallelConnection: + switch (static_cast(e).state) { + case EventParallelConnection::State::Established: + d.setValue("State", "Established"s); + break; + case EventParallelConnection::State::Lost: + d.setValue("State", "Lost"s); + break; + case EventParallelConnection::State::HostshipGained: + d.setValue("State", "HostshipGained"s); + break; + case EventParallelConnection::State::HostshipLost: + d.setValue("State", "HostshipLost"s); + break; + default: + throw ghoul::MissingCaseException(); + } + break; + case Event::Type::ApplicationShutdown: + switch (static_cast(e).state) { + case EventApplicationShutdown::State::Started: + d.setValue("State", "Started"s); + break; + case EventApplicationShutdown::State::Aborted: + d.setValue("State", "Aborted"s); + break; + case EventApplicationShutdown::State::Finished: + d.setValue("State", "Finished"s); + break; + } + break; + case Event::Type::ScreenSpaceRenderableAdded: + d.setValue( + "Renderable", + std::string( + static_cast(e).renderable + ) + ); + break; + case Event::Type::ScreenSpaceRenderableRemoved: + d.setValue( + "Renderable", + std::string( + static_cast(e).renderable + ) + ); + break; + case Event::Type::CameraFocusTransition: + d.setValue( + "Node", + std::string(static_cast(e).node) + ); + switch (static_cast(e).transition) { + case EventCameraFocusTransition::Transition::Approaching: + d.setValue("Transition", "Approaching"s); + break; + case EventCameraFocusTransition::Transition::Reaching: + d.setValue("Transition", "Reaching"s); + break; + case EventCameraFocusTransition::Transition::Receding: + d.setValue("Transition", "Receding"s); + break; + case EventCameraFocusTransition::Transition::Exiting: + d.setValue("Transition", "Exiting"s); + break; + default: + throw ghoul::MissingCaseException(); + } + break; + case Event::Type::PlanetEclipsed: + d.setValue( + "Eclipsee", + std::string(static_cast(e).eclipsee) + ); + d.setValue( + "Eclipser", + std::string(static_cast(e).eclipser) + ); + break; + case Event::Type::InterpolationFinished: + d.setValue( + "Property", + std::string(static_cast(e).property) + ); + break; + case Event::Type::FocusNodeChanged: + d.setValue( + "OldNode", + std::string(static_cast(e).oldNode) + ); + d.setValue( + "NewNode", + std::string(static_cast(e).newNode) + ); + break; + case Event::Type::LayerAdded: + d.setValue( + "Globe", + std::string(static_cast(e).node) + ); + d.setValue( + "Group", + std::string(static_cast(e).layerGroup) + ); + d.setValue( + "Layer", + std::string(static_cast(e).layer) + ); + break; + case Event::Type::LayerRemoved: + d.setValue( + "Globe", + std::string(static_cast(e).node) + ); + d.setValue( + "Group", + std::string(static_cast(e).layerGroup) + ); + d.setValue( + "Layer", + std::string(static_cast(e).layer) + ); + break; + case Event::Type::SessionRecordingPlayback: + switch (static_cast(e).state) { + case EventSessionRecordingPlayback::State::Started: + d.setValue("State", "Started"s); + break; + case EventSessionRecordingPlayback::State::Paused: + d.setValue("State", "Paused"s); + break; + case EventSessionRecordingPlayback::State::Resumed: + d.setValue("State", "Resumed"s); + break; + case EventSessionRecordingPlayback::State::Finished: + d.setValue("State", "Finished"s); + break; + } + break; + case Event::Type::Custom: + d.setValue( + "Subtype", std::string(static_cast(e).subtype) + ); + break; + default: + break; + } + return d; +} + +void logAllEvents(const Event* e) { + int i = 0; + while (e) { + switch (e->type) { + case Event::Type::SceneGraphNodeAdded: + log(i, *static_cast(e)); + break; + case Event::Type::SceneGraphNodeRemoved: + log(i, *static_cast(e)); + break; + case Event::Type::ParallelConnection: + log(i, *static_cast(e)); + break; + case Event::Type::ProfileLoadingFinished: + log(i, *static_cast(e)); + break; + case Event::Type::ApplicationShutdown: + log(i, *static_cast(e)); + break; + case Event::Type::ScreenSpaceRenderableAdded: + log(i, *static_cast(e)); + break; + case Event::Type::ScreenSpaceRenderableRemoved: + log(i, *static_cast(e)); + break; + case Event::Type::CameraFocusTransition: + log(i, *static_cast(e)); + break; + case Event::Type::TimeOfInterestReached: + log(i, *static_cast(e)); + break; + case Event::Type::MissionEventReached: + log(i, *static_cast(e)); + break; + case Event::Type::PlanetEclipsed: + log(i, *static_cast(e)); + break; + case Event::Type::InterpolationFinished: + log(i, *static_cast(e)); + break; + case Event::Type::FocusNodeChanged: + log(i, *static_cast(e)); + break; + case Event::Type::LayerAdded: + log(i, *static_cast(e)); + break; + case Event::Type::LayerRemoved: + log(i, *static_cast(e)); + break; + case Event::Type::SessionRecordingPlayback: + log(i, *static_cast(e)); + break; + case Event::Type::Custom: + log(i, *static_cast(e)); + break; + default: + LINFO(fmt::format("[{}]: Unknown {}", typeid(e).name())); + break; + } + + i++; + e = e->next; + } +} + +EventSceneGraphNodeAdded::EventSceneGraphNodeAdded(const SceneGraphNode* node_) + : Event(Type) + , node(temporaryString(node_->identifier())) +{} + +EventSceneGraphNodeRemoved::EventSceneGraphNodeRemoved(const SceneGraphNode* node_) + : Event(Type) + , node(temporaryString(node_->identifier())) +{} + +EventParallelConnection::EventParallelConnection(State state_) + : Event(Type) + , state(state_) +{} + +EventProfileLoadingFinished::EventProfileLoadingFinished() + : Event(Type) +{} + +EventApplicationShutdown::EventApplicationShutdown(State state_) + : Event(Type) + , state(state_) +{} + +EventScreenSpaceRenderableAdded::EventScreenSpaceRenderableAdded( + const ScreenSpaceRenderable* renderable_) + : Event(Type) + , renderable(temporaryString(renderable_->identifier())) +{} + +EventScreenSpaceRenderableRemoved::EventScreenSpaceRenderableRemoved( + const ScreenSpaceRenderable* renderable_) + : Event(Type) + , renderable(temporaryString(renderable_->identifier())) +{} + +EventCameraFocusTransition::EventCameraFocusTransition(const Camera* camera_, + const SceneGraphNode* node_, + Transition transition_) + : Event(Type) + , camera(camera_) + , node(temporaryString(node_->identifier())) + , transition(transition_) +{} + +EventTimeOfInterestReached::EventTimeOfInterestReached(const Time* time_, + const Camera* camera_) + : Event(Type) + , time(time_) + , camera(camera_) +{} + +EventMissionEventReached::EventMissionEventReached() + : Event(Type) +{} + +EventPlanetEclipsed::EventPlanetEclipsed(const SceneGraphNode* eclipsee_, + const SceneGraphNode* eclipser_) + : Event(Type) + , eclipsee(temporaryString(eclipsee_->identifier())) + , eclipser(temporaryString(eclipser_->identifier())) +{} + +EventInterpolationFinished::EventInterpolationFinished( + const properties::Property* property_) + : Event(Type) + , property(temporaryString(property_->fullyQualifiedIdentifier())) +{} + +EventFocusNodeChanged::EventFocusNodeChanged(const SceneGraphNode* oldNode_, + const SceneGraphNode* newNode_) + : Event(Type) + , oldNode(oldNode_ ? temporaryString(oldNode_->identifier()) : "") + , newNode(temporaryString(newNode_->identifier())) +{ + ghoul_assert(newNode_, "There must be a new node"); +} + +EventLayerAdded::EventLayerAdded(std::string_view node_, std::string_view layerGroup_, + std::string_view layer_) + : Event(Type) + , node(temporaryString(node_)) + , layerGroup(temporaryString(layerGroup_)) + , layer(temporaryString(layer_)) +{} + +EventLayerRemoved::EventLayerRemoved(std::string_view node_, std::string_view layerGroup_, + std::string_view layer_) + : Event(Type) + , node(temporaryString(node_)) + , layerGroup(temporaryString(layerGroup_)) + , layer(temporaryString(layer_)) +{} + +EventSessionRecordingPlayback::EventSessionRecordingPlayback(State state_) + : Event(Type) + , state(state_) +{} + +CustomEvent::CustomEvent(std::string_view subtype_, const void* payload_) + : Event(Type) + , subtype(subtype_) + , payload(payload_) +{} + +} // namespace openspace::events diff --git a/src/events/eventengine.cpp b/src/events/eventengine.cpp new file mode 100644 index 0000000000..b01e1502a0 --- /dev/null +++ b/src/events/eventengine.cpp @@ -0,0 +1,133 @@ +/***************************************************************************************** + * * + * 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 + +#include +#include + +#include "eventengine_lua.inl" + +namespace openspace { + +#ifdef _DEBUG +uint64_t EventEngine::nEvents = 0; +#endif // _DEBUG + +events::Event* EventEngine::firstEvent() const { + return _firstEvent; +} + +void EventEngine::postFrameCleanup() { + _memory.reset(); + _firstEvent = nullptr; + _lastEvent = nullptr; +#ifdef _DEBUG + nEvents = 0; +#endif // _DEBUG +} + +void EventEngine::registerEventAction(events::Event::Type type, + std::string identifier, + std::optional filter) +{ + ActionInfo ai; + ai.action = std::move(identifier); + ai.filter = std::move(filter); + const auto it = _eventActions.find(type); + if (it != _eventActions.end()) { + it->second.push_back(ai); + } + else { + _eventActions[type] = { std::move(ai) }; + } +} + +void EventEngine::unregisterEventAction(events::Event::Type type, + const std::string& identifier, + std::optional filter) +{ + const auto it = _eventActions.find(type); + if (it != _eventActions.end()) { + const auto jt = std::find_if( + it->second.begin(), it->second.end(), + [identifier, filter](const ActionInfo& ai) { + const bool a = ai.action == identifier; + const bool f = !filter.has_value() || *filter == ai.filter; + return a && f; + } + ); + if (jt != it->second.end()) { + it->second.erase(jt); + } + } +} + +void EventEngine::triggerActions() const { + if (_eventActions.empty()) { + // Nothing to do here + return; + } + + const events::Event* e = _firstEvent; + while (e) { + const auto it = _eventActions.find(e->type); + if (it != _eventActions.end()) { + ghoul::Dictionary params = toParameter(*e); + for (const ActionInfo& ai : it->second) { + if (!ai.filter.has_value() || params.isSubset(*ai.filter)) { + global::actionManager->triggerAction(ai.action, params); + } + } + } + + e = e->next; + } +} + +scripting::LuaLibrary EventEngine::luaLibrary() { + scripting::LuaLibrary res; + res.name = "event"; + res.functions.push_back({ + "registerEventAction", + &luascriptfunctions::registerEventAction, + {}, + "string, string [, table]", + "Registers an action (second parameter) to be executed whenever an event (first " + "parameter) is encountered. If the optional third parameter is provided, it " + "describes a filter that the event is being checked against and only if it " + "passes the filter, the action is triggered" + }); + res.functions.push_back({ + "unregisterEventAction", + &luascriptfunctions::unregisterEventAction, + {}, + "string, string [, table]", + "Unregisters a specific combination of event (first parameter), action (second " + "parameter), and potentially a filter (optional third argument)" + }); + return res; +} + +} // namespace openspace diff --git a/src/events/eventengine_lua.inl b/src/events/eventengine_lua.inl new file mode 100644 index 0000000000..0d6bef77ba --- /dev/null +++ b/src/events/eventengine_lua.inl @@ -0,0 +1,49 @@ +/***************************************************************************************** + * * + * 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. * + ****************************************************************************************/ + +namespace openspace::luascriptfunctions { + +int registerEventAction(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, { 2, 3 }, "lua::registerEventAction"); + auto [event, action, filter] = + ghoul::lua::values>(L); + + events::Event::Type type = events::fromString(event); + + global::eventEngine->registerEventAction(type, std::move(action), std::move(filter)); + return 0; +} + +int unregisterEventAction(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, { 2, 3 }, "lua::unregisterEventAction"); + auto [event, action, filter] = + ghoul::lua::values>(L); + + events::Event::Type type = events::fromString(event); + + global::eventEngine->unregisterEventAction(type, action, filter); + return 0; +} + +} // namespace openspace::luascriptfunctions diff --git a/src/interaction/actionmanager.cpp b/src/interaction/actionmanager.cpp index 89a112b0d5..b16f79c5d6 100644 --- a/src/interaction/actionmanager.cpp +++ b/src/interaction/actionmanager.cpp @@ -86,7 +86,14 @@ void ActionManager::triggerAction(const std::string& identifier, const ghoul::Dictionary& arguments) const { ghoul_assert(!identifier.empty(), "Identifier must not be empty"); - ghoul_assert(hasAction(identifier), "Action was not found in the list"); + + if (!hasAction(identifier)) { + LWARNINGC( + "ActionManager", + fmt::format("Action '{}' not found in the list", identifier) + ); + return; + } const Action& a = action(identifier); if (arguments.isEmpty()) { @@ -97,7 +104,7 @@ void ActionManager::triggerAction(const std::string& identifier, } else { global::scriptEngine->queueScript( - fmt::format("local args = {}\n{}", ghoul::formatLua(arguments), a.command), + fmt::format("args = {}\n{}", ghoul::formatLua(arguments), a.command), scripting::ScriptEngine::RemoteScripting(a.synchronization) ); } diff --git a/src/interaction/actionmanager_lua.inl b/src/interaction/actionmanager_lua.inl index 1c677d5226..2c03117c5a 100644 --- a/src/interaction/actionmanager_lua.inl +++ b/src/interaction/actionmanager_lua.inl @@ -200,7 +200,7 @@ int actions(lua_State* L) { action.synchronization == interaction::Action::IsSynchronized::Yes ); lua_settable(L, -3); - + lua_settable(L, -3); } diff --git a/src/interaction/sessionrecording.cpp b/src/interaction/sessionrecording.cpp index fe390e3648..44ab5daab0 100644 --- a/src/interaction/sessionrecording.cpp +++ b/src/interaction/sessionrecording.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -461,6 +462,9 @@ bool SessionRecording::startPlayback(std::string& filename, (_playbackForceSimTimeAtStart ? 1 : 0) )); + global::eventEngine->publishEvent( + events::EventSessionRecordingPlayback::State::Started + ); initializePlayback_triggerStart(); global::navigationHandler->orbitalNavigator().updateOnCameraInteraction(); @@ -527,12 +531,18 @@ void SessionRecording::setPlaybackPause(bool pause) { global::timeManager->setPause(true); } _state = SessionState::PlaybackPaused; + global::eventEngine->publishEvent( + events::EventSessionRecordingPlayback::State::Paused + ); } else if (!pause && _state == SessionState::PlaybackPaused) { if (!_playbackPausedWithinDeltaTimePause) { global::timeManager->setPause(false); } _state = SessionState::Playback; + global::eventEngine->publishEvent( + events::EventSessionRecordingPlayback::State::Resumed + ); } } @@ -575,7 +585,7 @@ void SessionRecording::signalPlaybackFinishedForComponent(RecordedType type) { if (!_playbackActive_camera && !_playbackActive_time && !_playbackActive_script) { if (_playbackLoopMode) { - //Loop back to the beginning to replay + // Loop back to the beginning to replay _saveRenderingDuringPlayback = false; initializePlayback_time(global::windowDelegate->applicationTime()); initializePlayback_modeFlags(); @@ -586,6 +596,9 @@ void SessionRecording::signalPlaybackFinishedForComponent(RecordedType type) { _state = SessionState::Idle; _cleanupNeeded = true; LINFO("Playback session finished"); + global::eventEngine->publishEvent( + events::EventSessionRecordingPlayback::State::Finished + ); } } } @@ -606,6 +619,9 @@ void SessionRecording::stopPlayback() { _state = SessionState::Idle; _cleanupNeeded = true; LINFO("Session playback stopped"); + global::eventEngine->publishEvent( + events::EventSessionRecordingPlayback::State::Finished + ); } } @@ -1963,9 +1979,13 @@ bool SessionRecording::processCameraKeyframe(double now) { } // getPrevTimestamp(); - double prevTime = appropriateTimestamp(_timeline[_idxTimeline_cameraPtrPrev].t3stamps); + double prevTime = appropriateTimestamp( + _timeline[_idxTimeline_cameraPtrPrev].t3stamps + ); // getNextTimestamp(); - double nextTime = appropriateTimestamp(_timeline[_idxTimeline_cameraPtrNext].t3stamps); + double nextTime = appropriateTimestamp( + _timeline[_idxTimeline_cameraPtrNext].t3stamps + ); double t; if ((nextTime - prevTime) < 1e-7) { diff --git a/src/navigation/navigationhandler.cpp b/src/navigation/navigationhandler.cpp index 73d74d4c66..10fd242a53 100644 --- a/src/navigation/navigationhandler.cpp +++ b/src/navigation/navigationhandler.cpp @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -175,6 +178,7 @@ void NavigationHandler::updateCamera(double deltaTime) { } else if (_pathNavigator.isPlayingPath()) { _pathNavigator.updateCamera(deltaTime); + updateCameraTransitions(); } else { if (_disableJoystickInputs) { @@ -186,6 +190,7 @@ void NavigationHandler::updateCamera(double deltaTime) { } _orbitalNavigator.updateStatesFromInput(_inputState, deltaTime); _orbitalNavigator.updateCameraStateFromStates(deltaTime); + updateCameraTransitions(); } _orbitalNavigator.updateCameraScalingFromAnchor(deltaTime); @@ -208,6 +213,118 @@ void NavigationHandler::applyNavigationState(const NavigationState& ns) { _orbitalNavigator.clearPreviousState(); } +void NavigationHandler::updateCameraTransitions() { + // This function is concerned with managing transitions of the camera between + // different distances of interest relative to the focus node. For each transition two + // scenarios are handled; SceneGraphNodes can have attached actions for each + // transition, which are automatically triggered. Additionally, an + // EventCameraTransition event is fired that contains information about the focus node + // and the transition state that caused the vent to fire. + + // Diagram of events for a camera moving from right-to-left. + // Interaction sphere is 'O' in middle, and ')' are spherical boundaries. The approach + // factor, reach factor, and interaction sphere radius are all taken from the current + // focus node. + // + // |<------------------->| Approach factor * Interaction sphere + // |<------>| Reach Factor * Interaction sphere + // + // ( ( O ) ) + // ^ ^ ^ ^ + // OnExit OnMoveAway OnReach OnApproach + const glm::dvec3 anchorPos = anchorNode()->worldPosition(); + const glm::dvec3 cameraPos = _camera->positionVec3(); + const double currDistance = glm::distance(anchorPos, cameraPos); + const double d = anchorNode()->interactionSphere(); + const double af = anchorNode()->approachFactor(); + const double rf = anchorNode()->reachFactor(); + + using namespace std::string_literals; + if (_inAnchorApproachSphere) { + if (currDistance > d * (af + InteractionHystersis)) { + // We left the approach sphere outwards + _inAnchorApproachSphere = false; + + if (!anchorNode()->onExitAction().empty()) { + ghoul::Dictionary dict; + dict.setValue("Node", anchorNode()->identifier()); + dict.setValue("Transition", "Exiting"s); + for (const std::string& action : anchorNode()->onExitAction()) { + global::actionManager->triggerAction(action, dict); + } + } + + global::eventEngine->publishEvent( + _camera, + anchorNode(), + events::EventCameraFocusTransition::Transition::Exiting + ); + } + else if (currDistance < d * (rf - InteractionHystersis)) { + // We transitioned from the approach sphere into the reach sphere + _inAnchorApproachSphere = false; + _inAnchorReachSphere = true; + + if (!anchorNode()->onReachAction().empty()) { + ghoul::Dictionary dict; + dict.setValue("Node", anchorNode()->identifier()); + dict.setValue("Transition", "Reaching"s); + for (const std::string& action : anchorNode()->onReachAction()) { + global::actionManager->triggerAction(action, dict); + } + } + + global::eventEngine->publishEvent( + _camera, + anchorNode(), + events::EventCameraFocusTransition::Transition::Reaching + ); + } + } + else if (_inAnchorReachSphere && currDistance > d * (rf + InteractionHystersis)) { + // We transitioned from the reach sphere to the approach sphere + _inAnchorReachSphere = false; + _inAnchorApproachSphere = true; + + if (!anchorNode()->onRecedeAction().empty()) { + ghoul::Dictionary dict; + dict.setValue("Node", anchorNode()->identifier()); + dict.setValue("Transition", "Receding"s); + for (const std::string& action : anchorNode()->onRecedeAction()) { + global::actionManager->triggerAction(action, dict); + } + } + + global::eventEngine->publishEvent( + _camera, + anchorNode(), + events::EventCameraFocusTransition::Transition::Receding + ); + } + else if (!_inAnchorApproachSphere && !_inAnchorReachSphere && + currDistance < d * (af - InteractionHystersis)) + { + // We moved into the approach sphere + _inAnchorApproachSphere = true; + + if (!anchorNode()->onApproachAction().empty()) { + ghoul::Dictionary dict; + dict.setValue("Node", anchorNode()->identifier()); + dict.setValue("Transition", "Approaching"s); + for (const std::string& action : anchorNode()->onApproachAction()) { + global::actionManager->triggerAction(action, dict); + } + } + + global::eventEngine->publishEvent( + _camera, + anchorNode(), + events::EventCameraFocusTransition::Transition::Approaching + ); + } +} + + void NavigationHandler::setEnableKeyFrameInteraction() { _useKeyFrameInteraction = true; } diff --git a/src/navigation/navigationhandler_lua.inl b/src/navigation/navigationhandler_lua.inl index c3c9b1e4a4..02cb94216c 100644 --- a/src/navigation/navigationhandler_lua.inl +++ b/src/navigation/navigationhandler_lua.inl @@ -217,7 +217,7 @@ int bindJoystickButton(lua_State* L) { actionStr = actionStr.value_or("Press"); isRemote = isRemote.value_or(true); - interaction::JoystickAction action = + interaction::JoystickAction action = ghoul::from_string(*actionStr); global::navigationHandler->bindJoystickButtonCommand( diff --git a/src/navigation/navigationstate.cpp b/src/navigation/navigationstate.cpp index 52a0346170..7d99eb4095 100644 --- a/src/navigation/navigationstate.cpp +++ b/src/navigation/navigationstate.cpp @@ -80,8 +80,8 @@ NavigationState::NavigationState(const ghoul::Dictionary& dictionary) { } NavigationState::NavigationState(std::string anchor_, std::string aim_, - std::string referenceFrame_, glm::dvec3 position_, - std::optional up_, + std::string referenceFrame_, glm::dvec3 position_, + std::optional up_, double yaw_, double pitch_) : anchor(std::move(anchor_)) , aim(std::move(aim_)) diff --git a/src/navigation/orbitalnavigator.cpp b/src/navigation/orbitalnavigator.cpp index 0be1d03c89..fded5fca7f 100644 --- a/src/navigation/orbitalnavigator.cpp +++ b/src/navigation/orbitalnavigator.cpp @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -353,7 +356,12 @@ OrbitalNavigator::OrbitalNavigator() } SceneGraphNode* node = sceneGraphNode(_anchor.value()); if (node) { + const SceneGraphNode* previousAnchor = _anchorNode; setAnchorNode(node); + global::eventEngine->publishEvent( + previousAnchor, + node + ); } else { LERROR(fmt::format( @@ -455,7 +463,9 @@ OrbitalNavigator::OrbitalNavigator() addPropertySubOwner(_linearFlight); addPropertySubOwner(_idleBehavior); - _idleBehaviorDampenInterpolator.setTransferFunction(ghoul::quadraticEaseInOut); + _idleBehaviorDampenInterpolator.setTransferFunction( + ghoul::quadraticEaseInOut + ); _idleBehavior.dampenInterpolationTime.onChange([&]() { _idleBehaviorDampenInterpolator.setInterpolationTime( _idleBehavior.dampenInterpolationTime @@ -804,8 +814,14 @@ glm::dvec3 OrbitalNavigator::cameraToSurfaceVector(const glm::dvec3& cameraPos, void OrbitalNavigator::setFocusNode(const SceneGraphNode* focusNode, bool resetVelocitiesOnChange) { + const SceneGraphNode* previousAnchor = _anchorNode; setAnchorNode(focusNode, resetVelocitiesOnChange); setAimNode(nullptr); + + global::eventEngine->publishEvent( + previousAnchor, + focusNode + ); } void OrbitalNavigator::setFocusNode(const std::string& focusNode, bool) { @@ -953,8 +969,8 @@ bool OrbitalNavigator::shouldFollowAnchorRotation(const glm::dvec3& cameraPositi const double distanceToCamera = glm::distance(cameraPosition, _anchorNode->worldPosition()); - - return distanceToCamera < maximumDistanceForRotation; + bool shouldFollow = distanceToCamera < maximumDistanceForRotation; + return shouldFollow; } bool OrbitalNavigator::followingAnchorRotation() const { @@ -1617,8 +1633,8 @@ void OrbitalNavigator::applyIdleBehavior(double deltaTime, glm::dvec3& position, break; case IdleBehavior::Behavior::OrbitAtConstantLat: { // Assume that "north" coincides with the local z-direction - // @TODO (2021-07-09, emmbr) Make each scene graph node aware of its own - // north/up, so that we can query this information rather than assuming it. + // @TODO (2021-07-09, emmbr) Make each scene graph node aware of its own + // north/up, so that we can query this information rather than assuming it. // The we could also combine this idle behavior with the next const glm::dvec3 north = glm::dvec3(0.0, 0.0, 1.0); orbitAroundAxis(north, deltaTime, position, globalRotation, speedScale); @@ -1658,14 +1674,14 @@ void OrbitalNavigator::orbitAnchor(double deltaTime, glm::dvec3& position, position += rotationDiffVec3; } -void OrbitalNavigator::orbitAroundAxis(const glm::dvec3 axis, double deltaTime, +void OrbitalNavigator::orbitAroundAxis(const glm::dvec3 axis, double deltaTime, glm::dvec3& position, glm::dquat& globalRotation, double speedScale) { ghoul_assert(_anchorNode != nullptr, "Node to orbit must be set!"); const glm::dmat4 modelTransform = _anchorNode->modelTransform(); - const glm::dvec3 axisInWorldCoords = + const glm::dvec3 axisInWorldCoords = glm::dmat3(modelTransform) * glm::normalize(axis); // Compute rotation to be applied around the axis diff --git a/src/navigation/path.cpp b/src/navigation/path.cpp index 8e6b03cdff..97a937cd41 100644 --- a/src/navigation/path.cpp +++ b/src/navigation/path.cpp @@ -59,7 +59,8 @@ namespace { // The desired duration traversing the specified path segment should take std::optional duration; - // (Node): The target node of the camera path. Not optional for 'Node' instructions + // (Node): The target node of the camera path. Not optional for 'Node' + // instructions std::optional target; // (Node): An optional position in relation to the target node, in model @@ -271,9 +272,11 @@ double Path::speedAlongPath(double traveledDistance) const { speed *= remainingDistance / closeUpDistance + 0.01; } - // TODO: also dampen speed based on curvature, or make sure the curve has a rounder shape + // TODO: also dampen speed based on curvature, or make sure the curve has a rounder + // shape - // TODO: check for when path is shorter than the starUpDistance or closeUpDistance variables + // TODO: check for when path is shorter than the starUpDistance or closeUpDistance + // variables return _speedFactorFromDuration * speed; } @@ -384,7 +387,9 @@ Waypoint computeWaypointFromNodeInfo(const NodeInfo& info, const Waypoint& start if (info.position.has_value()) { // The position in instruction is given in the targetNode's local coordinates. // Convert to world coordinates - targetPos = glm::dvec3(targetNode->modelTransform() * glm::dvec4(*info.position, 1.0)); + targetPos = glm::dvec3( + targetNode->modelTransform() * glm::dvec4(*info.position, 1.0) + ); } else { const double radius = Waypoint::findValidBoundingSphere(targetNode); diff --git a/src/navigation/pathcurves/zoomoutoverviewcurve.cpp b/src/navigation/pathcurves/zoomoutoverviewcurve.cpp index 965fbfe3ab..4f96553a44 100644 --- a/src/navigation/pathcurves/zoomoutoverviewcurve.cpp +++ b/src/navigation/pathcurves/zoomoutoverviewcurve.cpp @@ -80,7 +80,8 @@ ZoomOutOverviewCurve::ZoomOutOverviewCurve(const Waypoint& start, const Waypoint goodStepDirection = glm::normalize(n1 + n2); } - // Find a direction that is orthogonal to the line between the start and end position + // Find a direction that is orthogonal to the line between the start and end + // position const glm::dvec3 startPosToEndPos = end.position() - start.position(); const glm::dvec3 outwardStepVector = 0.5 * glm::length(startPosToEndPos) * goodStepDirection; diff --git a/src/navigation/pathnavigator.cpp b/src/navigation/pathnavigator.cpp index 87edf005e7..94dff5aa2c 100644 --- a/src/navigation/pathnavigator.cpp +++ b/src/navigation/pathnavigator.cpp @@ -72,7 +72,8 @@ namespace { "triggered once the path has reached its target." }; - constexpr const openspace::properties::Property::PropertyInfo MinBoundingSphereInfo = { + constexpr const openspace::properties::Property::PropertyInfo MinBoundingSphereInfo = + { "MinimalValidBoundingSphere", "Minimal Valid Bounding Sphere", "The minimal allowed value for a bounding sphere, in meters. Used for " @@ -203,8 +204,10 @@ void PathNavigator::updateCamera(double deltaTime) { if (_applyIdleBehaviorOnFinish) { constexpr const char* ApplyIdleBehaviorScript = - "local f = 'NavigationHandler.OrbitalNavigator.IdleBehavior.ApplyIdleBehavior';" - "openspace.setPropertyValueSingle(f, true);"; + "openspace.setPropertyValueSingle(" + "'NavigationHandler.OrbitalNavigator.IdleBehavior.ApplyIdleBehavior'," + "true" + ");"; global::scriptEngine->queueScript( ApplyIdleBehaviorScript, diff --git a/src/navigation/pathnavigator_lua.inl b/src/navigation/pathnavigator_lua.inl index a5a71f1c59..2e946a8492 100644 --- a/src/navigation/pathnavigator_lua.inl +++ b/src/navigation/pathnavigator_lua.inl @@ -86,9 +86,8 @@ int goTo(lua_State* L) { return ghoul::lua::luaError(L, "Unknown node name: " + nodeIdentifier); } - using namespace std::string_literals; ghoul::Dictionary insDict; - insDict.setValue("TargetType", "Node"s); + insDict.setValue("TargetType", std::string("Node")); insDict.setValue("Target", nodeIdentifier); if (useUpFromTargetOrDuration.has_value()) { if (std::holds_alternative(*useUpFromTargetOrDuration)) { @@ -134,9 +133,8 @@ int goToHeight(lua_State* L) { return ghoul::lua::luaError(L, "Unknown node name: " + nodeIdentifier); } - using namespace std::string_literals; ghoul::Dictionary insDict; - insDict.setValue("TargetType", "Node"s); + insDict.setValue("TargetType", std::string("Node")); insDict.setValue("Target", nodeIdentifier); insDict.setValue("Height", height); if (useUpFromTargetOrDuration.has_value()) { @@ -190,9 +188,8 @@ int goToNavigationState(lua_State* L) { ); } - using namespace std::string_literals; ghoul::Dictionary instruction; - instruction.setValue("TargetType", "NavigationState"s); + instruction.setValue("TargetType", std::string("NavigationState")); instruction.setValue("NavigationState", navigationState); if (duration.has_value()) { diff --git a/src/navigation/waypoint.cpp b/src/navigation/waypoint.cpp index 5799a92635..c1080533bb 100644 --- a/src/navigation/waypoint.cpp +++ b/src/navigation/waypoint.cpp @@ -53,7 +53,7 @@ Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::stri } Waypoint::Waypoint(const NavigationState& ns) { - const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); + const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); if (!anchorNode) { LERROR(fmt::format("Could not find node '{}' to target", ns.anchor)); @@ -92,7 +92,7 @@ double Waypoint::validBoundingSphere() const { double Waypoint::findValidBoundingSphere(const SceneGraphNode* node) { double bs = static_cast(node->boundingSphere()); - const double minValidBoundingSphere = + const double minValidBoundingSphere = global::navigationHandler->pathNavigator().minValidBoundingSphere(); if (bs < minValidBoundingSphere) { diff --git a/src/network/parallelpeer.cpp b/src/network/parallelpeer.cpp index a4f5f77f8a..b5ee89dd38 100644 --- a/src/network/parallelpeer.cpp +++ b/src/network/parallelpeer.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -215,7 +217,7 @@ void ParallelPeer::handleMessage(const ParallelConnection::Message& message) { nConnectionsMessageReceived(message.content); break; default: - //unknown message type + // unknown message type break; } } @@ -259,8 +261,7 @@ double ParallelPeer::latencyStandardDeviation() const { return std::sqrt(latencyVariance); } -void ParallelPeer::dataMessageReceived(const std::vector& message) -{ +void ParallelPeer::dataMessageReceived(const std::vector& message) { size_t offset = 0; // The type of data message received @@ -290,8 +291,10 @@ void ParallelPeer::dataMessageReceived(const std::vector& message) pose.scale = kf._scale; pose.followFocusNodeRotation = kf._followNodeRotation; - global::navigationHandler->keyframeNavigator().addKeyframe(convertedTimestamp, - pose); + global::navigationHandler->keyframeNavigator().addKeyframe( + convertedTimestamp, + pose + ); break; } case datamessagestructures::Type::TimelineData: { @@ -359,10 +362,9 @@ void ParallelPeer::dataMessageReceived(const std::vector& message) } } -void ParallelPeer::connectionStatusMessageReceived(const std::vector& message) - { +void ParallelPeer::connectionStatusMessageReceived(const std::vector& message) { if (message.size() < 2 * sizeof(uint32_t)) { - LERROR("Malformed connection status message."); + LERROR("Malformed connection status message"); return; } size_t pointer = 0; @@ -376,7 +378,7 @@ void ParallelPeer::connectionStatusMessageReceived(const std::vector& mess pointer += sizeof(uint32_t); if (hostNameSize > message.size() - pointer) { - LERROR("Malformed connection status message."); + LERROR("Malformed connection status message"); return; } @@ -387,7 +389,7 @@ void ParallelPeer::connectionStatusMessageReceived(const std::vector& mess pointer += hostNameSize; if (status > ParallelConnection::Status::Host) { - LERROR("Invalid status."); + LERROR("Invalid status"); return; } @@ -410,7 +412,7 @@ void ParallelPeer::connectionStatusMessageReceived(const std::vector& mess void ParallelPeer::nConnectionsMessageReceived(const std::vector& message) { if (message.size() < sizeof(uint32_t)) { - LERROR("Malformed host info message."); + LERROR("Malformed host info message"); return; } const uint32_t nConnections = *(reinterpret_cast(&message[0])); @@ -532,9 +534,54 @@ void ParallelPeer::preSynchronization() { void ParallelPeer::setStatus(ParallelConnection::Status status) { if (_status != status) { + ParallelConnection::Status prevStatus = _status; _status = status; _timeJumped = true; _connectionEvent->publish("statusChanged"); + + + EventEngine* ee = global::eventEngine; + const bool isConnected = + status == ParallelConnection::Status::ClientWithoutHost || + status == ParallelConnection::Status::ClientWithHost || + status == ParallelConnection::Status::Host; + const bool wasConnected = + prevStatus == ParallelConnection::Status::ClientWithoutHost || + prevStatus == ParallelConnection::Status::ClientWithHost || + prevStatus == ParallelConnection::Status::Host; + const bool isDisconnected = status == ParallelConnection::Status::Disconnected; + const bool wasDisconnected = + prevStatus == ParallelConnection::Status::Disconnected; + const bool isHost = status == ParallelConnection::Status::Host; + const bool wasHost = prevStatus == ParallelConnection::Status::Host; + const bool isClient = + status == ParallelConnection::Status::ClientWithoutHost || + status == ParallelConnection::Status::ClientWithHost; + const bool wasClient = + prevStatus == ParallelConnection::Status::ClientWithoutHost || + prevStatus == ParallelConnection::Status::ClientWithHost; + + + if (isDisconnected && wasConnected) { + ee->publishEvent( + events::EventParallelConnection::State::Lost + ); + } + if (isConnected && wasDisconnected) { + ee->publishEvent( + events::EventParallelConnection::State::Established + ); + } + if (isHost && (wasClient || wasDisconnected)) { + ee->publishEvent( + events::EventParallelConnection::State::HostshipGained + ); + } + if ((isClient || isDisconnected) && wasHost) { + ee->publishEvent( + events::EventParallelConnection::State::HostshipLost + ); + } } if (isHost()) { global::timeManager->addTimeJumpCallback([this]() { diff --git a/src/properties/propertyowner.cpp b/src/properties/propertyowner.cpp index 94c716e826..4426580f8d 100644 --- a/src/properties/propertyowner.cpp +++ b/src/properties/propertyowner.cpp @@ -24,6 +24,9 @@ #include +#include +#include +#include #include #include #include diff --git a/src/rendering/helper.cpp b/src/rendering/helper.cpp index cfa790b3d0..9d5d6f8138 100644 --- a/src/rendering/helper.cpp +++ b/src/rendering/helper.cpp @@ -458,7 +458,7 @@ std::pair, std::vector> createSphere(int nSegments // 0 -> PI // azimuth angle (east to west) const float theta = fi * glm::pi() / nSegments; - + // 0 -> 2*PI const float phi = fj * glm::pi() * 2.f / nSegments; diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index d7c68c6e1b..b9c4bafc9f 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -258,7 +260,7 @@ namespace { "Enabled Font Color", "The font color used for enabled options." }; - + constexpr openspace::properties::Property::PropertyInfo DisabledFontColorInfo = { "DisabledFontColor", "Disabled Font Color", @@ -397,7 +399,7 @@ RenderEngine::RenderEngine() _enabledFontColor.setViewOption(openspace::properties::Property::ViewOptions::Color); addProperty(_enabledFontColor); - + _disabledFontColor.setViewOption(openspace::properties::Property::ViewOptions::Color); addProperty(_disabledFontColor); } @@ -1105,8 +1107,11 @@ void RenderEngine::addScreenSpaceRenderable(std::unique_ptrinitialize(); s->initializeGL(); - global::screenSpaceRootPropertyOwner->addPropertySubOwner(s.get()); + ScreenSpaceRenderable* ssr = s.get(); + global::screenSpaceRootPropertyOwner->addPropertySubOwner(ssr); global::screenSpaceRenderables->push_back(std::move(s)); + + global::eventEngine->publishEvent(ssr); } void RenderEngine::removeScreenSpaceRenderable(ScreenSpaceRenderable* s) { @@ -1119,9 +1124,10 @@ void RenderEngine::removeScreenSpaceRenderable(ScreenSpaceRenderable* s) { if (it != global::screenSpaceRenderables->end()) { s->deinitialize(); global::screenSpaceRootPropertyOwner->removePropertySubOwner(s); - global::screenSpaceRenderables->erase(it); } + + global::eventEngine->publishEvent(s); } void RenderEngine::removeScreenSpaceRenderable(const std::string& identifier) { @@ -1131,8 +1137,7 @@ void RenderEngine::removeScreenSpaceRenderable(const std::string& identifier) { } } -ScreenSpaceRenderable* RenderEngine::screenSpaceRenderable( - const std::string& identifier) +ScreenSpaceRenderable* RenderEngine::screenSpaceRenderable(const std::string& identifier) { const auto it = std::find_if( global::screenSpaceRenderables->begin(), diff --git a/src/rendering/screenspacerenderable.cpp b/src/rendering/screenspacerenderable.cpp index ef4b358966..d001481b64 100644 --- a/src/rendering/screenspacerenderable.cpp +++ b/src/rendering/screenspacerenderable.cpp @@ -258,7 +258,7 @@ namespace { std::optional opacity [[codegen::inrange(0.f, 1.f)]]; // Defines either a single or multiple tags that apply to this - // ScreenSpaceRenderable, thus making it possible to address multiple, separate + // ScreenSpaceRenderable, thus making it possible to address multiple, separate // Renderables with a single property change std::optional>> tag; }; diff --git a/src/scene/profile.cpp b/src/scene/profile.cpp index 938335bcdc..9ded721ecc 100644 --- a/src/scene/profile.cpp +++ b/src/scene/profile.cpp @@ -598,13 +598,9 @@ void Profile::removeAsset(const std::string& path) { } const auto it = std::find(assets.cbegin(), assets.cend(), path); - if (it == assets.end()) { - throw ghoul::RuntimeError(fmt::format( - "Tried to remove non-existing asset '{}'", path - )); + if (it != assets.end()) { + assets.erase(it); } - - assets.erase(it); } std::string Profile::serialize() const { diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 5960f71cbf..857040cd88 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,9 @@ namespace { } } #endif // TRACY_ENABLE + + template struct overloaded : Ts... { using Ts::operator()...; }; + template overloaded(Ts...)->overloaded; } // namespace namespace openspace { @@ -121,6 +125,7 @@ void Scene::registerNode(SceneGraphNode* node) { _nodesByIdentifier[node->identifier()] = node; addPropertySubOwner(node); _dirtyNodeRegistry = true; + global::eventEngine->publishEvent(node); } void Scene::unregisterNode(SceneGraphNode* node) { @@ -140,6 +145,7 @@ void Scene::unregisterNode(SceneGraphNode* node) { } removePropertySubOwner(node); _dirtyNodeRegistry = true; + global::eventEngine->publishEvent(node); } void Scene::markNodeRegistryDirty() { @@ -581,6 +587,10 @@ void Scene::updateInterpolations() { i.prop->interpolateValue(t, i.easingFunction); i.isExpired = (t == 1.f); + + if (i.isExpired) { + global::eventEngine->publishEvent(i.prop); + } } _propertyInterpolationInfos.erase( @@ -613,10 +623,14 @@ void Scene::setPropertiesFromProfile(const Profile& p) { // Remove group name from start of regex and replace with '*' uriOrRegex = removeGroupNameFromUri(uriOrRegex); } + _profilePropertyName = uriOrRegex; ghoul::lua::push(L, uriOrRegex); ghoul::lua::push(L, 0.0); + + std::string workingValue = prop.value; + ghoul::trimSurroundingCharacters(workingValue, ' '); // Later functions expect the value to be at the last position on the stack - propertyPushValueFromProfileToLuaState(L, prop.value); + propertyPushProfileValueToLua(L, workingValue); applyRegularExpression( L, @@ -631,21 +645,149 @@ void Scene::setPropertiesFromProfile(const Profile& p) { } } -void propertyPushValueFromProfileToLuaState(ghoul::lua::LuaState& L, - const std::string& value) +void Scene::propertyPushProfileValueToLua(ghoul::lua::LuaState& L, + const std::string& value) { - if (luascriptfunctions::isBoolValue(value)) { - ghoul::lua::push(L, (value == "true") ? true : false); + _valueIsTable = false; + ProfilePropertyLua elem = propertyProcessValue(L, value); + if (!_valueIsTable) { + std::visit(overloaded{ + [&L](const bool value) { + ghoul::lua::push(L, value); + }, + [&L](const float value) { + ghoul::lua::push(L, value); + }, + [&L](const std::string value) { + ghoul::lua::push(L, value); + }, + [&L](const ghoul::lua::nil_t nilValue) { + ghoul::lua::push(L, nilValue); + } + }, elem); } - else if (luascriptfunctions::isFloatValue(value)) { - ghoul::lua::push(L, std::stof(value)); +} + +ProfilePropertyLua Scene::propertyProcessValue(ghoul::lua::LuaState& L, + const std::string& value) +{ + ProfilePropertyLua result; + PropertyValueType pType = propertyValueType(value); + + switch (pType) { + case PropertyValueType::Boolean: + result = (value == "true") ? true : false; + break; + case PropertyValueType::Float: + result = std::stof(value); + break; + case PropertyValueType::Nil: + result = ghoul::lua::nil_t(); + break; + case PropertyValueType::Table: + ghoul::trimSurroundingCharacters(const_cast(value), '{'); + ghoul::trimSurroundingCharacters(const_cast(value), '}'); + handlePropertyLuaTableEntry(L, value); + _valueIsTable = true; + break; + case PropertyValueType::String: + default: + ghoul::trimSurroundingCharacters(const_cast(value), '\"'); + ghoul::trimSurroundingCharacters(const_cast(value), '['); + ghoul::trimSurroundingCharacters(const_cast(value), ']'); + result = value; + break; + } + return result; +} + +void Scene::handlePropertyLuaTableEntry(ghoul::lua::LuaState& L, const std::string& value) +{ + PropertyValueType enclosedType; + size_t commaPos = value.find(',', 0); + if (commaPos != std::string::npos) { + enclosedType = propertyValueType(value.substr(0, commaPos)); } else { - std::string stringRepresentation = value; - if (value.compare("nil") != 0) { - stringRepresentation = "[[" + stringRepresentation + "]]"; + enclosedType = propertyValueType(value); + } + + switch (enclosedType) { + case PropertyValueType::Boolean: + LERROR(fmt::format( + "A lua table of bool values is not supported. (processing property {})", + _profilePropertyName) + ); + break; + case PropertyValueType::Float: + { + std::vector valsF; + processPropertyValueTableEntries(L, value, valsF); + ghoul::lua::push(L, valsF); + } + break; + case PropertyValueType::String: + { + std::vector valsS; + processPropertyValueTableEntries(L, value, valsS); + ghoul::lua::push(L, valsS); + } + break; + case PropertyValueType::Table: + default: + LERROR(fmt::format( + "Table-within-a-table values are not supported for profile a " + "property (processing property {})", _profilePropertyName + )); + break; + } +} + +template +void Scene::processPropertyValueTableEntries(ghoul::lua::LuaState& L, + const std::string& value, std::vector& table) +{ + size_t commaPos = 0; + size_t prevPos = 0; + std::string nextValue; + while (commaPos != std::string::npos) { + commaPos = value.find(',', prevPos); + if (commaPos != std::string::npos) { + nextValue = value.substr(prevPos, commaPos - prevPos); + prevPos = commaPos + 1; } - ghoul::lua::push(L, stringRepresentation); + else { + nextValue = value.substr(prevPos); + } + ghoul::trimSurroundingCharacters(nextValue, ' '); + ProfilePropertyLua tableElement = propertyProcessValue(L, nextValue); + try { + table.push_back(std::get(tableElement)); + } + catch (std::bad_variant_access&) { + LERROR(fmt::format( + "Error attempting to parse profile property setting for " + "{} using value = {}", _profilePropertyName, value + )); + } + } +} + +PropertyValueType Scene::propertyValueType(const std::string& value) { + if (luascriptfunctions::isBoolValue(value)) { + return PropertyValueType::Boolean; + } + else if (luascriptfunctions::isFloatValue(value)) { + return PropertyValueType::Float; + } + else if (luascriptfunctions::isNilValue(value)) { + return PropertyValueType::Nil; + } + else if (luascriptfunctions::isTableValue(value)) { + return PropertyValueType::Table; + } + else { + return PropertyValueType::String; } } @@ -769,14 +911,16 @@ scripting::LuaLibrary Scene::luaLibrary() { &luascriptfunctions::worldPosition, {}, "string", - "Returns the world position of the scene graph node with the given string as identifier" + "Returns the world position of the scene graph node with the given " + "string as identifier" }, { "worldRotation", & luascriptfunctions::worldRotation, {}, "string", - "Returns the world rotation matrix of the scene graph node with the given string as identifier" + "Returns the world rotation matrix of the scene graph node with the " + "given string as identifier" }, { "setParent", @@ -786,7 +930,6 @@ scripting::LuaLibrary Scene::luaLibrary() { "The scene graph node identified by the first string is reparented to be " "a child of the scene graph node identified by the second string." } - } }; } diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index 31492db5cd..bd6650045b 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -929,7 +931,7 @@ int setParent(lua_State* L) { * isBoolValue(const std::string& s): * Used to check if a string is a lua bool type. Returns false if not a valid bool string. */ -bool isBoolValue(const std::string& s) { +bool isBoolValue(std::string_view s) { return (s == "true" || s == "false"); } @@ -940,12 +942,32 @@ bool isBoolValue(const std::string& s) { */ bool isFloatValue(const std::string& s) { try { - float converted = std::stof(s); - return true; + float converted = std::numeric_limits::min(); + converted = std::stof(s); + return (converted != std::numeric_limits::min()); } catch (...) { return false; } } +/** + * \ingroup LuaScripts + * isNilValue(const std::string& s): + * Used to check if a string is a lua 'nil' value. Returns false if not. + */ +bool isNilValue(std::string_view s) { + return (s == "nil"); +} + +/** + * \ingroup LuaScripts + * isTableValue(const std::string& s): + * Used to check if a string contains a lua table rather than an individual value. + * Returns false if not. + */ +bool isTableValue(std::string_view s) { + return ((s.front() == '{') && (s.back() == '}')); +} + } // namespace openspace::luascriptfunctions diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 6ce162c4fa..d7c5efdc80 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -111,6 +111,20 @@ namespace { openspace::properties::Property::Visibility::Developer }; + constexpr openspace::properties::Property::PropertyInfo ApproachFactorInfo = { + "ApproachFactor", + "Approach Factor", + "This value is a multiplication factor for the interaction sphere that " + "determines when the camera is 'approaching' the scene graph node" + }; + + constexpr openspace::properties::Property::PropertyInfo ReachFactorInfo = { + "ReachFactor", + "Reach Factor", + "This value is a multiplication factor for the interaction sphere that " + "determines when the camera has 'reached' the scene graph node" + }; + constexpr openspace::properties::Property::PropertyInfo GuiPathInfo = { "GuiPath", "Gui Path", @@ -174,13 +188,10 @@ namespace { // children std::optional renderable [[codegen::reference("renderable")]]; - // A hard-coded bounding sphere to be used for the cases where the Renderable is - // not able to provide a reasonable bounding sphere or the calculated bounding - // sphere needs to be overwritten for some reason + // [[codegen::verbatim(BoundingSphereInfo.description)]] std::optional boundingSphere; - // A hard-coded radius for limiting the interaction radius, meaning the minimal - // distance that the camera can approach this scene graph node + // [[codegen::verbatim(InteractionSphereInfo.description)]] std::optional interactionSphere; struct Transform { @@ -208,6 +219,36 @@ namespace { // corresponding to a 'Translation', a 'Rotation', and a 'Scale' std::optional transform; + // This value is a multiplication factor for the interaction sphere that + // determines when the camera is 'approaching' the scene graph node. If this value + // is not specified, a default value of 5 is used instead. This value must be + // larger than the reachFactor or unexpected things might happen + std::optional approachFactor [[codegen::greaterequal(0.0)]]; + + // This value is a multiplication factor for the interaction sphere that + // determines when the camera has 'reached' the scene graph node. If this value is + // not specified, a default value of 1.25 is used instead. This value must be + // smaller than the approachFactor or unexpected things might happen + std::optional reachFactor [[codegen::greaterequal(0.0)]]; + + // One or multiple actions that are executed whenever the camera is focused on + // this scene graph node and if it enters the interaction sphere of the node + std::optional>> onApproach; + + // One or multiple actions that are executed whenever the camera is focused on + // this scene graph node and if it transitions from the approach distance to the + // reach distance of the node + std::optional>> onReach; + + // One or multiple actions that are executed whenever the camera is focused on + // this scene graph node and if it transitions from the reach distance to the + // approach distance of the node + std::optional>> onRecede; + + // One or multiple actions that are executed whenever the camera is focused on + // this scene graph node and if it exits the interaction sphere of the node + std::optional>> onExit; + // Specifies the time frame for when this node should be active std::optional timeFrame [[codegen::reference("core_time_frame")]]; @@ -287,6 +328,8 @@ ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( result->_overrideBoundingSphere = p.boundingSphere; result->_overrideInteractionSphere = p.interactionSphere; + result->_approachFactor = p.approachFactor.value_or(result->_approachFactor); + result->_reachFactor = p.reachFactor.value_or(result->_reachFactor); if (p.transform.has_value()) { if (p.transform->translation.has_value()) { @@ -378,6 +421,43 @@ ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( )); } + // Extracting the actions from the dictionary + if (p.onApproach.has_value()) { + if (std::holds_alternative(*p.onApproach)) { + result->_onApproachAction = { std::get(*p.onApproach) }; + } + else { + result->_onApproachAction = std::get>(*p.onApproach); + } + } + + if (p.onReach.has_value()) { + if (std::holds_alternative(*p.onReach)) { + result->_onReachAction = { std::get(*p.onReach) }; + } + else { + result->_onReachAction = std::get>(*p.onReach); + } + } + + if (p.onRecede.has_value()) { + if (std::holds_alternative(*p.onRecede)) { + result->_onRecedeAction = { std::get(*p.onRecede) }; + } + else { + result->_onRecedeAction = std::get>(*p.onRecede); + } + } + + if (p.onExit.has_value()) { + if (std::holds_alternative(*p.onExit)) { + result->_onExitAction = { std::get(*p.onExit) }; + } + else { + result->_onExitAction = std::get>(*p.onExit); + } + } + if (p.tag.has_value()) { if (std::holds_alternative(*p.tag)) { result->addTag(std::get(*p.tag)); @@ -425,6 +505,8 @@ SceneGraphNode::SceneGraphNode() } , _boundingSphere(BoundingSphereInfo, -1.0, -1.0, 1e12) , _interactionSphere(InteractionSphereInfo, -1.0, -1.0, 1e12) + , _approachFactor(ApproachFactorInfo, 5.0) + , _reachFactor(ReachFactorInfo, 1.25) , _computeScreenSpaceValues(ComputeScreenSpaceInfo, false) , _screenSpacePosition(ScreenSpacePositionInfo, glm::ivec2(-1, -1)) , _screenVisibility(ScreenVisibilityInfo, false) @@ -463,6 +545,8 @@ SceneGraphNode::SceneGraphNode() // negative values //_interactionSphere.setExponent(10.f); addProperty(_interactionSphere); + addProperty(_reachFactor); + addProperty(_approachFactor); addProperty(_showDebugSphere); } @@ -1066,6 +1150,22 @@ std::vector SceneGraphNode::children() const { return nodes; } +const std::vector& SceneGraphNode::onApproachAction() const { + return _onApproachAction; +} + +const std::vector& SceneGraphNode::onReachAction() const { + return _onReachAction; +} + +const std::vector& SceneGraphNode::onRecedeAction() const { + return _onRecedeAction; +} + +const std::vector& SceneGraphNode::onExitAction() const { + return _onExitAction; +} + double SceneGraphNode::boundingSphere() const { if (_overrideBoundingSphere.has_value()) { return glm::compMax(scale() * *_overrideBoundingSphere); @@ -1092,6 +1192,14 @@ double SceneGraphNode::interactionSphere() const { } } +double SceneGraphNode::reachFactor() const { + return _reachFactor; +} + +double SceneGraphNode::approachFactor() const { + return _approachFactor; +} + const Renderable* SceneGraphNode::renderable() const { return _renderable.get(); } diff --git a/src/scene/scenelicensewriter.cpp b/src/scene/scenelicensewriter.cpp index 8e1f9149e3..e9eba3ce1a 100644 --- a/src/scene/scenelicensewriter.cpp +++ b/src/scene/scenelicensewriter.cpp @@ -70,12 +70,30 @@ std::string SceneLicenseWriter::generateJson() const { constexpr const char* replStr = R"("{}": "{}", )"; constexpr const char* replStr2 = R"("{}": "{}")"; json << "{"; - json << fmt::format(replStr, "name", escapedJson(global::profile->meta->name.value())); - json << fmt::format(replStr, "version", escapedJson(global::profile->meta->version.value())); - json << fmt::format(replStr, "description", escapedJson(global::profile->meta->description.value())); - json << fmt::format(replStr, "author", escapedJson(global::profile->meta->author.value())); - json << fmt::format(replStr, "url", escapedJson(global::profile->meta->url.value())); - json << fmt::format(replStr2, "license", escapedJson(global::profile->meta->license.value())); + json << fmt::format( + replStr, + "name", escapedJson(global::profile->meta->name.value_or("")) + ); + json << fmt::format( + replStr, + "version", escapedJson(global::profile->meta->version.value_or("")) + ); + json << fmt::format( + replStr, + "description", escapedJson(global::profile->meta->description.value_or("")) + ); + json << fmt::format( + replStr, + "author", escapedJson(global::profile->meta->author.value_or("")) + ); + json << fmt::format( + replStr, + "url", escapedJson(global::profile->meta->url.value_or("")) + ); + json << fmt::format( + replStr2, + "license", escapedJson(global::profile->meta->license.value_or("")) + ); json << "}"; if (++metaCount != metaTotal) { diff --git a/src/util/coordinateconversion.cpp b/src/util/coordinateconversion.cpp index daaa9a34ea..a9ed1b325c 100644 --- a/src/util/coordinateconversion.cpp +++ b/src/util/coordinateconversion.cpp @@ -51,7 +51,8 @@ namespace { { throw ghoul::lua::LuaRuntimeException(fmt::format( "Ra or Dec '{}' format is incorrect. Correct format is: Ra 'XhYmZs', " - "and Dec 'XdYmZs'", str)); + "and Dec 'XdYmZs'", str + )); } // Construct the number strings @@ -66,7 +67,8 @@ namespace { if (std::floor(temp) != temp) { throw ghoul::lua::LuaRuntimeException(fmt::format( "Ra or Dec '{}' format is incorrect. Correct format is: Ra 'XhYmZs', " - "and Dec 'XdYmZs', where X must be an integer", str)); + "and Dec 'XdYmZs', where X must be an integer", str + )); } hoursOrDegrees = std::stoi(sHoursOrDegrees); @@ -75,83 +77,90 @@ namespace { if (std::floor(temp) != temp) { throw ghoul::lua::LuaRuntimeException(fmt::format( "Ra or Dec '{}' format is incorrect. Correct format is: Ra 'XhYmZs', " - "and Dec 'XdYmZs', where Y must be an integer", str)); + "and Dec 'XdYmZs', where Y must be an integer", str + )); } minutes = std::stoi(sMinutes); // Seconds is a double seconds = std::stod(sSeconds); - } catch (const std::invalid_argument& ia) { + } + catch (const std::invalid_argument&) { throw ghoul::lua::LuaRuntimeException(fmt::format( "Ra or Dec '{}' format is incorrect. Correct format is: Ra 'XhYmZs', " - "and Dec 'XdYmZs'", str)); + "and Dec 'XdYmZs'", str + )); } } void parseRa(const std::string& ra, int& hours, int& minutes, double& seconds) { if (ra.find('d') != std::string::npos) { throw ghoul::lua::LuaRuntimeException(fmt::format( - "Ra '{}' format is incorrect. Correct format is: 'XhYmZs'", ra)); + "Ra '{}' format is incorrect. Correct format is: 'XhYmZs'", ra + )); } parseString(ra, hours, minutes, seconds); } - void parseDec(const std::string& dec, int& degrees, int& minutes, - double& seconds) - { + void parseDec(const std::string& dec, int& degrees, int& minutes, double& seconds) { if (dec.find('h') != std::string::npos) { throw ghoul::lua::LuaRuntimeException(fmt::format( - "Dec '{}' format is incorrect. Correct format is: 'XdYmZs'", dec)); + "Dec '{}' format is incorrect. Correct format is: 'XdYmZs'", dec + )); } parseString(dec, degrees, minutes, seconds); } - bool isRaDecValid(int raH, int raM, double raS, int decD, - int decM, double decS) - { + bool isRaDecValid(int raH, int raM, double raS, int decD, int decM, double decS) { // Ra if (raH < 0.0 || raH >= 24.0) { - LWARNING(fmt::format("Right ascension hours '{}' is outside the allowed " - "range of 0 to 24 hours (exclusive)", raH) - ); + LWARNING(fmt::format( + "Right ascension hours '{}' is outside the allowed range of 0 to 24 " + "hours (exclusive)", raH + )); return false; } if (raM < 0.0 || raM >= 60.0) { - LWARNING(fmt::format("Right ascension minutes '{}' is outside the allowed " - "range of 0 to 60 minutes (exclusive)", raM) - ); + LWARNING(fmt::format( + "Right ascension minutes '{}' is outside the allowed range of 0 to 60 " + "minutes (exclusive)", raM + )); return false; } if (raS < 0.0 || raS >= 60.0) { - LWARNING(fmt::format("Right ascension seconds '{}' is outside the allowed " - "range of 0 to 60 seconds (exclusive)", raS) - ); + LWARNING(fmt::format( + "Right ascension seconds '{}' is outside the allowed " + "range of 0 to 60 seconds (exclusive)", raS + )); return false; } // Dec if (decD < -90.0 || decD > 90.0) { LWARNING(fmt::format("Declination degrees '{}' is outside the allowed range " - "of -90 to 90 degrees (inclusive)", decD) - ); + "of -90 to 90 degrees (inclusive)", decD + )); return false; } else if ((decD == -90.0 || decD == 90.0) && (decM != 0 || decS != 0)) { - LWARNING("Total declination is outside the allowed range of -90 to 90 " - "degrees (inclusive)" + LWARNING( + "Total declination is outside the allowed range of -90 to 90 degrees " + "(inclusive)" ); return false; } if (decM < 0.0 || decM >= 60.0) { - LWARNING(fmt::format("Declination minutes '{}' is outside the allowed range " - "of 0 to 60 minutes (exclusive)", decM) - ); + LWARNING(fmt::format( + "Declination minutes '{}' is outside the allowed range of 0 to 60 " + "minutes (exclusive)", decM + )); return false; } if (decS < 0.0 || decS >= 60.0) { - LWARNING(fmt::format("Declination seconds '{}' is outside the allowed range " - "of 0 to 60 seconds (exclusive)", decS) - ); + LWARNING(fmt::format( + "Declination seconds '{}' is outside the allowed range of 0 to 60 " + "seconds (exclusive)", decS + )); return false; } @@ -191,12 +200,14 @@ glm::dvec3 icrsToGalacticCartesian(double ra, double dec, double distance) { // Dec format 'XdYmZs', where X is a signed integer, Y is a positive integer and Z is a // positive double // Reference: -// https://math.stackexchange.com/questions/15323/how-do-i-calculate-the-cartesian-coordinates-of-stars +// https://math.stackexchange.com/questions/15323/how-do-i-calculate-the-cartesian- +// coordinates-of-stars glm::dvec2 icrsToDecimalDegrees(const std::string& ra, const std::string& dec) { if (ra.size() < 6 || dec.size() < 6) { throw ghoul::lua::LuaRuntimeException(fmt::format( "Ra '{}' or Dec '{}' format is incorrect. Correct format is: Ra 'XhYmZs', " - "and Dec 'XdYmZs'", ra, dec)); + "and Dec 'XdYmZs'", ra, dec + )); } // Parse right ascension @@ -218,9 +229,10 @@ glm::dvec2 icrsToDecimalDegrees(const std::string& ra, const std::string& dec) { ); if (!isValid) { - LWARNING(fmt::format("Ra '{}' or Dec '{}' is outside the allowed range, " - "result may be incorrect", ra, dec) - ); + LWARNING(fmt::format( + "Ra '{}' or Dec '{}' is outside the allowed range, result may be incorrect", + ra, dec + )); } // Convert from hours/degrees, minutes, seconds to decimal degrees @@ -266,7 +278,8 @@ glm::dvec3 galacticCartesianToIcrs(double x, double y, double z) { // Return a pair with two formatted strings from the decimal degrees ra and dec // References: // https://www.rapidtables.com/convert/number/degrees-to-degrees-minutes-seconds.html, -// https://math.stackexchange.com/questions/15323/how-do-i-calculate-the-cartesian-coordinates-of-stars +// https://math.stackexchange.com/questions/15323/how-do-i-calculate-the-cartesian- +// coordinates-of-stars std::pair decimalDegreesToIcrs(double ra, double dec) { // Radians to degrees double raDeg = ra; @@ -274,21 +287,22 @@ std::pair decimalDegreesToIcrs(double ra, double dec) // Check input if (raDeg < 0 || raDeg > 360 || decDeg < -90 || decDeg > 90) { - LWARNING(fmt::format("Given Ra '{}' or Dec '{}' is outside the allowed range, " - "result may be incorrect", ra, dec) - ); + LWARNING(fmt::format( + "Ra '{}' or Dec '{}' is outside the allowed range, result may be incorrect", + ra, dec + )); } // Calculate Ra - int raHours = std::trunc(raDeg) / 15.0; + int raHours = static_cast(std::trunc(raDeg) / 15.0); double raMinutesFull = (raDeg - raHours * 15.0) * 60.0 / 15.0; - int raMinutes = std::trunc(raMinutesFull); + int raMinutes = static_cast(std::trunc(raMinutesFull)); double raSeconds = (raMinutesFull - raMinutes) * 60.0; // Calculate Dec - int decDegrees = std::trunc(decDeg); + int decDegrees = static_cast(std::trunc(decDeg)); double decMinutesFull = (abs(decDeg) - abs(decDegrees)) * 60.0; - int decMinutes = std::trunc(decMinutesFull); + int decMinutes = static_cast(std::trunc(decMinutesFull)); double decSeconds = (decMinutesFull - decMinutes) * 60.0; // Construct strings @@ -311,9 +325,10 @@ std::pair decimalDegreesToIcrs(double ra, double dec) ); if (!isValid) { - LWARNING(fmt::format("Resulting Ra '{}' or Dec '{}' is outside the allowed range, " - "result may be incorrect", result.first, result.second) - ); + LWARNING(fmt::format( + "Resulting Ra '{}' or Dec '{}' is outside the allowed range, result may be " + "incorrect", result.first, result.second + )); } return result; } diff --git a/src/util/keys.cpp b/src/util/keys.cpp index 0078b379bd..8eb94c13e1 100644 --- a/src/util/keys.cpp +++ b/src/util/keys.cpp @@ -93,9 +93,9 @@ std::string keyToString(KeyWithModifier keyWithModifier) { std::string modifier; if (keyWithModifier.modifier != KeyModifier::None) { for (const openspace::KeyModifierInfo& kmi : openspace::KeyModifierInfos) { - // No need for an extra check for the empty modifier since that is mapped to 0, - // meaning that the `hasKeyModifier` will always fail for it since it checks - // internally against != 0 + // No need for an extra check for the empty modifier since that is mapped + // to 0, meaning that the `hasKeyModifier` will always fail for it since it + // checks internally against != 0 if (hasKeyModifier(keyWithModifier.modifier, kmi.modifier)) { modifier += fmt::format("{}+", kmi.identifier); @@ -126,7 +126,7 @@ std::string to_string(const openspace::Key& key) { return std::string(ki.name); } } - + throw ghoul::MissingCaseException(); } diff --git a/src/util/resourcesynchronization.cpp b/src/util/resourcesynchronization.cpp index 45b655ea30..06948ff505 100644 --- a/src/util/resourcesynchronization.cpp +++ b/src/util/resourcesynchronization.cpp @@ -33,7 +33,7 @@ namespace { struct [[codegen::Dictionary(ResourceSynchronization)]] Parameters { // This key specifies the type of ResourceSyncrhonization that gets created. It // has to be one of the valid ResourceSyncrhonizations that are available for - // creation (see the FactoryDocumentation for a list of possible + // creation (see the FactoryDocumentation for a list of possible // ResourceSyncrhonizations), which depends on the configration of the application std::string type [[codegen::annotation("A ResourceSynchronization created by a factory")]]; diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index b5500e1690..6bb9810180 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -1377,20 +1377,22 @@ scripting::LuaLibrary SpiceManager::luaLibrary() { &luascriptfunctions::rotationMatrix, {}, "{string, string, string}", - "Returns the rotationMatrix for a given body in a frame of reference at a specific" - "time. The first agument is the target body, the second is the frame of reference," - " the third is the time. Example: openspace.spice.rotationMatrix('" - "INSIGHT_LANDER_CRUISE','MARS', '2018 NOV 26 19:45:34')." + "Returns the rotationMatrix for a given body in a frame of reference at " + "a specific time. The first agument is the target body, the second is " + "the frame of reference, the third is the time. Example: " + "openspace.spice.rotationMatrix('INSIGHT_LANDER_CRUISE','MARS'," + "'2018 NOV 26 19:45:34')." }, { "position", &luascriptfunctions::position, {}, "{string, string, string, string}", - "Returns the position for a target by an observer in a frame of reference at a specific" - "time. The first agument is the target body, the second is the observer body, the third" - "is the frame of reference, and the fourth is the time. Example: openspace.spice." - "position('INSIGHT','MARS','GALACTIC', '2018 NOV 26 19:45:34')." + "Returns the position for a target by an observer in a frame of " + "reference at a specific time. The first agument is the target body, the " + "second is the observer body, the third is the frame of reference, and " + "the fourth is the time. Example: openspace.spice.position('INSIGHT'," + "'MARS','GALACTIC', '2018 NOV 26 19:45:34')." }, { "getSpiceBodies", diff --git a/src/util/spicemanager_lua.inl b/src/util/spicemanager_lua.inl index 268de67d63..9a00ecca00 100644 --- a/src/util/spicemanager_lua.inl +++ b/src/util/spicemanager_lua.inl @@ -58,7 +58,7 @@ int loadKernel(lua_State* L) { */ int unloadKernel(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::unloadKernel"); - std::variant argument = + std::variant argument = ghoul::lua::value>(L); if (std::holds_alternative(argument)) { @@ -161,7 +161,7 @@ int rotationMatrix(lua_State* L) { /** * position({string, string, string, string}): - * Returns the position for a given body relative to another body, + * Returns the position for a given body relative to another body, * in a given frame of reference, at a specific time. */ int position(lua_State* L) { diff --git a/src/util/time.cpp b/src/util/time.cpp index 2653ee2b31..0f94934e61 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -315,6 +315,14 @@ scripting::LuaLibrary Time::luaLibrary() { "Returns the current wall time as an ISO 8601 date string " "(YYYY-MM-DDTHH-MN-SS) in the UTC timezone" }, + { + "currentApplicationTime", + &luascriptfunctions::time_currentApplicationTime, + {}, + "", + "Returns the current application time as the number of seconds " + "since the OpenSpace application started" + }, { "advancedTime", &luascriptfunctions::time_advancedTime, diff --git a/src/util/time_lua.inl b/src/util/time_lua.inl index 45b62120c1..76fb105660 100644 --- a/src/util/time_lua.inl +++ b/src/util/time_lua.inl @@ -23,6 +23,7 @@ ****************************************************************************************/ #include +#include #include #include #include @@ -226,7 +227,7 @@ int time_interpolateTogglePause(lua_State* L) { */ int time_pauseToggleViaKeyboard(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::time_pauseToggleViaKeyboard"); - + if (global::sessionRecording->isPlayingBack()) { bool isPlaybackPaused = global::sessionRecording->isPlaybackPaused(); global::sessionRecording->setPlaybackPause(!isPlaybackPaused); @@ -404,6 +405,18 @@ int time_currentWallTime(lua_State* L) { return 1; } +/** + * \ingroup LuaScripts + * currentTime(): + * Returns the current application time as the number of seconds since the OpenSpace + * application started + */ +int time_currentApplicationTime(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::time_currentApplicationTime"); + ghoul::lua::push(L, global::windowDelegate->applicationTime()); + return 1; +} + int time_advancedTime(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::time_advanceTime"); auto [base, change] = @@ -447,24 +460,29 @@ int time_advancedTime(lua_State* L) { } ); - double value = std::stod(std::string(modifier.begin(), it)); + try { + double value = std::stod(std::string(modifier.begin(), it)); + std::string uName = std::string(it, modifier.end()); - std::string unitName = std::string(it, modifier.end()); + TimeUnit unit = TimeUnit::Second; + if (uName == "s") { unit = TimeUnit::Second; } + else if (uName == "m") { unit = TimeUnit::Minute; } + else if (uName == "h") { unit = TimeUnit::Hour; } + else if (uName == "d") { unit = TimeUnit::Day; } + else if (uName == "M") { unit = TimeUnit::Month; } + else if (uName == "y") { unit = TimeUnit::Year; } + else { + return ghoul::lua::luaError(L, fmt::format("Unknown unit '{}'", uName)); + } - TimeUnit unit = TimeUnit::Second; - if (unitName == "s") { unit = TimeUnit::Second; } - else if (unitName == "m") { unit = TimeUnit::Minute; } - else if (unitName == "h") { unit = TimeUnit::Hour; } - else if (unitName == "d") { unit = TimeUnit::Day; } - else if (unitName == "M") { unit = TimeUnit::Month; } - else if (unitName == "y") { unit = TimeUnit::Year; } - else { - return ghoul::lua::luaError(L, fmt::format("Unknown unit '{}'", unitName)); + dt = convertTime(value, unit, TimeUnit::Second); + if (isNegative) { + dt *= -1.0; + } } - - dt = convertTime(value, unit, TimeUnit::Second); - if (isNegative) { - dt *= -1.0; + catch (...) { + return ghoul::lua::luaError(L, fmt::format("Error parsing relative time " + "offset '{}'", modifier)); } } diff --git a/src/util/timemanager.cpp b/src/util/timemanager.cpp index b76c66da08..49628b609d 100644 --- a/src/util/timemanager.cpp +++ b/src/util/timemanager.cpp @@ -873,17 +873,22 @@ double TimeManager::previousApplicationTimeForInterpolation() const { void TimeManager::setTimeFromProfile(const Profile& p) { Time t; - switch (p.time.value().type) { - case Profile::Time::Type::Relative: - t.setTimeRelativeFromProfile(p.time.value().value); - break; + if (p.time.has_value()) { + switch (p.time.value().type) { + case Profile::Time::Type::Relative: + t.setTimeRelativeFromProfile(p.time.value().value); + break; - case Profile::Time::Type::Absolute: - t.setTimeAbsoluteFromProfile(p.time.value().value); - break; + case Profile::Time::Type::Absolute: + t.setTimeAbsoluteFromProfile(p.time.value().value); + break; - default: - throw ghoul::MissingCaseException(); + default: + throw ghoul::MissingCaseException(); + } + } + else { + throw ghoul::RuntimeError("No 'time' entry exists in the startup profile"); } } diff --git a/src/util/tstring.cpp b/src/util/tstring.cpp new file mode 100644 index 0000000000..15df8237fd --- /dev/null +++ b/src/util/tstring.cpp @@ -0,0 +1,51 @@ +/***************************************************************************************** + * * + * 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 + +#include +#include + +namespace openspace { + +tstring temporaryString(const std::string& str) { + void* ptr = global::memoryManager->TemporaryMemory.do_allocate(str.size(), 8); + std::strcpy(reinterpret_cast(ptr), str.data()); + return tstring(reinterpret_cast(ptr), str.size()); +} + +tstring temporaryString(std::string_view str) { + void* ptr = global::memoryManager->TemporaryMemory.do_allocate(str.size(), 8); + std::strcpy(reinterpret_cast(ptr), str.data()); + return tstring(reinterpret_cast(ptr), str.size()); +} + +tstring temporaryString(const char str[]) { + size_t size = strlen(str); + void* ptr = global::memoryManager->TemporaryMemory.do_allocate(size, 8); + std::strcpy(reinterpret_cast(ptr), str); + return tstring(reinterpret_cast(ptr), size); +} + +} // namespace openspace