mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-04 02:29:49 -06:00
Merge branch 'master' into thesis/2025/black-hole
This commit is contained in:
@@ -62,18 +62,16 @@ public:
|
||||
bool wasLaunchSelected() const;
|
||||
|
||||
/**
|
||||
* Returns the selected profile name when launcher window closed.
|
||||
* Returns the selected profile name when the launcher window closed.
|
||||
*
|
||||
* \return The name of selected profile (this is only the name without file extension
|
||||
* and without path)
|
||||
* \return The path to the selected profile
|
||||
*/
|
||||
std::string selectedProfile() const;
|
||||
|
||||
/**
|
||||
* Returns the selected sgct window configuration when launcher window closed.
|
||||
* Returns the selected SGCT window configuration when the launcher window closed.
|
||||
*
|
||||
* \return The name of selected profile (this is only the name without file extension
|
||||
* and without path)
|
||||
* \return The path to the selected profile
|
||||
*/
|
||||
std::string selectedWindowConfig() const;
|
||||
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
#include <openspace/scene/profile.h>
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDateTimeEdit;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QTabWidget;
|
||||
|
||||
class TimeDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
@@ -48,18 +48,15 @@ public:
|
||||
TimeDialog(QWidget* parent, std::optional<openspace::Profile::Time>* time);
|
||||
|
||||
private slots:
|
||||
void enableAccordingToType(int);
|
||||
void approved();
|
||||
|
||||
private:
|
||||
void createWidgets();
|
||||
void enableFormatForAbsolute(bool enableAbs);
|
||||
|
||||
std::optional<openspace::Profile::Time>* _time = nullptr;
|
||||
openspace::Profile::Time _timeData;
|
||||
bool _initializedAsAbsolute = true;
|
||||
|
||||
QComboBox* _typeCombo = nullptr;
|
||||
QTabWidget* _tabWidget = nullptr;
|
||||
QLabel* _absoluteLabel = nullptr;
|
||||
QDateTimeEdit* _absoluteEdit = nullptr;
|
||||
QLabel* _relativeLabel = nullptr;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <format>
|
||||
#include <algorithm>
|
||||
@@ -45,8 +46,6 @@ TimeDialog::TimeDialog(QWidget* parent, std::optional<openspace::Profile::Time>*
|
||||
setWindowTitle("Time");
|
||||
createWidgets();
|
||||
|
||||
const QStringList types = { "Absolute", "Relative" };
|
||||
_typeCombo->addItems(types);
|
||||
if (_time->has_value()) {
|
||||
_timeData = **_time;
|
||||
if (_timeData.type == Profile::Time::Type::Relative) {
|
||||
@@ -66,44 +65,75 @@ TimeDialog::TimeDialog(QWidget* parent, std::optional<openspace::Profile::Time>*
|
||||
}
|
||||
_startPaused->setChecked(_timeData.startPaused);
|
||||
|
||||
_initializedAsAbsolute = (_timeData.type == Profile::Time::Type::Absolute);
|
||||
enableAccordingToType(static_cast<int>(_timeData.type));
|
||||
if (_timeData.type == Profile::Time::Type::Relative) {
|
||||
_relativeEdit->setText(QString::fromStdString(_timeData.value));
|
||||
_relativeEdit->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
else {
|
||||
const size_t tIdx = _timeData.value.find_first_of('T', 0);
|
||||
const QString importDate = QString::fromStdString(
|
||||
_timeData.value.substr(0, tIdx)
|
||||
);
|
||||
const QString importTime = QString::fromStdString(
|
||||
_timeData.value.substr(tIdx + 1)
|
||||
);
|
||||
_absoluteEdit->setDate(QDate::fromString(importDate, Qt::DateFormat::ISODate));
|
||||
_absoluteEdit->setTime(QTime::fromString(importTime));
|
||||
_relativeEdit->clear();
|
||||
_absoluteEdit->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
|
||||
_tabWidget->setCurrentIndex(static_cast<int>(_timeData.type));
|
||||
}
|
||||
|
||||
void TimeDialog::createWidgets() {
|
||||
QBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
_tabWidget = new QTabWidget;
|
||||
|
||||
{
|
||||
layout->addWidget(new QLabel("Time Type"));
|
||||
_typeCombo = new QComboBox;
|
||||
_typeCombo->setAccessibleName("Time type");
|
||||
_typeCombo->setToolTip("Types: Absolute defined time or Relative to actual time");
|
||||
connect(
|
||||
_typeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &TimeDialog::enableAccordingToType
|
||||
);
|
||||
layout->addWidget(_typeCombo);
|
||||
}
|
||||
{
|
||||
QWidget* container = new QWidget;
|
||||
QBoxLayout* l = new QVBoxLayout(container);
|
||||
_absoluteLabel = new QLabel("Absolute UTC:");
|
||||
layout->addWidget(_absoluteLabel);
|
||||
l->addWidget(_absoluteLabel);
|
||||
|
||||
_absoluteEdit = new QDateTimeEdit;
|
||||
_absoluteEdit->setDisplayFormat("yyyy-MM-dd T hh:mm:ss");
|
||||
_absoluteEdit->setDateTime(QDateTime::currentDateTime());
|
||||
_absoluteEdit->setAccessibleName("Set absolute time");
|
||||
layout->addWidget(_absoluteEdit);
|
||||
l->addWidget(_absoluteEdit);
|
||||
|
||||
l->addStretch();
|
||||
|
||||
_tabWidget->addTab(container, "Absolute");
|
||||
}
|
||||
{
|
||||
QWidget* container = new QWidget;
|
||||
QBoxLayout* l = new QVBoxLayout(container);
|
||||
_relativeLabel = new QLabel("Relative Time:");
|
||||
layout->addWidget(_relativeLabel);
|
||||
l->addWidget(_relativeLabel);
|
||||
|
||||
_relativeEdit = new QLineEdit;
|
||||
_relativeEdit->setAccessibleName("Set relative time");
|
||||
_relativeEdit->setToolTip(
|
||||
"String for relative time to actual (e.g. \"-1d\" for back 1 day)"
|
||||
);
|
||||
layout->addWidget(_relativeEdit);
|
||||
l->addWidget(_relativeEdit);
|
||||
|
||||
QLabel* desc = new QLabel(
|
||||
"This field modifies the default start time. It has to be of the form "
|
||||
"[-]XX(s,m,h,d,M,y). For example '-1d' will cause the profile to start at "
|
||||
"yesterday's date."
|
||||
);
|
||||
desc->setObjectName("information");
|
||||
desc->setWordWrap(true);
|
||||
l->addWidget(desc);
|
||||
|
||||
_tabWidget->addTab(container, "Relative");
|
||||
}
|
||||
|
||||
layout->addWidget(_tabWidget);
|
||||
|
||||
{
|
||||
_startPaused = new QCheckBox("Start with time paused");
|
||||
_startPaused->setChecked(false);
|
||||
@@ -123,47 +153,9 @@ void TimeDialog::createWidgets() {
|
||||
}
|
||||
}
|
||||
|
||||
void TimeDialog::enableAccordingToType(int idx) {
|
||||
const Profile::Time::Type comboIdx = static_cast<Profile::Time::Type>(idx);
|
||||
const bool setFormatForAbsolute = (comboIdx == Profile::Time::Type::Absolute);
|
||||
enableFormatForAbsolute(setFormatForAbsolute);
|
||||
_typeCombo->setCurrentIndex(idx);
|
||||
if (comboIdx == Profile::Time::Type::Relative) {
|
||||
_relativeEdit->setText("<font color='black'>Relative Time:</font>");
|
||||
if (_initializedAsAbsolute) {
|
||||
_relativeEdit->setText("0d");
|
||||
}
|
||||
else {
|
||||
_relativeEdit->setText(QString::fromStdString(_timeData.value));
|
||||
}
|
||||
_relativeEdit->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
else {
|
||||
_relativeEdit->setText("<font color='gray'>Relative Time:</font>");
|
||||
const size_t tIdx = _timeData.value.find_first_of('T', 0);
|
||||
const QString importDate = QString::fromStdString(
|
||||
_timeData.value.substr(0, tIdx)
|
||||
);
|
||||
const QString importTime = QString::fromStdString(
|
||||
_timeData.value.substr(tIdx + 1)
|
||||
);
|
||||
_absoluteEdit->setDate(QDate::fromString(importDate, Qt::DateFormat::ISODate));
|
||||
_absoluteEdit->setTime(QTime::fromString(importTime));
|
||||
_relativeEdit->clear();
|
||||
_absoluteEdit->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeDialog::enableFormatForAbsolute(bool enableAbs) {
|
||||
_absoluteLabel->setEnabled(enableAbs);
|
||||
_absoluteEdit->setEnabled(enableAbs);
|
||||
_relativeLabel->setEnabled(!enableAbs);
|
||||
_relativeEdit->setEnabled(!enableAbs);
|
||||
}
|
||||
|
||||
void TimeDialog::approved() {
|
||||
constexpr int Relative = static_cast<int>(Profile::Time::Type::Relative);
|
||||
if (_typeCombo->currentIndex() == Relative) {
|
||||
if (_tabWidget->currentIndex() == Relative) {
|
||||
if (_relativeEdit->text().isEmpty()) {
|
||||
*_time = std::nullopt;
|
||||
}
|
||||
|
||||
@@ -1415,9 +1415,39 @@ int main(int argc, char* argv[]) {
|
||||
openspace::Settings settings = loadSettings();
|
||||
settings.hasStartedBefore = true;
|
||||
|
||||
const std::filesystem::path p = global::configuration->profile;
|
||||
const std::filesystem::path reducedName = p.filename().replace_extension();
|
||||
settings.profile = reducedName.string();
|
||||
const std::filesystem::path profile = global::configuration->profile;
|
||||
|
||||
const bool isDefaultProfile = ghoul::filesystem::isSubdirectory(
|
||||
profile,
|
||||
absPath("${PROFILES}")
|
||||
);
|
||||
const bool isUserProfile = ghoul::filesystem::isSubdirectory(
|
||||
profile,
|
||||
absPath("${USER_PROFILES}")
|
||||
);
|
||||
|
||||
if (isDefaultProfile) {
|
||||
std::filesystem::path p = std::filesystem::relative(
|
||||
profile,
|
||||
absPath("${PROFILES}")
|
||||
);
|
||||
p.replace_extension();
|
||||
settings.profile = p.string();
|
||||
}
|
||||
else if (isUserProfile) {
|
||||
std::filesystem::path p = std::filesystem::relative(
|
||||
profile,
|
||||
absPath("${USER_PROFILES}")
|
||||
);
|
||||
p.replace_extension();
|
||||
settings.profile = p.string();
|
||||
}
|
||||
else {
|
||||
LWARNING(
|
||||
"Cannot save remembered profile when starting a profile that is not in "
|
||||
"the data/profiles or user/data/profiles folder."
|
||||
);
|
||||
}
|
||||
|
||||
settings.configuration =
|
||||
isGeneratedWindowConfig ? "" : global::configuration->windowConfiguration;
|
||||
|
||||
Submodule ext/ghoul updated: e7ff4891f4...2972e7b963
@@ -77,6 +77,18 @@ public:
|
||||
*/
|
||||
void remove(const std::string& path);
|
||||
|
||||
/**
|
||||
* Reloads the asset at the provided \p path as a new root asset of the AssetManager. If the
|
||||
* asset at that path was not previously loaded, acts the same as the add function.
|
||||
*
|
||||
* \param path The path from which the Asset is loaded. This path can be either
|
||||
* relative to the base directory (the path starting with . or ..), an absolute
|
||||
* path (that path starting with *:/ or /) or relative to the global asset root
|
||||
* (if the path starts any other way)
|
||||
* \pre \p path must not be the empty string
|
||||
*/
|
||||
void reload(const std::string& path);
|
||||
|
||||
/**
|
||||
* Update function that should be called at least once per frame that will load all
|
||||
* queued asset loads and asset removal.
|
||||
@@ -180,6 +192,9 @@ private:
|
||||
std::filesystem::path generateAssetPath(const std::filesystem::path& baseDirectory,
|
||||
const std::string& assetPath) const;
|
||||
|
||||
void runRemoveQueue();
|
||||
void runAddQueue();
|
||||
|
||||
//
|
||||
// Assets
|
||||
//
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace {
|
||||
namespace openspace::globebrowsing {
|
||||
|
||||
GeoJsonManager::GeoJsonManager()
|
||||
: properties::PropertyOwner({ "GeoJson", "Geometry Overlays" })
|
||||
: properties::PropertyOwner({ "GeographicOverlays", "Geographic Overlays" })
|
||||
{}
|
||||
|
||||
void GeoJsonManager::initialize(RenderableGlobe* globe) {
|
||||
|
||||
@@ -20,25 +20,15 @@ SGCTConfig = sgct.config.single{vsync=false}
|
||||
-- Streaming OpenSpace via Spout to OBS
|
||||
-- SGCTConfig = sgct.config.single{2560, 1440, shared=true, name="WV_OBS_SPOUT1"}
|
||||
|
||||
-- Elumenati Configs
|
||||
-- SGCTConfig = "${CONFIG}/spout_output_flat.json"
|
||||
-- SGCTConfig = "${CONFIG}/spout_output_cubemap.json"
|
||||
-- SGCTConfig = "${CONFIG}/spout_output_equirectangular.json"
|
||||
-- SGCTConfig = "${CONFIG}/spout_output_fisheye.json"
|
||||
|
||||
-- for more details about sgct configuration options see:
|
||||
-- https://sgct.github.io/configuration-files.html
|
||||
|
||||
-- To use a sgct configuration file set the variable like below
|
||||
-- SGCTConfig = "${CONFIG}/single_gui.xml"
|
||||
-- SGCTConfig = "${CONFIG}/single_gui.json"
|
||||
-- In the config/ folder we provide the some predefined files which you can modify.
|
||||
-- fullscreen1080.json: fullscreen window at 1920x1080
|
||||
-- gui_projector.json: one window for the gui and a second fullscreen window for rendering
|
||||
-- on the second monitor
|
||||
-- openvr_htcVive.json: window for VR on HTC Vive, only works if OpenSpace is compiled
|
||||
-- custom with the openvr support
|
||||
-- openvr_oculusRiftCv1.json: window for VR on Oculus Rift, only works if OpenSpace is
|
||||
-- compiled custom with the openvr support
|
||||
-- single.json: regular window
|
||||
-- single_fisheye.json: regular window with fisheye rendering
|
||||
-- single_fisheye_gui.json: one window for the gui, one window for fisheye rendering
|
||||
@@ -50,14 +40,8 @@ SGCTConfig = sgct.config.single{vsync=false}
|
||||
-- mirror rendering
|
||||
-- two_nodes.json: a configuration for running two instances of openspace, used for
|
||||
-- multiple projection systems
|
||||
-- spout_output_flat.json: a window where the rendering is sent to spout instead of the
|
||||
-- display
|
||||
-- spout_output_cubemap.json: a window where the rendering is sent to spout (max of 6 spout
|
||||
-- instances) instead of the display
|
||||
-- spout_output_equirectangular.json: a window where the rendering is sent to spout
|
||||
-- (equirectangular output) instead of the display
|
||||
-- spout_output_fisheye.json: a window where the rendering is sent to spout (fisheye
|
||||
-- output) instead of the display
|
||||
|
||||
-- Variable: Profile
|
||||
-- Sets the profile that should be loaded by OpenSpace.
|
||||
@@ -120,7 +104,7 @@ Paths = {
|
||||
SCRIPTS = "${BASE}/scripts",
|
||||
SHADERS = "${BASE}/shaders",
|
||||
TEMPORARY = "${BASE}/temp",
|
||||
GLOBEBROWSING = os.getenv("OPENSPACE_GLOBEBROWSING") or "${BASE}/../OpenSpaceData"
|
||||
GLOBEBROWSING = os.getenv("OPENSPACE_GLOBEBROWSING") or "${USER}/globebrowsing"
|
||||
}
|
||||
|
||||
ModuleConfigurations = {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
-- Helper functions that are useful to customize the openspace.cfg loading
|
||||
-- Helper functions that are useful to customize the openspace.cfg loading. Only edit this
|
||||
-- file if you know what you are doing. There are a few implicit variables that are passed
|
||||
-- into this file and are available: `ScreenResolution` contains the size (in pixels) of
|
||||
-- the main monitor. `TableToJson` is a function that receives a Lua table and returns the
|
||||
-- string-representation of that table converted to JSON
|
||||
|
||||
-- #######################################################################################
|
||||
-- ## Public functions ##
|
||||
@@ -8,7 +12,7 @@
|
||||
sgct = {}
|
||||
sgct.config = {}
|
||||
|
||||
-- Creates a configuration file similar to the default 'single.xml':
|
||||
-- Creates a configuration file similar to the default 'single.json':
|
||||
-- The parameter is a table and can contain the follow attributes:
|
||||
|
||||
-- first argument: horizontal window size {default: 1280}
|
||||
@@ -40,38 +44,6 @@ function sgct.config.single(arg) end
|
||||
|
||||
|
||||
|
||||
-- Creates a configuration file similar to the default 'single_fisheye.xml'
|
||||
-- The parameter is a table and can contain the follow attributes:
|
||||
|
||||
-- first argument: horizontal window size {default: 1280}
|
||||
-- second argument: vertical window size {default: 720}
|
||||
-- res: A table containing the horizontal and vertical resolution [example: res={3840, 2160}]
|
||||
-- pos: The position of the window on the screen [example: pos={50, 100}] {default: {50, 50}}
|
||||
-- fullScreen: Whether the application should run in exclusive full screen [example: fullScreen=true] {default: false}
|
||||
-- border: Whether the application should have window decorations (aka. border) [example: border=false] {default: true}
|
||||
-- monitor: Determines the monitor on which the application is started [example: monitor=2] {default: 0}
|
||||
-- shared: Determines whether the contents of the window should be shared using the SPOUT library [example: shared=true] {default: false}
|
||||
|
||||
-- Expert settings:
|
||||
-- vsync: Whether the rendering speed is locked to the refreshrate [example: vsync=true] {default: true}
|
||||
-- refreshRate: If vsync is enabled, this is the target framerate [example: refreshRate=30] {default: infinity}
|
||||
-- stereo: Select the stereo rendering mode as supported by SGCT [example: stereo='anaglyph_red_cyan'] {default: 'none'}
|
||||
-- msaa: The multisampling anti-aliasing factor [example: msaa=8] {default: 4}
|
||||
-- scene: Global settings to all scene objects (offset, orientation, scaling; each optional)
|
||||
-- [example: scene = {offset = {x = 1.0, y = 1.0, z = 2.0}, orientation = { yaw = 120, pitch = 15, roll = 0.0 }, scale = 10.0}]
|
||||
-- sgctDebug: Determines whether a higher debug level in SGCT is enabled [example: sgctDebug=true] {default: false}
|
||||
-- fov: The field of view for the fisheye [example: fov=360] {default: 180}
|
||||
-- quality: The quality setting for the cubemap textures [example: quality="4k"] {default: "1k"}
|
||||
-- tilt: The forwards tilt of the fisheye, relative to the center [example: tilt=90] {default: 0.0}
|
||||
-- background: The background color used outside of the fisheye rendering [example: backgruound={r=1.0, g=0.25, b=0.25, a=1.0}] {default: {r=0.1, g=0.1, b=0.1, a=1.0}}
|
||||
|
||||
-- Thus this function can be called the following ways:
|
||||
-- sgct.config.fisheye() -> Leading to a 1280x720 resolution window
|
||||
-- sgct.config.fisheye(640, 640) -> Leading to a 640x650 resolution window
|
||||
-- sgct.config.fisheye(640, 360, res={3840, 3840}) -> 640x360 window with 4K rendering resolution
|
||||
-- sgct.config.fisheye(msaa=1) -> 1280x720 resolution without multisampling
|
||||
function sgct.config.fisheye(arg) end
|
||||
|
||||
-- Global variable storing the name of the config function called at initialization
|
||||
sgctconfiginitializeString = ""
|
||||
|
||||
@@ -84,233 +56,6 @@ sgctconfiginitializeString = ""
|
||||
##########################################################################################
|
||||
]]--
|
||||
|
||||
function generateSingleViewportFOV(down, up, left, right, tracked)
|
||||
local result = {}
|
||||
|
||||
table.insert(result, [[ {]])
|
||||
|
||||
if tracked ~= nil then
|
||||
table.insert(result, [[ "tracked": ]] .. tostring(tracked) .. [[,]])
|
||||
end
|
||||
|
||||
table.insert(result, [[ "projection": {]])
|
||||
table.insert(result, [[ "type": "PlanarProjection",]])
|
||||
table.insert(result, [[ "fov": {]])
|
||||
table.insert(result, [[ "left": ]] .. left .. [[,]])
|
||||
table.insert(result, [[ "right": ]] .. right .. [[,]])
|
||||
table.insert(result, [[ "up": ]] .. up .. [[,]])
|
||||
table.insert(result, [[ "down": ]] .. down)
|
||||
table.insert(result, [[ }]])
|
||||
table.insert(result, [[ }]])
|
||||
table.insert(result, [[ }]])
|
||||
|
||||
return table.concat(result, "\n")
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateFisheyeViewport(fov, quality, tilt, background, crop, offset, tracked)
|
||||
local result = {}
|
||||
|
||||
table.insert(result, [[ {]])
|
||||
|
||||
if tracked ~= nil then
|
||||
table.insert(result, [[ "tracked": ]] .. tostring(tracked) .. [[,]])
|
||||
end
|
||||
|
||||
table.insert(result, [[ "projection": {]])
|
||||
table.insert(result, [[ "type": "FisheyeProjection",]])
|
||||
|
||||
if fov then
|
||||
table.insert(result, [[ "fov": ]] .. fov .. [[,]])
|
||||
end
|
||||
table.insert(result, [[ "quality": "]] .. quality .. [[",]])
|
||||
table.insert(result, [[ "tilt": ]] .. tilt .. [[,]])
|
||||
|
||||
if crop then
|
||||
table.insert(result, [[ "crop": {]])
|
||||
table.insert(result, [[ "left": ]] .. crop["left"] .. [[,]])
|
||||
table.insert(result, [[ "right": ]] .. crop["right"] .. [[,]])
|
||||
table.insert(result, [[ "top": ]] .. crop["top"] .. [[,]])
|
||||
table.insert(result, [[ "bottom": ]] .. crop["bottom"])
|
||||
table.insert(result, [[ },]])
|
||||
end
|
||||
|
||||
if offset then
|
||||
table.insert(result, [[ "offset": {]])
|
||||
table.insert(result, [[ "x": ]] .. offset["x"] .. [[,]])
|
||||
table.insert(result, [[ "y": ]] .. offset["y"] .. [[,]])
|
||||
table.insert(result, [[ "z": ]] .. offset["z"])
|
||||
table.insert(result, [[ },]])
|
||||
end
|
||||
|
||||
table.insert(result, [[ "background": {]])
|
||||
table.insert(result, [[ "r": ]] .. background["r"] .. [[,]])
|
||||
table.insert(result, [[ "g": ]] .. background["g"] .. [[,]])
|
||||
table.insert(result, [[ "b": ]] .. background["b"] .. [[,]])
|
||||
table.insert(result, [[ "a": ]] .. background["a"])
|
||||
table.insert(result, [[ }]])
|
||||
table.insert(result, [[ }]])
|
||||
table.insert(result, [[ }]])
|
||||
|
||||
return table.concat(result, "\n")
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateWindow(result, fullScreen, msaa, border, monitor, tags, stereo, pos,
|
||||
size, res, viewport)
|
||||
table.insert(result, [[ "name": "OpenSpace",]])
|
||||
|
||||
if fullScreen ~= nil then
|
||||
table.insert(result, [[ "fullscreen": ]] .. tostring(fullScreen) .. [[,]])
|
||||
end
|
||||
|
||||
if msaa then
|
||||
table.insert(result, [[ "msaa": ]] .. msaa .. [[,]])
|
||||
end
|
||||
|
||||
if border ~= nil then
|
||||
table.insert(result, [[ "border": ]] .. tostring(border) .. [[,]])
|
||||
end
|
||||
|
||||
if monitor then
|
||||
table.insert(result, [[ "monitor": ]] .. monitor .. [[,]])
|
||||
end
|
||||
|
||||
if #(tags) > 0 then
|
||||
for i, v in ipairs(tags) do
|
||||
tags[i] = "\"" .. v .. "\""
|
||||
end
|
||||
local t = table.concat(tags, [[,]])
|
||||
table.insert(result, [[ "tags": [ ]] .. t .. [[ ], ]])
|
||||
end
|
||||
|
||||
if stereo then
|
||||
table.insert(result, [[ "stereo": "]] .. stereo .. [[",]])
|
||||
end
|
||||
|
||||
local px = pos[1]
|
||||
local py = pos[2]
|
||||
table.insert(result, [[ "pos": { "x": ]] .. px .. [[, "y": ]] .. py .. [[ },]])
|
||||
|
||||
local sx = size[1]
|
||||
local sy = size[2]
|
||||
table.insert(result, [[ "size": { "x": ]] .. sx .. [[, "y": ]] .. sy .. [[ },]])
|
||||
|
||||
if res then
|
||||
res[1] = res[1] or size[1]
|
||||
res[2] = res[2] or size[2]
|
||||
table.insert(result, [[ "res": {]])
|
||||
table.insert(result, [[ "x": ]] .. res[1] .. [[,]])
|
||||
table.insert(result, [[ "y": ]] .. res[2])
|
||||
table.insert(result, [[ },]])
|
||||
end
|
||||
|
||||
table.insert(result, [[ "viewports": []])
|
||||
|
||||
table.insert(result, viewport)
|
||||
table.insert(result, [[ ] ]])
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateUser(result)
|
||||
table.insert(result, [[ {]])
|
||||
table.insert(result, [[ "eyeseparation": 0.06,]])
|
||||
table.insert(result, [[ "pos": { "x": 0.0, "y": 0.0, "z": 0.0 }]])
|
||||
table.insert(result, [[ }]])
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateScene(result, scene)
|
||||
if scene == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local offset = scene["offset"] or { x = 0.0, y = 0.0, z = 0.0 }
|
||||
local orientation = scene["orientation"] or { yaw = 0.0, pitch = 0.0, roll = 0.0 }
|
||||
local scale = scene["scale"] or 1.0
|
||||
|
||||
table.insert(result, [[{]])
|
||||
table.insert(result, [[ "offset": {]])
|
||||
table.insert(result, [[ "x": ]] .. offset["x"] .. [[,]])
|
||||
table.insert(result, [[ "y": ]] .. offset["y"] .. [[,]])
|
||||
table.insert(result, [[ "z": ]] .. offset["z"] .. [[,]])
|
||||
table.insert(result, [[ },]])
|
||||
table.insert(result, [[ "orientation": {]])
|
||||
table.insert(result, [[ "yaw": ]] .. orientation["yaw"] .. [[,]])
|
||||
table.insert(result, [[ "pitch": ]] .. orientation["pitch"] .. [[,]])
|
||||
table.insert(result, [[ "roll": ]] .. orientation["roll"] .. [[,]])
|
||||
table.insert(result, [[ },]])
|
||||
table.insert(result, [[ "scale": ]] .. scale)
|
||||
table.insert(result, [[}]])
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateSettings(result, refreshRate, vsync)
|
||||
table.insert(result, [[ "display": {]])
|
||||
|
||||
if refreshRate then
|
||||
table.insert(result, [[ "refreshrate": ]] .. refreshRate .. [[,]])
|
||||
end
|
||||
|
||||
if vsync then
|
||||
table.insert(result, [[ "swapinterval": 1]])
|
||||
else
|
||||
table.insert(result, [[ "swapinterval": 0]])
|
||||
end
|
||||
|
||||
table.insert(result, [[ }]])
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateCluster(arg, viewport)
|
||||
local result = {}
|
||||
|
||||
table.insert(result, [[{]])
|
||||
table.insert(result, [[ "version": 1,]])
|
||||
table.insert(result, [[ "masteraddress": "127.0.0.1",]])
|
||||
|
||||
if arg["sgctDebug"] ~= nil then
|
||||
table.insert(result, [[ "debug": ]] .. tostring(arg["sgctdebug"]) .. [[,]])
|
||||
end
|
||||
|
||||
table.insert(result, [[ "settings": {]])
|
||||
generateSettings(result, arg["refreshRate"], arg["vsync"])
|
||||
table.insert(result, [[ },]])
|
||||
|
||||
if arg["scene"] then
|
||||
table.insert(result, [[ "scene": {]])
|
||||
generateScene(result, arg["scene"])
|
||||
table.insert(result, [[ },]])
|
||||
end
|
||||
|
||||
table.insert(result, [[ "nodes": []])
|
||||
table.insert(result, [[ {]])
|
||||
table.insert(result, [[ "address": "127.0.0.1",]])
|
||||
table.insert(result, [[ "port": 20401,]])
|
||||
table.insert(result, [[ "windows": []])
|
||||
table.insert(result, [[ {]])
|
||||
generateWindow(result, arg["fullScreen"], arg["msaa"], arg["border"], arg["monitor"],
|
||||
arg["tags"], arg["stereo"], arg["pos"], arg["size"], arg["res"], viewport)
|
||||
table.insert(result, [[ }]])
|
||||
table.insert(result, [[ ] ]])
|
||||
table.insert(result, [[ }]])
|
||||
table.insert(result, [[ ],]])
|
||||
table.insert(result, [[ "users": []])
|
||||
generateUser(result)
|
||||
table.insert(result, [[ ] ]])
|
||||
table.insert(result, [[}]])
|
||||
|
||||
return table.concat(result, "\n")
|
||||
end
|
||||
|
||||
|
||||
|
||||
function check(type_str, arg, param, subparam_type)
|
||||
local t = type(arg[param])
|
||||
assert(t == type_str or t == "nil", param .. " must be a " .. type_str .. " or nil")
|
||||
@@ -327,55 +72,151 @@ end
|
||||
|
||||
|
||||
|
||||
function generateSingleWindowConfig(arg, viewport)
|
||||
check("table", arg, "res", "number")
|
||||
check("table", arg, "tags", "string")
|
||||
check("table", arg, "pos", "number")
|
||||
check("boolean", arg, "shared")
|
||||
check("boolean", arg, "fullScreen")
|
||||
check("boolean", arg, "border")
|
||||
check("boolean", arg, "vsync")
|
||||
check("boolean", arg, "sgctDebug")
|
||||
check("number", arg, "monitor")
|
||||
check("number", arg, "msaa")
|
||||
check("number", arg, "refreshRate")
|
||||
check("string", arg, "stereo")
|
||||
function generateSingleViewportFOV(down, up, left, right, tracked)
|
||||
local result = {
|
||||
projection = {
|
||||
type = "PlanarProjection",
|
||||
fov = {
|
||||
left = left,
|
||||
right = right,
|
||||
up = up,
|
||||
down = down
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check("table", arg, "scene")
|
||||
if arg["scene"] then
|
||||
check("table", arg["scene"], "offset", "number")
|
||||
check("table", arg["scene"], "orientation", "number")
|
||||
check("number", arg["scene"], "scale")
|
||||
if tracked ~= nil then
|
||||
result["tracked"] = tracked
|
||||
end
|
||||
|
||||
if arg["shared"] ~= nil then
|
||||
local t = arg["tags"]
|
||||
t[#t + 1] = "Spout"
|
||||
end
|
||||
|
||||
return generateCluster(arg, viewport)
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
function sgct.makeConfig(config)
|
||||
--
|
||||
-- Create a file in the operating system's `temp` folder
|
||||
--
|
||||
function generateWindow(fullScreen, msaa, border, monitor, tags, stereo, pos, size, res,
|
||||
viewport)
|
||||
local result = {
|
||||
name = "OpenSpace",
|
||||
pos = { x = pos[1], y = pos[2] },
|
||||
size = { x = size[1], y = size[2] },
|
||||
viewports = { viewport }
|
||||
}
|
||||
|
||||
-- tmpname returns the name to a randomly chosen file within the `temp` folder
|
||||
local tempFile = os.tmpname()
|
||||
-- `package.config` contains the path separator used on the current platform
|
||||
local directorySeparator = package.config:match("([^\n]*)\n?")
|
||||
-- Remove the filename and only get the path to the temp folder
|
||||
local separatorIdx = tempFile:reverse():find(directorySeparator)
|
||||
local tempFolder = tempFile:sub(1, #tempFile - separatorIdx + 1)
|
||||
if fullScreen ~= nil then
|
||||
result["fullscreen"] = fullScreen
|
||||
end
|
||||
|
||||
local configFile = tempFolder .. "openspace-window-configuration.json"
|
||||
local file = io.open(configFile, "w+")
|
||||
file:write(config)
|
||||
io.close(file)
|
||||
return configFile
|
||||
if msaa then
|
||||
result["msaa"] = msaa
|
||||
end
|
||||
|
||||
if border ~= nil then
|
||||
result["border"] = border
|
||||
end
|
||||
|
||||
if monitor then
|
||||
result["monitor"] = monitor
|
||||
end
|
||||
|
||||
if #(tags) > 0 then
|
||||
for i, v in ipairs(tags) do
|
||||
tags[i] = "\"" .. v .. "\""
|
||||
end
|
||||
local t = table.concat(tags, [[,]])
|
||||
result["tags"] = t
|
||||
end
|
||||
|
||||
if stereo then
|
||||
result["stereo"] = stereo
|
||||
end
|
||||
|
||||
if res then
|
||||
res[1] = res[1] or size[1]
|
||||
res[2] = res[2] or size[2]
|
||||
result["res"] = { x = res[1], y = res[2] }
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateScene(result, scene)
|
||||
if scene == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local offset = scene["offset"] or { x = 0.0, y = 0.0, z = 0.0 }
|
||||
local orientation = scene["orientation"] or { yaw = 0.0, pitch = 0.0, roll = 0.0 }
|
||||
local scale = scene["scale"] or 1.0
|
||||
|
||||
local result = {
|
||||
offset = offset,
|
||||
orientation = orientation,
|
||||
scale = scale
|
||||
}
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateSettings(refreshRate, vsync)
|
||||
local result = {
|
||||
display = {}
|
||||
}
|
||||
|
||||
if refreshRate then
|
||||
result["display"]["refreshrate"] = refreshRate
|
||||
end
|
||||
|
||||
if vsync then
|
||||
result["display"]["swapinterval"] = 1
|
||||
else
|
||||
result["display"]["swapinterval"] = 0
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
function generateCluster(arg)
|
||||
local viewport = generateSingleViewportFOV(arg["fov"]["down"], arg["fov"]["up"],
|
||||
arg["fov"]["left"], arg["fov"]["right"], arg["tracked"])
|
||||
|
||||
local result = {
|
||||
version = 1,
|
||||
masteraddress = "127.0.0.1",
|
||||
settings = generateSettings(arg["refreshRate"], arg["vsync"]),
|
||||
nodes = {
|
||||
{
|
||||
address = "127.0.0.1",
|
||||
port = 20401,
|
||||
windows = {
|
||||
generateWindow(arg["fullScreen"], arg["msaa"], arg["border"], arg["monitor"],
|
||||
arg["tags"], arg["stereo"], arg["pos"], arg["size"], arg["res"], viewport)
|
||||
}
|
||||
}
|
||||
},
|
||||
users = {
|
||||
{
|
||||
eyeseparation = 0.06,
|
||||
pos = { x = 0.0, y = 0.0, z = 0.0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if arg["sgctDebug"] ~= nil then
|
||||
result["debug"] = arg["sgctdebug"]
|
||||
end
|
||||
|
||||
if arg["scene"] then
|
||||
result["scene"] = generateScene(arg["scene"])
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
@@ -428,59 +269,51 @@ function sgct.config.single(arg)
|
||||
end
|
||||
|
||||
check("boolean", arg, "tracked")
|
||||
check("table", arg, "res", "number")
|
||||
check("table", arg, "tags", "string")
|
||||
check("table", arg, "pos", "number")
|
||||
check("boolean", arg, "shared")
|
||||
check("boolean", arg, "fullScreen")
|
||||
check("boolean", arg, "border")
|
||||
check("boolean", arg, "vsync")
|
||||
check("boolean", arg, "sgctDebug")
|
||||
check("number", arg, "monitor")
|
||||
check("number", arg, "msaa")
|
||||
check("number", arg, "refreshRate")
|
||||
check("string", arg, "stereo")
|
||||
|
||||
check("table", arg, "scene")
|
||||
if arg["scene"] then
|
||||
check("table", arg["scene"], "offset", "number")
|
||||
check("table", arg["scene"], "orientation", "number")
|
||||
check("number", arg["scene"], "scale")
|
||||
end
|
||||
|
||||
if arg["shared"] ~= nil then
|
||||
local t = arg["tags"]
|
||||
t[#t + 1] = "Spout"
|
||||
end
|
||||
|
||||
sgctconfiginitializeString = "sgct.config.single"
|
||||
|
||||
arg["tracked"] = arg["tracked"] or true
|
||||
|
||||
local viewport = generateSingleViewportFOV(arg["fov"]["down"], arg["fov"]["up"],
|
||||
arg["fov"]["left"], arg["fov"]["right"], arg["tracked"])
|
||||
--
|
||||
-- Create a file in the operating system's `temp` folder
|
||||
--
|
||||
|
||||
return sgct.makeConfig(generateSingleWindowConfig(arg, viewport))
|
||||
end
|
||||
|
||||
|
||||
|
||||
function sgct.config.fisheye(arg)
|
||||
arg = arg or {}
|
||||
|
||||
check("number", arg, 1)
|
||||
check("number", arg, 2)
|
||||
|
||||
if type(arg[1]) == "number" and type(arg[2]) == "number" then
|
||||
arg["size"] = { arg[1], arg[2] }
|
||||
arg[1] = nil
|
||||
arg[2] = nil
|
||||
else
|
||||
-- No numbers specified, therefore we want to use the screen resolution of the primary
|
||||
-- monitor to derive the resolution
|
||||
-- ScreenResolution is a variable that got injected into the openspace.cfg by the
|
||||
-- OpenSpace code prior to loading this file
|
||||
|
||||
local scale_factor = 2.0/3.0;
|
||||
arg["size"] = { ScreenResolution.x * scale_factor, ScreenResolution.y * scale_factor }
|
||||
end
|
||||
|
||||
check("number", arg, "fov")
|
||||
check("number", arg, "tilt")
|
||||
check("string", arg, "quality")
|
||||
check("table", arg, "background", "number")
|
||||
check("table", arg, "crop", "number")
|
||||
check("table", arg, "offset", "number")
|
||||
|
||||
sgctconfiginitializeString = "sgct.config.fisheye"
|
||||
|
||||
arg["vsync"] = arg["vsync"] or false
|
||||
arg["tags"] = arg["tags"] or {}
|
||||
arg["pos"] = arg["pos"] or { 50, 50 }
|
||||
arg["size"] = arg["size"] or { 1024, 1024 }
|
||||
|
||||
arg["quality"] = arg["quality"] or "1k"
|
||||
arg["tilt"] = arg["tilt"] or 90.0
|
||||
arg["background"] = arg["background"] or { r = 0.0, g = 0.0, b = 0.0, a = 1.0 }
|
||||
arg["tracked"] = arg["tracked"] or false
|
||||
|
||||
local viewport = generateFisheyeViewport(arg["fov"], arg["quality"], arg["tilt"],
|
||||
arg["background"], arg["crop"], arg["offset"], arg["tracked"])
|
||||
|
||||
return sgct.makeConfig(generateSingleWindowConfig(arg, viewport))
|
||||
local config = generateCluster(arg)
|
||||
-- tmpname returns the name to a randomly chosen file within the `temp` folder
|
||||
local tempFile = os.tmpname()
|
||||
-- `package.config` contains the path separator used on the current platform
|
||||
local directorySeparator = package.config:match("([^\n]*)\n?")
|
||||
-- Remove the filename and only get the path to the temp folder
|
||||
local separatorIdx = tempFile:reverse():find(directorySeparator)
|
||||
local tempFolder = tempFile:sub(1, #tempFile - separatorIdx + 1)
|
||||
|
||||
local configFile = tempFolder .. "openspace-window-configuration.json"
|
||||
local file = io.open(configFile, "w+")
|
||||
file:write(TableToJson(config))
|
||||
io.close(file)
|
||||
return configFile
|
||||
end
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/settings.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/util/json_helper.h>
|
||||
#include <ghoul/filesystem/file.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
@@ -770,6 +771,19 @@ Configuration loadConfigurationFromFile(const std::filesystem::path& configurati
|
||||
);
|
||||
ghoul::lua::runScript(result.state, script);
|
||||
|
||||
// Local function to convert a dictionary to its JSON object's string representation
|
||||
constexpr auto TableToJson = [](lua_State* state) {
|
||||
if (!ghoul::lua::hasValue<ghoul::Dictionary>(state)) {
|
||||
throw ghoul::lua::LuaError("TableToJson must receive a table object");
|
||||
}
|
||||
ghoul::Dictionary dict = ghoul::lua::value<ghoul::Dictionary>(state);
|
||||
std::string stringRepresentation = formatJson(dict);
|
||||
ghoul::lua::push(state, std::move(stringRepresentation));
|
||||
return 1;
|
||||
};
|
||||
lua_pushcfunction(result.state, TableToJson);
|
||||
lua_setglobal(result.state, "TableToJson");
|
||||
|
||||
// If there is an initial config helper file, load it into the state
|
||||
if (std::filesystem::is_regular_file(absPath(InitialConfigHelper))) {
|
||||
ghoul::lua::runScriptFile(result.state, absPath(InitialConfigHelper));
|
||||
|
||||
@@ -144,6 +144,90 @@ void AssetManager::deinitialize() {
|
||||
_toBeDeleted.clear();
|
||||
}
|
||||
|
||||
void AssetManager::runRemoveQueue() {
|
||||
ZoneScoped;
|
||||
for (const std::string& asset : _assetRemoveQueue) {
|
||||
std::filesystem::path path = generateAssetPath(_assetRootDirectory, asset);
|
||||
|
||||
const auto it = std::find_if(
|
||||
_assets.cbegin(),
|
||||
_assets.cend(),
|
||||
[&path](const std::unique_ptr<Asset>& a) { return a->path() == path; }
|
||||
);
|
||||
if (it == _assets.cend()) {
|
||||
LWARNING(std::format("Tried to remove unknown asset '{}'. Skipping", asset));
|
||||
continue;
|
||||
}
|
||||
|
||||
Asset* a = it->get();
|
||||
auto jt = std::find(_rootAssets.cbegin(), _rootAssets.cend(), a);
|
||||
if (jt == _rootAssets.cend()) {
|
||||
// Trying to remove an asset from the middle of the tree might have some
|
||||
// unexpected behavior since if we were to remove an asset with children, we
|
||||
// would have to unload those children as well. Also, we don't know if that
|
||||
// Asset's parents are actually requiring the asset we are about to remove so
|
||||
// we might break things horribly for people.
|
||||
// We should figure out a way to fix this without reintroducing the
|
||||
// require/request confusion, but until then, we'll prohibit removing non-root
|
||||
// assets
|
||||
|
||||
LWARNING("Tried to remove an asset that was not on the root level. Skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
_rootAssets.erase(jt);
|
||||
// Even though we are removing a root asset, we might not be the only person that
|
||||
// is interested in the asset, so we can only deinitialize it if we were, in fact,
|
||||
// the only person, meaning that the asset never had any parents
|
||||
if (!a->hasInitializedParent()) {
|
||||
a->deinitialize();
|
||||
}
|
||||
if (!a->hasLoadedParent()) {
|
||||
a->unload();
|
||||
}
|
||||
global::profile->removeAsset(asset);
|
||||
}
|
||||
_assetRemoveQueue.clear();
|
||||
}
|
||||
|
||||
void AssetManager::runAddQueue() {
|
||||
ZoneScoped;
|
||||
for (const std::string& asset : _assetAddQueue) {
|
||||
|
||||
const std::filesystem::path path = generateAssetPath(_assetRootDirectory, asset);
|
||||
Asset* a = nullptr;
|
||||
try {
|
||||
a = retrieveAsset(path, "");
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto it = std::find(_rootAssets.cbegin(), _rootAssets.cend(), a);
|
||||
if (it != _rootAssets.cend()) {
|
||||
// Do nothing if the asset has already been requested on a root level. Even if
|
||||
// another asset has already required this asset, calling `load`,
|
||||
// `startSynchronization`, etc on it are still fine since all of those
|
||||
// functions bail out early if they already have been called before
|
||||
continue;
|
||||
}
|
||||
|
||||
a->load(nullptr);
|
||||
if (a->isFailed()) {
|
||||
// The loading might fail because of any number of reasons, most likely of
|
||||
// them some Lua syntax error
|
||||
continue;
|
||||
}
|
||||
_rootAssets.push_back(a);
|
||||
a->startSynchronizations();
|
||||
|
||||
_toBeInitialized.push_back(a);
|
||||
global::profile->addAsset(asset);
|
||||
}
|
||||
_assetAddQueue.clear();
|
||||
}
|
||||
|
||||
void AssetManager::update() {
|
||||
ZoneScoped;
|
||||
|
||||
@@ -187,87 +271,10 @@ void AssetManager::update() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Add all assets that have been queued for loading since the last `update` call
|
||||
for (const std::string& asset : _assetAddQueue) {
|
||||
ZoneScopedN("Adding queued assets");
|
||||
|
||||
const std::filesystem::path path = generateAssetPath(_assetRootDirectory, asset);
|
||||
Asset* a = nullptr;
|
||||
try {
|
||||
a = retrieveAsset(path, "");
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto it = std::find(_rootAssets.cbegin(), _rootAssets.cend(), a);
|
||||
if (it != _rootAssets.cend()) {
|
||||
// Do nothing if the asset has already been requested on a root level. Even if
|
||||
// another asset has already required this asset, calling `load`,
|
||||
// `startSynchronization`, etc on it are still fine since all of those
|
||||
// functions bail out early if they already have been called before
|
||||
continue;
|
||||
}
|
||||
|
||||
a->load(nullptr);
|
||||
if (a->isFailed()) {
|
||||
// The loading might fail because of any number of reasons, most likely of
|
||||
// them some Lua syntax error
|
||||
continue;
|
||||
}
|
||||
_rootAssets.push_back(a);
|
||||
a->startSynchronizations();
|
||||
|
||||
_toBeInitialized.push_back(a);
|
||||
global::profile->addAsset(asset);
|
||||
}
|
||||
_assetAddQueue.clear();
|
||||
|
||||
// Remove assets
|
||||
for (const std::string& asset : _assetRemoveQueue) {
|
||||
ZoneScopedN("Removing queued assets");
|
||||
std::filesystem::path path = generateAssetPath(_assetRootDirectory, asset);
|
||||
|
||||
const auto it = std::find_if(
|
||||
_assets.cbegin(),
|
||||
_assets.cend(),
|
||||
[&path](const std::unique_ptr<Asset>& a) { return a->path() == path; }
|
||||
);
|
||||
if (it == _assets.cend()) {
|
||||
LWARNING(std::format("Tried to remove unknown asset '{}'. Skipping", asset));
|
||||
continue;
|
||||
}
|
||||
|
||||
Asset* a = it->get();
|
||||
auto jt = std::find(_rootAssets.cbegin(), _rootAssets.cend(), a);
|
||||
if (jt == _rootAssets.cend()) {
|
||||
// Trying to remove an asset from the middle of the tree might have some
|
||||
// unexpected behavior since if we were to remove an asset with children, we
|
||||
// would have to unload those children as well. Also, we don't know if that
|
||||
// Asset's parents are actually requiring the asset we are about to remove so
|
||||
// we might break things horribly for people.
|
||||
// We should figure out a way to fix this without reintroducing the
|
||||
// require/request confusion, but until then, we'll prohibit removing non-root
|
||||
// assets
|
||||
|
||||
LWARNING("Tried to remove an asset that was not on the root level. Skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
_rootAssets.erase(jt);
|
||||
// Even though we are removing a root asset, we might not be the only person that
|
||||
// is interested in the asset, so we can only deinitialize it if we were, in fact,
|
||||
// the only person, meaning that the asset never had any parents
|
||||
if (!a->hasInitializedParent()) {
|
||||
a->deinitialize();
|
||||
}
|
||||
if (!a->hasLoadedParent()) {
|
||||
a->unload();
|
||||
}
|
||||
global::profile->removeAsset(asset);
|
||||
}
|
||||
_assetRemoveQueue.clear();
|
||||
runRemoveQueue();
|
||||
// Add all assets that have been queued for loading since the last `update` call
|
||||
runAddQueue();
|
||||
|
||||
// Change state based on synchronizations. If any of the unfinished synchronizations
|
||||
// has finished since the last call of this function, we should notify the assets and
|
||||
@@ -317,6 +324,12 @@ void AssetManager::remove(const std::string& path) {
|
||||
_assetRemoveQueue.push_back(path);
|
||||
}
|
||||
|
||||
void AssetManager::reload(const std::string& path) {
|
||||
ghoul_precondition(!path.empty(), "Path must not be empty");
|
||||
_assetRemoveQueue.push_back(path);
|
||||
_assetAddQueue.push_back(path);
|
||||
}
|
||||
|
||||
std::vector<const Asset*> AssetManager::allAssets() const {
|
||||
std::vector<const Asset*> res;
|
||||
res.reserve(_assets.size());
|
||||
@@ -1042,6 +1055,7 @@ scripting::LuaLibrary AssetManager::luaLibrary() {
|
||||
{
|
||||
codegen::lua::Add,
|
||||
codegen::lua::Remove,
|
||||
codegen::lua::Reload,
|
||||
codegen::lua::RemoveAll,
|
||||
codegen::lua::IsLoaded,
|
||||
codegen::lua::AllAssets,
|
||||
|
||||
@@ -41,6 +41,15 @@ namespace {
|
||||
openspace::global::openSpaceEngine->assetManager().remove(assetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the asset with the specified name. If the asset was previously loaded explicity
|
||||
* it will be removed and then re-added. If the asset was not previously loaded, it will
|
||||
* only be loaded instead.
|
||||
*/
|
||||
[[codegen::luawrap]] void reload(std::string assetName) {
|
||||
openspace::global::openSpaceEngine->assetManager().reload(assetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all assets that are currently loaded
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <openspace/scene/sceneinitializer.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <openspace/util/updatestructures.h>
|
||||
#include <ghoul/lua/lua_helper.h>
|
||||
#include <stack>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user