From ee735618b480e3939db455d28ecf712332508730 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 4 May 2022 16:06:08 -0700 Subject: [PATCH] User Properties (#2064) Add the ability to add user-defined properties using a new `openspace.addCustomProperty` function that takes an identifier and a type and creates a new property of that type. The new property is then available under `UserProperties.` --- include/openspace/engine/globals.h | 1 + src/engine/globals.cpp | 12 +- src/scene/scene.cpp | 2 + src/scene/scene_lua.inl | 181 +++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 1 deletion(-) diff --git a/include/openspace/engine/globals.h b/include/openspace/engine/globals.h index 9dfb8dca64..918a0fba83 100644 --- a/include/openspace/engine/globals.h +++ b/include/openspace/engine/globals.h @@ -99,6 +99,7 @@ inline interaction::SessionRecording* sessionRecording; inline interaction::ShortcutManager* shortcutManager; inline properties::PropertyOwner* rootPropertyOwner; inline properties::PropertyOwner* screenSpaceRootPropertyOwner; +inline properties::PropertyOwner* userPropertyOwner; inline scripting::ScriptEngine* scriptEngine; inline scripting::ScriptScheduler* scriptScheduler; inline Profile* profile; diff --git a/src/engine/globals.cpp b/src/engine/globals.cpp index fec7e42b86..2589c92acf 100644 --- a/src/engine/globals.cpp +++ b/src/engine/globals.cpp @@ -98,6 +98,7 @@ namespace { sizeof(interaction::SessionRecording) + sizeof(properties::PropertyOwner) + sizeof(properties::PropertyOwner) + + sizeof(properties::PropertyOwner) + sizeof(scripting::ScriptEngine) + sizeof(scripting::ScriptScheduler) + sizeof(Profile); @@ -345,6 +346,14 @@ void create() { screenSpaceRootPropertyOwner = new properties::PropertyOwner({ "ScreenSpace" }); #endif // WIN32 +#ifdef WIN32 + userPropertyOwner = new (currentPos) properties::PropertyOwner({ "UserProperties" }); + ghoul_assert(userPropertyOwner, "No userPropertyOwner"); + currentPos += sizeof(properties::PropertyOwner); +#else // ^^^ WIN32 / !WIN32 vvv + userPropertyOwner = new properties::PropertyOwner({ "UserProperties" }); +#endif // WIN32 + #ifdef WIN32 scriptEngine = new (currentPos) scripting::ScriptEngine; ghoul_assert(scriptEngine, "No scriptEngine"); @@ -375,7 +384,6 @@ void initialize() { rootPropertyOwner->addPropertySubOwner(global::moduleEngine); - navigationHandler->setPropertyOwner(global::rootPropertyOwner); // New property subowners also have to be added to the ImGuiModule callback! rootPropertyOwner->addPropertySubOwner(global::navigationHandler); rootPropertyOwner->addPropertySubOwner(global::interactionMonitor); @@ -390,6 +398,8 @@ void initialize() { rootPropertyOwner->addPropertySubOwner(global::luaConsole); rootPropertyOwner->addPropertySubOwner(global::dashboard); + rootPropertyOwner->addPropertySubOwner(global::userPropertyOwner); + syncEngine->addSyncable(global::scriptEngine); } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 430dfc2e6f..de13c63935 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -879,6 +879,8 @@ scripting::LuaLibrary Scene::luaLibrary() { "Returns a list of property identifiers that match the passed regular " "expression" }, + codegen::lua::AddCustomProperty, + codegen::lua::RemoveCustomProperty, codegen::lua::AddSceneGraphNode, codegen::lua::RemoveSceneGraphNode, codegen::lua::RemoveSceneGraphNodesFromRegex, diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index fc4968091d..375bb74b3b 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -22,6 +22,36 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + namespace { template @@ -917,6 +947,157 @@ namespace { return is; } +template +void createCustomProperty(openspace::properties::Property::PropertyInfo info, + std::optional onChange) +{ + T* p = new T(info); + if (onChange.has_value()) { + p->onChange( + [p, script = *onChange]() { + using namespace ghoul::lua; + LuaState s; + openspace::global::scriptEngine->initializeLuaState(s); + ghoul::lua::push(s, p->value()); + lua_setglobal(s, "value"); + ghoul::lua::runScript(s, script); + } + ); + } + openspace::global::userPropertyOwner->addProperty(p); +} + +[[codegen::luawrap]] void addCustomProperty(std::string identifier, std::string type, + std::optional guiName, + std::optional description, + std::optional onChange) +{ + using namespace openspace; + + if (identifier.empty()) { + throw ghoul::lua::LuaError("Identifier must not empty"); + } + + if (global::userPropertyOwner->hasProperty(identifier)) { + throw ghoul::lua::LuaError(fmt::format( + "Failed to register property '{}' since a user-defined property with that " + "name already exists", + identifier + )); + } + + // @TODO (abock, 2022-05-01) These if statements here are a bit gnarly since it + // requires us to update them as soon as we add a new property type. It would be nicer + // to have a factory function for this but right now this is the only place where that + // factory would be used. + + const char* gui = + guiName.has_value() && !guiName->empty() ? + guiName->c_str() : + identifier.c_str(); + + properties::Property::PropertyInfo info = { + identifier.c_str(), + gui, + description.has_value() ? description->c_str() : "" + }; + if (type == "DMat2Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "DMat3Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "DMat4Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "Mat2Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "Mat3Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "Mat4Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "BoolProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "DoubleProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "FloatProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "IntProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "LongProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "ShortProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "UIntProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "ULongProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "UShortProperty") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "DVec2Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "DVec3Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "DVec4Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "IVec2Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "IVec3Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "IVec4Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "UVec2Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "UVec3Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "UVec4Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "Vec2Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "Vec3Property") { + createCustomProperty(info, std::move(onChange)); + } + else if (type == "Vec4Property") { + createCustomProperty(info, std::move(onChange)); + } +} + +[[codegen::luawrap]] void removeCustomProperty(std::string identifier) { + using namespace openspace; + properties::Property* p = global::userPropertyOwner->property(identifier); + if (p) { + global::userPropertyOwner->removeProperty(p); + delete p; + } + else { + throw ghoul::lua::LuaError(fmt::format( + "Could not find user-defined property '{}'", identifier + )); + } +} + } // namespace #include "scene_lua_codegen.cpp"