/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2021 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include #include #include #include #include #include namespace openspace::luascriptfunctions { /** * \ingroup LuaScripts * toggleShutdown(): * Toggles the shutdown mode that will close the application after the countdown timer is * reached */ int toggleShutdown(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::toggleShutdown"); global::openSpaceEngine->toggleShutdownMode(); ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; } /** * \ingroup LuaScripts * writeDocumentation(): * Writes out documentation files */ int writeDocumentation(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::writeDocumentation"); global::openSpaceEngine->writeStaticDocumentation(); global::openSpaceEngine->writeSceneDocumentation(); ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; } /** * \ingroup LuaScripts * addVirtualProperty(): * Adds a virtual property that will set a group of properties */ int addVirtualProperty(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, { 5, 7 }, "lua::addVirtualProperty"); const std::string& type = ghoul::lua::value(L, 1); const std::string& name = ghoul::lua::value(L, 2); const std::string& identifier = ghoul::lua::value(L, 3); const std::string& description = ghoul::lua::value(L, 4); std::unique_ptr prop; if (type == "BoolProperty") { const bool v = ghoul::lua::value(L, 5); prop = std::make_unique( properties::Property::PropertyInfo { identifier.c_str(), name.c_str(), description.c_str() }, v ); } else if (type == "IntProperty") { const int v = ghoul::lua::value(L, 5); const int min = ghoul::lua::value(L, 6); const int max = ghoul::lua::value(L, 7); prop = std::make_unique( properties::Property::PropertyInfo { identifier.c_str(), name.c_str(), description.c_str() }, v, min, max ); } else if (type == "FloatProperty") { const float v = ghoul::lua::value(L, 5); const float min = ghoul::lua::value(L, 6); const float max = ghoul::lua::value(L, 7); prop = std::make_unique( properties::Property::PropertyInfo { identifier.c_str(), name.c_str(), description.c_str() }, v, min, max ); } else if (type == "TriggerProperty") { prop = std::make_unique( properties::Property::PropertyInfo { identifier.c_str(), name.c_str(), description.c_str() } ); } else { lua_settop(L, 0); return ghoul::lua::luaError(L, fmt::format("Unknown property type '{}'", type)); } lua_settop(L, 0); global::virtualPropertyManager->addProperty(std::move(prop)); ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; } /** * \ingroup LuaScripts * removeVirtualProperty(): * Removes a previously added virtual property */ int removeVirtualProperty(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::removeVirtualProperty"); const std::string& name = ghoul::lua::value(L, 1); properties::Property* p = global::virtualPropertyManager->property(name); if (p) { global::virtualPropertyManager->removeProperty(p); } else { LWARNINGC( "removeVirtualProperty", fmt::format("Virtual Property with name '{}'' did not exist", name) ); } lua_settop(L, 0); ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; } /** * \ingroup LuaScripts * removeAllVirtualProperties(): * Remove all registered virtual properties */ int removeAllVirtualProperties(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::removeAllVirtualProperties"); const std::vector& ps = global::virtualPropertyManager->properties(); for (properties::Property* p : ps) { global::virtualPropertyManager->removeProperty(p); delete p; } ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; } int setScreenshotFolder(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::setScreenshotFolder"); std::string arg = ghoul::lua::value(L); lua_pop(L, 0); std::string folder = FileSys.absolutePath(arg); if (!std::filesystem::exists(folder)) { std::filesystem::create_directory(folder); } FileSys.registerPathToken( "${SCREENSHOTS}", folder, ghoul::filesystem::FileSystem::Override::Yes ); global::windowDelegate->setScreenshotFolder(folder); return 0; } /** * \ingroup LuaScripts * addTag() * Adds a Tag to a SceneGraphNode */ int addTag(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::addTag"); const std::string& uri = ghoul::lua::value(L, 1); std::string tag = ghoul::lua::value(L, 2); lua_settop(L, 0); SceneGraphNode* node = global::renderEngine->scene()->sceneGraphNode(uri); if (!node) { return ghoul::lua::luaError( L, fmt::format("Unknown scene graph node type '{}'", uri) ); } node->addTag(std::move(tag)); ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; } /** * \ingroup LuaScripts * removeTag(): * Removes a tag from a SceneGraphNode */ int removeTag(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::addTag"); const std::string& uri = ghoul::lua::value(L, 1); const std::string& tag = ghoul::lua::value(L, 2); lua_settop(L, 0); SceneGraphNode* node = global::renderEngine->scene()->sceneGraphNode(uri); if (!node) { return ghoul::lua::luaError( L, fmt::format("Unknown scene graph node type '{}'", uri) ); } node->removeTag(tag); ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; } /** * \ingroup LuaScripts * downloadFile(): * Downloads a file from Lua interpreter */ int downloadFile(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::addTag"); const std::string& uri = ghoul::lua::value(L, 1); const std::string& savePath = ghoul::lua::value(L, 2); lua_settop(L, 0); LINFOC("OpenSpaceEngine", fmt::format("Downloading file from {}", uri)); std::shared_ptr future = global::downloadManager->downloadFile( uri, savePath, DownloadManager::OverrideFile::Yes, DownloadManager::FailOnError::Yes, 5 ); if (!future || !future->isFinished) { return ghoul::lua::luaError( L, future ? "Download failed: " + future->errorMessage : "Download failed" ); } ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; } /** * \ingroup LuaScripts * createSingleColorImage(): * Creates a one pixel image with a given color and returns the path to the cached file */ int createSingleColorImage(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::createSingleColorImage"); const std::string& name = ghoul::lua::value(L, 1); const ghoul::Dictionary& d = ghoul::lua::value(L, 2); lua_settop(L, 0); // @TODO (emmbr 2020-12-18) Verify that the input dictionary is a vec3 // Would like to clean this up with a more direct use of the Verifier in the future using namespace openspace::documentation; const std::string& key = "color"; ghoul::Dictionary colorDict; colorDict.setValue(key, d); TestResult res = Color3Verifier()(colorDict, key); if (!res.success) { return ghoul::lua::luaError( L, "Invalid color. Expected three double values {r, g, b} in range 0 to 1" ); } const glm::dvec3 color = colorDict.value(key); const std::string& fileName = FileSys.cacheManager()->cachedFilename( fmt::format("{}.ppm", name), "", ghoul::filesystem::CacheManager::Persistent::Yes ); const bool hasCachedFile = FileSys.fileExists(fileName); if (hasCachedFile) { LDEBUGC("OpenSpaceEngine", fmt::format("Cached file '{}' used", fileName)); ghoul::lua::push(L, fileName); return 1; } else { // Write the color to a ppm file static std::mutex fileMutex; std::lock_guard guard(fileMutex); std::ofstream ppmFile(fileName, std::ofstream::binary | std::ofstream::trunc); unsigned int width = 1; unsigned int height = 1; unsigned int size = width * height; std::vector img(size * 3); img[0] = static_cast(255 * color.r); img[1] = static_cast(255 * color.g); img[2] = static_cast(255 * color.b); if (ppmFile.is_open()) { ppmFile << "P6" << std::endl; ppmFile << width << " " << height << std::endl; ppmFile << 255 << std::endl; ppmFile.write(reinterpret_cast(&img[0]), size * 3); ppmFile.close(); ghoul::lua::push(L, fileName); return 1; } else { return ghoul::lua::luaError(L, "Could not open ppm file for writing."); } } } int isMaster(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::isMaster"); ghoul::lua::push(L, global::windowDelegate->isMaster()); return 1; } } // namespace openspace::luascriptfunctions