Event System (#1741)

* Add implementation of the EventEngine to handle global event chains
* Add properties to SceneGraphNodes to determine two distance radii for camera-based events
This commit is contained in:
Alexander Bock
2021-10-11 21:53:00 +02:00
committed by GitHub
parent d230675181
commit 76dd45e5ce
36 changed files with 2176 additions and 59 deletions

View File

@@ -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

View File

@@ -28,6 +28,7 @@
#include <openspace/engine/logfactory.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/events/eventengine.h>
#include <openspace/interaction/actionmanager.h>
#include <openspace/interaction/keybindingmanager.h>
#include <openspace/interaction/sessionrecording.h>
@@ -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());

View File

@@ -295,6 +295,11 @@ namespace {
// 'false'
std::optional<bool> 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<bool> 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()) {

View File

@@ -24,14 +24,15 @@
#include <openspace/engine/globals.h>
#include <openspace/engine/downloadmanager.h>
#include <openspace/engine/configuration.h>
#include <openspace/engine/downloadmanager.h>
#include <openspace/engine/globalscallbacks.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/syncengine.h>
#include <openspace/engine/virtualpropertymanager.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/events/eventengine.h>
#include <openspace/interaction/actionmanager.h>
#include <openspace/interaction/interactionmonitor.h>
#include <openspace/interaction/keybindingmanager.h>
@@ -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");
@@ -573,13 +583,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();
@@ -615,6 +618,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();
}

View File

@@ -36,6 +36,8 @@
#include <openspace/engine/syncengine.h>
#include <openspace/engine/virtualpropertymanager.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/events/event.h>
#include <openspace/events/eventengine.h>
#include <openspace/interaction/actionmanager.h>
#include <openspace/interaction/interactionmonitor.h>
#include <openspace/interaction/keybindingmanager.h>
@@ -53,6 +55,7 @@
#include <openspace/scene/assetloader.h>
#include <openspace/scene/profile.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scene/rotation.h>
#include <openspace/scene/scale.h>
#include <openspace/scene/timeframe.h>
@@ -101,13 +104,22 @@ namespace {
template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
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<ghoul::TemplateFactory<Renderable>>(),
@@ -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<events::EventProfileLoadingFinished>();
}
else if (_isRenderingFirstFrame) {
global::profile->ignoreUpdates = true;
@@ -1186,6 +1204,9 @@ void OpenSpaceEngine::postSynchronizationPreDraw() {
if (_shutdown.inShutdown) {
if (_shutdown.timer <= 0.f) {
global::eventEngine->publishEvent<events::EventApplicationShutdown>(
events::EventApplicationShutdown::State::Finished
);
global::windowDelegate->terminate();
return;
}
@@ -1311,6 +1332,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)");
@@ -1544,11 +1582,17 @@ 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>(
events::EventApplicationShutdown::State::Aborted
);
}
else {
// Else, we have to enable it
_shutdown.timer = _shutdown.waitTime;
_shutdown.inShutdown = true;
global::eventEngine->publishEvent<events::EventApplicationShutdown>(
events::EventApplicationShutdown::State::Started
);
}
}

595
src/events/event.cpp Normal file
View File

