From bb3256d9e4afb35a936dd58bbeeb68567b27cdd3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 2 Jun 2017 11:50:55 -0400 Subject: [PATCH 1/5] Add Lua callbacks that will walk a directory tree and return values as a table --- ext/sgct | 2 +- src/scripting/scriptengine.cpp | 30 +++++ src/scripting/scriptengine_lua.inl | 203 +++++++++++++++++++++-------- 3 files changed, 183 insertions(+), 52 deletions(-) diff --git a/ext/sgct b/ext/sgct index 55e6eb877d..40ea897067 160000 --- a/ext/sgct +++ b/ext/sgct @@ -1 +1 @@ -Subproject commit 55e6eb877d8fc02d3e3851e94fc5417a5b856217 +Subproject commit 40ea8970673c58a172dabab6ddccc45f0a0e92ac diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index ffd76288e0..cfb685a36a 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -428,6 +428,36 @@ void ScriptEngine::addBaseLibrary() { "string, string", "Registers a new path token provided by the first argument to the path " "provided in the second argument" + }, + { + "walkDirectory", + &luascriptfunctions::walkDirectory, + "string [bool, bool]", + "Walks a directory and returns all contents (files and directories) of " + "the directory as absolute paths. The first argument is the path of the " + "directory that should be walked, the second argument determines if the " + "walk is recursive and will continue in contained directories. The third " + "argument determines whether the table that is returned is sorted." + }, + { + "walkDirectoryFiles", + &luascriptfunctions::walkDirectoryFiles, + "string [bool, bool]", + "Walks a directory and returns the files of the directory as absolute " + "paths. The first argument is the path of the directory that should be " + "walked, the second argument determines if the walk is recursive and " + "will continue in contained directories. The third argument determines " + "whether the table that is returned is sorted." + }, + { + "walkDirectoryFolder", + &luascriptfunctions::walkDirectoryFolder, + "string [bool, bool]", + "Walks a directory and returns the subfolders of the directory as " + "absolute paths. The first argument is the path of the directory that " + "should be walked, the second argument determines if the walk is " + "recursive and will continue in contained directories. The third " + "argument determines whether the table that is returned is sorted." } } }; diff --git a/src/scripting/scriptengine_lua.inl b/src/scripting/scriptengine_lua.inl index 9fdf8c0a0d..d4dd178ba9 100644 --- a/src/scripting/scriptengine_lua.inl +++ b/src/scripting/scriptengine_lua.inl @@ -22,15 +22,75 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ +#include + namespace openspace { namespace luascriptfunctions { +namespace { + +using walkFunc = std::vector(ghoul::filesystem::Directory::*)( + ghoul::filesystem::Directory::Recursive, ghoul::filesystem::Directory::Sort) const; + +// Defining a common walk function that works off a pointer-to-member function (defined +// above) allows us to easily reuse this code +int walkCommon(lua_State* L, walkFunc func) { + // @CPP17 Replace with std::invoke +#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember)) + + using namespace ghoul::filesystem; + const int nArguments = lua_gettop(L); + if (nArguments < 1 || nArguments > 3) { + return luaL_error(L, "Expected %i-%i arguments, got %i", 1, 3, nArguments); + } + + std::vector result; + if (nArguments == 1) { + // Only the path was passed + const std::string path = luaL_checkstring(L, -1); + result = CALL_MEMBER_FN(Directory(path), func)( + ghoul::filesystem::Directory::Recursive::No, + ghoul::filesystem::Directory::Sort::No + ); + } + else if (nArguments == 2) { + // The path and the recursive value were passed + const std::string path = luaL_checkstring(L, -2); + const bool recursive = lua_toboolean(L, -1) != 0; + result = CALL_MEMBER_FN(Directory(path), func)( + ghoul::filesystem::Directory::Recursive(recursive), + ghoul::filesystem::Directory::Sort::No + ); + } + else if (nArguments == 3) { + // All three arguments were passed + const std::string path = luaL_checkstring(L, -3); + const bool recursive = lua_toboolean(L, -2) != 0; + const bool sorted = lua_toboolean(L, -1) != 0; + result = CALL_MEMBER_FN(Directory(path), func)( + ghoul::filesystem::Directory::Recursive(recursive), + ghoul::filesystem::Directory::Sort(sorted) + ); + } + + // Copy values into the lua_State + lua_newtable(L); + + for (int i = 0; i < result.size(); ++i) { + lua_pushstring(L, result[i].c_str()); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} +} // namespace + int printInternal(ghoul::logging::LogLevel level, lua_State* L) { using ghoul::lua::luaTypeToString; const std::string _loggerCat = "print"; - int nArguments = lua_gettop(L); + const int nArguments = lua_gettop(L); if (nArguments != 1) { return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); } @@ -61,79 +121,79 @@ int printInternal(ghoul::logging::LogLevel level, lua_State* L) { } /** - * \ingroup LuaScripts - * printTrace(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Trace'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ + * \ingroup LuaScripts + * printTrace(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Trace'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ int printTrace(lua_State* L) { return printInternal(ghoul::logging::LogLevel::Trace, L); } /** - * \ingroup LuaScripts - * printDebug(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Debug'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ + * \ingroup LuaScripts + * printDebug(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Debug'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ int printDebug(lua_State* L) { return printInternal(ghoul::logging::LogLevel::Debug, L); } /** - * \ingroup LuaScripts - * printInfo(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Info'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ + * \ingroup LuaScripts + * printInfo(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Info'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ int printInfo(lua_State* L) { return printInternal(ghoul::logging::LogLevel::Info, L); } /** - * \ingroup LuaScripts - * printWarning(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Warning'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ + * \ingroup LuaScripts + * printWarning(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Warning'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ int printWarning(lua_State* L) { return printInternal(ghoul::logging::LogLevel::Warning, L); } /** - * \ingroup LuaScripts - * printError(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Error'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ + * \ingroup LuaScripts + * printError(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Error'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ int printError(lua_State* L) { return printInternal(ghoul::logging::LogLevel::Error, L); } /** - * \ingroup LuaScripts - * printFatal(*): - * Logs the passed value to the installed LogManager with a LogLevel of 'Fatal'. - * For Boolean, numbers, and strings, the internal values are printed, for all other - * types, the type is printed instead - */ + * \ingroup LuaScripts + * printFatal(*): + * Logs the passed value to the installed LogManager with a LogLevel of 'Fatal'. + * For Boolean, numbers, and strings, the internal values are printed, for all other + * types, the type is printed instead + */ int printFatal(lua_State* L) { return printInternal(ghoul::logging::LogLevel::Fatal, L); } /** - * \ingroup LuaScripts - * absPath(string): - * Passes the argument to FileSystem::absolutePath, which resolves occuring path - * tokens and returns the absolute path. - */ + * \ingroup LuaScripts + * absPath(string): + * Passes the argument to FileSystem::absolutePath, which resolves occuring path + * tokens and returns the absolute path. + */ int absolutePath(lua_State* L) { - int nArguments = lua_gettop(L); + const int nArguments = lua_gettop(L); if (nArguments != 1) { return luaL_error(L, "Expected %d arguments, got %d", 1, nArguments); } @@ -145,19 +205,19 @@ int absolutePath(lua_State* L) { } /** - * \ingroup LuaScripts - * setPathToken(string, string): - * Registers the path token provided by the first argument to the path in the second - * argument. If the path token already exists, it will be silently overridden. - */ + * \ingroup LuaScripts + * setPathToken(string, string): + * Registers the path token provided by the first argument to the path in the second + * argument. If the path token already exists, it will be silently overridden. + */ int setPathToken(lua_State* L) { - int nArguments = lua_gettop(L); + const int nArguments = lua_gettop(L); if (nArguments != 2) { return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); } - std::string path = luaL_checkstring(L, -1); - std::string pathToken = luaL_checkstring(L, -2); + const std::string path = luaL_checkstring(L, -1); + const std::string pathToken = luaL_checkstring(L, -2); FileSys.registerPathToken( pathToken, path, @@ -166,6 +226,47 @@ int setPathToken(lua_State* L) { return 0; } +/** + * \ingroup LuaScripts + * walkDirectory(string, bool, bool): + * Walks a directory and returns the contents of the directory as absolute paths. The + * first argument is the path of the directory that should be walked, the second argument + * determines if the walk is recursive and will continue in contained directories. The + * default value for this parameter is "false". The third argument determines whether the + * table that is returned is sorted. The default value for this parameter is "false". + */ +int walkDirectory(lua_State* L) { + return walkCommon(L, &ghoul::filesystem::Directory::read); +} + +/** + * \ingroup LuaScripts + * walkDirectoryFiles(string, bool, bool): + * Walks a directory and returns the files of the directory as absolute paths. The first + * argument is the path of the directory that should be walked, the second argument + * determines if the walk is recursive and will continue in contained directories. The + * default value for this parameter is "false". The third argument determines whether the + * table that is returned is sorted. The default value for this parameter is "false". + */ +int walkDirectoryFiles(lua_State* L) { + return walkCommon(L, &ghoul::filesystem::Directory::readFiles); + +} + +/** +* \ingroup LuaScripts +* walkDirectory(string, bool, bool): +* Walks a directory and returns the subfolders of the directory as absolute paths. The +* first argument is the path of the directory that should be walked, the second argument +* determines if the walk is recursive and will continue in contained directories. The +* default value for this parameter is "false". The third argument determines whether the +* table that is returned is sorted. The default value for this parameter is "false". +*/ +int walkDirectoryFolder(lua_State* L) { + return walkCommon(L, &ghoul::filesystem::Directory::readDirectories); + +} + } // namespace luascriptfunctions } // namespace openspace From 994ba32f44576617b4a0e3c022e631a2a23dedf6 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 2 Jun 2017 16:33:17 -0400 Subject: [PATCH 2/5] Add the ability for LuaLibrary%s to reference script files that will be executed (closing #328) Create documentation for Lua scripts Add Lua script to check if a file exists --- ext/ghoul | 2 +- include/openspace/scripting/lualibrary.h | 18 +- include/openspace/scripting/scriptengine.h | 7 +- modules/globebrowsing/globebrowsingmodule.cpp | 10 ++ modules/globebrowsing/globebrowsingmodule.h | 6 +- .../globebrowsing/scripts/layer_support.lua | 37 +++++ src/rendering/renderengine.cpp | 6 +- src/scripting/scriptengine.cpp | 157 ++++++++++++------ src/scripting/scriptengine_lua.inl | 24 +++ 9 files changed, 210 insertions(+), 57 deletions(-) create mode 100644 modules/globebrowsing/scripts/layer_support.lua diff --git a/ext/ghoul b/ext/ghoul index 56bd0eb34a..a8489bba6c 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 56bd0eb34a300e9420e6f894803b4f3a7d3fe383 +Subproject commit a8489bba6c31204140cb8021970e3ff141da6c2e diff --git a/include/openspace/scripting/lualibrary.h b/include/openspace/scripting/lualibrary.h index 4f881f21ad..e49adc1026 100644 --- a/include/openspace/scripting/lualibrary.h +++ b/include/openspace/scripting/lualibrary.h @@ -55,8 +55,24 @@ struct LuaLibrary { }; /// The name of the library std::string name; - /// The list of all functions for this library + /// The list of all C-based callback functions for this library std::vector functions; + /// A list of script files that are executed for each Lua state + std::vector scripts; + + /// This struct contains information about a function or constant that is defined in + /// a Lua script + struct Documentation { + /// The name of the function/variable + std::string name; + /// The description of the parameters for a function + std::string parameter; + /// The description of the function/variable + std::string description; + }; + /// The list of documentations will be populated automatically by parsing the Lua + /// scripts + std::vector documentations; /// Comparison function that compares two LuaLibrary%s name bool operator<(const LuaLibrary& rhs) const; diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index c35bc3cbb9..5c6e42a6da 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -101,8 +101,8 @@ public: static std::string OpenSpaceLibraryName; private: - bool registerLuaLibrary(lua_State* state, const LuaLibrary& library); - void addLibraryFunctions(lua_State* state, const LuaLibrary& library, bool replace); + bool registerLuaLibrary(lua_State* state, LuaLibrary& library); + void addLibraryFunctions(lua_State* state, LuaLibrary& library, bool replace); bool isLibraryNameAllowed(lua_State* state, const std::string& name); @@ -112,7 +112,8 @@ private: std::string generateJson() const override; ghoul::lua::LuaState _state; - std::set _registeredLibraries; + std::vector _registeredLibraries; + //sync variables std::mutex _mutex; diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index dc77dcdb57..79621716c0 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -111,4 +111,14 @@ globebrowsing::cache::MemoryAwareTileCache* GlobeBrowsingModule::tileCache() { return _tileCache.get(); } +scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const { + return { + "globebrowsing", + {}, + { + "${MODULE_GLOBEBROWSING}/scripts/layer_support.lua" + } + }; +} + } // namespace openspace diff --git a/modules/globebrowsing/globebrowsingmodule.h b/modules/globebrowsing/globebrowsingmodule.h index 9952b2cabb..b07b845cb6 100644 --- a/modules/globebrowsing/globebrowsingmodule.h +++ b/modules/globebrowsing/globebrowsingmodule.h @@ -38,14 +38,18 @@ namespace cache { class GlobeBrowsingModule : public OpenSpaceModule { public: + static const std::string name; + GlobeBrowsingModule(); globebrowsing::cache::MemoryAwareTileCache* tileCache(); - static const std::string name; + + scripting::LuaLibrary luaLibrary() const override; protected: void internalInitialize() override; + private: std::unique_ptr _tileCache; }; diff --git a/modules/globebrowsing/scripts/layer_support.lua b/modules/globebrowsing/scripts/layer_support.lua new file mode 100644 index 0000000000..576fb52ed0 --- /dev/null +++ b/modules/globebrowsing/scripts/layer_support.lua @@ -0,0 +1,37 @@ +openspace.globebrowsing.documentation = { + { + Name = "createTextureLayers", + Arguments = "table", + Documentation = "Creates a table used in the 'ColorLayers', 'GrayScaleLayers', or 'GrayScaleColorOverlays' of a RenderableGlobe." + }, + { + Name = "createHeightLayers", + Arguments = "table", + Documentation = "Creates a table used in the 'HeightLayers' of a RenderableGlobe." + } +} + +-- Creates a table used in the 'ColorLayers', 'GrayScaleLayers', or 'GrayScaleColorOverlays' +-- of a RenderableGlobe +-- Usage: +-- table.unpack(openspace.globebrowsing.createTextureLayers(p)) +-- where p is an array that contains tables with 'Name' and 'Texture' values +openspace.globebrowsing.createTextureLayers = function (patches) + result = {} + for k,v in pairs(patches) do + table.insert(result, { Name = v["Name"], FilePath = v["Texture"] }) + end + return result +end + +-- Creates a table used in the 'HeightLayers' of a RenderableGlobe +-- Usage: +-- table.unpack(openspace.globebrowsing.openspace.globebrowsing.createHeightLayers(p)) +-- where p is an array that contains tables with 'Name' and 'Height' values +openspace.globebrowsing.createHeightLayers = function (patches) + result = {} + for k,v in pairs(patches) do + table.insert(result, { Name = v["Name"], FilePath = v["Height"], TilePixelSize = 90, DoPreProcessing = true }) + end + return result +end diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 3b947ea5b2..90436c4969 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -311,7 +311,7 @@ void RenderEngine::deinitialize() { } void RenderEngine::updateScene() { - const Time& currentTime = OsEng.timeManager().time(); + const Time& currentTime = OsEng.timeManager().time(); _scene->update({ { glm::dvec3(0), glm::dmat3(1), 1.0 }, currentTime, @@ -485,9 +485,9 @@ void RenderEngine::renderShutdownInformation(float timer, float fullTime) { } void RenderEngine::postDraw() { - Time& currentTime = OsEng.timeManager().time(); + Time& currentTime = OsEng.timeManager().time(); if (currentTime.timeJumped()) { - currentTime.setTimeJumped(false); + currentTime.setTimeJumped(false); } if (_shouldTakeScreenshot) { diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index cfb685a36a..f81e9d7f2c 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -76,8 +76,8 @@ void ScriptEngine::initialize() { addBaseLibrary(); LDEBUG("Initializing Lua state"); initializeLuaState(_state); - LDEBUG("Remapping Print functions"); - remapPrintFunction(); + //LDEBUG("Remapping Print functions"); + //remapPrintFunction(); } void ScriptEngine::deinitialize() {} @@ -88,7 +88,7 @@ void ScriptEngine::initializeLuaState(lua_State* state) { lua_setglobal(state, OpenSpaceLibraryName.c_str()); LDEBUG("Add OpenSpace modules"); - for (const LuaLibrary& lib : _registeredLibraries) { + for (LuaLibrary& lib : _registeredLibraries) { registerLuaLibrary(state, lib); } } @@ -106,7 +106,8 @@ void ScriptEngine::addLibrary(LuaLibrary library) { if (it == _registeredLibraries.end()) { // If not, we can add it after we sorted it std::sort(library.functions.begin(), library.functions.end(), sortFunc); - _registeredLibraries.insert(std::move(library)); + _registeredLibraries.push_back(std::move(library)); + std::sort(_registeredLibraries.begin(), _registeredLibraries.end()); } else { // otherwise, we merge the libraries @@ -127,15 +128,21 @@ void ScriptEngine::addLibrary(LuaLibrary library) { "' has been defined twice"); return; } - else + else { merged.functions.push_back(fun); + } + } + + for (const std::string& script : library.scripts) { + merged.scripts.push_back(script); } _registeredLibraries.erase(it); // Sort the merged library before inserting it std::sort(merged.functions.begin(), merged.functions.end(), sortFunc); - _registeredLibraries.insert(std::move(merged)); + _registeredLibraries.push_back(std::move(merged)); + std::sort(_registeredLibraries.begin(), _registeredLibraries.end()); } } @@ -345,13 +352,11 @@ bool ScriptEngine::isLibraryNameAllowed(lua_State* state, const std::string& nam return result; } -void ScriptEngine::addLibraryFunctions(lua_State* state, const LuaLibrary& library, bool replace) { - assert(state); +void ScriptEngine::addLibraryFunctions(lua_State* state, LuaLibrary& library, bool replace) { + ghoul_assert(state, "State must not be nullptr"); for (LuaLibrary::Function p : library.functions) { if (!replace) { - //ghoul::lua::logStack(_state); lua_getfield(state, -1, p.name.c_str()); - //ghoul::lua::logStack(_state); const bool isNil = lua_isnil(state, -1); if (!isNil) { LERROR("Function name '" << p.name << "' was already assigned"); @@ -359,13 +364,49 @@ void ScriptEngine::addLibraryFunctions(lua_State* state, const LuaLibrary& libra } lua_pop(state, 1); } - //ghoul::lua::logStack(_state); lua_pushstring(state, p.name.c_str()); - //ghoul::lua::logStack(_state); lua_pushcfunction(state, p.function); - //ghoul::lua::logStack(_state); lua_settable(state, TableOffset); - //ghoul::lua::logStack(_state); + } + + for (const std::string& script : library.scripts) { + // First we run the script to set its values in the current state + ghoul::lua::runScriptFile(state, absPath(script)); + + library.documentations.clear(); + // Then, we extract the documentation information from the file + lua_pushstring(state, "documentation"); + lua_gettable(state, -2); + if (lua_isnil(state, -1)) { + LERROR( + "Module '" << library.name << "' did not provide a documentation in " << + "script file '" << script << "'"); + } + else { + lua_pushnil(state); + while (lua_next(state, -2)) { + lua_pushstring(state, "Name"); + lua_gettable(state, -2); + const std::string name = lua_tostring(state, -1); + lua_pop(state, 1); + + lua_pushstring(state, "Arguments"); + lua_gettable(state, -2); + const std::string arguments = lua_tostring(state, -1); + lua_pop(state, 1); + + lua_pushstring(state, "Documentation"); + lua_gettable(state, -2); + const std::string documentation = lua_tostring(state, -1); + lua_pop(state, 1); + + lua_pop(state, 1); + + library.documentations.push_back({ name, arguments, documentation }); + + } + lua_pop(state, 1); + } } } @@ -422,6 +463,12 @@ void ScriptEngine::addBaseLibrary() { "Returns the absolute path to the passed path, resolving path tokens as " "well as resolving relative paths" }, + { + "fileExists", + &luascriptfunctions::fileExists, + "string", + "Checks whether the provided file exists." + }, { "setPathToken", &luascriptfunctions::setPathToken, @@ -476,42 +523,37 @@ void ScriptEngine::remapPrintFunction() { //ghoul::lua::logStack(_state); } -bool ScriptEngine::registerLuaLibrary(lua_State* state, const LuaLibrary& library) { +bool ScriptEngine::registerLuaLibrary(lua_State* state, LuaLibrary& library) { ghoul_assert(state, "State must not be nullptr"); - if (library.functions.empty()) { - LERROR("Lua library '" << library.name << "' does not have any functions"); - return false; - } - - //ghoul::lua::logStack(_state); lua_getglobal(state, OpenSpaceLibraryName.c_str()); - //ghoul::lua::logStack(_state); if (library.name.empty()) { - //ghoul::lua::logStack(_state); addLibraryFunctions(state, library, true); - //ghoul::lua::logStack(_state); lua_pop(state, 1); - //ghoul::lua::logStack(_state); } else { const bool allowed = isLibraryNameAllowed(state, library.name); if (!allowed) { return false; } - - //ghoul::lua::logStack(_state); - - lua_pushstring(state, library.name.c_str()); - //ghoul::lua::logStack(_state); - lua_newtable(state); - //ghoul::lua::logStack(_state); - addLibraryFunctions(state, library, false); - lua_settable(state, TableOffset); - //ghoul::lua::logStack(_state); - //_registeredLibraries.insert(library); - //_registeredLibraries.push_back(library); + // We need to first create the table and then retrieve it as the table will + // probably be used by scripts already + + // Add the table + lua_pushstring(state, library.name.c_str()); + lua_newtable(state); + lua_settable(state, TableOffset); + + // Retrieve the table + lua_pushstring(state, library.name.c_str()); + lua_gettable(state, -2); + + // Add the library functions into the table + addLibraryFunctions(state, library, false); + + // Pop the table + lua_pop(state, 1); } return true; } @@ -522,11 +564,21 @@ std::vector ScriptEngine::allLuaFunctions() const { for (const LuaLibrary& library : _registeredLibraries) { for (const LuaLibrary::Function& function : library.functions) { std::string total = "openspace."; - if (!library.name.empty()) + if (!library.name.empty()) { total += library.name + "."; + } total += function.name; result.push_back(std::move(total)); } + + for (const LuaLibrary::Documentation& doc : library.documentations) { + std::string total = "openspace."; + if (!library.name.empty()) { + total += library.name + "."; + } + total += doc.name; + result.push_back(std::move(total)); + } } return result; @@ -554,10 +606,23 @@ std::string ScriptEngine::generateJson() const { json << "\"arguments\": \"" << f.argumentText << "\", "; json << "\"help\": \"" << f.helpText << "\""; json << "}"; - if (&f != &l.functions.back()) { + if (&f != &l.functions.back() || !l.documentations.empty()) { json << ","; } } + + for (const LuaLibrary::Documentation& doc : l.documentations) { + json << "{"; + json << "\"name\": \"" << doc.name << "\", "; + json << "\"arguments\": \"" << doc.parameter<< "\", "; + json << "\"help\": \"" << doc.description<< "\""; + json << "}"; + if (&doc != &l.documentations.back()) { + json << ","; + } + } + + json << "]}"; } @@ -624,15 +689,15 @@ bool ScriptEngine::writeLog(const std::string& script) { } void ScriptEngine::presync(bool isMaster) { - if (!isMaster) return; + if (!isMaster) { + return; + } _mutex.lock(); - if (!_queuedScripts.empty()) { _currentSyncedScript = _queuedScripts.back().first; bool remoteScripting = _queuedScripts.back().second; - //Not really a received script but the master also needs to run the script... _receivedScripts.push_back(_currentSyncedScript); _queuedScripts.pop_back(); @@ -640,11 +705,8 @@ void ScriptEngine::presync(bool isMaster) { if (OsEng.parallelConnection().isHost() && remoteScripting) { OsEng.parallelConnection().sendScript(_currentSyncedScript); } - } - _mutex.unlock(); - } void ScriptEngine::encode(SyncBuffer* syncBuffer) { @@ -682,13 +744,12 @@ void ScriptEngine::postsync(bool) { } void ScriptEngine::queueScript(const std::string &script, ScriptEngine::RemoteScripting remoteScripting){ - if (script.empty()) + if (script.empty()) { return; + } _mutex.lock(); - _queuedScripts.insert(_queuedScripts.begin(), std::make_pair(script, remoteScripting)); - _mutex.unlock(); } diff --git a/src/scripting/scriptengine_lua.inl b/src/scripting/scriptengine_lua.inl index d4dd178ba9..a65f70f3d4 100644 --- a/src/scripting/scriptengine_lua.inl +++ b/src/scripting/scriptengine_lua.inl @@ -226,6 +226,30 @@ int setPathToken(lua_State* L) { return 0; } +/** + * \ingroup LuaScripts + * walkDirectory(string, bool, bool): + * Walks a directory and returns the contents of the directory as absolute paths. The + * first argument is the path of the directory that should be walked, the second argument + * determines if the walk is recursive and will continue in contained directories. The + * default value for this parameter is "false". The third argument determines whether the + * table that is returned is sorted. The default value for this parameter is "false". + */ +int fileExists(lua_State* L) { + const int nArguments = lua_gettop(L); + if (nArguments != 1) { + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + } + + const std::string file = luaL_checkstring(L, -1); + const bool e = FileSys.fileExists(absPath(file)); + lua_pushboolean( + L, + e ? 1 : 0 + ); + return 1; +} + /** * \ingroup LuaScripts * walkDirectory(string, bool, bool): From ea8442d996b7181164c4c93ea2483921e18372f9 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 3 Jun 2017 13:38:02 -0400 Subject: [PATCH 3/5] Enable networked specifications for ScreenSpaceImages --- modules/onscreengui/src/gui.cpp | 9 +++------ src/scripting/scriptengine_lua.inl | 1 + 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/onscreengui/src/gui.cpp b/modules/onscreengui/src/gui.cpp index 7a44eb4583..33cfa20ae1 100644 --- a/modules/onscreengui/src/gui.cpp +++ b/modules/onscreengui/src/gui.cpp @@ -210,12 +210,9 @@ void addScreenSpaceRenderable(std::string texturePath) { return; } - texturePath = absPath(texturePath); - texturePath = FileSys.convertPathSeparator(texturePath, '/'); - - std::string luaTable = - "{Type = 'ScreenSpaceImage', TexturePath = '" + texturePath + "' }"; - std::string script = "openspace.registerScreenSpaceRenderable(" + luaTable + ");"; + const std::string luaTable = + "{Type = 'ScreenSpaceImage', TexturePath = openspace.absPath('" + texturePath + "') }"; + const std::string script = "openspace.registerScreenSpaceRenderable(" + luaTable + ");"; OsEng.scriptEngine().queueScript(script, openspace::scripting::ScriptEngine::RemoteScripting::Yes); } } // namespace diff --git a/src/scripting/scriptengine_lua.inl b/src/scripting/scriptengine_lua.inl index a65f70f3d4..0eda39fd16 100644 --- a/src/scripting/scriptengine_lua.inl +++ b/src/scripting/scriptengine_lua.inl @@ -200,6 +200,7 @@ int absolutePath(lua_State* L) { std::string path = luaL_checkstring(L, -1); path = absPath(path); + path = FileSys.convertPathSeparator(path, '/'); lua_pushstring(L, path.c_str()); return 1; } From 013e448c36680df7e3b30b09e3c2bf9df2f0a2c2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 3 Jun 2017 14:29:33 -0400 Subject: [PATCH 4/5] Adding a Gui window to show the registered file paths (closes #329) --- modules/onscreengui/CMakeLists.txt | 2 + modules/onscreengui/include/gui.h | 2 + .../include/guifilepathcomponent.h | 43 +++++++++++++ modules/onscreengui/src/gui.cpp | 10 +++ .../onscreengui/src/guifilepathcomponent.cpp | 62 +++++++++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 modules/onscreengui/include/guifilepathcomponent.h create mode 100644 modules/onscreengui/src/guifilepathcomponent.cpp diff --git a/modules/onscreengui/CMakeLists.txt b/modules/onscreengui/CMakeLists.txt index eead578fc5..3a0720a818 100644 --- a/modules/onscreengui/CMakeLists.txt +++ b/modules/onscreengui/CMakeLists.txt @@ -27,6 +27,7 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/gui.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guicomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guifilepathcomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guihelpcomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guiorigincomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guiperformancecomponent.h @@ -42,6 +43,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/gui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/gui_lua.inl ${CMAKE_CURRENT_SOURCE_DIR}/src/guicomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guifilepathcomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guihelpcomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guiorigincomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guiperformancecomponent.cpp diff --git a/modules/onscreengui/include/gui.h b/modules/onscreengui/include/gui.h index 04d3056b16..d8a2c91dee 100644 --- a/modules/onscreengui/include/gui.h +++ b/modules/onscreengui/include/gui.h @@ -26,6 +26,7 @@ #define __OPENSPACE_MODULE_ONSCREENGUI___GUI___H__ #include +#include #include #include #include @@ -65,6 +66,7 @@ public: //protected: GuiHelpComponent _help; + GuiFilePathComponent _filePath; GuiOriginComponent _origin; GuiPerformanceComponent _performance; GuiPropertyComponent _globalProperty; diff --git a/modules/onscreengui/include/guifilepathcomponent.h b/modules/onscreengui/include/guifilepathcomponent.h new file mode 100644 index 0000000000..a859bc7bf1 --- /dev/null +++ b/modules/onscreengui/include/guifilepathcomponent.h @@ -0,0 +1,43 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_ONSCREENGUI___GUIFILEPATHCOMPONENT___H__ +#define __OPENSPACE_MODULE_ONSCREENGUI___GUIFILEPATHCOMPONENT___H__ + +#include + +namespace openspace { +namespace gui { + +class GuiFilePathComponent : public GuiComponent { +public: + GuiFilePathComponent(); + + void render() override; +}; + +} // namespace gui +} // namespace openspace + +#endif // __OPENSPACE_MODULE_ONSCREENGUI___GUIFILEPATHCOMPONENT___H__ diff --git a/modules/onscreengui/src/gui.cpp b/modules/onscreengui/src/gui.cpp index 33cfa20ae1..c3e520fa33 100644 --- a/modules/onscreengui/src/gui.cpp +++ b/modules/onscreengui/src/gui.cpp @@ -235,6 +235,7 @@ GUI::GUI() addPropertySubOwner(_property); addPropertySubOwner(_screenSpaceProperty); addPropertySubOwner(_virtualProperty); + addPropertySubOwner(_filePath); addPropertySubOwner(_time); addPropertySubOwner(_iswa); } @@ -318,6 +319,7 @@ void GUI::initialize() { _globalProperty.initialize(); _globalProperty.setHasRegularProperties(true); _virtualProperty.initialize(); + _filePath.initialize(); _performance.initialize(); _help.initialize(); _parallel.initialize(); @@ -334,6 +336,7 @@ void GUI::deinitialize() { _globalProperty.deinitialize(); _screenSpaceProperty.deinitialize(); _virtualProperty.deinitialize(); + _filePath.deinitialize(); _property.deinitialize(); delete iniFileBuffer; @@ -485,6 +488,9 @@ void GUI::endFrame() { if (_iswa.isEnabled()) { _iswa.render(); } + if (_filePath.isEnabled()) { + _filePath.render(); + } } ImGui::Render(); @@ -580,6 +586,10 @@ void GUI::render() { ImGui::Checkbox("Virtual Properties", &virtualProperty); _virtualProperty.setEnabled(virtualProperty); + bool filePath = _filePath.isEnabled(); + ImGui::Checkbox("File Paths", &filePath); + _filePath.setEnabled(filePath); + #ifdef OPENSPACE_MODULE_ISWA_ENABLED bool iswa = _iswa.isEnabled(); ImGui::Checkbox("iSWA", &iswa); diff --git a/modules/onscreengui/src/guifilepathcomponent.cpp b/modules/onscreengui/src/guifilepathcomponent.cpp new file mode 100644 index 0000000000..4327fc071f --- /dev/null +++ b/modules/onscreengui/src/guifilepathcomponent.cpp @@ -0,0 +1,62 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * 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 "imgui.h" + +namespace openspace { +namespace gui { + +GuiFilePathComponent::GuiFilePathComponent() + : GuiComponent("File Path") +{} + +void GuiFilePathComponent::render() { + bool v = _isEnabled; + ImGui::Begin("File Path", &v); + + ImGui::Text( + "%s", + "These are file paths registered in the current OpenSpace instance." + ); + ImGui::Separator(); + + ImGui::Columns(2); + ImGui::Separator(); + std::vector tokens = FileSys.tokens(); + for (const std::string& t : tokens) { + ImGui::Text("%s", t.c_str()); + ImGui::NextColumn(); + ImGui::Text("%s", absPath(t).c_str()); + ImGui::NextColumn(); + ImGui::Separator(); + } + ImGui::End(); +} + +} // gui +} // openspace From c77fdf23ae1f53dc4abba90400ab640e1188424c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 6 Jun 2017 09:25:06 -0400 Subject: [PATCH 5/5] Add predefined SGCT config for Spout sharing --- openspace.cfg | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openspace.cfg b/openspace.cfg index 27e2e2a4de..4736a0e50e 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -9,8 +9,6 @@ return { -- A regular 1280x720 window SGCTConfig = sgct.config.single{}, - -- SGCTConfig = sgct.config.single{res={1920, 1080}, shared=true}, - -- A regular 1920x1080 window -- SGCTConfig = sgct.config.single{1920, 1080}, @@ -20,6 +18,10 @@ return { -- A 4k fisheye rendering in a 1024x1024 window -- SGCTConfig = sgct.config.fisheye{1024, 1024, res={4096, 4096}, quality="2k", tilt=27}, + -- Streaming OpenSpace via Spout to OBS + -- SGCTConfig = sgct.config.single{2560, 1440, shared=true, name="WV_OBS_SPOUT1"}, + + --SGCTConfig = "${SGCT}/openvr_oculusRiftCv1.xml", --SGCTConfig = "${SGCT}/openvr_htcVive.xml",