Add the ability to run as task file at startup. Add new model conversion task that requires an OpenGL context. Add ability to use FileVerifier and DirectoryVerifier on files that don't exist (#3612)

This commit is contained in:
Alexander Bock
2025-04-29 09:50:29 +02:00
committed by GitHub
parent e0df6d6c2e
commit f024c25666
7 changed files with 231 additions and 13 deletions

View File

@@ -29,6 +29,9 @@
#include <openspace/engine/settings.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/interaction/joystickinputstate.h>
#include <openspace/util/progressbar.h>
#include <openspace/util/task.h>
#include <openspace/util/taskloader.h>
#include <openspace/openspace.h>
#include <ghoul/format.h>
#include <ghoul/ghoul.h>
@@ -100,6 +103,11 @@ glm::ivec2 currentDrawResolution;
Window* FirstOpenVRWindow = nullptr;
#endif
// This value is specified from the commandline options and kept around to be run after
// everything has been initialized. It's going to be std::nullopt unless a user wants to
// run a task
std::optional<std::string> taskToRun;
//
// SPOUT-support
//
@@ -401,6 +409,40 @@ void mainInitFunc(GLFWwindow*) {
// Query joystick status, those connected before start up
checkJoystickStatus();
if (taskToRun.has_value()) {
// If a task was specified on the commandline line, we are loading that file and
// executing everything within
TaskLoader loader;
std::vector<std::unique_ptr<Task>> tasks = loader.tasksFromFile(*taskToRun);
size_t nTasks = tasks.size();
if (nTasks == 1) {
LINFO("Task queue has 1 item");
}
else {
LINFO(std::format("Task queue has {} items", tasks.size()));
}
for (size_t i = 0; i < tasks.size(); i++) {
Task& task = *tasks[i].get();
LINFO(std::format(
"Performing task {} out of {}: {}",
i + 1, tasks.size(), task.description()
));
ProgressBar progressBar = ProgressBar(100);
auto onProgress = [&progressBar](float progress) {
progressBar.print(static_cast<int>(progress * 100.f));
};
task.perform(onProgress);
}
std::cout << "Done performing tasks" << std::endl;
// Done with the tasks, so we can terminate
Engine::instance().terminate();
}
LTRACE("main::mainInitFunc(end)");
}
@@ -1142,41 +1184,56 @@ int main(int argc, char* argv[]) {
CommandlineArguments commandlineArguments;
parser.addCommand(std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
commandlineArguments.configuration, "--file", "-f",
commandlineArguments.configuration,
"--file",
"-f",
"Provides the path to the OpenSpace configuration file. Only the '${TEMPORARY}' "
"path token is available and any other path has to be specified relative to the "
"current working directory"
));
parser.addCommand(std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
commandlineArguments.windowConfig, "--config", "-c",
commandlineArguments.windowConfig,
"--config",
"-c",
"Specifies the window configuration file that should be used to start OpenSpace "
"and that will override whatever is specified in the `openspace.cfg` or the "
"settings. This value can include path tokens, so for example "
"`${CONFIG}/single.json` is a valid value."
));
parser.addCommand(std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
commandlineArguments.profile, "--profile", "-p",
commandlineArguments.profile,
"--profile",
"-p",
"Specifies the profile that should be used to start OpenSpace and that overrides "
"the profile specified in the `openspace.cfg` and the settings."
));
parser.addCommand(std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
commandlineArguments.propertyVisibility, "--propertyVisibility", "",
commandlineArguments.propertyVisibility,
"--propertyVisibility",
"",
"Specifies UI visibility settings for properties that this OpenSpace is using. "
"This value overrides the values specified in the `openspace.cfg` and the "
"settings and also the environment variable, if that value is provided. Allowed "
"values for this parameter are: `Developer`, `AdvancedUser`, `User`, and "
"`NoviceUser`."
));
parser.addCommand(std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
commandlineArguments.task,
"--task",
"-t",
"Specifies a task that will be run after OpenSpace has been initialized. Once "
"the task finishes, the application will automatically close again. All other "
"commandline arguments are ignored, if a task is specified."
));
parser.addCommand(std::make_unique<ghoul::cmdparser::SingleCommandZeroArguments>(
commandlineArguments.bypassLauncher, "--bypassLauncher", "-b",
commandlineArguments.bypassLauncher,
"--bypassLauncher",
"-b",
"Specifies whether the Launcher should be shown at startup or not. This value "
"overrides the value specified in the `openspace.cfg` and the settings."
));
// setCommandLine returns a reference to the vector that will be filled later
const std::vector<std::string>& sgctArguments = parser.setCommandLine(
{ argv, argv + argc }
);
parser.setCommandLine({ argv, argv + argc });
try {
const bool showHelp = parser.execute();
@@ -1189,8 +1246,16 @@ int main(int argc, char* argv[]) {
LFATALC(e.component, e.message);
exit(EXIT_FAILURE);
}
// Take an actual copy of the arguments
std::vector<std::string> arguments = sgctArguments;
if (commandlineArguments.task.has_value()) {
// If a task was specified, we want to overwrite the used window and profile and
// not display the launcher
commandlineArguments.windowConfig = "${CONFIG}/single.json";
commandlineArguments.profile = "empty";
commandlineArguments.bypassLauncher = true;
taskToRun = *commandlineArguments.task;
}
//
// Set up SGCT functions for window delegate

View File

@@ -0,0 +1,7 @@
return {
{
Type = "ConvertModelTask",
InputFilePath = "< in path >",
OutputFilePath = "< out path >"
}
}

View File

@@ -71,6 +71,8 @@ struct CommandlineArguments {
std::optional<std::string> profile;
std::optional<std::string> propertyVisibility;
std::optional<bool> bypassLauncher;
std::optional<std::string> task;
};
class OpenSpaceEngine : public properties::PropertyOwner {

View File

@@ -87,6 +87,7 @@ set(HEADER_FILES
scale/staticscale.h
scale/timedependentscale.h
scale/timelinescale.h
task/convertmodeltask.h
timeframe/timeframeinterval.h
timeframe/timeframeunion.h
translation/globetranslation.h
@@ -161,6 +162,7 @@ set(SOURCE_FILES
scale/staticscale.cpp
scale/timedependentscale.cpp
scale/timelinescale.cpp
task/convertmodeltask.cpp
timeframe/timeframeinterval.cpp
timeframe/timeframeunion.cpp
translation/globetranslation.cpp

View File

@@ -83,6 +83,7 @@
#include <modules/base/scale/staticscale.h>
#include <modules/base/scale/timedependentscale.h>
#include <modules/base/scale/timelinescale.h>
#include <modules/base/task/convertmodeltask.h>
#include <modules/base/translation/timelinetranslation.h>
#include <modules/base/translation/globetranslation.h>
#include <modules/base/translation/luatranslation.h>
@@ -227,13 +228,20 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
ghoul::TemplateFactory<Translation>* fTranslation =
FactoryManager::ref().factory<Translation>();
ghoul_assert(fTranslation, "Ephemeris factory was not created");
ghoul_assert(fTranslation, "Translation factory was not created");
fTranslation->registerClass<GlobeTranslation>("GlobeTranslation");
fTranslation->registerClass<LuaTranslation>("LuaTranslation");
fTranslation->registerClass<MultiTranslation>("MultiTranslation");
fTranslation->registerClass<StaticTranslation>("StaticTranslation");
fTranslation->registerClass<TimelineTranslation>("TimelineTranslation");
ghoul::TemplateFactory<Task>* fTask =
FactoryManager::ref().factory<Task>();
ghoul_assert(fTask, "Task factory was not created");
fTask->registerClass<ConvertModelTask>("ConvertModelTask");
}
void BaseModule::internalDeinitializeGL() {
@@ -316,7 +324,9 @@ std::vector<documentation::Documentation> BaseModule::documentations() const {
LuaTranslation::Documentation(),
MultiTranslation::Documentation(),
StaticTranslation::Documentation(),
TimelineTranslation::Documentation()
TimelineTranslation::Documentation(),
ConvertModelTask::Documentation()
};
}

View File

@@ -0,0 +1,80 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* 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 <modules/base/task/convertmodeltask.h>
#include <ghoul/io/model/modelgeometry.h>
#include <ghoul/io/model/modelreaderassimp.h>
namespace {
// This task converts a 3D model format from a format that is natively supported both
// by OpenSpace and common 3D modelling tools and converts it into an OpenSpace
// proprietary format that can be loaded more efficiently, but more important can be
// distributed without violating terms of service for various 3D model hosting
// websites.
//
// The resulting output file can be used everywhere in OpenSpace in place of the
// source material.
//
// The list of supported files can be found here:
// https://github.com/assimp/assimp/blob/master/doc/Fileformats.md
struct [[codegen::Dictionary(ConvertModelTask)]] Parameters {
// The path to the source file
std::filesystem::path inputFilePath;
// The path to the output file
std::filesystem::path outputFilePath [[codegen::mustexist(false)]];
};
#include "convertmodeltask_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation ConvertModelTask::Documentation() {
return codegen::doc<Parameters>("base_convert_model_task");
}
ConvertModelTask::ConvertModelTask(const ghoul::Dictionary& dictionary) {
const Parameters p = codegen::bake<Parameters>(dictionary);
_inFilePath = p.inputFilePath;
_outFilePath = p.outputFilePath;
}
std::string ConvertModelTask::description() {
return "This task converts a 3D model format from a format that is natively "
"supported both by OpenSpace and common 3D modelling tools and converts it into "
"an OpenSpace proprietary format that can be loaded more efficiently, but more "
"important can be distributed without violating terms of service for various 3D "
"model hosting websites";
}
void ConvertModelTask::perform(const Task::ProgressCallback&) {
ghoul::io::ModelReaderAssimp reader;
std::unique_ptr<ghoul::modelgeometry::ModelGeometry> geometry =
reader.loadModel(_inFilePath, false, true);
geometry->saveToCacheFile(_outFilePath);
}
} // namespace openspace

View File

@@ -0,0 +1,52 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___CONVERTMODELTASK___H__
#define __OPENSPACE_CORE___CONVERTMODELTASK___H__
#include <openspace/util/task.h>
#include <filesystem>
#include <string>
namespace openspace {
class ConvertModelTask : public Task {
public:
explicit ConvertModelTask(const ghoul::Dictionary& dictionary);
~ConvertModelTask() override = default;
std::string description() override;
void perform(const Task::ProgressCallback& processCallback) override;
static documentation::Documentation Documentation();
private:
std::filesystem::path _inFilePath;
std::filesystem::path _outFilePath;
};
} // namespace openspace
#endif // __OPENSPACE_CORE___CONVERTMODELTASK___H__