@@ -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 <openspace/events/event.h>
#include <openspace/properties/property.h>
#include <openspace/rendering/screenspacerenderable.h>
#include <openspace/scene/profile.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/time.h>
#include <openspace/util/tstring.h>
#include <ghoul/fmt.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/assert.h>
#include <functional>
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<const void*>(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<const void*>(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<const EventSceneGraphNodeAdded&>(e).node)
);
break;
case Event::Type::SceneGraphNodeRemoved:
d.setValue(
"Node",
std::string(static_cast<const EventSceneGraphNodeRemoved&>(e).node)
);
break;
case Event::Type::ParallelConnection:
switch (static_cast<const EventParallelConnection&>(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<const EventApplicationShutdown&>(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<const EventScreenSpaceRenderableAdded&>(e).renderable
)
);
break;
case Event::Type::ScreenSpaceRenderableRemoved:
d.setValue(
"Renderable",
std::string(
static_cast<const EventScreenSpaceRenderableRemoved&>(e).renderable
)
);
break;
case Event::Type::CameraFocusTransition:
d.setValue(
"Node",
std::string(static_cast<const EventCameraFocusTransition&>(e).node)
);
switch (static_cast<const EventCameraFocusTransition&>(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<const EventPlanetEclipsed&>(e).eclipsee)
);
d.setValue(
"Eclipser",
std::string(static_cast<const EventPlanetEclipsed&>(e).eclipser)
);
break;
case Event::Type::InterpolationFinished:
d.setValue(
"Property",
std::string(static_cast<const EventInterpolationFinished&>(e).property)
);
break;
case Event::Type::FocusNodeChanged:
d.setValue(
"OldNode",
std::string(static_cast<const EventFocusNodeChanged&>(e).oldNode)
);
d.setValue(
"NewNode",
std::string(static_cast<const EventFocusNodeChanged&>(e).newNode)
);
break;
case Event::Type::LayerAdded:
d.setValue(
"Globe",
std::string(static_cast<const EventLayerAdded&>(e).node)
);
d.setValue(
"Group",
std::string(static_cast<const EventLayerAdded&>(e).layerGroup)
);
d.setValue(
"Layer",
std::string(static_cast<const EventLayerAdded&>(e).layer)
);
break;
case Event::Type::LayerRemoved:
d.setValue(
"Globe",
std::string(static_cast<const EventLayerRemoved&>(e).node)
);
d.setValue(
"Group",
std::string(static_cast<const EventLayerRemoved&>(e).layerGroup)
);
d.setValue(
"Layer",
std::string(static_cast<const EventLayerRemoved&>(e).layer)
);
break;
case Event::Type::SessionRecordingPlayback:
switch (static_cast<const EventSessionRecordingPlayback&>(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<const CustomEvent&>(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<const EventSceneGraphNodeAdded*>(e));
break;
case Event::Type::SceneGraphNodeRemoved:
log(i, *static_cast<const EventSceneGraphNodeRemoved*>(e));
break;
case Event::Type::ParallelConnection:
log(i, *static_cast<const EventParallelConnection*>(e));
break;
case Event::Type::ProfileLoadingFinished:
log(i, *static_cast<const EventProfileLoadingFinished*>(e));
break;
case Event::Type::ApplicationShutdown:
log(i, *static_cast<const EventApplicationShutdown*>(e));
break;
case Event::Type::ScreenSpaceRenderableAdded:
log(i, *static_cast<const EventScreenSpaceRenderableAdded*>(e));
break;
case Event::Type::ScreenSpaceRenderableRemoved:
log(i, *static_cast<const EventScreenSpaceRenderableRemoved*>(e));
break;
case Event::Type::CameraFocusTransition:
log(i, *static_cast<const EventCameraFocusTransition*>(e));
break;
case Event::Type::TimeOfInterestReached:
log(i, *static_cast<const EventTimeOfInterestReached*>(e));
break;
case Event::Type::MissionEventReached:
log(i, *static_cast<const EventMissionEventReached*>(e));
break;
case Event::Type::PlanetEclipsed:
log(i, *static_cast<const EventPlanetEclipsed*>(e));
break;
case Event::Type::InterpolationFinished:
log(i, *static_cast<const EventInterpolationFinished*>(e));
break;
case Event::Type::FocusNodeChanged:
log(i, *static_cast<const EventFocusNodeChanged*>(e));
break;
case Event::Type::LayerAdded:
log(i, *static_cast<const EventLayerAdded*>(e));
break;
case Event::Type::LayerRemoved:
log(i, *static_cast<const EventLayerRemoved*>(e));
break;
case Event::Type::SessionRecordingPlayback:
log(i, *static_cast<const EventSessionRecordingPlayback*>(e));
break;
case Event::Type::Custom:
log(i, *static_cast<const CustomEvent*>(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

133
src/events/eventengine.cpp Normal file
View File

@@ -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 <openspace/events/eventengine.h>
#include <openspace/engine/globals.h>
#include <openspace/interaction/actionmanager.h>
#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<ghoul::Dictionary> 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<ghoul::Dictionary> 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

View File

@@ -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<std::string, std::string, std::optional<ghoul::Dictionary>>(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<std::string, std::string, std::optional<ghoul::Dictionary>>(L);
events::Event::Type type = events::fromString(event);
global::eventEngine->unregisterEventAction(type, action, filter);
return 0;
}
} // namespace openspace::luascriptfunctions

View File

@@ -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)
);
}

View File

@@ -27,6 +27,7 @@
#include <openspace/camera/camera.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/events/eventengine.h>
#include <openspace/interaction/tasks/convertrecfileversiontask.h>
#include <openspace/interaction/tasks/convertrecformattask.h>
#include <openspace/navigation/keyframenavigator.h>
@@ -461,6 +462,9 @@ bool SessionRecording::startPlayback(std::string& filename,
(_playbackForceSimTimeAtStart ? 1 : 0)
));
global::eventEngine->publishEvent<events::EventSessionRecordingPlayback>(
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>(
events::EventSessionRecordingPlayback::State::Paused
);
}
else if (!pause && _state == SessionState::PlaybackPaused) {
if (!_playbackPausedWithinDeltaTimePause) {
global::timeManager->setPause(false);
}
_state = SessionState::Playback;
global::eventEngine->publishEvent<events::EventSessionRecordingPlayback>(
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>(
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>(
events::EventSessionRecordingPlayback::State::Finished
);
}
}

View File

@@ -27,6 +27,9 @@
#include <openspace/camera/camera.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/events/event.h>
#include <openspace/events/eventengine.h>
#include <openspace/interaction/actionmanager.h>
#include <openspace/navigation/navigationstate.h>
#include <openspace/network/parallelpeer.h>
#include <openspace/scene/profile.h>
@@ -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<events::EventCameraFocusTransition>(
_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<events::EventCameraFocusTransition>(
_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<events::EventCameraFocusTransition>(
_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<events::EventCameraFocusTransition>(
_camera,
anchorNode(),
events::EventCameraFocusTransition::Transition::Approaching
);
}
}
void NavigationHandler::setEnableKeyFrameInteraction() {
_useKeyFrameInteraction = true;
}

View File

@@ -27,6 +27,9 @@
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/updatestructures.h>
#include <openspace/query/query.h>
#include <openspace/engine/globals.h>
#include <openspace/events/event.h>
#include <openspace/events/eventengine.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/easing.h>
#include <glm/gtx/rotate_vector.hpp>
@@ -353,7 +356,12 @@ OrbitalNavigator::OrbitalNavigator()
}
SceneGraphNode* node = sceneGraphNode(_anchor.value());
if (node) {
const SceneGraphNode* previousAnchor = _anchorNode;
setAnchorNode(node);
global::eventEngine->publishEvent<events::EventFocusNodeChanged>(
previousAnchor,
node
);
}
else {
LERROR(fmt::format(
@@ -804,8 +812,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<events::EventFocusNodeChanged>(
previousAnchor,
focusNode
);
}
void OrbitalNavigator::setFocusNode(const std::string& focusNode, bool) {
@@ -953,8 +967,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 {

View File

@@ -27,6 +27,8 @@
#include <openspace/camera/camera.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/events/event.h>
#include <openspace/events/eventengine.h>
#include <openspace/navigation/keyframenavigator.h>
#include <openspace/navigation/navigationhandler.h>
#include <openspace/navigation/orbitalnavigator.h>
@@ -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<char>& message)
{
void ParallelPeer::dataMessageReceived(const std::vector<char>& message) {
size_t offset = 0;
// The type of data message received
@@ -290,8 +291,10 @@ void ParallelPeer::dataMessageReceived(const std::vector<char>& 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<char>& message)
}
}
void ParallelPeer::connectionStatusMessageReceived(const std::vector<char>& message)
{
void ParallelPeer::connectionStatusMessageReceived(const std::vector<char>& 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<char>& 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<char>& 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<char>& mess
void ParallelPeer::nConnectionsMessageReceived(const std::vector<char>& message)
{
if (message.size() < sizeof(uint32_t)) {
LERROR("Malformed host info message.");
LERROR("Malformed host info message");
return;
}
const uint32_t nConnections = *(reinterpret_cast<const uint32_t*>(&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>(
events::EventParallelConnection::State::Lost
);
}
if (isConnected && wasDisconnected) {
ee->publishEvent<events::EventParallelConnection>(
events::EventParallelConnection::State::Established
);
}
if (isHost && (wasClient || wasDisconnected)) {
ee->publishEvent<events::EventParallelConnection>(
events::EventParallelConnection::State::HostshipGained
);
}
if ((isClient || isDisconnected) && wasHost) {
ee->publishEvent<events::EventParallelConnection>(
events::EventParallelConnection::State::HostshipLost
);
}
}
if (isHost()) {
global::timeManager->addTimeJumpCallback([this]() {

View File

@@ -24,6 +24,9 @@
#include <openspace/properties/propertyowner.h>
#include <openspace/engine/globals.h>
#include <openspace/events/event.h>
#include <openspace/events/eventengine.h>
#include <openspace/properties/property.h>
#include <openspace/scene/scene.h>
#include <openspace/util/json_helper.h>

View File

@@ -30,6 +30,8 @@
#include <openspace/engine/globalscallbacks.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/events/event.h>
#include <openspace/events/eventengine.h>
#include <openspace/navigation/navigationhandler.h>
#include <openspace/navigation/orbitalnavigator.h>
#include <openspace/mission/missionmanager.h>
@@ -1105,8 +1107,11 @@ void RenderEngine::addScreenSpaceRenderable(std::unique_ptr<ScreenSpaceRenderabl
s->initialize();
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<events::EventScreenSpaceRenderableAdded>(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<events::EventScreenSpaceRenderableRemoved>(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(),

View File

@@ -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 {

View File

@@ -125,6 +125,7 @@ void Scene::registerNode(SceneGraphNode* node) {
_nodesByIdentifier[node->identifier()] = node;
addPropertySubOwner(node);
_dirtyNodeRegistry = true;
global::eventEngine->publishEvent<events::EventSceneGraphNodeAdded>(node);
}
void Scene::unregisterNode(SceneGraphNode* node) {
@@ -144,6 +145,7 @@ void Scene::unregisterNode(SceneGraphNode* node) {
}
removePropertySubOwner(node);
_dirtyNodeRegistry = true;
global::eventEngine->publishEvent<events::EventSceneGraphNodeRemoved>(node);
}
void Scene::markNodeRegistryDirty() {
@@ -585,6 +587,10 @@ void Scene::updateInterpolations() {
i.prop->interpolateValue(t, i.easingFunction);
i.isExpired = (t == 1.f);
if (i.isExpired) {
global::eventEngine->publishEvent<events::EventInterpolationFinished>(i.prop);
}
}
_propertyInterpolationInfos.erase(

View File

@@ -25,6 +25,8 @@
#include <openspace/documentation/documentation.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/events/event.h>
#include <openspace/events/eventengine.h>
#include <openspace/navigation/navigationhandler.h>
#include <openspace/scene/profile.h>
#include <ghoul/lua/luastate.h>

View File

@@ -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<ghoul::Dictionary> renderable [[codegen::reference("renderable")]];
// A hard-coded bounding sphere to be used for the cases where the Renderable is
// not able to provide a reasonable bounding sphere or the calculated bounding
// sphere needs to be overwritten for some reason
// [[codegen::verbatim(BoundingSphereInfo.description)]]
std::optional<double> 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<double> interactionSphere;
struct Transform {
@@ -208,6 +219,36 @@ namespace {
// corresponding to a 'Translation', a 'Rotation', and a 'Scale'
std::optional<Transform> 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<double> 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<double> 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<std::variant<std::string, std::vector<std::string>>> 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<std::variant<std::string, std::vector<std::string>>> 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<std::variant<std::string, std::vector<std::string>>> 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<std::variant<std::string, std::vector<std::string>>> onExit;
// Specifies the time frame for when this node should be active
std::optional<ghoul::Dictionary> timeFrame
[[codegen::reference("core_time_frame")]];
@@ -287,6 +328,8 @@ ghoul::mm_unique_ptr<SceneGraphNode> 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> SceneGraphNode::createFromDictionary(
));
}
// Extracting the actions from the dictionary
if (p.onApproach.has_value()) {
if (std::holds_alternative<std::string>(*p.onApproach)) {
result->_onApproachAction = { std::get<std::string>(*p.onApproach) };
}
else {
result->_onApproachAction = std::get<std::vector<std::string>>(*p.onApproach);
}
}
if (p.onReach.has_value()) {
if (std::holds_alternative<std::string>(*p.onReach)) {
result->_onReachAction = { std::get<std::string>(*p.onReach) };
}
else {
result->_onReachAction = std::get<std::vector<std::string>>(*p.onReach);
}
}
if (p.onRecede.has_value()) {
if (std::holds_alternative<std::string>(*p.onRecede)) {
result->_onRecedeAction = { std::get<std::string>(*p.onRecede) };
}
else {
result->_onRecedeAction = std::get<std::vector<std::string>>(*p.onRecede);
}
}
if (p.onExit.has_value()) {
if (std::holds_alternative<std::string>(*p.onExit)) {
result->_onExitAction = { std::get<std::string>(*p.onExit) };
}
else {
result->_onExitAction = std::get<std::vector<std::string>>(*p.onExit);
}
}
if (p.tag.has_value()) {
if (std::holds_alternative<std::string>(*p.tag)) {
result->addTag(std::get<std::string>(*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*> SceneGraphNode::children() const {
return nodes;
}
const std::vector<std::string>& SceneGraphNode::onApproachAction() const {
return _onApproachAction;
}
const std::vector<std::string>& SceneGraphNode::onReachAction() const {
return _onReachAction;
}
const std::vector<std::string>& SceneGraphNode::onRecedeAction() const {
return _onRecedeAction;
}
const std::vector<std::string>& 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();
}

51
src/util/tstring.cpp Normal file
View File

@@ -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 <openspace/util/tstring.h>
#include <openspace/engine/globals.h>
#include <openspace/util/memorymanager.h>
namespace openspace {
tstring temporaryString(const std::string& str) {
void* ptr = global::memoryManager->TemporaryMemory.do_allocate(str.size(), 8);
std::strcpy(reinterpret_cast<char*>(ptr), str.data());
return tstring(reinterpret_cast<char*>(ptr), str.size());
}
tstring temporaryString(std::string_view str) {
void* ptr = global::memoryManager->TemporaryMemory.do_allocate(str.size(), 8);
std::strcpy(reinterpret_cast<char*>(ptr), str.data());
return tstring(reinterpret_cast<char*>(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<char*>(ptr), str);
return tstring(reinterpret_cast<char*>(ptr), size);
}
} // namespace openspace