Fixed merge conflicts with master

This commit is contained in:
GPayne
2021-05-20 19:55:49 -06:00
928 changed files with 31104 additions and 35653 deletions
@@ -113,7 +113,7 @@ namespace {
constexpr const char* GlslDeferredcastVsPath =
"${MODULES}/atmosphere/shaders/atmosphere_deferred_vs.glsl";
constexpr const float ATM_EPS = 2.f;
constexpr const float ATM_EPS = 2000.f;
constexpr const float KM_TO_M = 1000.f;
@@ -213,7 +213,7 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& renderData,
renderData.camera.sgctInternal.projectionMatrix()
) * renderData.camera.combinedViewMatrix();
const float totalAtmosphere = (_atmosphereRadius + ATM_EPS)* KM_TO_M;
const double totalAtmosphere = (scaledRadius + ATM_EPS);
if (!isAtmosphereInFrustum(MV, tPlanetPosWorld, totalAtmosphere)) {
program.setUniform(_uniformCache.cullAtmosphere, 1);
}
@@ -253,18 +253,18 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary)
, _atmosphereHeight(AtmosphereHeightInfo, 60.f, 0.1f, 99.0f)
, _groundAverageReflectance(AverageGroundReflectanceInfo, 0.f, 0.f, 1.f)
, _groundRadianceEmission(GroundRadianceEmittioninfo, 0.f, 0.f, 1.f)
, _rayleighHeightScale(RayleighHeightScaleInfo, 0.f, 0.1f, 20.f)
, _rayleighHeightScale(RayleighHeightScaleInfo, 0.f, 0.1f, 50.f)
, _rayleighScatteringCoeff(
RayleighScatteringCoeffInfo,
glm::vec3(0.f), glm::vec3(0.00001f), glm::vec3(0.1f)
)
, _ozoneEnabled(OzoneLayerInfo, false)
, _ozoneHeightScale(OzoneHeightScaleInfo, 0.f, 0.1f, 20.f)
, _ozoneHeightScale(OzoneHeightScaleInfo, 0.f, 0.1f, 50.f)
, _ozoneCoeff(
OzoneLayerCoeffInfo,
glm::vec3(0.f), glm::vec3(0.00001f), glm::vec3(0.001f)
)
, _mieHeightScale(MieHeightScaleInfo, 0.f, 0.1f, 20.f)
, _mieHeightScale(MieHeightScaleInfo, 0.f, 0.1f, 50.f)
, _mieScatteringCoeff(
MieScatteringCoeffInfo,
glm::vec3(0.004f), glm::vec3(0.00001f), glm::vec3(1.f)
@@ -381,6 +381,8 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary)
_hardShadowsEnabled.onChange(updateWithoutCalculation);
addProperty(_hardShadowsEnabled);
}
setBoundingSphere(_planetRadius * 1000.0);
}
void RenderableAtmosphere::deinitializeGL() {
@@ -390,33 +392,12 @@ void RenderableAtmosphere::deinitializeGL() {
void RenderableAtmosphere::initializeGL() {
_deferredcaster = std::make_unique<AtmosphereDeferredcaster>();
_deferredcaster->setAtmosphereRadius(_planetRadius + _atmosphereHeight);
_deferredcaster->setPlanetRadius(_planetRadius);
_deferredcaster->setPlanetAverageGroundReflectance(_groundAverageReflectance);
_deferredcaster->setPlanetGroundRadianceEmittion(_groundRadianceEmission);
_deferredcaster->setRayleighHeightScale(_rayleighHeightScale);
_deferredcaster->enableOzone(_ozoneEnabled);
_deferredcaster->setOzoneHeightScale(_ozoneHeightScale);
_deferredcaster->setMieHeightScale(_mieHeightScale);
_deferredcaster->setMiePhaseConstant(_miePhaseConstant);
_deferredcaster->setSunRadianceIntensity(_sunIntensity);
_deferredcaster->setRayleighScatteringCoefficients(_rayleighScatteringCoeff);
_deferredcaster->setOzoneExtinctionCoefficients(_ozoneCoeff);
_deferredcaster->setMieScatteringCoefficients(_mieScatteringCoeff);
_deferredcaster->setMieExtinctionCoefficients(_mieExtinctionCoeff);
// TODO: Fix the ellipsoid nature of the renderable globe (JCC)
//_deferredcaster->setEllipsoidRadii(_ellipsoid.radii());
_deferredcaster->enableSunFollowing(_sunFollowingCameraEnabled);
_deferredcaster->setPrecalculationTextureScale(_preCalculatedTexturesScale);
if (_saveCalculationsToTexture)
_deferredcaster->enablePrecalculationTexturesSaving();
updateAtmosphereParameters();
if (_shadowEnabled) {
_deferredcaster->setShadowConfigArray(_shadowConfArray);
// We no longer need it
_shadowConfArray.clear();
_deferredcaster->setHardShadows(_hardShadowsEnabled);
}
_deferredcaster->initialize();
@@ -479,8 +460,14 @@ void RenderableAtmosphere::updateAtmosphereParameters() {
_deferredcaster->setMieScatteringCoefficients(_mieScatteringCoeff);
_deferredcaster->setMieExtinctionCoefficients(_mieExtinctionCoeff);
_deferredcaster->enableSunFollowing(_sunFollowingCameraEnabled);
// TODO: Fix the ellipsoid nature of the renderable globe (JCC)
//_deferredcaster->setEllipsoidRadii(_ellipsoid.radii());
_deferredcaster->setPrecalculationTextureScale(_preCalculatedTexturesScale);
if (_saveCalculationsToTexture) {
_deferredcaster->enablePrecalculationTexturesSaving();
}
if (_shadowEnabled) {
_deferredcaster->setHardShadows(_hardShadowsEnabled);
}
@@ -368,7 +368,7 @@ void DashboardItemDistance::render(glm::vec2& penPosition) {
}
else {
const DistanceUnit unit = static_cast<DistanceUnit>(_requestedUnit.value());
const double convertedD = convertDistance(d, unit);
const double convertedD = convertMeters(d, unit);
dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) };
}
@@ -399,7 +399,7 @@ glm::vec2 DashboardItemDistance::size() const {
}
else {
DistanceUnit unit = static_cast<DistanceUnit>(_requestedUnit.value());
double convertedD = convertDistance(d, unit);
double convertedD = convertMeters(d, unit);
dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) };
}
@@ -133,7 +133,7 @@ void DashboardItemVelocity::render(glm::vec2& penPosition) {
}
else {
const DistanceUnit unit = static_cast<DistanceUnit>(_requestedUnit.value());
const double convertedD = convertDistance(speedPerSecond, unit);
const double convertedD = convertMeters(speedPerSecond, unit);
dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) };
}
@@ -159,7 +159,7 @@ glm::vec2 DashboardItemVelocity::size() const {
}
else {
DistanceUnit unit = static_cast<DistanceUnit>(_requestedUnit.value());
double convertedD = convertDistance(d, unit);
double convertedD = convertMeters(d, unit);
dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) };
}
@@ -92,7 +92,7 @@ RenderableGrid::RenderableGrid(const ghoul::Dictionary& dictionary)
, _color(ColorInfo, glm::vec3(0.5f), glm::vec3(0.f), glm::vec3(1.f))
, _segments(SegmentsInfo, glm::uvec2(10), glm::uvec2(1), glm::uvec2(200))
, _lineWidth(LineWidthInfo, 0.5f, 1.f, 20.f)
, _size(SizeInfo, glm::vec2(1e20f), glm::vec2(1.f), glm::vec2(1e35f))
, _size(SizeInfo, glm::vec2(1e10f), glm::vec2(1.f), glm::vec2(1e20f))
{
const Parameters p = codegen::bake<Parameters>(dictionary);
@@ -110,6 +110,7 @@ RenderableGrid::RenderableGrid(const ghoul::Dictionary& dictionary)
_lineWidth = p.lineWidth.value_or(_lineWidth);
addProperty(_lineWidth);
_size.setViewOption(properties::Property::ViewOptions::Logarithmic);
_size = p.size.value_or(_size);
_size.onChange([&]() { _gridIsDirty = true; });
addProperty(_size);
+3 -2
View File
@@ -96,9 +96,10 @@ RenderableDisc::RenderableDisc(const ghoul::Dictionary& dictionary)
const Parameters p = codegen::bake<Parameters>(dictionary);
_texturePath = p.texture.string();
_texturePath.onChange([&]() { _texture->loadFromFile(_texturePath); });
_texturePath.onChange([&]() { _texture->loadFromFile(_texturePath.value()); });
addProperty(_texturePath);
_size.setViewOption(properties::Property::ViewOptions::Logarithmic);
_size = p.size.value_or(_size);
setBoundingSphere(_size);
_size.onChange([&]() { _planeIsDirty = true; });
@@ -128,7 +129,7 @@ void RenderableDisc::initialize() {
void RenderableDisc::initializeGL() {
initializeShader();
_texture->loadFromFile(_texturePath);
_texture->loadFromFile(_texturePath.value());
_texture->uploadToGpu();
_plane->initialize();
@@ -123,7 +123,6 @@ private:
std::shared_ptr<ghoul::fontrendering::Font> _font;
std::string _speckFile;
std::string _colorMapFile;
std::string _labelFile;
std::string _colorOptionString;
+457 -291
View File
@@ -30,6 +30,7 @@
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/time.h>
#include <openspace/util/timeconversion.h>
#include <openspace/util/updatestructures.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/lightsource.h>
@@ -40,13 +41,12 @@
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/openglstatecache.h>
#include <ghoul/opengl/programobject.h>
#include <filesystem>
#include <optional>
namespace {
constexpr const char* _loggerCat = "RenderableModel";
constexpr const char* ProgramName = "ModelProgram";
constexpr const char* KeyGeomModelFile = "GeometryFile";
constexpr const char* KeyForceRenderInvisible = "ForceRenderInvisible";
constexpr const int DefaultBlending = 0;
constexpr const int AdditiveBlending = 1;
@@ -62,6 +62,12 @@ namespace {
{ "Color Adding", ColorAddingBlending }
};
constexpr openspace::properties::Property::PropertyInfo EnableAnimationInfo = {
"EnableAnimation",
"Enable Animation",
"Enable or disable the animation for the model if it has any"
};
constexpr const std::array<const char*, 12> UniformNames = {
"opacity", "nLightSources", "lightDirectionsViewSpace", "lightIntensities",
"modelViewTransform", "normalTransform", "projectionTransform",
@@ -126,9 +132,10 @@ namespace {
};
constexpr openspace::properties::Property::PropertyInfo BlendingOptionInfo = {
"BledingOption",
"BlendingOption",
"Blending Options",
"Debug option for blending colors."
"Changes the blending function used to calculate the colors of the model with "
"respect to the opacity."
};
constexpr openspace::properties::Property::PropertyInfo EnableOpacityBlendingInfo = {
@@ -136,110 +143,113 @@ namespace {
"Enable Opacity Blending",
"Enable Opacity Blending."
};
struct [[codegen::Dictionary(RenderableModel)]] Parameters {
// The file or files that should be loaded in this RenderableModel. The file can
// contain filesystem tokens. This specifies the model that is rendered by
// the Renderable.
std::filesystem::path geometryFile;
enum class ScaleUnit {
Nanometer,
Micrometer,
Millimeter,
Centimeter,
Decimeter,
Meter,
Kilometer
};
// The scale of the model. For example if the model is in centimeters
// then ModelScale = Centimeter or ModelScale = 0.01
std::optional<std::variant<ScaleUnit, double>> modelScale;
// Set if invisible parts (parts with no textures or materials) of the model
// should be forced to render or not.
std::optional<bool> forceRenderInvisible;
// [[codegen::verbatim(EnableAnimationInfo.description)]]
std::optional<bool> enableAnimation;
// The date and time that the model animation should start.
// In format 'YYYY MM DD hh:mm:ss'.
std::optional<std::string> animationStartTime [[codegen::datetime()]];
enum class AnimationTimeUnit {
Nanosecond,
Microsecond,
Millisecond,
Second,
Minute
};
// The time scale for the animation relative to seconds.
// Ex, if animation is in milliseconds then AnimationTimeScale = 0.001 or
// AnimationTimeScale = Millisecond, default is Second
std::optional<std::variant<AnimationTimeUnit, float>> animationTimeScale;
enum class AnimationMode {
Once,
LoopFromStart,
LoopInfinitely,
BounceFromStart,
BounceInfinitely
};
// The mode of how the animation should be played back.
// Default is animation is played back once at the start time.
// For a more detailed description see:
// http://wiki.openspaceproject.com/docs/builders/model-animation
std::optional<AnimationMode> animationMode;
// [[codegen::verbatim(AmbientIntensityInfo.description)]]
std::optional<double> ambientIntensity;
// [[codegen::verbatim(DiffuseIntensityInfo.description)]]
std::optional<double> diffuseIntensity;
// [[codegen::verbatim(SpecularIntensityInfo.description)]]
std::optional<double> specularIntensity;
// [[codegen::verbatim(ShadingInfo.description)]]
std::optional<bool> performShading;
// [[codegen::verbatim(DisableFaceCullingInfo.description)]]
std::optional<bool> disableFaceCulling;
// [[codegen::verbatim(ModelTransformInfo.description)]]
std::optional<glm::dmat3x3> modelTransform;
// [[codegen::verbatim(RotationVecInfo.description)]]
std::optional<glm::dvec3> rotationVector;
// [[codegen::verbatim(LightSourcesInfo.description)]]
std::optional<std::vector<ghoul::Dictionary>> lightSources
[[codegen::reference("core_light_source")]];
// [[codegen::verbatim(DisableDepthTestInfo.description)]]
std::optional<bool> disableDepthTest;
// [[codegen::verbatim(BlendingOptionInfo.description)]]
std::optional<std::string> blendingOption;
// [[codegen::verbatim(EnableOpacityBlendingInfo.description)]]
std::optional<bool> enableOpacityBlending;
};
#include "renderablemodel_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderableModel::Documentation() {
using namespace documentation;
return {
"RenderableModel",
"base_renderable_model",
{
{
KeyGeomModelFile,
new OrVerifier({ new StringVerifier, new StringListVerifier }),
Optional::No,
"The file or files that should be loaded in this RenderableModel. The file can "
"contain filesystem tokens or can be specified relatively to the "
"location of the .mod file. "
"This specifies the model that is rendered by the Renderable."
},
{
KeyForceRenderInvisible,
new BoolVerifier,
Optional::Yes,
"Set if invisible parts (parts with no textures or materials) of the model "
"should be forced to render or not."
},
{
AmbientIntensityInfo.identifier,
new DoubleVerifier,
Optional::Yes,
AmbientIntensityInfo.description
},
{
DiffuseIntensityInfo.identifier,
new DoubleVerifier,
Optional::Yes,
DiffuseIntensityInfo.description
},
{
SpecularIntensityInfo.identifier,
new DoubleVerifier,
Optional::Yes,
SpecularIntensityInfo.description
},
{
ShadingInfo.identifier,
new BoolVerifier,
Optional::Yes,
ShadingInfo.description
},
{
DisableFaceCullingInfo.identifier,
new BoolVerifier,
Optional::Yes,
DisableFaceCullingInfo.description
},
{
ModelTransformInfo.identifier,
new DoubleMatrix3Verifier,
Optional::Yes,
ModelTransformInfo.description
},
{
RotationVecInfo.identifier,
new DoubleVector3Verifier,
Optional::Yes,
RotationVecInfo.description
},
{
LightSourcesInfo.identifier,
new TableVerifier({
{
"*",
new ReferencingVerifier("core_light_source"),
Optional::Yes
}
}),
Optional::Yes,
LightSourcesInfo.description
},
{
DisableDepthTestInfo.identifier,
new BoolVerifier,
Optional::Yes,
DisableDepthTestInfo.description
},
{
BlendingOptionInfo.identifier,
new StringVerifier,
Optional::Yes,
BlendingOptionInfo.description
},
{
EnableOpacityBlendingInfo.identifier,
new BoolVerifier,
Optional::Yes,
EnableOpacityBlendingInfo.description
},
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "base_renderable_model";
return doc;
}
RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _enableAnimation(EnableAnimationInfo, false)
, _ambientIntensity(AmbientIntensityInfo, 0.2f, 0.f, 1.f)
, _diffuseIntensity(DiffuseIntensityInfo, 1.f, 0.f, 1.f)
, _specularIntensity(SpecularIntensityInfo, 1.f, 0.f, 1.f)
@@ -260,17 +270,13 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
)
, _lightSourcePropertyOwner({ "LightSources", "Light Sources" })
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableModel"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
addProperty(_opacity);
registerUpdateRenderBinFromOpacity();
if (dictionary.hasKey(KeyForceRenderInvisible)) {
_forceRenderInvisible = dictionary.value<bool>(KeyForceRenderInvisible);
if (p.forceRenderInvisible.has_value()) {
_forceRenderInvisible = *p.forceRenderInvisible;
if (!_forceRenderInvisible) {
// Asset file have specifically said to not render invisible parts,
@@ -279,103 +285,165 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
}
}
if (dictionary.hasKey(KeyGeomModelFile)) {
std::string file;
std::string file = absPath(p.geometryFile.string()).string();
_geometry = ghoul::io::ModelReader::ref().loadModel(
file,
ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible),
ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped)
);
if (dictionary.hasValue<std::string>(KeyGeomModelFile)) {
// Handle single file
file = absPath(dictionary.value<std::string>(KeyGeomModelFile));
_geometry = ghoul::io::ModelReader::ref().loadModel(
file,
ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible),
ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped)
);
if (p.modelScale.has_value()) {
if (std::holds_alternative<Parameters::ScaleUnit>(*p.modelScale)) {
Parameters::ScaleUnit scaleUnit =
std::get<Parameters::ScaleUnit>(*p.modelScale);
DistanceUnit distanceUnit;
switch (scaleUnit) {
case Parameters::ScaleUnit::Nanometer:
distanceUnit = DistanceUnit::Nanometer;
break;
case Parameters::ScaleUnit::Micrometer:
distanceUnit = DistanceUnit::Micrometer;
break;
case Parameters::ScaleUnit::Millimeter:
distanceUnit = DistanceUnit::Millimeter;
break;
case Parameters::ScaleUnit::Centimeter:
distanceUnit = DistanceUnit::Centimeter;
break;
case Parameters::ScaleUnit::Decimeter:
distanceUnit = DistanceUnit::Decimeter;
break;
case Parameters::ScaleUnit::Meter:
distanceUnit = DistanceUnit::Meter;
break;
case Parameters::ScaleUnit::Kilometer:
distanceUnit = DistanceUnit::Kilometer;
break;
default:
throw ghoul::MissingCaseException();
}
_modelScale = convertUnit(distanceUnit, DistanceUnit::Meter);
}
else if (dictionary.hasValue<ghoul::Dictionary>(KeyGeomModelFile)) {
LWARNING("Loading a model with several files is deprecated and will be "
"removed in a future release"
);
ghoul::Dictionary fileDictionary = dictionary.value<ghoul::Dictionary>(
KeyGeomModelFile
);
std::vector<std::unique_ptr<ghoul::modelgeometry::ModelGeometry>> geometries;
for (std::string_view k : fileDictionary.keys()) {
// Handle each file
file = absPath(fileDictionary.value<std::string>(k));
geometries.push_back(ghoul::io::ModelReader::ref().loadModel(
file,
ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible),
ghoul::io::ModelReader::NotifyInvisibleDropped(_notifyInvisibleDropped)
));
}
if (!geometries.empty()) {
std::unique_ptr<ghoul::modelgeometry::ModelGeometry> combinedGeometry =
std::move(geometries[0]);
// Combine all models into one ModelGeometry
for (unsigned int i = 1; i < geometries.size(); ++i) {
for (ghoul::io::ModelMesh& mesh : geometries[i]->meshes()) {
combinedGeometry->meshes().push_back(
std::move(mesh)
);
}
for (ghoul::modelgeometry::ModelGeometry::TextureEntry& texture :
geometries[i]->textureStorage())
{
combinedGeometry->textureStorage().push_back(
std::move(texture)
);
}
}
_geometry = std::move(combinedGeometry);
_geometry->calculateBoundingRadius();
}
else if (std::holds_alternative<double>(*p.modelScale)) {
_modelScale = std::get<double>(*p.modelScale);
}
else {
throw ghoul::MissingCaseException();
}
}
if (dictionary.hasKey(ModelTransformInfo.identifier)) {
_modelTransform = dictionary.value<glm::dmat3>(ModelTransformInfo.identifier);
if (p.animationStartTime.has_value()) {
if (!_geometry->hasAnimation()) {
LWARNING("Animation start time given to model without animation");
}
_animationStart = *p.animationStartTime;
}
if (dictionary.hasKey(AmbientIntensityInfo.identifier)) {
_ambientIntensity = dictionary.value<double>(AmbientIntensityInfo.identifier);
}
if (dictionary.hasKey(DiffuseIntensityInfo.identifier)) {
_diffuseIntensity = dictionary.value<double>(DiffuseIntensityInfo.identifier);
}
if (dictionary.hasKey(SpecularIntensityInfo.identifier)) {
_specularIntensity = dictionary.value<double>(SpecularIntensityInfo.identifier);
if (p.enableAnimation.has_value()) {
if (!_geometry->hasAnimation()) {
LWARNING("Attempting to enable animation for a model that does not have any");
}
else if (*p.enableAnimation && _animationStart.empty()) {
LWARNING("Cannot enable animation without a given start time");
}
else {
_enableAnimation = *p.enableAnimation;
_geometry->enableAnimation(_enableAnimation);
}
}
if (dictionary.hasKey(ShadingInfo.identifier)) {
_performShading = dictionary.value<bool>(ShadingInfo.identifier);
if (p.animationTimeScale.has_value()) {
if (!_geometry->hasAnimation()) {
LWARNING("Animation time scale given to model without animation");
}
else if (std::holds_alternative<float>(*p.animationTimeScale)) {
_geometry->setTimeScale(std::get<float>(*p.animationTimeScale));
}
else if (std::holds_alternative<Parameters::AnimationTimeUnit>(*p.animationTimeScale)) {
Parameters::AnimationTimeUnit animationTimeUnit =
std::get<Parameters::AnimationTimeUnit>(*p.animationTimeScale);
TimeUnit timeUnit;
switch (animationTimeUnit) {
case Parameters::AnimationTimeUnit::Nanosecond:
timeUnit = TimeUnit::Nanosecond;
break;
case Parameters::AnimationTimeUnit::Microsecond:
timeUnit = TimeUnit::Microsecond;
break;
case Parameters::AnimationTimeUnit::Millisecond:
timeUnit = TimeUnit::Millisecond;
break;
case Parameters::AnimationTimeUnit::Second:
timeUnit = TimeUnit::Second;
break;
case Parameters::AnimationTimeUnit::Minute:
timeUnit = TimeUnit::Minute;
break;
default:
throw ghoul::MissingCaseException();
}
_geometry->setTimeScale(convertTime(1.0, timeUnit, TimeUnit::Second));
}
else {
throw ghoul::MissingCaseException();
}
}
if (dictionary.hasKey(DisableDepthTestInfo.identifier)) {
_disableDepthTest = dictionary.value<bool>(DisableDepthTestInfo.identifier);
if (p.animationMode.has_value()) {
if (!_geometry->hasAnimation()) {
LWARNING("Animation mode given to model without animation");
}
switch (*p.animationMode) {
case Parameters::AnimationMode::LoopFromStart:
_animationMode = AnimationMode::LoopFromStart;
break;
case Parameters::AnimationMode::LoopInfinitely:
_animationMode = AnimationMode::LoopInfinitely;
break;
case Parameters::AnimationMode::BounceFromStart:
_animationMode = AnimationMode::BounceFromStart;
break;
case Parameters::AnimationMode::BounceInfinitely:
_animationMode = AnimationMode::BounceInfinitely;
break;
case Parameters::AnimationMode::Once:
_animationMode = AnimationMode::Once;
break;
default:
throw ghoul::MissingCaseException();
}
}
if (dictionary.hasKey(DisableFaceCullingInfo.identifier)) {
_disableFaceCulling = dictionary.value<bool>(DisableFaceCullingInfo.identifier);
if (p.modelTransform.has_value()) {
_modelTransform = *p.modelTransform;
}
if (dictionary.hasKey(LightSourcesInfo.identifier)) {
const ghoul::Dictionary& lsDictionary =
dictionary.value<ghoul::Dictionary>(LightSourcesInfo.identifier);
_ambientIntensity = p.ambientIntensity.value_or(_ambientIntensity);
_diffuseIntensity = p.diffuseIntensity.value_or(_diffuseIntensity);
_specularIntensity = p.specularIntensity.value_or(_specularIntensity);
_performShading = p.performShading.value_or(_performShading);
_disableDepthTest = p.disableDepthTest.value_or(_disableDepthTest);
_disableFaceCulling = p.disableFaceCulling.value_or(_disableFaceCulling);
for (std::string_view k : lsDictionary.keys()) {
std::unique_ptr<LightSource> lightSource = LightSource::createFromDictionary(
lsDictionary.value<ghoul::Dictionary>(k)
);
if (p.lightSources.has_value()) {
std::vector<ghoul::Dictionary> lightsources = *p.lightSources;
for (const ghoul::Dictionary& lsDictionary : lightsources) {
std::unique_ptr<LightSource> lightSource =
LightSource::createFromDictionary(lsDictionary);
_lightSourcePropertyOwner.addPropertySubOwner(lightSource.get());
_lightSources.push_back(std::move(lightSource));
}
}
if (_geometry->hasAnimation()) {
addProperty(_enableAnimation);
}
addPropertySubOwner(_lightSourcePropertyOwner);
addProperty(_ambientIntensity);
addProperty(_diffuseIntensity);
@@ -390,9 +458,21 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
_modelTransform = glm::mat4_cast(glm::quat(glm::radians(_rotationVec.value())));
});
_enableAnimation.onChange([this]() {
if (!_geometry->hasAnimation()) {
LWARNING("Attempting to enable animation for a model that does not have any");
}
else if (_enableAnimation && _animationStart.empty()) {
LWARNING("Cannot enable animation without a given start time");
_enableAnimation = false;
}
else {
_geometry->enableAnimation(_enableAnimation);
}
});
if (dictionary.hasKey(RotationVecInfo.identifier)) {
_rotationVec = dictionary.value<glm::dvec3>(RotationVecInfo.identifier);
if (p.rotationVector.has_value()) {
_rotationVec = *p.rotationVector;
}
_blendingFuncOption.addOption(DefaultBlending, "Default");
@@ -403,18 +483,12 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
addProperty(_blendingFuncOption);
if (dictionary.hasKey(BlendingOptionInfo.identifier)) {
const std::string blendingOpt = dictionary.value<std::string>(
BlendingOptionInfo.identifier
);
if (p.blendingOption.has_value()) {
const std::string blendingOpt = *p.blendingOption;
_blendingFuncOption.set(BlendingMapping[blendingOpt]);
}
if (dictionary.hasKey(DisableDepthTestInfo.identifier)) {
_enableOpacityBlending = dictionary.value<bool>(
EnableOpacityBlendingInfo.identifier
);
}
_enableOpacityBlending = p.enableOpacityBlending.value_or(_enableOpacityBlending);
addProperty(_enableOpacityBlending);
}
@@ -426,6 +500,15 @@ bool RenderableModel::isReady() const {
void RenderableModel::initialize() {
ZoneScoped
if (_geometry->hasAnimation() && _enableAnimation && _animationStart.empty()) {
LWARNING("Model with animation not given any start time");
}
else if (_geometry->hasAnimation() && !_enableAnimation) {
LINFO("Model with deactivated animation was found. "
"The animation can be activated by entering a start time in the asset file"
);
}
for (const std::unique_ptr<LightSource>& ls : _lightSources) {
ls->initialize();
}
@@ -449,7 +532,7 @@ void RenderableModel::initializeGL() {
_geometry->initialize();
_geometry->calculateBoundingRadius();
setBoundingSphere(glm::sqrt(_geometry->boundingRadius()));
setBoundingSphere(_geometry->boundingRadius() * _modelScale);
}
void RenderableModel::deinitializeGL() {
@@ -466,114 +549,197 @@ void RenderableModel::deinitializeGL() {
}
void RenderableModel::render(const RenderData& data, RendererTasks&) {
_program->activate();
const double distanceToCamera = glm::distance(
data.camera.positionVec3(),
data.modelTransform.translation
);
_program->setUniform(_uniformCache.opacity, _opacity);
// This distance will be enough to render the model as one pixel if the field of
// view is 'fov' radians and the screen resolution is 'res' pixels.
// Formula from RenderableGlobe
constexpr double tfov = 0.5773502691896257;
constexpr int res = 2880;
const double maxDistance = res * boundingSphere() / tfov;
// Model transform and view transform needs to be in double precision
const glm::dmat4 modelTransform =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation
glm::dmat4(data.modelTransform.rotation) * // Spice rotation
glm::scale(
glm::dmat4(_modelTransform.value()), glm::dvec3(data.modelTransform.scale)
);
const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() *
modelTransform;
if (distanceToCamera < maxDistance) {
_program->activate();
int nLightSources = 0;
_lightIntensitiesBuffer.resize(_lightSources.size());
_lightDirectionsViewSpaceBuffer.resize(_lightSources.size());
for (const std::unique_ptr<LightSource>& lightSource : _lightSources) {
if (!lightSource->isEnabled()) {
continue;
_program->setUniform(_uniformCache.opacity, _opacity);
// Model transform and view transform needs to be in double precision
const glm::dmat4 modelTransform =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation
glm::dmat4(data.modelTransform.rotation) * // Spice rotation
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) *
glm::scale(
glm::dmat4(_modelTransform.value()),
glm::dvec3(_modelScale) // Model scale unit
);
const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() *
modelTransform;
int nLightSources = 0;
_lightIntensitiesBuffer.resize(_lightSources.size());
_lightDirectionsViewSpaceBuffer.resize(_lightSources.size());
for (const std::unique_ptr<LightSource>& lightSource : _lightSources) {
if (!lightSource->isEnabled()) {
continue;
}
_lightIntensitiesBuffer[nLightSources] = lightSource->intensity();
_lightDirectionsViewSpaceBuffer[nLightSources] =
lightSource->directionViewSpace(data);
++nLightSources;
}
_lightIntensitiesBuffer[nLightSources] = lightSource->intensity();
_lightDirectionsViewSpaceBuffer[nLightSources] =
lightSource->directionViewSpace(data);
++nLightSources;
_program->setUniform(
_uniformCache.nLightSources,
nLightSources
);
_program->setUniform(
_uniformCache.lightIntensities,
_lightIntensitiesBuffer
);
_program->setUniform(
_uniformCache.lightDirectionsViewSpace,
_lightDirectionsViewSpaceBuffer
);
_program->setUniform(
_uniformCache.modelViewTransform,
glm::mat4(modelViewTransform)
);
glm::dmat4 normalTransform = glm::transpose(glm::inverse(modelViewTransform));
_program->setUniform(
_uniformCache.normalTransform,
glm::mat4(normalTransform)
);
_program->setUniform(
_uniformCache.projectionTransform,
data.camera.projectionMatrix()
);
_program->setUniform(_uniformCache.ambientIntensity, _ambientIntensity);
_program->setUniform(_uniformCache.diffuseIntensity, _diffuseIntensity);
_program->setUniform(_uniformCache.specularIntensity, _specularIntensity);
_program->setUniform(_uniformCache.performShading, _performShading);
_program->setUniform(_uniformCache.opacityBlending, _enableOpacityBlending);
if (_disableFaceCulling) {
glDisable(GL_CULL_FACE);
}
glEnablei(GL_BLEND, 0);
switch (_blendingFuncOption) {
case DefaultBlending:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case AdditiveBlending:
glBlendFunc(GL_ONE, GL_ONE);
break;
case PointsAndLinesBlending:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case PolygonBlending:
glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE);
break;
case ColorAddingBlending:
glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);
break;
};
if (_disableDepthTest) {
glDisable(GL_DEPTH_TEST);
}
_geometry->render(*_program);
if (_disableFaceCulling) {
glEnable(GL_CULL_FACE);
}
global::renderEngine->openglStateCache().resetBlendState();
if (_disableDepthTest) {
glEnable(GL_DEPTH_TEST);
}
_program->deactivate();
}
_program->setUniform(
_uniformCache.nLightSources,
nLightSources
);
_program->setUniform(
_uniformCache.lightIntensities,
_lightIntensitiesBuffer
);
_program->setUniform(
_uniformCache.lightDirectionsViewSpace,
_lightDirectionsViewSpaceBuffer
);
_program->setUniform(
_uniformCache.modelViewTransform,
glm::mat4(modelViewTransform)
);
glm::dmat4 normalTransform = glm::transpose(glm::inverse(modelViewTransform));
_program->setUniform(
_uniformCache.normalTransform,
glm::mat4(normalTransform)
);
_program->setUniform(
_uniformCache.projectionTransform,
data.camera.projectionMatrix()
);
_program->setUniform(_uniformCache.ambientIntensity, _ambientIntensity);
_program->setUniform(_uniformCache.diffuseIntensity, _diffuseIntensity);
_program->setUniform(_uniformCache.specularIntensity, _specularIntensity);
_program->setUniform(_uniformCache.performShading, _performShading);
_program->setUniform(_uniformCache.opacityBlending, _enableOpacityBlending);
if (_disableFaceCulling) {
glDisable(GL_CULL_FACE);
}
glEnablei(GL_BLEND, 0);
switch (_blendingFuncOption) {
case DefaultBlending:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case AdditiveBlending:
glBlendFunc(GL_ONE, GL_ONE);
break;
case PointsAndLinesBlending:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case PolygonBlending:
glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE);
break;
case ColorAddingBlending:
glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);
break;
};
if (_disableDepthTest) {
glDisable(GL_DEPTH_TEST);
}
_geometry->render(*_program);
if (_disableFaceCulling) {
glEnable(GL_CULL_FACE);
}
global::renderEngine->openglStateCache().resetBlendState();
if (_disableDepthTest) {
glEnable(GL_DEPTH_TEST);
}
_program->deactivate();
}
void RenderableModel::update(const UpdateData&) {
void RenderableModel::update(const UpdateData& data) {
if (_program->isDirty()) {
_program->rebuildFromFile();
ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames);
}
if (_geometry->hasAnimation() && !_animationStart.empty()) {
double relativeTime;
double now = data.time.j2000Seconds();
double startTime = data.time.convertTime(_animationStart);
double duration = _geometry->animationDuration();
// The animation works in a time range 0 to duration where 0 in the animation is
// the given _animationStart time in OpenSpace. The time in OpenSpace then has to
// be converted to the animation time range, so the animation knows which
// keyframes it should interpolate between for each frame. The conversion is
// done in different ways depending on the animation mode.
// Explanation: s indicates start time, / indicates animation is played once forwards,
// \ indicates animation is played once backwards, time moves to the right.
switch (_animationMode) {
case AnimationMode::LoopFromStart:
// Start looping from the start time
// s//// ...
relativeTime = std::fmod(now - startTime, duration);
break;
case AnimationMode::LoopInfinitely:
// Loop both before and after the start time where the model is
// in the initial position at the start time. std::fmod is not a
// true modulo function, it just calculates the remainder of the division
// which can be negative. To make it true modulo it is bumped up to
// positive values when it is negative
// //s// ...
relativeTime = std::fmod(now - startTime, duration);
if (relativeTime < 0.0) {
relativeTime += duration;
}
break;
case AnimationMode::BounceFromStart:
// Bounce from the start position. Bounce means to do the animation
// and when it ends, play the animation in reverse to make sure the model
// goes back to its initial position before starting again. Avoids a
// visible jump from the last position to the first position when loop
// starts again
// s/\/\/\/\ ...
relativeTime =
duration - abs(fmod(now - startTime, 2 * duration) - duration);
break;
case AnimationMode::BounceInfinitely: {
// Bounce both before and after the start time where the model is
// in the initial position at the start time
// /\/\s/\/\ ...
double modulo = fmod(now - startTime, 2 * duration);
if (modulo < 0.0) {
modulo += 2 * duration;
}
relativeTime = duration - abs(modulo - duration);
break;
}
case AnimationMode::Once:
// Play animation once starting from the start time and stay at the
// animation's last position when animation is over
// s/
relativeTime = now - startTime;
if (relativeTime > duration) {
relativeTime = duration;
}
break;
default:
throw ghoul::MissingCaseException();
}
_geometry->update(relativeTime);
}
}
} // namespace openspace
+13
View File
@@ -33,6 +33,7 @@
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/vector/vec3property.h>
#include <openspace/util/distanceconversion.h>
#include <ghoul/misc/managedmemoryuniqueptr.h>
#include <ghoul/io/model/modelreader.h>
#include <ghoul/opengl/uniformcache.h>
@@ -70,9 +71,21 @@ public:
static documentation::Documentation Documentation();
private:
enum class AnimationMode {
Once = 0,
LoopFromStart,
LoopInfinitely,
BounceFromStart,
BounceInfinitely
};
std::unique_ptr<ghoul::modelgeometry::ModelGeometry> _geometry;
double _modelScale = 1.0;
bool _forceRenderInvisible = false;
bool _notifyInvisibleDropped = true;
std::string _animationStart;
AnimationMode _animationMode = AnimationMode::Once;
properties::BoolProperty _enableAnimation;
properties::FloatProperty _ambientIntensity;
@@ -127,6 +127,7 @@ RenderableNodeLine::RenderableNodeLine(const ghoul::Dictionary& dictionary)
addProperty(_end);
_lineColor = p.color.value_or(_lineColor);
_lineColor.setViewOption(properties::Property::ViewOptions::Color);
addProperty(_lineColor);
_lineWidth = p.lineWidth.value_or(_lineWidth);
+5 -1
View File
@@ -91,7 +91,7 @@ namespace {
// [[codegen::verbatim(BlendModeInfo.description)]]
std::optional<BlendMode> blendMode;
// [[codegen::verbatim(BlendModeInfo.description)]]
// [[codegen::verbatim(MultiplyColorInfo.description)]]
std::optional<glm::vec3> multiplyColor [[codegen::color()]];
};
#include "renderableplane_codegen.cpp"
@@ -153,9 +153,11 @@ RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary)
}
_multiplyColor = p.multiplyColor.value_or(_multiplyColor);
_multiplyColor.setViewOption(properties::Property::ViewOptions::Color);
addProperty(_billboard);
_size.setViewOption(properties::Property::ViewOptions::Logarithmic);
addProperty(_size);
_size.onChange([this](){ _planeIsDirty = true; });
@@ -273,6 +275,7 @@ void RenderablePlane::render(const RenderData& data, RendererTasks&) {
glBindVertexArray(_quad);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
if (additiveBlending) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -325,6 +328,7 @@ void RenderablePlane::createPlane() {
sizeof(GLfloat) * 6,
reinterpret_cast<void*>(sizeof(GLfloat) * 4)
);
glBindVertexArray(0);
}
} // namespace openspace
@@ -101,14 +101,12 @@ RenderablePlaneImageLocal::RenderablePlaneImageLocal(const ghoul::Dictionary& di
addProperty(_blendMode);
_texturePath = absPath(p.texture);
_textureFile = std::make_unique<ghoul::filesystem::File>(_texturePath);
_texturePath = absPath(p.texture).string();
_textureFile = std::make_unique<ghoul::filesystem::File>(_texturePath.value());
addProperty(_texturePath);
_texturePath.onChange([this]() { loadTexture(); });
_textureFile->setCallback(
[this](const ghoul::filesystem::File&) { _textureIsDirty = true; }
);
_textureFile->setCallback([this]() { _textureIsDirty = true; });
if (p.renderType.has_value()) {
switch (*p.renderType) {
@@ -193,7 +191,7 @@ void RenderablePlaneImageLocal::loadTexture() {
std::to_string(hash),
[path = _texturePath]() -> std::unique_ptr<ghoul::opengl::Texture> {
std::unique_ptr<ghoul::opengl::Texture> texture =
ghoul::io::TextureReader::ref().loadTexture(absPath(path));
ghoul::io::TextureReader::ref().loadTexture(absPath(path).string());
LDEBUGC(
"RenderablePlaneImageLocal",
@@ -209,10 +207,8 @@ void RenderablePlaneImageLocal::loadTexture() {
BaseModule::TextureManager.release(t);
_textureFile = std::make_unique<ghoul::filesystem::File>(_texturePath);
_textureFile->setCallback(
[&](const ghoul::filesystem::File&) { _textureIsDirty = true; }
);
_textureFile = std::make_unique<ghoul::filesystem::File>(_texturePath.value());
_textureFile->setCallback([this]() { _textureIsDirty = true; });
}
}
@@ -75,8 +75,6 @@ RenderablePlaneImageOnline::RenderablePlaneImageOnline(
const Parameters p = codegen::bake<Parameters>(dictionary);
_texturePath.onChange([this]() { _textureIsDirty = true; });
addProperty(_texturePath);
_texturePath = p.url;
addProperty(_texturePath);
}
+6 -1
View File
@@ -215,8 +215,12 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary)
}
addProperty(_orientation);
_size.setViewOption(properties::Property::ViewOptions::Logarithmic);
_size.onChange([this]() {
setBoundingSphere(_size);
_sphereIsDirty = true;
});
addProperty(_size);
_size.onChange([this]() { _sphereIsDirty = true; });
addProperty(_segments);
_segments.onChange([this]() { _sphereIsDirty = true; });
@@ -257,6 +261,7 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary)
setRenderBin(Renderable::RenderBin::Background);
}
setBoundingSphere(_size);
setRenderBinFromOpacity();
}
@@ -204,6 +204,7 @@ RenderableTrail::Appearance::Appearance()
{ RenderingModeLinesPoints, "Lines+Points" }
});
lineColor.setViewOption(properties::Property::ViewOptions::Color);
addProperty(lineColor);
addProperty(useLineFade);
addProperty(lineFade);
@@ -32,6 +32,7 @@
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureconversion.h>
#include <filesystem>
#include <optional>
namespace {
@@ -81,7 +82,7 @@ ScreenSpaceImageLocal::ScreenSpaceImageLocal(const ghoul::Dictionary& dictionary
setIdentifier(identifier);
_texturePath.onChange([this]() {
if (!FileSys.fileExists(FileSys.absolutePath(_texturePath))) {
if (!std::filesystem::is_regular_file(absPath(_texturePath))) {
LWARNINGC(
"ScreenSpaceImageLocal",
fmt::format("Image {} did not exist for {}", _texturePath, _identifier)
@@ -94,8 +95,8 @@ ScreenSpaceImageLocal::ScreenSpaceImageLocal(const ghoul::Dictionary& dictionary
addProperty(_texturePath);
if (p.texturePath.has_value()) {
if (FileSys.fileExists(FileSys.absolutePath(*p.texturePath))) {
_texturePath = FileSys.absolutePath(*p.texturePath);
if (std::filesystem::is_regular_file(absPath(*p.texturePath))) {
_texturePath = absPath(*p.texturePath).string();
}
else {
LWARNINGC(
@@ -115,7 +116,7 @@ bool ScreenSpaceImageLocal::deinitializeGL() {
void ScreenSpaceImageLocal::update() {
if (_textureIsDirty && !_texturePath.value().empty()) {
std::unique_ptr<ghoul::opengl::Texture> texture =
ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath));
ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath).string());
if (texture) {
// Images don't need to start on 4-byte boundaries, for example if the
@@ -81,8 +81,8 @@ ScreenSpaceImageOnline::ScreenSpaceImageOnline(const ghoul::Dictionary& dictiona
setIdentifier(std::move(identifier));
_texturePath.onChange([this]() { _textureIsDirty = true; });
addProperty(_texturePath);
_texturePath = p.url.value_or(_texturePath);
addProperty(_texturePath);
}
ScreenSpaceImageOnline::~ScreenSpaceImageOnline() {} // NOLINT
+18 -18
View File
@@ -46,7 +46,7 @@ namespace {
};
constexpr const openspace::properties::Property::PropertyInfo XAxisTypeInfo = {
"xAxis-Type",
"xAxisType",
"xAxis: Specification Type",
"This value specifies how this axis is being specified, that is whether it is "
"referencing another object, specifying an absolute vector, or whether it is "
@@ -55,7 +55,7 @@ namespace {
};
constexpr const openspace::properties::Property::PropertyInfo YAxisTypeInfo = {
"yAxis-Type",
"yAxisType",
"yAxis: Specification Type",
"This value specifies how this axis is being specified, that is whether it is "
"referencing another object, specifying an absolute vector, or whether it is "
@@ -64,7 +64,7 @@ namespace {
};
constexpr const openspace::properties::Property::PropertyInfo ZAxisTypeInfo = {
"zAxis-Type",
"zAxisType",
"zAxis: Specification Type",
"This value specifies how this axis is being specified, that is whether it is "
"referencing another object, specifying an absolute vector, or whether it is "
@@ -73,7 +73,7 @@ namespace {
};
constexpr const openspace::properties::Property::PropertyInfo XAxisObjectInfo = {
"xAxis-Object",
"xAxisObject",
"xAxis: Focus Object",
"This is the object that the axis will focus on. This object must name an "
"existing scene graph node in the currently loaded scene and the rotation will "
@@ -81,7 +81,7 @@ namespace {
};
constexpr const openspace::properties::Property::PropertyInfo YAxisObjectInfo = {
"yAxis-Object",
"yAxisObject",
"yAxis: Focus Object",
"This is the object that the axis will focus on. This object must name an "
"existing scene graph node in the currently loaded scene and the rotation will "
@@ -89,7 +89,7 @@ namespace {
};
constexpr const openspace::properties::Property::PropertyInfo ZAxisObjectInfo = {
"zAxis-Object",
"zAxisObject",
"zAxis: Focus Object",
"This is the object that the axis will focus on. This object must name an "
"existing scene graph node in the currently loaded scene and the rotation will "
@@ -98,7 +98,7 @@ namespace {
constexpr const openspace::properties::Property::PropertyInfo XAxisInvertObjectInfo =
{
"xAxis-InvertObject",
"xAxisInvertObject",
"xAxis: Invert Object Point Direction",
"If this value is set to 'true', and the type is set to 'Object', the inverse of "
"the pointing direction is used, causing the object to point away from the "
@@ -107,7 +107,7 @@ namespace {
constexpr const openspace::properties::Property::PropertyInfo YAxisInvertObjectInfo =
{
"yAxis-InvertObject",
"yAxisInvertObject",
"yAxis: Invert Object Point Direction",
"If this value is set to 'true', and the type is set to 'Object', the inverse of "
"the pointing direction is used, causing the object to point away from the "
@@ -116,7 +116,7 @@ namespace {
constexpr const openspace::properties::Property::PropertyInfo ZAxisInvertObjectInfo =
{
"zAxis-InvertObject",
"zAxisInvertObject",
"zAxis: Invert Object Point Direction",
"If this value is set to 'true', and the type is set to 'Object', the inverse of "
"the pointing direction is used, causing the object to point away from the "
@@ -124,21 +124,21 @@ namespace {
};
constexpr const openspace::properties::Property::PropertyInfo XAxisVectorInfo = {
"xAxis-Vector",
"xAxisVector",
"xAxis: Direction vector",
"This value specifies a static direction vector that is used for a fixed "
"rotation."
};
constexpr const openspace::properties::Property::PropertyInfo YAxisVectorInfo = {
"yAxis-Vector",
"yAxisVector",
"yAxis: Direction vector",
"This value specifies a static direction vector that is used for a fixed "
"rotation."
};
constexpr const openspace::properties::Property::PropertyInfo ZAxisVectorInfo = {
"zAxis-Vector",
"zAxisVector",
"zAxis: Direction vector",
"This value specifies a static direction vector that is used for a fixed "
"rotation."
@@ -147,7 +147,7 @@ namespace {
constexpr const openspace::properties::Property::PropertyInfo
XAxisOrthogonalVectorInfo =
{
"xAxis-Orthogonal",
"xAxisOrthogonal",
"xAxis: Vector is orthogonal",
"This value determines whether the vector specified is used directly, or whether "
"it is used together with another non-coordinate system completion vector to "
@@ -157,7 +157,7 @@ namespace {
constexpr const openspace::properties::Property::PropertyInfo
YAxisOrthogonalVectorInfo =
{
"yAxis-Orthogonal",
"yAxisOrthogonal",
"yAxis: Vector is orthogonal",
"This value determines whether the vector specified is used directly, or whether "
"it is used together with another non-coordinate system completion vector to "
@@ -167,7 +167,7 @@ namespace {
constexpr const openspace::properties::Property::PropertyInfo
ZAxisOrthogonalVectorInfo =
{
"zAxis-Orthogonal",
"zAxisOrthogonal",
"zAxis: Vector is orthogonal",
"This value determines whether the vector specified is used directly, or whether "
"it is used together with another non-coordinate system completion vector to "
@@ -194,7 +194,7 @@ namespace {
std::optional<bool> xAxisOrthogonal;
// [[codegen::verbatim(XAxisInvertObjectInfo.description)]]
std::optional<bool> xAxisInvert [[codegen::key("xAxis - InvertObject")]];
std::optional<bool> xAxisInvert;
// This value specifies the direction of the new Y axis. If this value is not
// specified, it will be computed by completing a right handed coordinate system
@@ -207,7 +207,7 @@ namespace {
std::optional<bool> yAxisOrthogonal;
// [[codegen::verbatim(YAxisInvertObjectInfo.description)]]
std::optional<bool> yAxisInvert [[codegen::key("yAxis - InvertObject")]];
std::optional<bool> yAxisInvert;
// This value specifies the direction of the new Z axis. If this value is not
// specified, it will be computed by completing a right handed coordinate system
@@ -220,7 +220,7 @@ namespace {
std::optional<bool> zAxisOrthogonal;
// [[codegen::verbatim(ZAxisInvertObjectInfo.description)]]
std::optional<bool> zAxisInvert [[codegen::key("zAxis - InvertObject")]];
std::optional<bool> zAxisInvert;
// [[codegen::verbatim(AttachedInfo.description)]]
std::optional<std::string> attached;
+3 -5
View File
@@ -69,17 +69,15 @@ LuaRotation::LuaRotation()
_luaScriptFile.onChange([&]() {
requireUpdate();
_fileHandle = std::make_unique<ghoul::filesystem::File>(_luaScriptFile);
_fileHandle->setCallback([&](const ghoul::filesystem::File&) {
requireUpdate();
});
_fileHandle = std::make_unique<ghoul::filesystem::File>(_luaScriptFile.value());
_fileHandle->setCallback([this]() { requireUpdate(); });
});
}
LuaRotation::LuaRotation(const ghoul::Dictionary& dictionary) : LuaRotation() {
const Parameters p = codegen::bake<Parameters>(dictionary);
_luaScriptFile = absPath(p.script);
_luaScriptFile = absPath(p.script).string();
}
glm::dmat3 LuaRotation::matrix(const UpdateData& data) const {
+3 -5
View File
@@ -68,16 +68,14 @@ LuaScale::LuaScale()
_luaScriptFile.onChange([&]() {
requireUpdate();
_fileHandle = std::make_unique<ghoul::filesystem::File>(_luaScriptFile);
_fileHandle->setCallback([&](const ghoul::filesystem::File&) {
requireUpdate();
});
_fileHandle = std::make_unique<ghoul::filesystem::File>(_luaScriptFile.value());
_fileHandle->setCallback([this]() { requireUpdate(); });
});
}
LuaScale::LuaScale(const ghoul::Dictionary& dictionary) : LuaScale() {
const Parameters p = codegen::bake<Parameters>(dictionary);
_luaScriptFile = absPath(p.script);
_luaScriptFile = absPath(p.script).string();
}
glm::dvec3 LuaScale::scaleValue(const UpdateData& data) const {
+6 -4
View File
@@ -40,9 +40,11 @@ out mat3 TBN;
uniform mat4 modelViewTransform;
uniform mat4 projectionTransform;
uniform mat4 normalTransform;
uniform mat4 meshTransform;
uniform mat4 meshNormalTransform;
void main() {
vs_positionCameraSpace = modelViewTransform * in_position;
vs_positionCameraSpace = modelViewTransform * (meshTransform * in_position);
vec4 positionClipSpace = projectionTransform * vs_positionCameraSpace;
vec4 positionScreenSpace = z_normalization(positionClipSpace);
@@ -50,11 +52,11 @@ void main() {
vs_st = in_st;
vs_screenSpaceDepth = positionScreenSpace.w;
vs_normalViewSpace = normalize(mat3(normalTransform) * in_normal);
vs_normalViewSpace = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_normal));
// TBN matrix for normal mapping
vec3 T = normalize(vec3(modelViewTransform * vec4(in_tangent, 0.0)));
vec3 N = normalize(vec3(modelViewTransform * vec4(in_normal, 0.0)));
vec3 T = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_tangent));
vec3 N = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_normal));
// Re-orthogonalize T with respect to N
T = normalize(T - dot(T, N) * N);
+3 -3
View File
@@ -29,13 +29,13 @@ in vec2 vs_st;
in vec4 vs_position;
uniform sampler2D texture1;
uniform float Alpha;
uniform vec3 MultiplyColor = vec3(1.0, 1.0, 1.0);
uniform float Alpha = 1.0;
Fragment getFragment() {
Fragment frag;
frag.color = texture(texture1, vs_st);
frag.color.a = Alpha * frag.color.a;
frag.color = texture(texture1, vs_st) * vec4(MultiplyColor, Alpha);
if (frag.color.a == 0.0) {
discard;
}
+3 -3
View File
@@ -69,8 +69,8 @@ LuaTranslation::LuaTranslation()
_luaScriptFile.onChange([&]() {
requireUpdate();
_fileHandle = std::make_unique<ghoul::filesystem::File>(_luaScriptFile);
_fileHandle->setCallback([&](const ghoul::filesystem::File&) {
_fileHandle = std::make_unique<ghoul::filesystem::File>(_luaScriptFile.value());
_fileHandle->setCallback([this]() {
requireUpdate();
notifyObservers();
});
@@ -79,7 +79,7 @@ LuaTranslation::LuaTranslation()
LuaTranslation::LuaTranslation(const ghoul::Dictionary& dictionary) : LuaTranslation() {
const Parameters p = codegen::bake<Parameters>(dictionary);
_luaScriptFile = absPath(p.script);
_luaScriptFile = absPath(p.script).string();
}
glm::dvec3 LuaTranslation::position(const UpdateData& data) const {
@@ -58,6 +58,7 @@ StaticTranslation::StaticTranslation()
glm::dvec3(std::numeric_limits<double>::max())
)
{
_position.setViewOption(properties::Property::ViewOptions::Logarithmic);
addProperty(_position);
_position.onChange([this]() {
@@ -116,12 +116,13 @@ RenderableDebugPlane::RenderableDebugPlane(const ghoul::Dictionary& dictionary)
_texture = p.texture.value_or(_texture);
addProperty(_texture);
_size.setViewOption(properties::Property::ViewOptions::Logarithmic);
_size.onChange([this](){ _planeIsDirty = true; });
_size = p.size.value_or(_size);
setBoundingSphere(_size);
addProperty(_size);
_billboard = p.billboard.value_or(_billboard);
addProperty(_billboard);
@@ -47,6 +47,7 @@
#include <ghoul/glm.h>
#include <glm/gtx/string_cast.hpp>
#include <array>
#include <filesystem>
#include <fstream>
#include <cstdint>
#include <locale>
@@ -381,7 +382,7 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di
const Parameters p = codegen::bake<Parameters>(dictionary);
if (p.file.has_value()) {
_speckFile = absPath(*p.file);
_speckFile = absPath(*p.file).string();
}
_hasSpeckFile = p.file.has_value();
@@ -433,24 +434,22 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di
}
}
else {
LWARNING("No unit given for RenderableBillboardsCloud. Using meters as units.");
LWARNING("No unit given for RenderableBillboardsCloud. Using meters as units");
_unit = Meter;
}
if (p.texture.has_value()) {
_spriteTexturePath = absPath(*p.texture);
_spriteTexturePath = absPath(*p.texture).string();
_spriteTexturePath.onChange([&]() { _spriteTextureIsDirty = true; });
// @TODO (abock, 2021-01-31) I don't know why we only add this property if the
// texture is given, but I think it's a bug
addProperty(_spriteTexturePath);
}
_hasSpriteTexture = p.texture.has_value();
if (p.colorMap.has_value()) {
_colorMapFile = absPath(*p.colorMap);
_colorMapFile = absPath(*p.colorMap).string();
_hasColorMapFile = true;
if (p.colorOption.has_value()) {
@@ -517,7 +516,7 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di
_drawLabels = p.drawLabels.value_or(_drawLabels);
addProperty(_drawLabels);
_labelFile = absPath(*p.labelFile);
_labelFile = absPath(*p.labelFile).string();
_hasLabel = true;
_textColor = p.textColor.value_or(_textColor);
@@ -571,14 +570,14 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di
_setRangeFromData.onChange([this]() {
const int colorMapInUse =
_hasColorMapFile ? _variableDataPositionMap[_colorOptionString] : 0;
_hasColorMapFile ? _dataset.index(_colorOptionString) : 0;
float minValue = std::numeric_limits<float>::max();
float maxValue = std::numeric_limits<float>::min();
for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) {
float colorIdx = _fullData[i + 3 + colorMapInUse];
maxValue = colorIdx >= maxValue ? colorIdx : maxValue;
minValue = colorIdx < minValue ? colorIdx : minValue;
float maxValue = -std::numeric_limits<float>::max();
for (const speck::Dataset::Entry& e : _dataset.entries) {
float color = e.data[colorMapInUse];
minValue = std::min(minValue, color);
maxValue = std::max(maxValue, color);
}
_optionColorRangeData = glm::vec2(minValue, maxValue);
@@ -591,15 +590,25 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di
}
bool RenderableBillboardsCloud::isReady() const {
return ((_program != nullptr) && (!_fullData.empty())) || (!_labelData.empty());
return (_program && (!_dataset.entries.empty())) || (!_labelset.entries.empty());
}
void RenderableBillboardsCloud::initialize() {
ZoneScoped
bool success = loadData();
if (!success) {
throw ghoul::RuntimeError("Error loading data");
if (_hasSpeckFile) {
_dataset = speck::data::loadFileWithCache(_speckFile);
}
if (_hasColorMapFile) {
_colorMap = speck::color::loadFileWithCache(_colorMapFile);
}
if (!_labelFile.empty()) {
_labelset = speck::label::loadFileWithCache(_labelFile);
for (speck::Labelset::Entry& e : _labelset.entries) {
e.position = glm::vec3(_transformationMatrix * glm::dvec4(e.position, 1.0));
}
}
if (!_colorOptionString.empty() && (_colorRangeData.size() > 1)) {
@@ -645,7 +654,7 @@ void RenderableBillboardsCloud::initializeGL() {
}
if (_hasLabel) {
if (_font == nullptr) {
if (!_font) {
size_t _fontSize = 50;
_font = global::fontManager->font(
"Mono",
@@ -749,11 +758,7 @@ void RenderableBillboardsCloud::renderBillboards(const RenderData& data,
_program->setUniform(_uniformCache.hasColormap, _hasColorMapFile);
glBindVertexArray(_vao);
const GLsizei nAstronomicalObjects = static_cast<GLsizei>(
_fullData.size() / _nValuesPerAstronomicalObject
);
glDrawArrays(GL_POINTS, 0, nAstronomicalObjects);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_dataset.entries.size()));
glBindVertexArray(0);
_program->deactivate();
@@ -767,35 +772,7 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data,
const glm::dvec3& orthoUp,
float fadeInVariable)
{
float scale = 0.f;
switch (_unit) {
case Meter:
scale = 1.f;
break;
case Kilometer:
scale = 1e3f;
break;
case Parsec:
scale = static_cast<float>(PARSEC);
break;
case Kiloparsec:
scale = static_cast<float>(1e3 * PARSEC);
break;
case Megaparsec:
scale = static_cast<float>(1e6 * PARSEC);
break;
case Gigaparsec:
scale = static_cast<float>(1e9 * PARSEC);
break;
case GigalightYears:
scale = static_cast<float>(306391534.73091 * PARSEC);
break;
}
glm::vec4 textColor = glm::vec4(
glm::vec3(_textColor),
_textOpacity * fadeInVariable
);
glm::vec4 textColor = glm::vec4(glm::vec3(_textColor), _textOpacity * fadeInVariable);
ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo;
labelInfo.orthoRight = orthoRight;
@@ -810,14 +787,13 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data,
labelInfo.enableDepth = true;
labelInfo.enableFalseDepth = false;
for (const std::pair<glm::vec3, std::string>& pair : _labelData) {
//glm::vec3 scaledPos(_transformationMatrix * glm::dvec4(pair.first, 1.0));
glm::vec3 scaledPos(pair.first);
scaledPos *= scale;
for (const speck::Labelset::Entry& e : _labelset.entries) {
glm::vec3 scaledPos(e.position);
scaledPos *= unitToMeter(_unit);
ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render(
*_font,
scaledPos,
pair.second,
e.text,
textColor,
labelInfo
);
@@ -825,39 +801,16 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data,
}
void RenderableBillboardsCloud::render(const RenderData& data, RendererTasks&) {
float scale = 0.f;
switch (_unit) {
case Meter:
scale = 1.f;
break;
case Kilometer:
scale = 1e3f;
break;
case Parsec:
scale = static_cast<float>(PARSEC);
break;
case Kiloparsec:
scale = static_cast<float>(1e3 * PARSEC);
break;
case Megaparsec:
scale = static_cast<float>(1e6 * PARSEC);
break;
case Gigaparsec:
scale = static_cast<float>(1e9 * PARSEC);
break;
case GigalightYears:
scale = static_cast<float>(306391534.73091 * PARSEC);
break;
}
float fadeInVariable = 1.f;
float fadeInVar = 1.f;
if (!_disableFadeInDistance) {
float distCamera = static_cast<float>(glm::length(data.camera.positionVec3()));
const glm::vec2 fadeRange = _fadeInDistance;
const float a = 1.f / ((fadeRange.y - fadeRange.x) * scale);
const float a = static_cast<float>(
1.f / ((fadeRange.y - fadeRange.x) * unitToMeter(_unit))
);
const float b = -(fadeRange.x / (fadeRange.y - fadeRange.x));
const float funcValue = a * distCamera + b;
fadeInVariable *= funcValue > 1.f ? 1.f : funcValue;
fadeInVar *= funcValue > 1.f ? 1.f : funcValue;
if (funcValue < 0.01f) {
return;
@@ -890,23 +843,11 @@ void RenderableBillboardsCloud::render(const RenderData& data, RendererTasks&) {
glm::dvec3 orthoUp = glm::normalize(glm::cross(cameraViewDirectionWorld, orthoRight));
if (_hasSpeckFile && _drawElements) {
renderBillboards(
data,
modelMatrix,
orthoRight,
orthoUp,
fadeInVariable
);
renderBillboards(data, modelMatrix, orthoRight, orthoUp, fadeInVar);
}
if (_drawLabels && _hasLabel) {
renderLabels(
data,
modelViewProjectionMatrix,
orthoRight,
orthoUp,
fadeInVariable
);
renderLabels(data, modelViewProjectionMatrix, orthoRight, orthoUp, fadeInVar);
}
}
@@ -918,9 +859,9 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
TracyGpuZone("Data dirty")
LDEBUG("Regenerating data");
createDataSlice();
std::vector<float> slice = createDataSlice();
int size = static_cast<int>(_slicedData.size());
int size = static_cast<int>(slice.size());
if (_vao == 0) {
glGenVertexArrays(1, &_vao);
@@ -933,12 +874,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
glBindVertexArray(_vao);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(
GL_ARRAY_BUFFER,
size * sizeof(float),
&_slicedData[0],
GL_STATIC_DRAW
);
glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), slice.data(), GL_STATIC_DRAW);
GLint positionAttrib = _program->attributeLocation("in_position");
if (_hasColorMapFile && _hasDatavarSize) {
@@ -948,7 +884,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
4,
GL_FLOAT,
GL_FALSE,
sizeof(float) * 9,
9 * sizeof(float),
nullptr
);
@@ -959,8 +895,8 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
4,
GL_FLOAT,
GL_FALSE,
sizeof(float) * 9,
reinterpret_cast<void*>(sizeof(float) * 4)
9 * sizeof(float),
reinterpret_cast<void*>(4 * sizeof(float))
);
GLint dvarScalingAttrib = _program->attributeLocation("in_dvarScaling");
@@ -970,8 +906,8 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
1,
GL_FLOAT,
GL_FALSE,
sizeof(float) * 9,
reinterpret_cast<void*>(sizeof(float) * 8)
9 * sizeof(float),
reinterpret_cast<void*>(8 * sizeof(float))
);
}
else if (_hasColorMapFile) {
@@ -981,7 +917,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
4,
GL_FLOAT,
GL_FALSE,
sizeof(float) * 8,
8 * sizeof(float),
nullptr
);
@@ -992,8 +928,8 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
4,
GL_FLOAT,
GL_FALSE,
sizeof(float) * 8,
reinterpret_cast<void*>(sizeof(float) * 4)
8 * sizeof(float),
reinterpret_cast<void*>(4 * sizeof(float))
);
}
else if (_hasDatavarSize) {
@@ -1003,7 +939,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
4,
GL_FLOAT,
GL_FALSE,
sizeof(float) * 8,
8 * sizeof(float),
nullptr
);
@@ -1014,8 +950,8 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
1,
GL_FLOAT,
GL_FALSE,
sizeof(float) * 5,
reinterpret_cast<void*>(sizeof(float) * 4)
5 * sizeof(float),
reinterpret_cast<void*>(4 * sizeof(float))
);
}
else {
@@ -1049,7 +985,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
[path = _spriteTexturePath]() -> std::unique_ptr<ghoul::opengl::Texture> {
LINFO(fmt::format("Loaded texture from '{}'", absPath(path)));
std::unique_ptr<ghoul::opengl::Texture> t =
ghoul::io::TextureReader::ref().loadTexture(absPath(path));
ghoul::io::TextureReader::ref().loadTexture(absPath(path).string());
t->uploadTexture();
t->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
t->purgeFromRAM();
@@ -1062,495 +998,72 @@ void RenderableBillboardsCloud::update(const UpdateData&) {
}
}
bool RenderableBillboardsCloud::loadData() {
bool success = true;
success &= loadSpeckData();
if (_hasColorMapFile) {
if (!_hasSpeckFile) {
success = true;
}
success &= readColorMapFile();
double RenderableBillboardsCloud::unitToMeter(Unit unit) const {
// @TODO (abock, 2021-05-10) This should be moved to a centralized conversion code
switch (unit) {
case Meter: return 1.0;
case Kilometer: return 1e3;
case Parsec: return PARSEC;
case Kiloparsec: return 1000 * PARSEC;
case Megaparsec: return 1e6 * PARSEC;
case Gigaparsec: return 1e9 * PARSEC;
case GigalightYears: return 306391534.73091 * PARSEC;
default: throw ghoul::MissingCaseException();
}
success &= loadLabelData();
return success;
}
bool RenderableBillboardsCloud::loadSpeckData() {
if (!_hasSpeckFile) {
return true;
}
bool success = true;
const std::string& cachedFile = FileSys.cacheManager()->cachedFilename(
ghoul::filesystem::File(_speckFile),
"RenderableDUMeshes|" + identifier(),
ghoul::filesystem::CacheManager::Persistent::Yes
);
const bool hasCachedFile = FileSys.fileExists(cachedFile);
if (hasCachedFile) {
LINFO(fmt::format(
"Cached file '{}' used for Speck file '{}'",
cachedFile, _speckFile
));
success = loadCachedFile(cachedFile);
if (success) {
return true;
}
else {
FileSys.cacheManager()->removeCacheFile(_speckFile);
// Intentional fall-through to the 'else' to generate the cache
// file for the next run
}
}
else {
LINFO(fmt::format("Cache for Speck file '{}' not found", _speckFile));
}
LINFO(fmt::format("Loading Speck file '{}'", _speckFile));
success = readSpeckFile();
if (!success) {
return false;
}
success &= saveCachedFile(cachedFile);
return success;
}
bool RenderableBillboardsCloud::loadLabelData() {
if (_labelFile.empty()) {
return true;
}
bool success = true;
// I disabled the cache as it didn't work on Mac --- abock
const std::string& cachedFile = FileSys.cacheManager()->cachedFilename(
ghoul::filesystem::File(_labelFile),
ghoul::filesystem::CacheManager::Persistent::Yes
);
if (!_hasSpeckFile && !_hasColorMapFile) {
success = true;
}
const bool hasCachedFile = FileSys.fileExists(cachedFile);
if (hasCachedFile) {
LINFO(fmt::format(
"Cached file '{}' used for Label file '{}'",
cachedFile, _labelFile
));
success &= loadCachedFile(cachedFile);
if (!success) {
FileSys.cacheManager()->removeCacheFile(_labelFile);
// Intentional fall-through to the 'else' to generate the cache
// file for the next run
}
}
else {
LINFO(fmt::format("Cache for Label file '{}' not found", _labelFile));
LINFO(fmt::format("Loading Label file '{}'", _labelFile));
success &= readLabelFile();
if (!success) {
return false;
}
}
return success;
}
bool RenderableBillboardsCloud::readSpeckFile() {
std::ifstream file(_speckFile);
if (!file.good()) {
LERROR(fmt::format("Failed to open Speck file '{}'", _speckFile));
return false;
}
_nValuesPerAstronomicalObject = 0;
// The beginning of the speck file has a header that either contains comments
// (signaled by a preceding '#') or information about the structure of the file
// (signaled by the keywords 'datavar', 'texturevar', and 'texture')
std::string line;
while (true) {
std::getline(file, line);
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() - 1);
}
if (line.empty() || line[0] == '#') {
continue;
}
if (line.substr(0, 7) != "datavar" &&
line.substr(0, 10) != "texturevar" &&
line.substr(0, 7) != "texture" &&
line.substr(0, 10) != "polyorivar" &&
line.substr(0, 10) != "maxcomment")
{
// Started reading data
break;
}
if (line.substr(0, 7) == "datavar") {
// datavar lines are structured as follows:
// datavar # description
// where # is the index of the data variable; so if we repeatedly overwrite
// the 'nValues' variable with the latest index, we will end up with the total
// number of values (+3 since X Y Z are not counted in the Speck file index)
std::stringstream str(line);
std::string dummy;
str >> dummy; // command
str >> _nValuesPerAstronomicalObject; // variable index
dummy.clear();
str >> dummy; // variable name
_variableDataPositionMap.insert({ dummy, _nValuesPerAstronomicalObject });
// We want the number, but the index is 0 based
_nValuesPerAstronomicalObject += 1;
}
}
_nValuesPerAstronomicalObject += 3; // X Y Z are not counted in the Speck file indices
do {
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() - 1);
}
if (line.empty()) {
std::getline(file, line);
continue;
}
else if (line[0] == '#') {
std::getline(file, line);
continue;
}
std::stringstream str(line);
std::vector<float> values(_nValuesPerAstronomicalObject);
for (int i = 0; i < _nValuesPerAstronomicalObject; ++i) {
str >> values[i];
}
_fullData.insert(_fullData.end(), values.begin(), values.end());
// reads new line
std::getline(file, line);
} while (!file.eof());
return true;
}
bool RenderableBillboardsCloud::readColorMapFile() {
std::string _file = _colorMapFile;
std::ifstream file(_file);
if (!file.good()) {
LERROR(fmt::format("Failed to open Color Map file '{}'", _file));
return false;
}
std::size_t numberOfColors = 0;
// The beginning of the speck file has a header that either contains comments
// (signaled by a preceding '#') or information about the structure of the file
// (signaled by the keywords 'datavar', 'texturevar', and 'texture')
std::string line;
while (true) {
// std::streampos position = file.tellg();
std::getline(file, line);
if (line[0] == '#' || line.empty()) {
continue;
}
// Initial number of colors
std::locale loc;
if (std::isdigit(line[0], loc)) {
std::string::size_type sz;
numberOfColors = std::stoi(line, &sz);
break;
}
else if (file.eof()) {
return false;
}
}
for (size_t i = 0; i < numberOfColors; ++i) {
std::getline(file, line);
std::stringstream str(line);
glm::vec4 color;
// Each color in the colormap must be defined as (R,G,B,A)
for (int j = 0; j < 4; ++j) {
str >> color[j];
}
_colorMapData.push_back(color);
}
return true;
}
bool RenderableBillboardsCloud::readLabelFile() {
std::string _file = _labelFile;
std::ifstream file(_file);
if (!file.good()) {
LERROR(fmt::format("Failed to open Label file '{}'", _file));
return false;
}
// The beginning of the speck file has a header that either contains comments
// (signaled by a preceding '#') or information about the structure of the file
// (signaled by the keywords 'datavar', 'texturevar', and 'texture')
std::string line;
while (true) {
std::streampos position = file.tellg();
std::getline(file, line);
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() - 1);
}
if (line.empty() || line[0] == '#') {
continue;
}
if (line.substr(0, 9) != "textcolor") {
// we read a line that doesn't belong to the header, so we have to jump back
// before the beginning of the current line
file.seekg(position);
continue;
}
if (line.substr(0, 9) == "textcolor") {
// textcolor lines are structured as follows:
// textcolor # description
// where # is color text defined in configuration file
std::stringstream str(line);
// TODO: handle cases of labels with different colors
break;
}
}
do {
std::vector<float> values(_nValuesPerAstronomicalObject);
std::getline(file, line);
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() - 1);
}
if (line.empty()) {
continue;
}
std::stringstream str(line);
glm::vec3 position = glm::vec3(0.f);
for (int j = 0; j < 3; ++j) {
str >> position[j];
}
std::string dummy;
str >> dummy; // text keyword
std::string label;
str >> label;
dummy.clear();
while (str >> dummy) {
if (dummy == "#") {
break;
}
label += " " + dummy;
dummy.clear();
}
glm::vec3 transformedPos = glm::vec3(
_transformationMatrix * glm::dvec4(position, 1.0)
);
_labelData.emplace_back(std::make_pair(transformedPos, label));
} while (!file.eof());
return true;
}
bool RenderableBillboardsCloud::loadCachedFile(const std::string& file) {
std::ifstream fileStream(file, std::ifstream::binary);
if (!fileStream.good()) {
LERROR(fmt::format("Error opening file '{}' for loading cache file", file));
return false;
}
int8_t version = 0;
fileStream.read(reinterpret_cast<char*>(&version), sizeof(int8_t));
if (version != CurrentCacheVersion) {
LINFO("The format of the cached file has changed: deleting old cache");
fileStream.close();
FileSys.deleteFile(file);
return false;
}
int32_t nValues = 0;
fileStream.read(reinterpret_cast<char*>(&nValues), sizeof(int32_t));
fileStream.read(
reinterpret_cast<char*>(&_nValuesPerAstronomicalObject),
sizeof(int32_t)
);
_fullData.resize(nValues);
fileStream.read(
reinterpret_cast<char*>(&_fullData[0]),
nValues * sizeof(_fullData[0])
);
if (_hasColorMapFile) {
int32_t nItems = 0;
fileStream.read(reinterpret_cast<char*>(&nItems), sizeof(int32_t));
for (int i = 0; i < nItems; ++i) {
int32_t keySize = 0;
fileStream.read(reinterpret_cast<char*>(&keySize), sizeof(int32_t));
std::vector<char> buffer(keySize);
fileStream.read(buffer.data(), keySize);
std::string key(buffer.begin(), buffer.end());
int32_t value = 0;
fileStream.read(reinterpret_cast<char*>(&value), sizeof(int32_t));
_variableDataPositionMap.insert({ key, value });
}
}
bool success = fileStream.good();
return success;
}
bool RenderableBillboardsCloud::saveCachedFile(const std::string& file) const {
std::ofstream fileStream(file, std::ofstream::binary);
if (!fileStream.good()) {
LERROR(fmt::format("Error opening file '{}' for save cache file", file));
return false;
}
fileStream.write(reinterpret_cast<const char*>(&CurrentCacheVersion), sizeof(int8_t));
int32_t nValues = static_cast<int32_t>(_fullData.size());
if (nValues == 0) {
LERROR("Error writing cache: No values were loaded");
return false;
}
fileStream.write(reinterpret_cast<const char*>(&nValues), sizeof(int32_t));
int32_t nValuesPerAstronomicalObject = static_cast<int32_t>(
_nValuesPerAstronomicalObject
);
fileStream.write(
reinterpret_cast<const char*>(&nValuesPerAstronomicalObject),
sizeof(int32_t)
);
size_t nBytes = nValues * sizeof(_fullData[0]);
fileStream.write(reinterpret_cast<const char*>(&_fullData[0]), nBytes);
if (_hasColorMapFile) {
int32_t nItems = static_cast<int32_t>(_variableDataPositionMap.size());
fileStream.write(reinterpret_cast<const char*>(&nItems), sizeof(int32_t));
for (const std::pair<const std::string, int>& pair : _variableDataPositionMap) {
int32_t keySize = static_cast<int32_t>(pair.first.size());
fileStream.write(reinterpret_cast<const char*>(&keySize), sizeof(int32_t));
fileStream.write(pair.first.data(), keySize);
int32_t value = static_cast<int32_t>(pair.second);
fileStream.write(reinterpret_cast<const char*>(&value), sizeof(int32_t));
}
}
return fileStream.good();
}
void RenderableBillboardsCloud::createDataSlice() {
std::vector<float> RenderableBillboardsCloud::createDataSlice() {
ZoneScoped
_slicedData.clear();
if (_fullData.empty() || _nValuesPerAstronomicalObject == 0) {
return;
if (_dataset.entries.empty()) {
return std::vector<float>();
}
std::vector<float> result;
if (_hasColorMapFile) {
_slicedData.reserve(8 * (_fullData.size() / _nValuesPerAstronomicalObject));
result.reserve(8 * _dataset.entries.size());
}
else {
_slicedData.reserve(4 * (_fullData.size() / _nValuesPerAstronomicalObject));
result.reserve(4 * _dataset.entries.size());
}
// what datavar in use for the index color
int colorMapInUse =
_hasColorMapFile ? _variableDataPositionMap[_colorOptionString] : 0;
int colorMapInUse = _hasColorMapFile ? _dataset.index(_colorOptionString) : 0;
// what datavar in use for the size scaling (if present)
int sizeScalingInUse = _hasDatavarSize ?
_variableDataPositionMap[_datavarSizeOptionString] : -1;
auto addDatavarSizeScalling = [&](size_t i, int datavarInUse) {
_slicedData.push_back(_fullData[i + 3 + datavarInUse]);
};
auto addPosition = [&](const glm::vec4 &pos) {
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(pos[j]);
}
};
int sizeScalingInUse =
_hasDatavarSize ? _dataset.index(_datavarSizeOptionString) : -1;
float minColorIdx = std::numeric_limits<float>::max();
float maxColorIdx = std::numeric_limits<float>::min();
for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) {
float colorIdx = _fullData[i + 3 + colorMapInUse];
maxColorIdx = colorIdx >= maxColorIdx ? colorIdx : maxColorIdx;
minColorIdx = colorIdx < minColorIdx ? colorIdx : minColorIdx;
float maxColorIdx = -std::numeric_limits<float>::max();
for (const speck::Dataset::Entry& e : _dataset.entries) {
float color = e.data[colorMapInUse];
minColorIdx = std::min(color, minColorIdx);
maxColorIdx = std::max(color, maxColorIdx);
}
double maxRadius = 0.0;
float biggestCoord = -1.f;
for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) {
glm::dvec4 transformedPos = _transformationMatrix * glm::dvec4(
_fullData[i + 0],
_fullData[i + 1],
_fullData[i + 2],
1.0
);
// W-normalization
transformedPos /= transformedPos.w;
glm::vec4 position(glm::vec3(transformedPos), static_cast<float>(_unit));
for (const speck::Dataset::Entry& e : _dataset.entries) {
glm::vec3 transformedPos = glm::vec3(_transformationMatrix * glm::vec4(
e.position, 1.0
));
glm::vec4 position(transformedPos, static_cast<float>(_unit));
const double unitMeter = unitToMeter(_unit);
glm::dvec3 p = glm::dvec3(position) * unitMeter;
const double r = glm::length(p);
maxRadius = std::max(maxRadius, r);
if (_hasColorMapFile) {
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(position[j]);
biggestCoord = biggestCoord < position[j] ? position[j] : biggestCoord;
result.push_back(position[j]);
}
biggestCoord = std::max(biggestCoord, glm::compMax(position));
// Note: if exact colormap option is not selected, the first color and the
// last color in the colormap file are the outliers colors.
float variableColor = _fullData[i + 3 + colorMapInUse];
float variableColor = e.data[colorMapInUse];
float cmax, cmin;
if (_colorRangeData.empty()) {
@@ -1566,39 +1079,37 @@ void RenderableBillboardsCloud::createDataSlice() {
if (_isColorMapExact) {
int colorIndex = variableColor + cmin;
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(_colorMapData[colorIndex][j]);
result.push_back(_colorMap.entries[colorIndex][j]);
}
}
else {
if (_useLinearFiltering) {
const float value = variableColor;
float valueT = (value - cmin) / (cmax - cmin); // in [0, 1)
float valueT = (variableColor - cmin) / (cmax - cmin); // in [0, 1)
valueT = std::clamp(valueT, 0.f, 1.f);
const float idx = valueT * (_colorMapData.size() - 1);
const float idx = valueT * (_colorMap.entries.size() - 1);
const int floorIdx = static_cast<int>(std::floor(idx));
const int ceilIdx = static_cast<int>(std::ceil(idx));
const glm::vec4 floorColor = _colorMapData[floorIdx];
const glm::vec4 ceilColor = _colorMapData[ceilIdx];
const glm::vec4 floorColor = _colorMap.entries[floorIdx];
const glm::vec4 ceilColor = _colorMap.entries[ceilIdx];
if (floorColor != ceilColor) {
const glm::vec4 c = floorColor + idx * (ceilColor - floorColor);
_slicedData.push_back(c.r);
_slicedData.push_back(c.g);
_slicedData.push_back(c.b);
_slicedData.push_back(c.a);
result.push_back(c.r);
result.push_back(c.g);
result.push_back(c.b);
result.push_back(c.a);
}
else {
_slicedData.push_back(floorColor.r);
_slicedData.push_back(floorColor.g);
_slicedData.push_back(floorColor.b);
_slicedData.push_back(floorColor.a);
result.push_back(floorColor.r);
result.push_back(floorColor.g);
result.push_back(floorColor.b);
result.push_back(floorColor.a);
}
}
else {
float ncmap = static_cast<float>(_colorMapData.size());
float ncmap = static_cast<float>(_colorMap.entries.size());
float normalization = ((cmax != cmin) && (ncmap > 2)) ?
(ncmap - 2) / (cmax - cmin) : 0;
int colorIndex = (variableColor - cmin) * normalization + 1;
@@ -1606,24 +1117,30 @@ void RenderableBillboardsCloud::createDataSlice() {
colorIndex = colorIndex >= ncmap ? ncmap - 1 : colorIndex;
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(_colorMapData[colorIndex][j]);
result.push_back(_colorMap.entries[colorIndex][j]);
}
}
}
if (_hasDatavarSize) {
addDatavarSizeScalling(i, sizeScalingInUse);
result.push_back(e.data[sizeScalingInUse]);
}
}
else if (_hasDatavarSize) {
addDatavarSizeScalling(i, sizeScalingInUse);
addPosition(position);
result.push_back(e.data[sizeScalingInUse]);
for (int j = 0; j < 4; ++j) {
result.push_back(position[j]);
}
}
else {
addPosition(position);
for (int j = 0; j < 4; ++j) {
result.push_back(position[j]);
}
}
}
setBoundingSphere(maxRadius);
_fadeInDistance.setMaxValue(glm::vec2(10.f * biggestCoord));
return result;
}
void RenderableBillboardsCloud::createPolygonTexture() {
@@ -1683,20 +1200,13 @@ void RenderableBillboardsCloud::loadPolygonGeometryForRendering() {
glBindVertexArray(_polygonVao);
glBindBuffer(GL_ARRAY_BUFFER, _polygonVbo);
const GLfloat vertex_data[] = {
constexpr const std::array<GLfloat, 4> VertexData = {
// x y z w
0.f, 0.f, 0.f, 1.f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
glVertexAttribPointer(
0,
4,
GL_FLOAT,
GL_FALSE,
sizeof(GLfloat) * 4,
nullptr
);
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
@@ -1711,8 +1221,8 @@ void RenderableBillboardsCloud::renderPolygonGeometry(GLuint vao) {
);
program->activate();
static const float black[] = { 0.f, 0.f, 0.f, 0.f };
glClearBufferfv(GL_COLOR, 0, black);
constexpr const glm::vec4 Black = glm::vec4(0.f, 0.f, 0.f, 0.f);
glClearBufferfv(GL_COLOR, 0, glm::value_ptr(Black));
program->setUniform("sides", _polygonSides);
program->setUniform("polygonColor", _pointColor);
@@ -27,6 +27,7 @@
#include <openspace/rendering/renderable.h>
#include <modules/space/speckloader.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/triggerproperty.h>
@@ -76,8 +77,9 @@ private:
Gigaparsec = 5,
GigalightYears = 6
};
double unitToMeter(Unit unit) const;
void createDataSlice();
std::vector<float> createDataSlice();
void createPolygonTexture();
void renderToTexture(GLuint textureToRenderTo, GLuint textureWidth,
GLuint textureHeight);
@@ -88,15 +90,6 @@ private:
void renderLabels(const RenderData& data, const glm::dmat4& modelViewProjectionMatrix,
const glm::dvec3& orthoRight, const glm::dvec3& orthoUp, float fadeInVariable);
bool loadData();
bool loadSpeckData();
bool loadLabelData();
bool readSpeckFile();
bool readColorMapFile();
bool readLabelFile();
bool loadCachedFile(const std::string& file);
bool saveCachedFile(const std::string& file) const;
bool _hasSpeckFile = false;
bool _dataIsDirty = true;
bool _textColorIsDirty = true;
@@ -134,8 +127,6 @@ private:
properties::FloatProperty _correctionSizeFactor;
properties::BoolProperty _useLinearFiltering;
properties::TriggerProperty _setRangeFromData;
// DEBUG:
properties::OptionProperty _renderOption;
ghoul::opengl::Texture* _polygonTexture = nullptr;
@@ -143,8 +134,9 @@ private:
ghoul::opengl::ProgramObject* _program = nullptr;
ghoul::opengl::ProgramObject* _renderToPolygonProgram = nullptr;
UniformCache(cameraViewProjectionMatrix, modelMatrix, cameraPos, cameraLookup,
renderOption, minBillboardSize, maxBillboardSize, correctionSizeEndDistance,
UniformCache(
cameraViewProjectionMatrix, modelMatrix, cameraPos, cameraLookup, renderOption,
minBillboardSize, maxBillboardSize, correctionSizeEndDistance,
correctionSizeFactor, color, alphaValue, scaleFactor, up, right, fadeInValue,
screenSize, spriteTexture, hasColormap, enabledRectSizeControl, hasDvarScaling
) _uniformCache;
@@ -159,17 +151,14 @@ private:
Unit _unit = Parsec;
std::vector<float> _slicedData;
std::vector<float> _fullData;
std::vector<glm::vec4> _colorMapData;
speck::Dataset _dataset;
speck::Labelset _labelset;
speck::ColorMap _colorMap;
std::vector<glm::vec2> _colorRangeData;
std::vector<std::pair<glm::vec3, std::string>> _labelData;
std::unordered_map<std::string, int> _variableDataPositionMap;
std::unordered_map<int, std::string> _optionConversionMap;
std::unordered_map<int, std::string> _optionConversionSizeMap;
int _nValuesPerAstronomicalObject = 0;
glm::dmat4 _transformationMatrix = glm::dmat4(1.0);
GLuint _vao = 0;
@@ -44,6 +44,7 @@
#include <ghoul/opengl/textureunit.h>
#include <array>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <optional>
@@ -202,7 +203,7 @@ RenderableDUMeshes::RenderableDUMeshes(const ghoul::Dictionary& dictionary)
addProperty(_opacity);
registerUpdateRenderBinFromOpacity();
_speckFile = absPath(p.file);
_speckFile = absPath(p.file).string();
_hasSpeckFile = true;
_drawElements.onChange([&]() { _hasSpeckFile = !_hasSpeckFile; });
addProperty(_drawElements);
@@ -256,7 +257,7 @@ RenderableDUMeshes::RenderableDUMeshes(const ghoul::Dictionary& dictionary)
addProperty(_drawLabels);
if (p.labelFile.has_value()) {
_labelFile = absPath(*p.labelFile);
_labelFile = absPath(*p.labelFile).string();
_hasLabel = true;
_textColor = p.textColor.value_or(_textColor);
@@ -288,7 +289,7 @@ RenderableDUMeshes::RenderableDUMeshes(const ghoul::Dictionary& dictionary)
bool RenderableDUMeshes::isReady() const {
return (_program != nullptr) &&
(!_renderingMeshesMap.empty() || (!_labelData.empty()));
(!_renderingMeshesMap.empty() || (!_labelset.entries.empty()));
}
void RenderableDUMeshes::initializeGL() {
@@ -431,13 +432,13 @@ void RenderableDUMeshes::renderLabels(const RenderData& data,
glm::vec4 textColor = glm::vec4(glm::vec3(_textColor), _textOpacity);
for (const std::pair<glm::vec3, std::string>& pair : _labelData) {
glm::vec3 scaledPos(pair.first);
for (const speck::Labelset::Entry& e : _labelset.entries) {
glm::vec3 scaledPos(e.position);
scaledPos *= scale;
ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render(
*_font,
scaledPos,
pair.second,
e.text,
textColor,
labelInfo
);
@@ -494,31 +495,7 @@ void RenderableDUMeshes::update(const UpdateData&) {
bool RenderableDUMeshes::loadData() {
bool success = false;
if (_hasSpeckFile) {
// I disabled the cache as it didn't work on Mac --- abock
// std::string cachedFile = FileSys.cacheManager()->cachedFilename(
// _speckFile,
// ghoul::filesystem::CacheManager::Persistent::Yes
// );
// bool hasCachedFile = FileSys.fileExists(cachedFile);
// if (hasCachedFile) {
// LINFO(
// "Cached file '" << cachedFile <<
// "' used for Speck file '" << _speckFile << "'"
// );
// success = loadCachedFile(cachedFile);
// if (!success) {
// FileSys.cacheManager()->removeCacheFile(_speckFile);
// // Intentional fall-through to the 'else' to generate the cache
// // file for the next run
// }
// }
// else
// {
// LINFO("Cache for Speck file '" << _speckFile << "' not found");
LINFO(fmt::format("Loading Speck file '{}'", _speckFile));
success = readSpeckFile();
if (!success) {
return false;
@@ -527,35 +504,7 @@ bool RenderableDUMeshes::loadData() {
std::string labelFile = _labelFile;
if (!labelFile.empty()) {
// I disabled the cache as it didn't work on Mac --- abock
// std::string cachedFile = FileSys.cacheManager()->cachedFilename(
// labelFile,
// ghoul::filesystem::CacheManager::Persistent::Yes
// );
// bool hasCachedFile = FileSys.fileExists(cachedFile);
// if (hasCachedFile) {
// LINFO(
// "Cached file '" << cachedFile << "' used for Label file '" <<
// labelFile << "'"
// );
// success &= loadCachedFile(cachedFile);
// if (!success) {
// FileSys.cacheManager()->removeCacheFile(labelFile);
// // Intentional fall-through to the 'else' to generate the cache
// // file for the next run
// }
// }
// else {
// LINFO("Cache for Label file '" << labelFile << "' not found");
LINFO(fmt::format("Loading Label file '{}'", labelFile));
success &= readLabelFile();
if (!success) {
return false;
}
// }
_labelset = speck::label::loadFileWithCache(_labelFile);
}
return success;
@@ -688,158 +637,6 @@ bool RenderableDUMeshes::readSpeckFile() {
return true;
}
bool RenderableDUMeshes::readLabelFile() {
std::ifstream file(_labelFile);
if (!file.good()) {
LERROR(fmt::format("Failed to open Label file '{}'", _labelFile));
return false;
}
// The beginning of the speck file has a header that either contains comments
// (signaled by a preceding '#') or information about the structure of the file
// (signaled by the keywords 'datavar', 'texturevar', and 'texture')
std::string line;
while (true) {
std::streampos position = file.tellg();
std::getline(file, line);
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() - 1);
}
if (line.empty() || line[0] == '#') {
continue;
}
if (line.substr(0, 9) != "textcolor") {
// we read a line that doesn't belong to the header, so we have to jump back
// before the beginning of the current line
file.seekg(position);
continue;
}
if (line.substr(0, 9) == "textcolor") {
// textcolor lines are structured as follows:
// textcolor # description
// where # is color text defined in configuration file
std::stringstream str(line);
// TODO: handle cases of labels with different colors
break;
}
}
do {
std::vector<float> values(_nValuesPerAstronomicalObject);
std::getline(file, line);
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() - 1);
}
if (line.empty()) {
continue;
}
std::stringstream str(line);
glm::vec3 position = glm::vec3(0.f);
for (int j = 0; j < 3; ++j) {
str >> position[j];
}
std::string dummy;
str >> dummy; // text keyword
std::string label;
str >> label;
dummy.clear();
while (str >> dummy) {
label += " " + dummy;
dummy.clear();
}
_labelData.emplace_back(std::make_pair(position, label));
} while (!file.eof());
return true;
}
bool RenderableDUMeshes::loadCachedFile(const std::string& file) {
std::ifstream fileStream(file, std::ifstream::binary);
if (!fileStream.good()) {
LERROR(fmt::format("Error opening file '{}' for loading cache file", file));
return false;
}
int8_t version = 0;
fileStream.read(reinterpret_cast<char*>(&version), sizeof(int8_t));
if (version != CurrentCacheVersion) {
LINFO("The format of the cached file has changed: deleting old cache");
fileStream.close();
FileSys.deleteFile(file);
return false;
}
int32_t nValues = 0;
fileStream.read(reinterpret_cast<char*>(&nValues), sizeof(int32_t));
fileStream.read(
reinterpret_cast<char*>(&_nValuesPerAstronomicalObject),
sizeof(int32_t)
);
_fullData.resize(nValues);
fileStream.read(
reinterpret_cast<char*>(&_fullData[0]),
nValues * sizeof(_fullData[0])
);
bool success = fileStream.good();
return success;
}
bool RenderableDUMeshes::saveCachedFile(const std::string& file) const {
std::ofstream fileStream(file, std::ofstream::binary);
if (!fileStream.good()) {
LERROR(fmt::format("Error opening file '{}' for save cache file", file));
return false;
}
fileStream.write(
reinterpret_cast<const char*>(&CurrentCacheVersion),
sizeof(int8_t)
);
const int32_t nValues = static_cast<int32_t>(_fullData.size());
if (nValues == 0) {
LERROR("Error writing cache: No values were loaded");
return false;
}
fileStream.write(reinterpret_cast<const char*>(&nValues), sizeof(int32_t));
const int32_t nValuesPerAstronomicalObject = static_cast<int32_t>(
_nValuesPerAstronomicalObject
);
fileStream.write(
reinterpret_cast<const char*>(&nValuesPerAstronomicalObject),
sizeof(int32_t)
);
const size_t nBytes = nValues * sizeof(_fullData[0]);
fileStream.write(reinterpret_cast<const char*>(&_fullData[0]), nBytes);
const bool success = fileStream.good();
return success;
}
void RenderableDUMeshes::createMeshes() {
if (!(_dataIsDirty && _hasSpeckFile)) {
return;
@@ -27,6 +27,7 @@
#include <openspace/rendering/renderable.h>
#include <modules/space/speckloader.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
@@ -108,9 +109,6 @@ private:
bool loadData();
bool readSpeckFile();
bool readLabelFile();
bool loadCachedFile(const std::string& file);
bool saveCachedFile(const std::string& file) const;
bool _hasSpeckFile = false;
bool _dataIsDirty = true;
@@ -140,7 +138,7 @@ private:
Unit _unit = Parsec;
std::vector<float> _fullData;
std::vector<std::pair<glm::vec3, std::string>> _labelData;
speck::Labelset _labelset;
int _nValuesPerAstronomicalObject = 0;
std::unordered_map<int, glm::vec3> _meshColorMap;
@@ -30,6 +30,7 @@
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/font/fontmanager.h>
#include <ghoul/font/fontrenderer.h>
@@ -41,6 +42,7 @@
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <array>
#include <filesystem>
#include <fstream>
#include <optional>
#include <string>
@@ -49,11 +51,12 @@ namespace {
constexpr const char* _loggerCat = "RenderablePlanesCloud";
constexpr const char* ProgramObjectName = "RenderablePlanesCloud";
constexpr const int PlanesVertexDataSize = 36;
constexpr std::array<const char*, 4> UniformNames = {
"modelViewProjectionTransform", "alphaValue", "fadeInValue", "galaxyTexture"
};
constexpr int8_t CurrentCacheVersion = 2;
constexpr double PARSEC = 0.308567756E17;
enum BlendMode {
@@ -271,7 +274,7 @@ RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary
addProperty(_opacity);
if (p.file.has_value()) {
_speckFile = absPath(*p.file);
_speckFile = absPath(*p.file).string();
_hasSpeckFile = true;
_drawElements.onChange([&]() { _hasSpeckFile = !_hasSpeckFile; });
addProperty(_drawElements);
@@ -319,7 +322,7 @@ RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary
_scaleFactor.onChange([&]() { _dataIsDirty = true; });
if (p.labelFile.has_value()) {
_labelFile = absPath(*p.labelFile);
_labelFile = absPath(*p.labelFile).string();
_hasLabel = true;
_textColor = p.textColor.value_or(_textColor);
@@ -367,12 +370,11 @@ RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary
}
}
_texturesPath = absPath(p.texturePath);
_texturesPath = absPath(p.texturePath).string();
_luminosityVar = p.luminosity.value_or(_luminosityVar);
_sluminosity = p.scaleLuminosity.value_or(_sluminosity);
if (p.fadeInDistances.has_value()) {
_fadeInDistance = *p.fadeInDistances;
_disableFadeInDistance = false;
@@ -388,15 +390,25 @@ RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary
}
bool RenderablePlanesCloud::isReady() const {
return ((_program != nullptr) && (!_fullData.empty())) || (!_labelData.empty());
return (_program && (!_dataset.entries.empty())) || (!_labelset.entries.empty());
}
void RenderablePlanesCloud::initialize() {
ZoneScoped
const bool success = loadData();
if (!success) {
throw ghoul::RuntimeError("Error loading data");
if (_hasSpeckFile && std::filesystem::is_regular_file(_speckFile)) {
_dataset = speck::data::loadFileWithCache(_speckFile);
if (_dataset.entries.empty()) {
throw ghoul::RuntimeError("Error loading data");
}
}
if (!_labelFile.empty()) {
LINFO(fmt::format("Loading Label file '{}'", _labelFile));
_labelset = speck::label::loadFileWithCache(_labelFile);
for (speck::Labelset::Entry& e : _labelset.entries) {
e.position = glm::vec3(_transformationMatrix * glm::dvec4(e.position, 1.0));
}
}
}
@@ -417,7 +429,6 @@ void RenderablePlanesCloud::initializeGL() {
ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames);
createPlanes();
loadTextures();
if (_hasLabel) {
@@ -433,7 +444,6 @@ void RenderablePlanesCloud::initializeGL() {
}
}
void RenderablePlanesCloud::deleteDataGPUAndCPU() {
for (std::unordered_map<int, PlaneAggregate>::reference pAMapItem : _planesMap) {
glDeleteBuffers(1, &pAMapItem.second.vbo);
@@ -484,8 +494,7 @@ void RenderablePlanesCloud::renderPlanes(const RenderData&,
_program->setUniform(_uniformCache.galaxyTexture, unit);
int currentTextureIndex = -1;
for (std::unordered_map<int, PlaneAggregate>::reference pAMapItem : _planesMap)
{
for (std::unordered_map<int, PlaneAggregate>::reference pAMapItem : _planesMap) {
// For planes with undefined textures references
if (pAMapItem.first == 30) {
continue;
@@ -514,35 +523,8 @@ void RenderablePlanesCloud::renderLabels(const RenderData& data,
const glm::dvec3& orthoRight,
const glm::dvec3& orthoUp, float fadeInVariable)
{
float scale = 0.f;
switch (_unit) {
case Meter:
scale = 1.f;
break;
case Kilometer:
scale = 1e3f;
break;
case Parsec:
scale = static_cast<float>(PARSEC);
break;
case Kiloparsec:
scale = static_cast<float>(1e3 * PARSEC);
break;
case Megaparsec:
scale = static_cast<float>(1e6 * PARSEC);
break;
case Gigaparsec:
scale = static_cast<float>(1e9 * PARSEC);
break;
case GigalightYears:
scale = static_cast<float>(306391534.73091 * PARSEC);
break;
}
glm::vec4 textColor = glm::vec4(
glm::vec3(_textColor),
_textOpacity * fadeInVariable
);
double scale = unitToMeter(_unit);
glm::vec4 textColor = glm::vec4(glm::vec3(_textColor), _textOpacity * fadeInVariable);
ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo;
labelInfo.orthoRight = orthoRight;
@@ -557,14 +539,12 @@ void RenderablePlanesCloud::renderLabels(const RenderData& data,
labelInfo.enableDepth = true;
labelInfo.enableFalseDepth = false;
for (const std::pair<glm::vec3, std::string>& pair : _labelData) {
//glm::vec3 scaledPos(_transformationMatrix * glm::dvec4(pair.first, 1.0));
glm::vec3 scaledPos(pair.first);
scaledPos *= scale;
for (const speck::Labelset::Entry& e : _labelset.entries) {
glm::dvec3 scaledPos = glm::dvec3(e.position) * scale;
ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render(
*_font,
scaledPos,
pair.second,
e.text,
textColor,
labelInfo
);
@@ -572,35 +552,12 @@ void RenderablePlanesCloud::renderLabels(const RenderData& data,
}
void RenderablePlanesCloud::render(const RenderData& data, RendererTasks&) {
float scale = 0.f;
switch (_unit) {
case Meter:
scale = 1.f;
break;
case Kilometer:
scale = 1e3f;
break;
case Parsec:
scale = static_cast<float>(PARSEC);
break;
case Kiloparsec:
scale = static_cast<float>(1e3 * PARSEC);
break;
case Megaparsec:
scale = static_cast<float>(1e6 * PARSEC);
break;
case Gigaparsec:
scale = static_cast<float>(1e9 * PARSEC);
break;
case GigalightYears:
scale = static_cast<float>(306391534.73091 * PARSEC);
break;
}
const double scale = unitToMeter(_unit);
float fadeInVariable = 1.f;
if (!_disableFadeInDistance) {
float distCamera = static_cast<float>(glm::length(data.camera.positionVec3()));
distCamera /= scale;
distCamera = static_cast<float>(distCamera / scale);
const glm::vec2 fadeRange = _fadeInDistance;
//const float a = 1.f / ((fadeRange.y - fadeRange.x) * scale);
const float a = 1.f / ((fadeRange.y - fadeRange.x));
@@ -620,8 +577,7 @@ void RenderablePlanesCloud::render(const RenderData& data, RendererTasks&) {
const glm::dmat4 modelViewMatrix = data.camera.combinedViewMatrix() * modelMatrix;
const glm::mat4 projectionMatrix = data.camera.projectionMatrix();
const glm::dmat4 modelViewProjectionMatrix = glm::dmat4(projectionMatrix) *
modelViewMatrix;
const glm::dmat4 mvpMatrix = glm::dmat4(projectionMatrix) * modelViewMatrix;
const glm::dmat4 invMVPParts = glm::inverse(modelMatrix) *
glm::inverse(data.camera.combinedViewMatrix()) *
@@ -638,13 +594,7 @@ void RenderablePlanesCloud::render(const RenderData& data, RendererTasks&) {
}
if (_hasLabel) {
renderLabels(
data,
modelViewProjectionMatrix,
orthoRight,
orthoUp,
fadeInVariable
);
renderLabels(data, mvpMatrix, orthoRight, orthoUp, fadeInVariable);
}
}
@@ -661,457 +611,82 @@ void RenderablePlanesCloud::update(const UpdateData&) {
}
}
bool RenderablePlanesCloud::loadData() {
bool success = false;
if (_hasSpeckFile) {
// I disabled the cache as it didn't work on Mac --- abock
// std::string cachedFile = FileSys.cacheManager()->cachedFilename(
// _speckFile,
// ghoul::filesystem::CacheManager::Persistent::Yes
// );
void RenderablePlanesCloud::loadTextures() {
for (const speck::Dataset::Texture& tex : _dataset.textures) {
std::filesystem::path fullPath = absPath(_texturesPath + '/' + tex.file);
std::filesystem::path pngPath = fullPath;
pngPath.replace_extension(".png");
// bool hasCachedFile = FileSys.fileExists(cachedFile);
// if (hasCachedFile) {
// LINFO(
// "Cached file '" << cachedFile <<
// "' used for Speck file '" << _speckFile << "'"
// );
// success = loadCachedFile(cachedFile);
// if (!success) {
// FileSys.cacheManager()->removeCacheFile(_speckFile);
// // Intentional fall-through to the 'else' to generate the cache
// // file for the next run
// }
// }
// else
// {
// LINFO("Cache for Speck file '" << _speckFile << "' not found");
LINFO(fmt::format("Loading Speck file '{}'", _speckFile));
success = readSpeckFile();
if (!success) {
return false;
}
// LINFO("Saving cache");
//success &= saveCachedFile(cachedFile);
// }
}
if (!_labelFile.empty()) {
// I disabled the cache as it didn't work on Mac --- abock
// std::string cachedFile = FileSys.cacheManager()->cachedFilename(
// _labelFile,
// ghoul::filesystem::CacheManager::Persistent::Yes
// );
// bool hasCachedFile = FileSys.fileExists(cachedFile);
// if (hasCachedFile) {
// LINFO(
// "Cached file '" << cachedFile <<
// "' used for Label file '" << _labelFile << "'"
// );
//
// success &= loadCachedFile(cachedFile);
// if (!success) {
// FileSys.cacheManager()->removeCacheFile(_labelFile);
// // Intentional fall-through to the 'else' to generate the cache
// // file for the next run
// }
// }
// else
// {
// LINFO("Cache for Label file '" << _labelFile << "' not found");
LINFO(fmt::format("Loading Label file '{}'", _labelFile));
success &= readLabelFile();
if (!success) {
return false;
}
// }
}
return success;
}
bool RenderablePlanesCloud::loadTextures() {
if (!_textureFileMap.empty()) {
for (const std::pair<const int, std::string>& pair : _textureFileMap) {
const auto& p = _textureMap.insert(std::make_pair(
pair.first,
ghoul::io::TextureReader::ref().loadTexture(pair.second)
std::filesystem::path path;
if (std::filesystem::is_regular_file(fullPath)) {
path = fullPath;
}
else if (std::filesystem::is_regular_file(pngPath)) {
path = pngPath;
}
else {
// We can't really recover from this as it would crash during rendering anyway
throw ghoul::RuntimeError(fmt::format(
"Could not find image file '{}'", tex.file
));
if (p.second) {
LINFOC(
"RenderablePlanesCloud",
fmt::format("Loaded texture from '{}'", pair.second)
);
p.first->second->uploadTexture();
p.first->second->setFilter(
ghoul::opengl::Texture::FilterMode::LinearMipMap
);
p.first->second->purgeFromRAM();
}
}
}
else {
return false;
}
return true;
}
bool RenderablePlanesCloud::readSpeckFile() {
std::ifstream file(_speckFile);
if (!file.good()) {
LERROR(fmt::format("Failed to open Speck file '{}'", _speckFile));
return false;
}
_nValuesPerAstronomicalObject = 0;
// The beginning of the speck file has a header that either contains comments
// (signaled by a preceding '#') or information about the structure of the file
// (signaled by the keywords 'datavar', 'texturevar', and 'texture')
std::string line;
while (true) {
std::getline(file, line);
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() -1);
}
if (line.empty() || line[0] == '#') {
continue;
std::unique_ptr<ghoul::opengl::Texture> t =
ghoul::io::TextureReader::ref().loadTexture(path.string());
if (t) {
LINFOC("RenderablePlanesCloud", fmt::format("Loaded texture '{}'", path));
t->uploadTexture();
t->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap);
t->purgeFromRAM();
}
else {
// Same here, we won't be able to recover from this nullptr
throw ghoul::RuntimeError(fmt::format(
"Could not find image file '{}'", tex.file
));
}
if (line.substr(0, 7) != "datavar" &&
line.substr(0, 10) != "texturevar" &&
line.substr(0, 7) != "texture" &&
line.substr(0, 10) != "polyorivar" &&
line.substr(0, 10) != "maxcomment")
{
// Started reading data
break;
}
if (line.substr(0, 7) == "datavar") {
// datavar lines are structured as follows:
// datavar # description
// where # is the index of the data variable; so if we repeatedly overwrite
// the 'nValues' variable with the latest index, we will end up with the total
// number of values (+3 since X Y Z are not counted in the Speck file index)
std::stringstream str(line);
std::string dummy;
str >> dummy; // command
str >> _nValuesPerAstronomicalObject; // variable index
dummy.clear();
str >> dummy; // variable name
// +3 because of the x, y and z at the begining of each line.
_variableDataPositionMap.insert({ dummy, _nValuesPerAstronomicalObject + 3});
if ((dummy == "orientation") || (dummy == "ori")) { // 3d vectors u and v
// We want the number, but the index is 0 based
_nValuesPerAstronomicalObject += 6;
}
else {
// We want the number, but the index is 0 based
_nValuesPerAstronomicalObject += 1;
}
}
if (line.substr(0, 10) == "polyorivar") {
_planeStartingIndexPos = 0;
std::stringstream str(line);
std::string dummy;
str >> dummy; // command
str >> _planeStartingIndexPos;
_planeStartingIndexPos += 3; // 3 for xyz
}
if (line.substr(0, 10) == "texturevar") {
_textureVariableIndex = 0;
std::stringstream str(line);
std::string dummy;
str >> dummy; // command
str >> _textureVariableIndex;
_textureVariableIndex += 3; // 3 for xyz
}
if (line.substr(0, 8) == "texture ") {
std::stringstream str(line);
std::size_t found = line.find('-');
int textureIndex = 0;
std::string dummy;
str >> dummy; // command
if (found != std::string::npos) {
std::string option; // Not being used right now.
str >> option;
}
str >> textureIndex;
std::string fileName;
str >> fileName; // texture file name
std::string fullPath = absPath(_texturesPath + '/' + fileName);
std::string pngPath =
ghoul::filesystem::File(fullPath).fullBaseName() + ".png";
if (FileSys.fileExists(fullPath)) {
_textureFileMap.insert({ textureIndex, fullPath });
}
else if (FileSys.fileExists(pngPath)) {
_textureFileMap.insert({ textureIndex, pngPath });
}
else {
LWARNING(fmt::format("Could not find image file {}", fileName));
_textureFileMap.insert({ textureIndex, "" });
}
}
}
_nValuesPerAstronomicalObject += 3; // X Y Z are not counted in the Speck file indices
do {
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() -1);
}
if (line.empty()) {
std::getline(file, line);
continue;
}
else if (line[0] == '#') {
std::getline(file, line);
continue;
}
std::stringstream str(line);
glm::vec3 u(0.f);
glm::vec3 v(0.f);
std::vector<float> values(_nValuesPerAstronomicalObject);
for (int i = 0; i < _nValuesPerAstronomicalObject; ++i) {
str >> values[i];
if ((i >= _planeStartingIndexPos) &&
(i <= _planeStartingIndexPos + 6)) { // vectors u and v
int index = i - _planeStartingIndexPos;
switch (index) {
case 0:
u.x = values[i];
break;
case 1:
u.y = values[i];
break;
case 2:
u.z = values[i];
break;
case 3:
v.x = values[i];
break;
case 4:
v.y = values[i];
break;
case 5:
v.z = values[i];
break;
}
}
}
_fullData.insert(_fullData.end(), values.begin(), values.end());
// reads new line
std::getline(file, line);
} while (!file.eof());
return true;
}
bool RenderablePlanesCloud::readLabelFile() {
std::ifstream file(_labelFile);
if (!file.good()) {
LERROR(fmt::format("Failed to open Label file '{}'", _labelFile));
return false;
}
// The beginning of the speck file has a header that either contains comments
// (signaled by a preceding '#') or information about the structure of the file
// (signaled by the keywords 'datavar', 'texturevar', and 'texture')
std::string line;
while (true) {
std::streampos position = file.tellg();
std::getline(file, line);
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() -1);
}
if (line.empty() || line[0] == '#') {
continue;
}
if (line.substr(0, 9) != "textcolor") {
// we read a line that doesn't belong to the header, so we have to jump back
// before the beginning of the current line
file.seekg(position);
continue;
}
if (line.substr(0, 9) == "textcolor") {
// textcolor lines are structured as follows:
// textcolor # description
// where # is color text defined in configuration file
std::stringstream str(line);
// TODO: handle cases of labels with different colors
break;
}
}
do {
std::vector<float> values(_nValuesPerAstronomicalObject);
std::getline(file, line);
// Guard against wrong line endings (copying files from Windows to Mac) causes
// lines to have a final \r
if (!line.empty() && line.back() == '\r') {
line = line.substr(0, line.length() -1);
}
if (line.empty()) {
continue;
}
std::stringstream str(line);
glm::vec3 position = glm::vec3(0.f);
for (int j = 0; j < 3; ++j) {
str >> position[j];
}
std::string dummy;
str >> dummy; // text keyword
std::string label;
str >> label;
dummy.clear();
while (str >> dummy) {
label += " " + dummy;
dummy.clear();
}
glm::vec3 transformedPos = glm::vec3(
_transformationMatrix * glm::dvec4(position, 1.0)
);
_labelData.emplace_back(std::make_pair(transformedPos, label));
} while (!file.eof());
return true;
}
bool RenderablePlanesCloud::loadCachedFile(const std::string& file) {
std::ifstream fileStream(file, std::ifstream::binary);
if (fileStream.good()) {
int8_t version = 0;
fileStream.read(reinterpret_cast<char*>(&version), sizeof(int8_t));
if (version != CurrentCacheVersion) {
LINFO("The format of the cached file has changed: deleting old cache");
fileStream.close();
FileSys.deleteFile(file);
return false;
}
int32_t nValues = 0;
fileStream.read(reinterpret_cast<char*>(&nValues), sizeof(int32_t));
fileStream.read(reinterpret_cast<char*>(
&_nValuesPerAstronomicalObject),
sizeof(int32_t)
);
_fullData.resize(nValues);
fileStream.read(reinterpret_cast<char*>(&_fullData[0]),
nValues * sizeof(_fullData[0]));
bool success = fileStream.good();
return success;
}
else {
LERROR(fmt::format("Error opening file '{}' for loading cache file", file));
return false;
_textureMap.insert(std::pair(tex.index, std::move(t)));
}
}
bool RenderablePlanesCloud::saveCachedFile(const std::string& file) const {
std::ofstream fileStream(file, std::ofstream::binary);
if (fileStream.good()) {
fileStream.write(reinterpret_cast<const char*>(&CurrentCacheVersion),
sizeof(int8_t));
const int32_t nValues = static_cast<int32_t>(_fullData.size());
if (nValues == 0) {
LERROR("Error writing cache: No values were loaded");
return false;
}
fileStream.write(reinterpret_cast<const char*>(&nValues), sizeof(int32_t));
const int32_t nValuesPerAstronomicalObject = static_cast<int32_t>(
_nValuesPerAstronomicalObject
);
fileStream.write(reinterpret_cast<const char*>(
&nValuesPerAstronomicalObject),
sizeof(int32_t)
);
const size_t nBytes = nValues * sizeof(_fullData[0]);
fileStream.write(reinterpret_cast<const char*>(&_fullData[0]), nBytes);
bool success = fileStream.good();
return success;
}
else {
LERROR(fmt::format("Error opening file '{}' for save cache file", file));
return false;
double RenderablePlanesCloud::unitToMeter(Unit unit) const {
switch (unit) {
case Meter: return 1.0;
case Kilometer: return 1e3;
case Parsec: return PARSEC;
case Kiloparsec: return 1000 * PARSEC;
case Megaparsec: return 1e6 * PARSEC;
case Gigaparsec: return 1e9 * PARSEC;
case GigalightYears: return 306391534.73091 * PARSEC;
default: throw ghoul::MissingCaseException();
}
}
void RenderablePlanesCloud::createPlanes() {
if (_dataIsDirty && _hasSpeckFile) {
const int lumIdx = std::max(_dataset.index(_luminosityVar), 0);
const double scale = unitToMeter(_unit);
LDEBUG("Creating planes...");
float maxSize = 0.f;
for (size_t p = 0; p < _fullData.size(); p += _nValuesPerAstronomicalObject) {
double maxRadius = 0.0;
for (const speck::Dataset::Entry& e : _dataset.entries) {
const glm::vec4 transformedPos = glm::vec4(
_transformationMatrix *
glm::dvec4(_fullData[p + 0], _fullData[p + 1], _fullData[p + 2], 1.0)
_transformationMatrix * glm::dvec4(e.position, 1.0)
);
const double r = glm::length(glm::dvec3(transformedPos) * scale);
maxRadius = std::max(maxRadius, r);
// Plane vectors u and v
glm::vec4 u = glm::vec4(
_transformationMatrix *
glm::dvec4(
_fullData[p + _planeStartingIndexPos + 0],
_fullData[p + _planeStartingIndexPos + 1],
_fullData[p + _planeStartingIndexPos + 2],
e.data[_dataset.orientationDataIndex + 0],
e.data[_dataset.orientationDataIndex + 1],
e.data[_dataset.orientationDataIndex + 2],
1.f
)
);
@@ -1121,9 +696,9 @@ void RenderablePlanesCloud::createPlanes() {
glm::vec4 v = glm::vec4(
_transformationMatrix *
glm::dvec4(
_fullData[p + _planeStartingIndexPos + 3],
_fullData[p + _planeStartingIndexPos + 4],
_fullData[p + _planeStartingIndexPos + 5],
e.data[_dataset.orientationDataIndex + 3],
e.data[_dataset.orientationDataIndex + 4],
e.data[_dataset.orientationDataIndex + 5],
1.f
)
);
@@ -1131,8 +706,7 @@ void RenderablePlanesCloud::createPlanes() {
v.w = 0.f;
if (!_luminosityVar.empty()) {
float lumS = _fullData[p + _variableDataPositionMap[_luminosityVar]] *
_sluminosity;
float lumS = e.data[lumIdx] * _sluminosity;
u *= lumS;
v *= lumS;
}
@@ -1145,31 +719,6 @@ void RenderablePlanesCloud::createPlanes() {
glm::vec4 vertex2 = transformedPos - u + v;
glm::vec4 vertex4 = transformedPos + u - v;
float scale = 0.f;
switch (_unit) {
case Meter:
scale = 1.f;
break;
case Kilometer:
scale = 1e3f;
break;
case Parsec:
scale = static_cast<float>(PARSEC);
break;
case Kiloparsec:
scale = static_cast<float>(1e3 * PARSEC);
break;
case Megaparsec:
scale = static_cast<float>(1e6 * PARSEC);
break;
case Gigaparsec:
scale = static_cast<float>(1e9 * PARSEC);
break;
case GigalightYears:
scale = static_cast<float>(306391534.73091 * PARSEC);
break;
}
for (int i = 0; i < 3; ++i) {
maxSize = std::max(maxSize, vertex0[i]);
maxSize = std::max(maxSize, vertex1[i]);
@@ -1177,12 +726,12 @@ void RenderablePlanesCloud::createPlanes() {
maxSize = std::max(maxSize, vertex4[i]);
}
vertex0 *= scale;
vertex1 *= scale;
vertex2 *= scale;
vertex4 *= scale;
vertex0 = glm::vec4(glm::dvec4(vertex0) * scale);
vertex1 = glm::vec4(glm::dvec4(vertex1) * scale);
vertex2 = glm::vec4(glm::dvec4(vertex2) * scale);
vertex4 = glm::vec4(glm::dvec4(vertex4) * scale);
GLfloat vertexData[] = {
const std::array<GLfloat, 36> VertexData = {
// x y z w s t
vertex0.x, vertex0.y, vertex0.z, 1.f, 0.f, 0.f,
vertex1.x, vertex1.y, vertex1.z, 1.f, 1.f, 1.f,
@@ -1192,12 +741,12 @@ void RenderablePlanesCloud::createPlanes() {
vertex1.x, vertex1.y, vertex1.z, 1.f, 1.f, 1.f,
};
int textureIndex = static_cast<int>(_fullData[p + _textureVariableIndex]);
int textureIndex = static_cast<int>(e.data[_dataset.textureDataIndex]);
std::unordered_map<int, PlaneAggregate>::iterator found =
_planesMap.find(textureIndex);
if (found != _planesMap.end()) {
for (int i = 0; i < PLANES_VERTEX_DATA_SIZE; ++i) {
found->second.planesCoordinates.push_back(vertexData[i]);
for (int i = 0; i < PlanesVertexDataSize; ++i) {
found->second.planesCoordinates.push_back(VertexData[i]);
}
found->second.numberOfPlanes++;
}
@@ -1207,10 +756,10 @@ void RenderablePlanesCloud::createPlanes() {
glGenVertexArrays(1, &pA.vao);
glGenBuffers(1, &pA.vbo);
pA.numberOfPlanes = 1;
for (int i = 0; i < PLANES_VERTEX_DATA_SIZE; ++i) {
pA.planesCoordinates.push_back(vertexData[i]);
for (int i = 0; i < PlanesVertexDataSize; ++i) {
pA.planesCoordinates.push_back(VertexData[i]);
}
_planesMap.insert(std::pair<int, PlaneAggregate>(textureIndex, pA));
_planesMap.insert(std::pair(textureIndex, pA));
}
}
@@ -1220,21 +769,13 @@ void RenderablePlanesCloud::createPlanes() {
glBindBuffer(GL_ARRAY_BUFFER, pAMapItem.second.vbo);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(GLfloat) * PLANES_VERTEX_DATA_SIZE *
pAMapItem.second.numberOfPlanes,
sizeof(GLfloat) * PlanesVertexDataSize * pAMapItem.second.numberOfPlanes,
pAMapItem.second.planesCoordinates.data(),
GL_STATIC_DRAW
);
// in_position
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0,
4,
GL_FLOAT,
GL_FALSE,
sizeof(GLfloat) * 6,
nullptr
);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), nullptr);
// texture coords
glEnableVertexAttribArray(1);
@@ -1243,8 +784,8 @@ void RenderablePlanesCloud::createPlanes() {
2,
GL_FLOAT,
GL_FALSE,
sizeof(GLfloat) * 6,
reinterpret_cast<GLvoid*>(sizeof(GLfloat) * 4)
6 * sizeof(GLfloat),
reinterpret_cast<GLvoid*>(4 * sizeof(GLfloat))
);
glBindVertexArray(0);
@@ -1252,6 +793,7 @@ void RenderablePlanesCloud::createPlanes() {
_dataIsDirty = false;
setBoundingSphere(maxRadius * _scaleFactor);
_fadeInDistance.setMaxValue(glm::vec2(10.f * maxSize));
}
@@ -27,6 +27,7 @@
#include <openspace/rendering/renderable.h>
#include <modules/space/speckloader.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
@@ -50,7 +51,6 @@ namespace ghoul::opengl {
namespace openspace {
// (x, y, z, w, s, t) * 6 = 36
const int PLANES_VERTEX_DATA_SIZE = 36;
namespace documentation { struct Documentation; }
@@ -80,6 +80,7 @@ private:
Gigaparsec = 5,
GigalightYears = 6
};
double unitToMeter(Unit unit) const;
struct PlaneAggregate {
int textureIndex;
@@ -97,12 +98,7 @@ private:
const glm::dmat4& modelViewProjectionMatrix, const glm::dvec3& orthoRight,
const glm::dvec3& orthoUp, float fadeInVariable);
bool loadData();
bool loadTextures();
bool readSpeckFile();
bool readLabelFile();
bool loadCachedFile(const std::string& file);
bool saveCachedFile(const std::string& file) const;
void loadTextures();
bool _hasSpeckFile = false;
bool _dataIsDirty = true;
@@ -112,8 +108,6 @@ private:
int _textMinSize = 0;
int _textMaxSize = 200;
int _planeStartingIndexPos = 0;
int _textureVariableIndex = 0;
properties::FloatProperty _scaleFactor;
properties::Vec3Property _textColor;
@@ -124,13 +118,12 @@ private:
properties::Vec2Property _fadeInDistance;
properties::BoolProperty _disableFadeInDistance;
properties::FloatProperty _planeMinSize;
// DEBUG:
properties::OptionProperty _renderOption;
ghoul::opengl::ProgramObject* _program = nullptr;
UniformCache(modelViewProjectionTransform, alphaValue, fadeInValue,
galaxyTexture) _uniformCache;
UniformCache(
modelViewProjectionTransform, alphaValue, fadeInValue, galaxyTexture
) _uniformCache;
std::shared_ptr<ghoul::fontrendering::Font> _font = nullptr;
std::unordered_map<int, std::unique_ptr<ghoul::opengl::Texture>> _textureMap;
std::unordered_map<int, std::string> _textureFileMap;
@@ -143,18 +136,14 @@ private:
Unit _unit = Parsec;
std::vector<float> _fullData;
std::vector<std::pair<glm::vec3, std::string>> _labelData;
std::unordered_map<std::string, int> _variableDataPositionMap;
int _nValuesPerAstronomicalObject = 0;
speck::Dataset _dataset;
speck::Labelset _labelset;
float _sluminosity = 1.f;
glm::dmat4 _transformationMatrix = glm::dmat4(1.0);
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEPLANESCLOUD___H__
@@ -40,6 +40,7 @@
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <array>
#include <filesystem>
#include <fstream>
#include <locale>
#include <cstdint>
@@ -54,7 +55,6 @@ namespace {
"spriteTexture", "hasColorMap"
};
constexpr int8_t CurrentCacheVersion = 1;
constexpr double PARSEC = 0.308567756E17;
constexpr openspace::properties::Property::PropertyInfo SpriteTextureInfo = {
@@ -137,7 +137,7 @@ RenderablePoints::RenderablePoints(const ghoul::Dictionary& dictionary)
addProperty(_opacity);
registerUpdateRenderBinFromOpacity();
_speckFile = absPath(p.file);
_speckFile = absPath(p.file).string();
if (p.unit.has_value()) {
switch (*p.unit) {
@@ -170,25 +170,24 @@ RenderablePoints::RenderablePoints(const ghoul::Dictionary& dictionary)
}
_pointColor = p.color;
_pointColor.setViewOption(properties::Property::ViewOptions::Color);
addProperty(_pointColor);
if (p.texture.has_value()) {
_spriteTexturePath = absPath(*p.texture);
_spriteTexturePath = absPath(*p.texture).string();
_spriteTextureFile = std::make_unique<ghoul::filesystem::File>(
_spriteTexturePath
_spriteTexturePath.value()
);
_spriteTexturePath.onChange([&] { _spriteTextureIsDirty = true; });
_spriteTextureFile->setCallback(
[&](const ghoul::filesystem::File&) { _spriteTextureIsDirty = true; }
);
_spriteTexturePath.onChange([this]() { _spriteTextureIsDirty = true; });
_spriteTextureFile->setCallback([this]() { _spriteTextureIsDirty = true; });
addProperty(_spriteTexturePath);
_hasSpriteTexture = true;
}
if (p.colorMap.has_value()) {
_colorMapFile = absPath(*p.colorMap);
_colorMapFile = absPath(*p.colorMap).string();
_hasColorMapFile = true;
}
@@ -197,26 +196,22 @@ RenderablePoints::RenderablePoints(const ghoul::Dictionary& dictionary)
}
bool RenderablePoints::isReady() const {
return (_program != nullptr) && (!_fullData.empty());
return _program && (!_dataset.entries.empty());
}
void RenderablePoints::initialize() {
ZoneScoped
bool success = loadData();
if (!success) {
throw ghoul::RuntimeError("Error loading data");
_dataset = speck::data::loadFileWithCache(_speckFile);
if (_hasColorMapFile) {
readColorMapFile();
}
}
void RenderablePoints::initializeGL() {
ZoneScoped
// OBS: The ProgramObject name is later used to release the program as well, so the
// name parameter to requestProgramObject and the first parameter to
// buildRenderProgram has to be the same or an assertion will be thrown at the
// end of the program.
if (_hasSpriteTexture) {
_program = DigitalUniverseModule::ProgramObjectManager.request(
"RenderablePoints Sprite",
@@ -288,10 +283,7 @@ void RenderablePoints::render(const RenderData& data, RendererTasks&) {
glEnable(GL_PROGRAM_POINT_SIZE);
glBindVertexArray(_vao);
const GLsizei nAstronomicalObjects = static_cast<GLsizei>(
_fullData.size() / _nValuesPerAstronomicalObject
);
glDrawArrays(GL_POINTS, 0, nAstronomicalObjects);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_dataset.entries.size()));
glDisable(GL_PROGRAM_POINT_SIZE);
glBindVertexArray(0);
@@ -304,7 +296,7 @@ void RenderablePoints::update(const UpdateData&) {
if (_dataIsDirty) {
LDEBUG("Regenerating data");
createDataSlice();
std::vector<double> slice = createDataSlice();
if (_vao == 0) {
glGenVertexArrays(1, &_vao);
@@ -317,19 +309,13 @@ void RenderablePoints::update(const UpdateData&) {
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(
GL_ARRAY_BUFFER,
_slicedData.size() * sizeof(double),
&_slicedData[0],
slice.size() * sizeof(double),
slice.data(),
GL_STATIC_DRAW
);
GLint positionAttrib = _program->attributeLocation("in_position");
if (_hasColorMapFile) {
// const size_t nAstronomicalObjects = _fullData.size() /
// _nValuesPerAstronomicalObject;
// const size_t nValues = _slicedData.size() / nAstronomicalObjects;
// GLsizei stride = static_cast<GLsizei>(sizeof(double) * nValues);
glEnableVertexAttribArray(positionAttrib);
glVertexAttribLPointer(
positionAttrib, 4, GL_DOUBLE, sizeof(double) * 8, nullptr
@@ -341,8 +327,8 @@ void RenderablePoints::update(const UpdateData&) {
colorMapAttrib,
4,
GL_DOUBLE,
sizeof(double) * 8,
reinterpret_cast<void*>(sizeof(double) * 4)
8 * sizeof(double),
reinterpret_cast<void*>(4 * sizeof(double))
);
}
else {
@@ -360,7 +346,7 @@ void RenderablePoints::update(const UpdateData&) {
_spriteTexture = nullptr;
if (!_spriteTexturePath.value().empty()) {
_spriteTexture = ghoul::io::TextureReader::ref().loadTexture(
absPath(_spriteTexturePath)
absPath(_spriteTexturePath).string()
);
if (_spriteTexture) {
LDEBUG(fmt::format(
@@ -373,134 +359,20 @@ void RenderablePoints::update(const UpdateData&) {
);
_spriteTextureFile = std::make_unique<ghoul::filesystem::File>(
_spriteTexturePath
);
_spriteTextureFile->setCallback(
[&](const ghoul::filesystem::File&) { _spriteTextureIsDirty = true; }
_spriteTexturePath.value()
);
_spriteTextureFile->setCallback([this]() { _spriteTextureIsDirty = true; });
}
_spriteTextureIsDirty = false;
}
}
bool RenderablePoints::loadData() {
std::string cachedFile = FileSys.cacheManager()->cachedFilename(
_speckFile,
ghoul::filesystem::CacheManager::Persistent::Yes
);
bool hasCachedFile = FileSys.fileExists(cachedFile);
if (hasCachedFile) {
LINFO(fmt::format(
"Cached file '{}' used for Speck file '{}'",
cachedFile, _speckFile
));
bool success = loadCachedFile(cachedFile);
if (success) {
if (_hasColorMapFile) {
success &= readColorMapFile();
}
return success;
}
else {
FileSys.cacheManager()->removeCacheFile(_speckFile);
// Intentional fall-through to the 'else' to generate the cache file for
// the next run
}
}
else {
LINFO(fmt::format("Cache for Speck file '{}' not found", _speckFile));
}
LINFO(fmt::format("Loading Speck file '{}'", _speckFile));
bool success = readSpeckFile();
if (!success) {
return false;
}
LINFO("Saving cache");
success = saveCachedFile(cachedFile);
if (_hasColorMapFile) {
success &= readColorMapFile();
}
return success;
}
bool RenderablePoints::readSpeckFile() {
std::ifstream file(_speckFile);
if (!file.good()) {
LERROR(fmt::format("Failed to open Speck file '{}'", _speckFile));
return false;
}
_nValuesPerAstronomicalObject = 0;
// The beginning of the speck file has a header that either contains comments
// (signaled by a preceding '#') or information about the structure of the file
// (signaled by the keywords 'datavar', 'texturevar', and 'texture')
std::string line;
while (true) {
std::streampos position = file.tellg();
std::getline(file, line);
if (line[0] == '#' || line.empty()) {
continue;
}
if (line.substr(0, 7) != "datavar" &&
line.substr(0, 10) != "texturevar" &&
line.substr(0, 7) != "texture")
{
// we read a line that doesn't belong to the header, so we have to jump
// back before the beginning of the current line
file.seekg(position);
break;
}
if (line.substr(0, 7) == "datavar") {
// datavar lines are structured as follows:
// datavar # description
// where # is the index of the data variable; so if we repeatedly
// overwrite the 'nValues' variable with the latest index, we will end up
// with the total number of values (+3 since X Y Z are not counted in the
// Speck file index)
std::stringstream str(line);
std::string dummy;
str >> dummy;
str >> _nValuesPerAstronomicalObject;
// We want the number, but the index is 0 based
_nValuesPerAstronomicalObject += 1;
}
}
// X Y Z are not counted in the Speck file indices
_nValuesPerAstronomicalObject += 3;
do {
std::vector<float> values(_nValuesPerAstronomicalObject);
std::getline(file, line);
std::stringstream str(line);
for (int i = 0; i < _nValuesPerAstronomicalObject; ++i) {
str >> values[i];
}
_fullData.insert(_fullData.end(), values.begin(), values.end());
} while (!file.eof());
return true;
}
bool RenderablePoints::readColorMapFile() {
void RenderablePoints::readColorMapFile() {
std::ifstream file(_colorMapFile);
if (!file.good()) {
LERROR(fmt::format("Failed to open Color Map file '{}'", _colorMapFile));
return false;
throw ghoul::RuntimeError(fmt::format(
"Failed to open Color Map file '{}'", _colorMapFile
));
}
std::size_t numberOfColors = 0;
@@ -525,7 +397,9 @@ bool RenderablePoints::readColorMapFile() {
break;
}
else if (file.eof()) {
return false;
throw ghoul::RuntimeError(fmt::format(
"Failed to load colors from Color Map file '{}'", _colorMapFile
));
}
}
@@ -534,106 +408,26 @@ bool RenderablePoints::readColorMapFile() {
std::stringstream str(line);
glm::vec4 color;
for (int j = 0; j < 4; ++j) {
str >> color[j];
}
str >> color.r >> color.g >> color.b >> color.a;
_colorMapData.push_back(color);
}
return true;
}
bool RenderablePoints::loadCachedFile(const std::string& file) {
std::ifstream fileStream(file, std::ifstream::binary);
if (fileStream.good()) {
int8_t version = 0;
fileStream.read(reinterpret_cast<char*>(&version), sizeof(int8_t));
if (version != CurrentCacheVersion) {
LINFO("The format of the cached file has changed: deleting old cache");
fileStream.close();
FileSys.deleteFile(file);
return false;
}
int32_t nValues = 0;
fileStream.read(reinterpret_cast<char*>(&nValues), sizeof(int32_t));
fileStream.read(
reinterpret_cast<char*>(&_nValuesPerAstronomicalObject),
sizeof(int32_t)
);
_fullData.resize(nValues);
fileStream.read(reinterpret_cast<char*>(
&_fullData[0]),
nValues * sizeof(_fullData[0])
);
const bool success = fileStream.good();
return success;
}
else {
LERROR(fmt::format(
"Error opening file '{}' for loading cache file",
file
));
return false;
}
}
bool RenderablePoints::saveCachedFile(const std::string& file) const {
std::ofstream fileStream(file, std::ofstream::binary);
if (fileStream.good()) {
fileStream.write(
reinterpret_cast<const char*>(&CurrentCacheVersion),
sizeof(int8_t)
);
const int32_t nValues = static_cast<int32_t>(_fullData.size());
if (nValues == 0) {
LERROR("Error writing cache: No values were loaded");
return false;
}
fileStream.write(reinterpret_cast<const char*>(&nValues), sizeof(int32_t));
const int32_t nValuesPerAstronomicalObject = static_cast<int32_t>(
_nValuesPerAstronomicalObject
);
fileStream.write(
reinterpret_cast<const char*>(&nValuesPerAstronomicalObject),
sizeof(int32_t)
);
const size_t nBytes = nValues * sizeof(_fullData[0]);
fileStream.write(reinterpret_cast<const char*>(&_fullData[0]), nBytes);
const bool success = fileStream.good();
return success;
}
else {
LERROR(fmt::format("Error opening file '{}' for save cache file", file));
return false;
}
}
void RenderablePoints::createDataSlice() {
_slicedData.clear();
std::vector<double> RenderablePoints::createDataSlice() {
std::vector<double> slice;
if (_hasColorMapFile) {
_slicedData.reserve(8 * (_fullData.size() / _nValuesPerAstronomicalObject));
slice.reserve(8 * _dataset.entries.size());
}
else {
_slicedData.reserve(4 * (_fullData.size()/_nValuesPerAstronomicalObject));
slice.reserve(4 * _dataset.entries.size());
}
int colorIndex = 0;
for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) {
glm::dvec3 p = glm::dvec3(
_fullData[i + 0],
_fullData[i + 1],
_fullData[i + 2]
);
for (const speck::Dataset::Entry& e : _dataset.entries) {
glm::dvec3 p = e.position;
// Converting untis
// Converting units
if (_unit == Kilometer) {
p *= 1E3;
}
@@ -657,15 +451,15 @@ void RenderablePoints::createDataSlice() {
if (_hasColorMapFile) {
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(position[j]);
slice.push_back(position[j]);
}
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(_colorMapData[colorIndex][j]);
slice.push_back(_colorMapData[colorIndex][j]);
}
}
else {
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(position[j]);
slice.push_back(position[j]);
}
}
@@ -673,6 +467,8 @@ void RenderablePoints::createDataSlice() {
0 :
colorIndex + 1;
}
return slice;
}
} // namespace openspace
@@ -27,6 +27,7 @@
#include <openspace/rendering/renderable.h>
#include <modules/space/speckloader.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
@@ -63,7 +64,6 @@ public:
static documentation::Documentation Documentation();
private:
enum Unit {
Meter = 0,
Kilometer = 1,
@@ -74,13 +74,9 @@ private:
GigalightYears = 6
};
void createDataSlice();
std::vector<double> createDataSlice();
bool loadData();
bool readSpeckFile();
bool readColorMapFile();
bool loadCachedFile(const std::string& file);
bool saveCachedFile(const std::string& file) const;
void readColorMapFile();
bool _dataIsDirty = true;
bool _hasSpriteTexture = false;
@@ -94,19 +90,20 @@ private:
std::unique_ptr<ghoul::opengl::Texture> _spriteTexture;
std::unique_ptr<ghoul::filesystem::File> _spriteTextureFile;
ghoul::opengl::ProgramObject* _program = nullptr;
UniformCache(modelViewProjectionTransform, color, sides, alphaValue, scaleFactor,
spriteTexture, hasColorMap) _uniformCache;
UniformCache(
modelViewProjectionTransform, color, sides, alphaValue, scaleFactor,
spriteTexture, hasColorMap
) _uniformCache;
std::string _speckFile;
std::string _colorMapFile;
Unit _unit = Parsec;
std::vector<double> _slicedData;
std::vector<float> _fullData;
speck::Dataset _dataset;
std::vector<glm::vec4> _colorMapData;
int _nValuesPerAstronomicalObject = 0;
//int _nValuesPerAstronomicalObject = 0;
GLuint _vao = 0;
GLuint _vbo = 0;
+3 -3
View File
@@ -199,14 +199,14 @@ ExoplanetsModule::ExoplanetsModule()
std::string ExoplanetsModule::exoplanetsDataPath() const {
return absPath(
fmt::format("{}/{}", _exoplanetsDataFolder, ExoplanetsDataFileName)
);
fmt::format("{}/{}", _exoplanetsDataFolder.value(), ExoplanetsDataFileName)
).string();
};
std::string ExoplanetsModule::lookUpTablePath() const {
return absPath(
fmt::format("{}/{}", _exoplanetsDataFolder, LookupTableFileName)
);
).string();
};
std::string ExoplanetsModule::bvColormapPath() const {
+1 -1
View File
@@ -512,7 +512,7 @@ void createExoplanetSystem(const std::string& starName) {
// the luminosity of a star is proportional to: (radius^2)*(temperature^4)
// Maybe a better option would be to compute the size based on the aboslute
// magnitude or star luminosity, but for now this looks good enough.
float size = 59.f * radiusInMeter;
double size = 59.0 * radiusInMeter;
if (hasTeff) {
constexpr const float sunTeff = 5780.f;
size *= std::pow(system.starData.teff / sunTeff, 2.0);
@@ -118,7 +118,7 @@ RenderableOrbitDisc::RenderableOrbitDisc(const ghoul::Dictionary& dictionary)
setBoundingSphere(_size + _offset.value().y * _size);
_texturePath = p.texture.string();
_texturePath.onChange([&]() { _texture->loadFromFile(_texturePath); });
_texturePath.onChange([&]() { _texture->loadFromFile(_texturePath.value()); });
addProperty(_texturePath);
_eccentricity = p.eccentricity;
@@ -148,7 +148,7 @@ void RenderableOrbitDisc::initializeGL() {
ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);
_texture->loadFromFile(_texturePath);
_texture->loadFromFile(_texturePath.value());
_texture->uploadToGpu();
_plane->initialize();
@@ -34,39 +34,55 @@
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <charconv>
#include <filesystem>
#include <fstream>
namespace {
constexpr const char* KeyInputDataFile = "InputDataFile";
constexpr const char* KeyInputSpeck = "InputSPECK";
constexpr const char* KeyOutputBin = "OutputBIN";
constexpr const char* KeyOutputLut = "OutputLUT";
constexpr const char* KeyTeffToBv = "TeffToBvFile";
constexpr const char* _loggerCat = "ExoplanetsDataPreparationTask";
struct [[codegen::Dictionary(ExoplanetsDataPreparationTask)]] Parameters {
// The csv file to extract data from
std::filesystem::path inputDataFile;
// The speck file with star locations
std::filesystem::path inputSPECK;
// The bin file to export data into
std::string outputBIN [[codegen::annotation("A valid filepath")]];
// The txt file to write look-up table into
std::string outputLUT [[codegen::annotation("A valid filepath")]];
// The path to a teff to bv conversion file. Should be a txt file where each line
// has the format 'teff,bv'
std::filesystem::path teffToBvFile;
};
#include "exoplanetsdatapreparationtask_codegen.cpp"
} // namespace
namespace openspace::exoplanets {
documentation::Documentation ExoplanetsDataPreparationTask::documentation() {
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "exoplanets_data_preparation_task";
return doc;
}
ExoplanetsDataPreparationTask::ExoplanetsDataPreparationTask(
const ghoul::Dictionary& dictionary)
{
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"ExoplanetsDataPreparationTask"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_inputDataPath = absPath(dictionary.value<std::string>(KeyInputDataFile));
_inputSpeckPath = absPath(dictionary.value<std::string>(KeyInputSpeck));
_outputBinPath = absPath(dictionary.value<std::string>(KeyOutputBin));
_outputLutPath = absPath(dictionary.value<std::string>(KeyOutputLut));
_teffToBvFilePath = absPath(dictionary.value<std::string>(KeyTeffToBv));
_inputDataPath = absPath(p.inputDataFile.string());
_inputSpeckPath = absPath(p.inputSPECK.string());
_outputBinPath = absPath(p.outputBIN);
_outputLutPath = absPath(p.outputLUT);
_teffToBvFilePath = absPath(p.teffToBvFile.string());
}
std::string ExoplanetsDataPreparationTask::description() {
return fmt::format(
"Extract data about exoplanets from file '{}' and write as bin to '{}'. The data "
"Extract data about exoplanets from file {} and write as bin to {}. The data "
"file should be a csv version of the Planetary Systems Composite Data from the "
"NASA exoplanets archive (https://exoplanetarchive.ipac.caltech.edu/).",
_inputDataPath, _outputBinPath
@@ -78,7 +94,7 @@ void ExoplanetsDataPreparationTask::perform(
{
std::ifstream inputDataFile(_inputDataPath);
if (!inputDataFile.good()) {
LERROR(fmt::format("Failed to open input file '{}'", _inputDataPath));
LERROR(fmt::format("Failed to open input file {}", _inputDataPath));
return;
}
@@ -441,45 +457,4 @@ float ExoplanetsDataPreparationTask::bvFromTeff(float teff) {
return bv;
}
documentation::Documentation ExoplanetsDataPreparationTask::documentation() {
using namespace documentation;
return {
"ExoplanetsDataPreparationTask",
"exoplanets_data_preparation_task",
{
{
KeyInputDataFile,
new FileVerifier,
Optional::No,
"The csv file to extract data from"
},
{
KeyInputSpeck,
new FileVerifier,
Optional::No,
"The speck file with star locations"
},
{
KeyOutputBin,
new StringAnnotationVerifier("A valid filepath"),
Optional::No,
"The bin file to export data into"
},
{
KeyOutputLut,
new StringAnnotationVerifier("A valid filepath"),
Optional::No,
"The txt file to write look-up table into"
},
{
KeyTeffToBv,
new FileVerifier,
Optional::No,
"The path to a teff to bv conversion file. Should be a txt file where "
"each line has the format 'teff,bv'"
}
}
};
}
} // namespace openspace::exoplanets
@@ -25,8 +25,10 @@
#ifndef __OPENSPACE_MODULE_EXOPLANETS___EXOPLANETSDATAPREPARATIONTASK___H__
#define __OPENSPACE_MODULE_EXOPLANETS___EXOPLANETSDATAPREPARATIONTASK___H__
#include <openspace/properties/vector/vec3property.h>
#include <openspace/util/task.h>
#include <openspace/properties/vector/vec3property.h>
#include <filesystem>
#include <string>
namespace openspace::exoplanets {
@@ -39,11 +41,11 @@ public:
static documentation::Documentation documentation();
private:
std::string _inputDataPath;
std::string _inputSpeckPath;
std::string _outputBinPath;
std::string _outputLutPath;
std::string _teffToBvFilePath;
std::filesystem::path _inputDataPath;
std::filesystem::path _inputSpeckPath;
std::filesystem::path _outputBinPath;
std::filesystem::path _outputLutPath;
std::filesystem::path _teffToBvFilePath;
glm::vec3 starPosition(const std::string& starName);
@@ -192,7 +192,7 @@ void RenderableFieldlines::initializeDefaultPropertyValues() {
std::string seedPointSourceFile = _seedPointsInfo.value<std::string>(
KeySeedPointsFile
);
_seedPointSourceFile = absPath(seedPointSourceFile);
_seedPointSourceFile = absPath(seedPointSourceFile).string();
}
}
else if (sourceType == SeedPointsSourceTable) {
@@ -435,7 +435,7 @@ RenderableFieldlines::generateFieldlinesVolumeKameleon()
return {};
}
std::string fileName = _vectorFieldInfo.value<std::string>(KeyVectorFieldFile);
fileName = absPath(fileName);
fileName = absPath(fileName).string();
//KameleonWrapper::Model modelType;
if (model != VectorFieldKameleonModelBATSRUS) {
@@ -50,7 +50,9 @@ namespace openspace {
std::string FieldlinesSequenceModule::DefaultTransferFunctionFile = "";
FieldlinesSequenceModule::FieldlinesSequenceModule() : OpenSpaceModule(Name) {
DefaultTransferFunctionFile = absPath("${TEMPORARY}/default_transfer_function.txt");
DefaultTransferFunctionFile = absPath(
"${TEMPORARY}/default_transfer_function.txt"
).string();
std::ofstream file(DefaultTransferFunctionFile);
file << DefaultTransferfunctionSource;
@@ -38,6 +38,7 @@
#include <ghoul/logging/logmanager.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/textureunit.h>
#include <filesystem>
#include <fstream>
#include <thread>
@@ -294,7 +295,9 @@ void RenderableFieldlinesSequence::initializeGL() {
// Set a default color table, just in case the (optional) user defined paths are
// corrupt or not provided!
_colorTablePaths.push_back(FieldlinesSequenceModule::DefaultTransferFunctionFile);
_transferFunction = std::make_unique<TransferFunction>(absPath(_colorTablePaths[0]));
_transferFunction = std::make_unique<TransferFunction>(
absPath(_colorTablePaths[0]).string()
);
// EXTRACT OPTIONAL INFORMATION FROM DICTIONARY
std::string outputFolderPath;
@@ -417,13 +420,16 @@ bool RenderableFieldlinesSequence::extractMandatoryInfoFromDictionary(
// Ensure that the source folder exists and then extract
// the files with the same extension as <inputFileTypeString>
ghoul::filesystem::Directory sourceFolder(sourceFolderPath);
if (FileSys.directoryExists(sourceFolder)) {
if (std::filesystem::is_directory(sourceFolderPath)) {
// Extract all file paths from the provided folder
_sourceFiles = sourceFolder.readFiles(
ghoul::filesystem::Directory::Recursive::No,
ghoul::filesystem::Directory::Sort::Yes
);
_sourceFiles.clear();
namespace fs = std::filesystem;
for (const fs::directory_entry& e : fs::directory_iterator(sourceFolderPath)) {
if (e.is_regular_file()) {
_sourceFiles.push_back(e.path().string());
}
}
std::sort(_sourceFiles.begin(), _sourceFiles.end());
// Remove all files that don't have <inputFileTypeString> as extension
_sourceFiles.erase(
@@ -471,9 +477,8 @@ void RenderableFieldlinesSequence::extractOptionalInfoFromDictionary(
// ------------------- EXTRACT OPTIONAL VALUES FROM DICTIONARY ------------------- //
if (_dictionary->hasValue<std::string>(KeyOutputFolder)) {
outputFolderPath = _dictionary->value<std::string>(KeyOutputFolder);
ghoul::filesystem::Directory outputFolder(outputFolderPath);
if (FileSys.directoryExists(outputFolder)) {
outputFolderPath = absPath(outputFolderPath);
if (std::filesystem::is_directory(outputFolderPath)) {
outputFolderPath = absPath(outputFolderPath).string();
}
else {
LERROR(fmt::format(
@@ -605,8 +610,9 @@ void RenderableFieldlinesSequence::loadOsflsStatesIntoRAM(const std::string& out
if (newState.loadStateFromOsfls(filePath)) {
addStateToSequence(newState);
if (!outputFolder.empty()) {
ghoul::filesystem::File tmpFile(filePath);
newState.saveStateToJson(outputFolder + tmpFile.baseName());
newState.saveStateToJson(
outputFolder + std::filesystem::path(filePath).stem().string()
);
}
}
else {
@@ -916,9 +922,8 @@ bool RenderableFieldlinesSequence::extractCdfInfoFromDictionary(std::string& see
{
if (_dictionary->hasValue<std::string>(KeyCdfSeedPointFile)) {
seedFilePath = _dictionary->value<std::string>(KeyCdfSeedPointFile);
ghoul::filesystem::File seedPointFile(seedFilePath);
if (FileSys.fileExists(seedPointFile)) {
seedFilePath = absPath(seedFilePath);
if (std::filesystem::is_regular_file(seedFilePath)) {
seedFilePath = absPath(seedFilePath).string();
}
else {
LERROR(fmt::format(
@@ -962,7 +967,7 @@ bool RenderableFieldlinesSequence::extractSeedPointsFromFile(const std::string&
std::vector<glm::vec3>& outVec)
{
std::ifstream seedFile(FileSys.relativePath(path));
std::ifstream seedFile(path);
if (!seedFile.good()) {
LERROR(fmt::format("Could not open seed points file '{}'", path));
return false;
+23 -25
View File
@@ -462,11 +462,11 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
const Parameters p = codegen::bake<Parameters>(dictionary);
_filePath = absPath(p.file);
_dataFile = std::make_unique<File>(_filePath);
_dataFile->setCallback([&](const File&) { _dataIsDirty = true; });
_filePath = absPath(p.file).string();
_dataFile = std::make_unique<File>(_filePath.value());
_dataFile->setCallback([this]() { _dataIsDirty = true; });
_filePath.onChange([&]() { _dataIsDirty = true; });
_filePath.onChange([this]() { _dataIsDirty = true; });
addProperty(_filePath);
_fileReaderOption.addOptions({
@@ -567,19 +567,21 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
});
addProperty(_shaderOption);
_pointSpreadFunctionTexturePath = absPath(p.texture);
_pointSpreadFunctionTexturePath = absPath(p.texture).string();
_pointSpreadFunctionTexturePath.onChange(
[&](){ _pointSpreadFunctionTextureIsDirty = true; }
[this](){ _pointSpreadFunctionTextureIsDirty = true; }
);
_pointSpreadFunctionFile = std::make_unique<File>(
_pointSpreadFunctionTexturePath.value()
);
_pointSpreadFunctionFile = std::make_unique<File>(_pointSpreadFunctionTexturePath);
_pointSpreadFunctionFile->setCallback(
[&](const File&) { _pointSpreadFunctionTextureIsDirty = true; }
[this]() { _pointSpreadFunctionTextureIsDirty = true; }
);
_colorTexturePath = absPath(p.colorMap);
_colorTextureFile = std::make_unique<File>(_colorTexturePath);
_colorTexturePath.onChange([&]() { _colorTextureIsDirty = true; });
_colorTextureFile->setCallback([&](const File&) { _colorTextureIsDirty = true; });
_colorTexturePath = absPath(p.colorMap).string();
_colorTextureFile = std::make_unique<File>(_colorTexturePath.value());
_colorTexturePath.onChange([this]() { _colorTextureIsDirty = true; });
_colorTextureFile->setCallback([this]() { _colorTextureIsDirty = true; });
_luminosityMultiplier = p.luminosityMultiplier.value_or(_luminosityMultiplier);
_magnitudeBoost = p.magnitudeBoost.value_or(_magnitudeBoost);
@@ -1701,10 +1703,10 @@ void RenderableGaiaStars::update(const UpdateData&) {
}
case gaia::ShaderOption::Billboard_SSBO:
case gaia::ShaderOption::Billboard_VBO: {
std::string vs = absPath(
std::filesystem::path vs = absPath(
"${MODULE_GAIA}/shaders/gaia_tonemapping_vs.glsl"
);
std::string fs = absPath(
std::filesystem::path fs = absPath(
"${MODULE_GAIA}/shaders/gaia_tonemapping_billboard_fs.glsl"
);
std::unique_ptr<ghoul::opengl::ProgramObject> programTM =
@@ -2101,12 +2103,12 @@ void RenderableGaiaStars::update(const UpdateData&) {
_pointSpreadFunctionTexture = nullptr;
if (!_pointSpreadFunctionTexturePath.value().empty()) {
_pointSpreadFunctionTexture = ghoul::io::TextureReader::ref().loadTexture(
absPath(_pointSpreadFunctionTexturePath)
absPath(_pointSpreadFunctionTexturePath).string()
);
if (_pointSpreadFunctionTexture) {
LDEBUG(fmt::format(
"Loaded texture from '{}'", absPath(_pointSpreadFunctionTexturePath)
"Loaded texture from {}", absPath(_pointSpreadFunctionTexturePath)
));
_pointSpreadFunctionTexture->uploadTexture();
}
@@ -2115,12 +2117,10 @@ void RenderableGaiaStars::update(const UpdateData&) {
);
_pointSpreadFunctionFile = std::make_unique<ghoul::filesystem::File>(
_pointSpreadFunctionTexturePath
_pointSpreadFunctionTexturePath.value()
);
_pointSpreadFunctionFile->setCallback(
[&](const ghoul::filesystem::File&) {
_pointSpreadFunctionTextureIsDirty = true;
}
[this]() { _pointSpreadFunctionTextureIsDirty = true; }
);
}
_pointSpreadFunctionTextureIsDirty = false;
@@ -2131,7 +2131,7 @@ void RenderableGaiaStars::update(const UpdateData&) {
_colorTexture = nullptr;
if (!_colorTexturePath.value().empty()) {
_colorTexture = ghoul::io::TextureReader::ref().loadTexture(
absPath(_colorTexturePath)
absPath(_colorTexturePath).string()
);
if (_colorTexture) {
LDEBUG(fmt::format(
@@ -2141,11 +2141,9 @@ void RenderableGaiaStars::update(const UpdateData&) {
}
_colorTextureFile = std::make_unique<ghoul::filesystem::File>(
_colorTexturePath
);
_colorTextureFile->setCallback(
[&](const ghoul::filesystem::File&) { _colorTextureIsDirty = true; }
_colorTexturePath.value()
);
_colorTextureFile->setCallback([this]() { _colorTextureIsDirty = true; });
}
_colorTextureIsDirty = false;
}
+1 -1
View File
@@ -29,8 +29,8 @@
#include <modules/gaia/rendering/octreemanager.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringlistproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/list/stringlistproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/scalar/intproperty.h>
+28 -17
View File
@@ -28,9 +28,9 @@
#include <openspace/documentation/verifier.h>
#include <ghoul/fmt.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/directory.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <filesystem>
#include <fstream>
#include <thread>
@@ -295,8 +295,10 @@ ConstructOctreeTask::ConstructOctreeTask(const ghoul::Dictionary& dictionary) {
}
std::string ConstructOctreeTask::description() {
return "Read bin file (or files in folder): " + _inFileOrFolderPath + "\n "
"and write octree data file (or files) into: " + _outFileOrFolderPath + "\n";
return fmt::format(
"Read bin file (or files in folder): {} and write octree data file (or files) "
"into: {}", _inFileOrFolderPath, _outFileOrFolderPath
);
}
void ConstructOctreeTask::perform(const Task::ProgressCallback& onProgress) {
@@ -323,7 +325,7 @@ void ConstructOctreeTask::constructOctreeFromSingleFile(
_octreeManager->initOctree(0, _maxDist, _maxStarsPerNode);
LINFO("Reading data file: " + _inFileOrFolderPath);
LINFO(fmt::format("Reading data file: {}", _inFileOrFolderPath));
LINFO(fmt::format(
"MAX DIST: {} - MAX STARS PER NODE: {}",
@@ -404,8 +406,7 @@ void ConstructOctreeTask::constructOctreeFromSingleFile(
}
else {
LERROR(fmt::format(
"Error opening file '{}' for loading preprocessed file!",
_inFileOrFolderPath
"Error opening file {} for loading preprocessed file", _inFileOrFolderPath
));
}
LINFO(fmt::format("{} of {} read stars were filtered", nFilteredStars, nTotalStars));
@@ -413,7 +414,7 @@ void ConstructOctreeTask::constructOctreeFromSingleFile(
// Slice LOD data before writing to files.
_octreeManager->sliceLodData();
LINFO("Writing octree to: " + _outFileOrFolderPath);
LINFO(fmt::format("Writing octree to: {}", _outFileOrFolderPath));
std::ofstream outFileStream(_outFileOrFolderPath, std::ofstream::binary);
if (outFileStream.good()) {
if (nValues == 0) {
@@ -425,7 +426,7 @@ void ConstructOctreeTask::constructOctreeFromSingleFile(
}
else {
LERROR(fmt::format(
"Error opening file: {} as output data file.", _outFileOrFolderPath
"Error opening file: {} as output data file", _outFileOrFolderPath
));
}
}
@@ -452,8 +453,16 @@ void ConstructOctreeTask::constructOctreeFromFolder(
//int starsOutside2000 = 0;
//int starsOutside5000 = 0;
ghoul::filesystem::Directory currentDir(_inFileOrFolderPath);
std::vector<std::string> allInputFiles = currentDir.readFiles();
std::vector<std::filesystem::path> allInputFiles;
if (std::filesystem::is_directory(_inFileOrFolderPath)) {
namespace fs = std::filesystem;
for (const fs::directory_entry& e : fs::directory_iterator(_inFileOrFolderPath)) {
if (!e.is_regular_file()) {
allInputFiles.push_back(e.path());
}
}
}
std::vector<float> filterValues;
auto writeThreads = std::vector<std::thread>(8);
@@ -467,10 +476,10 @@ void ConstructOctreeTask::constructOctreeFromFolder(
));
for (size_t idx = 0; idx < allInputFiles.size(); ++idx) {
std::string inFilePath = allInputFiles[idx];
std::filesystem::path inFilePath = allInputFiles[idx];
int nStarsInfile = 0;
LINFO("Reading data file: " + inFilePath);
LINFO(fmt::format("Reading data file: {}", inFilePath));
std::ifstream inFileStream(inFilePath, std::ifstream::binary);
if (inFileStream.good()) {
@@ -528,7 +537,7 @@ void ConstructOctreeTask::constructOctreeFromFolder(
}
else {
LERROR(fmt::format(
"Error opening file '{}' for loading preprocessed file!", inFilePath
"Error opening file {} for loading preprocessed file!", inFilePath
));
}
@@ -552,7 +561,7 @@ void ConstructOctreeTask::constructOctreeFromFolder(
std::thread t(
&OctreeManager::writeToMultipleFiles,
_indexOctreeManager,
_outFileOrFolderPath,
_outFileOrFolderPath.string(),
idx
);
writeThreads[idx] = std::move(t);
@@ -582,17 +591,19 @@ void ConstructOctreeTask::constructOctreeFromFolder(
// " - 5000kPc is " + std::to_string(starsOutside5000));
// Write index file of Octree structure.
std::string indexFileOutPath = _outFileOrFolderPath + "index.bin";
std::filesystem::path indexFileOutPath = fmt::format(
"{}/index.bin", _outFileOrFolderPath.string()
);
std::ofstream outFileStream(indexFileOutPath, std::ofstream::binary);
if (outFileStream.good()) {
LINFO("Writing index file!");
LINFO("Writing index file");
_indexOctreeManager->writeToFile(outFileStream, false);
outFileStream.close();
}
else {
LERROR(fmt::format(
"Error opening file: {} as index output file.", indexFileOutPath
"Error opening file: {} as index output file", indexFileOutPath
));
}
+3 -2
View File
@@ -29,6 +29,7 @@
#include <modules/gaia/rendering/octreeculler.h>
#include <modules/gaia/rendering/octreemanager.h>
#include <filesystem>
namespace openspace {
@@ -78,8 +79,8 @@ private:
*/
bool filterStar(const glm::vec2& range, float filterValue, float normValue = 0.f);
std::string _inFileOrFolderPath;
std::string _outFileOrFolderPath;
std::filesystem::path _inFileOrFolderPath;
std::filesystem::path _outFileOrFolderPath;
int _maxDist = 0;
int _maxStarsPerNode = 0;
bool _singleFileInput = false;
+18 -9
View File
@@ -30,10 +30,9 @@
#include <ghoul/misc/dictionary.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/directory.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/fmt.h>
#include <filesystem>
#include <fstream>
#include <set>
#include <optional>
@@ -132,7 +131,7 @@ void ReadFitsTask::readSingleFitsFile(const Task::ProgressCallback& progressCall
FitsFileReader fileReader(false);
std::vector<float> fullData = fileReader.readFitsFile(
_inFileOrFolderPath,
_inFileOrFolderPath.string(),
nValuesPerStar,
_firstRow,
_lastRow,
@@ -166,7 +165,7 @@ void ReadFitsTask::readSingleFitsFile(const Task::ProgressCallback& progressCall
}
else {
LERROR(fmt::format(
"Error opening file: {} as output data file.", _outFileOrFolderPath
"Error opening file: {} as output data file", _outFileOrFolderPath
));
}
}
@@ -185,8 +184,16 @@ void ReadFitsTask::readAllFitsFilesFromFolder(const Task::ProgressCallback&) {
ConcurrentJobManager<std::vector<std::vector<float>>> jobManager(threadPool);
// Get all files in specified folder.
ghoul::filesystem::Directory currentDir(_inFileOrFolderPath);
std::vector<std::string> allInputFiles = currentDir.readFiles();
std::vector<std::filesystem::path> allInputFiles;
if (std::filesystem::is_directory(_inFileOrFolderPath)) {
namespace fs = std::filesystem;
for (const fs::directory_entry& e : fs::directory_iterator(_inFileOrFolderPath)) {
if (e.is_regular_file()) {
allInputFiles.push_back(e.path());
}
}
}
size_t nInputFiles = allInputFiles.size();
LINFO("Files to read: " + std::to_string(nInputFiles));
@@ -238,12 +245,12 @@ void ReadFitsTask::readAllFitsFilesFromFolder(const Task::ProgressCallback&) {
// Divide all files into ReadFilejobs and then delegate them onto several threads!
while (!allInputFiles.empty()) {
std::string fileToRead = allInputFiles.back();
std::filesystem::path fileToRead = allInputFiles.back();
allInputFiles.erase(allInputFiles.end() - 1);
// Add reading of file to jobmanager, which will distribute it to our threadpool.
auto readFileJob = std::make_shared<gaia::ReadFileJob>(
fileToRead,
fileToRead.string(),
_allColumnNames,
_firstRow,
_lastRow,
@@ -294,7 +301,9 @@ void ReadFitsTask::readAllFitsFilesFromFolder(const Task::ProgressCallback&) {
int ReadFitsTask::writeOctantToFile(const std::vector<float>& octantData, int index,
std::vector<bool>& isFirstWrite, int nValuesPerStar)
{
std::string outPath = fmt::format("{}octant_{}.bin", _outFileOrFolderPath, index);
std::string outPath = fmt::format(
"{}octant_{}.bin", _outFileOrFolderPath.string(), index
);
std::ofstream fileStream(outPath, std::ofstream::binary | std::ofstream::app);
if (fileStream.good()) {
int32_t nValues = static_cast<int32_t>(octantData.size());
+4 -2
View File
@@ -26,9 +26,11 @@
#define __OPENSPACE_MODULE_GAIA___READFITSTASK___H__
#include <openspace/util/task.h>
#include <openspace/util/threadpool.h>
#include <openspace/util/concurrentjobmanager.h>
#include <modules/fitsfilereader/include/fitsfilereader.h>
#include <filesystem>
namespace openspace {
@@ -67,8 +69,8 @@ private:
int writeOctantToFile(const std::vector<float>& data, int index,
std::vector<bool>& isFirstWrite, int nValuesPerStar);
std::string _inFileOrFolderPath;
std::string _outFileOrFolderPath;
std::filesystem::path _inFileOrFolderPath;
std::filesystem::path _outFileOrFolderPath;
bool _singleFileProcess = false;
size_t _threadsToUse = 1;
int _firstRow = 0;
+4 -1
View File
@@ -72,7 +72,10 @@ void ReadSpeckTask::perform(const Task::ProgressCallback& onProgress) {
int32_t nRenderValues = 0;
FitsFileReader fileReader(false);
std::vector<float> fullData = fileReader.readSpeckFile(_inFilePath, nRenderValues);
std::vector<float> fullData = fileReader.readSpeckFile(
_inFilePath.string(),
nRenderValues
);
onProgress(0.9f);
+3 -2
View File
@@ -27,6 +27,7 @@
#include <openspace/util/task.h>
#include <filesystem>
#include <string>
namespace openspace {
@@ -43,8 +44,8 @@ public:
static documentation::Documentation Documentation();
private:
std::string _inFilePath;
std::string _outFilePath;
std::filesystem::path _inFilePath;
std::filesystem::path _outFilePath;
};
} // namespace openspace
+118 -194
View File
@@ -27,6 +27,7 @@
#include <modules/galaxy/rendering/galaxyraycaster.h>
#include <modules/volume/rawvolume.h>
#include <modules/volume/rawvolumereader.h>
#include <openspace/documentation/documentation.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/raycastermanager.h>
#include <openspace/rendering/renderable.h>
@@ -47,7 +48,9 @@
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <glm/gtc/matrix_transform.hpp>
#include <filesystem>
#include <fstream>
#include <optional>
namespace {
constexpr int8_t CurrentCacheVersion = 1;
@@ -94,12 +97,6 @@ namespace {
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo TranslationInfo = {
"Translation",
"Translation",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo RotationInfo = {
"Rotation",
"Euler rotation",
@@ -134,6 +131,57 @@ namespace {
"This value set the number of integration steps during the raycasting procedure."
};
struct [[codegen::Dictionary(RenderableGalaxy)]] Parameters {
// [[codegen::verbatim(VolumeRenderingEnabledInfo.description)]]
std::optional<bool> volumeRenderingEnabled;
// [[codegen::verbatim(StarRenderingEnabledInfo.description)]]
std::optional<bool> starRenderingEnabled;
// [[codegen::verbatim(StepSizeInfo.description)]]
std::optional<float> stepSizeInfo;
// [[codegen::verbatim(AbsorptionMultiplyInfo.description)]]
std::optional<float> absorptionMultiply;
// [[codegen::verbatim(EmissionMultiplyInfo.description)]]
std::optional<float> emissionMultiply;
enum class StarRenderingMethod {
Points,
Billboards
};
// [[codegen::verbatim(StarRenderingMethodInfo.description)]]
std::optional<StarRenderingMethod> starRenderingMethod;
// [[codegen::verbatim(RotationInfo.description)]]
std::optional<glm::vec3> rotation;
struct Volume {
std::filesystem::path filename;
glm::ivec3 dimensions;
glm::vec3 size;
// [[codegen::verbatim(NumberOfRayCastingStepsInfo.description)]]
std::optional<float> steps;
// [[codegen::verbatim(DownscaleVolumeRenderingInfo.description)]]
std::optional<float> downscale;
};
Volume volume;
struct Points {
std::filesystem::path filename;
std::filesystem::path texture;
// [[codegen::verbatim(EnabledPointsRatioInfo.description)]]
std::optional<float> enabledPointsRatio;
};
Points points;
};
#include "renderablegalaxy_codegen.cpp"
void saveCachedFile(const std::string& file, const std::vector<glm::vec3>& positions,
const std::vector<glm::vec3>& colors, int64_t nPoints,
float pointsRatio)
@@ -165,6 +213,12 @@ namespace {
);
}
float safeLength(const glm::vec3& vector) {
const float maxComponent = std::max(
std::max(std::abs(vector.x), std::abs(vector.y)), std::abs(vector.z)
);
return glm::length(vector / maxComponent) * maxComponent;
}
} // namespace
namespace openspace {
@@ -181,7 +235,6 @@ RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary)
properties::OptionProperty::DisplayType::Dropdown
)
, _enabledPointsRatio(EnabledPointsRatioInfo, 0.5f, 0.01f, 1.0f)
, _translation(TranslationInfo, glm::vec3(0.f), glm::vec3(0.f), glm::vec3(1.f))
, _rotation(
RotationInfo,
glm::vec3(0.f),
@@ -191,138 +244,44 @@ RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary)
, _downScaleVolumeRendering(DownscaleVolumeRenderingInfo, 1.f, 0.1f, 1.f)
, _numberOfRayCastingSteps(NumberOfRayCastingStepsInfo, 1000.f, 1.f, 1000.f)
{
if (dictionary.hasKey("VolumeRenderingEnabled")) {
_volumeRenderingEnabled = dictionary.value<bool>("VolumeRenderingEnabled");
}
if (dictionary.hasKey("StarRenderingEnabled")) {
_starRenderingEnabled = dictionary.value<bool>("StarRenderingEnabled");
}
const Parameters p = codegen::bake<Parameters>(dictionary);
if (dictionary.hasKey("StarRenderingMethod")) {
_starRenderingMethod = dictionary.value<int>("StarRenderingMethod");
}
if (dictionary.hasValue<bool>(VolumeRenderingEnabledInfo.identifier)) {
_volumeRenderingEnabled = dictionary.value<bool>(
VolumeRenderingEnabledInfo.identifier
);
}
if (dictionary.hasValue<bool>(StarRenderingEnabledInfo.identifier)) {
_starRenderingEnabled = static_cast<bool>(StarRenderingEnabledInfo.identifier);
}
if (dictionary.hasValue<double>(StepSizeInfo.identifier)) {
_stepSize = static_cast<float>(dictionary.value<double>(StepSizeInfo.identifier));
}
if (dictionary.hasValue<double>(AbsorptionMultiplyInfo.identifier)) {
_absorptionMultiply = static_cast<float>(
dictionary.value<double>(AbsorptionMultiplyInfo.identifier)
);
}
if (dictionary.hasValue<double>(EmissionMultiplyInfo.identifier)) {
_emissionMultiply = static_cast<float>(
dictionary.value<double>(EmissionMultiplyInfo.identifier)
);
}
_volumeRenderingEnabled = p.volumeRenderingEnabled.value_or(_volumeRenderingEnabled);
_starRenderingEnabled = p.starRenderingEnabled.value_or(_starRenderingEnabled);
_volumeRenderingEnabled = p.volumeRenderingEnabled.value_or(_volumeRenderingEnabled);
_stepSize = p.stepSizeInfo.value_or(_stepSize);
_absorptionMultiply = p.absorptionMultiply.value_or(_absorptionMultiply);
_emissionMultiply = p.emissionMultiply.value_or(_emissionMultiply);
_starRenderingMethod.addOptions({
{ 0, "Points" },
{ 1, "Billboards" }
});
if (dictionary.hasKey(StarRenderingMethodInfo.identifier)) {
const std::string starRenderingMethod = dictionary.value<std::string>(
StarRenderingMethodInfo.identifier
);
if (starRenderingMethod == "Points") {
_starRenderingMethod = 0;
}
else if (starRenderingMethod == "Billboards") {
_starRenderingMethod = 1;
if (p.starRenderingMethod.has_value()) {
switch (*p.starRenderingMethod) {
case Parameters::StarRenderingMethod::Points:
_starRenderingMethod = 0;
break;
case Parameters::StarRenderingMethod::Billboards:
_starRenderingMethod = 1;
break;
}
}
if (dictionary.hasValue<glm::dvec3>(TranslationInfo.identifier)) {
_translation = dictionary.value<glm::dvec3>(TranslationInfo.identifier);
}
_rotation = p.rotation.value_or(_rotation);
if (dictionary.hasValue<glm::dvec3>(RotationInfo.identifier)) {
_rotation = dictionary.value<glm::dvec3>(RotationInfo.identifier);
}
_volumeFilename = p.volume.filename.string();
_volumeDimensions = p.volume.dimensions;
_volumeSize = p.volume.size;
_numberOfRayCastingSteps = p.volume.steps.value_or(_numberOfRayCastingSteps);
_downScaleVolumeRendering = p.volume.downscale.value_or(_downScaleVolumeRendering);
if (!dictionary.hasValue<ghoul::Dictionary>("Volume")) {
LERROR("No volume dictionary specified.");
}
ghoul::Dictionary volumeDictionary = dictionary.value<ghoul::Dictionary>("Volume");
if (volumeDictionary.hasValue<std::string>("Filename")) {
_volumeFilename = absPath(volumeDictionary.value<std::string>("Filename"));
}
else {
LERROR("No volume filename specified");
}
if (volumeDictionary.hasValue<glm::dvec3>("Dimensions")) {
_volumeDimensions = volumeDictionary.value<glm::dvec3>("Dimensions");
}
else {
LERROR("No volume dimensions specifieds");
}
if (volumeDictionary.hasValue<glm::dvec3>("Size")) {
_volumeSize = volumeDictionary.value<glm::dvec3>("Size");
}
else {
LERROR("No volume dimensions specified.");
}
if (volumeDictionary.hasKey(NumberOfRayCastingStepsInfo.identifier)) {
_numberOfRayCastingSteps = static_cast<float>(
volumeDictionary.value<double>(NumberOfRayCastingStepsInfo.identifier)
);
}
else {
LINFO("Number of raycasting steps not specified. Using default value.");
}
_downScaleVolumeRendering.setVisibility(properties::Property::Visibility::Developer);
if (volumeDictionary.hasKey(DownscaleVolumeRenderingInfo.identifier)) {
_downScaleVolumeRendering = static_cast<float>(
volumeDictionary.value<double>(DownscaleVolumeRenderingInfo.identifier)
);
}
if (!dictionary.hasValue<ghoul::Dictionary>("Points")) {
LERROR("No points dictionary specified.");
}
ghoul::Dictionary pointsDictionary = dictionary.value<ghoul::Dictionary>("Points");
if (pointsDictionary.hasValue<std::string>("Filename")) {
_pointsFilename = absPath(pointsDictionary.value<std::string>("Filename"));
}
else {
LERROR("No points filename specified.");
}
if (pointsDictionary.hasValue<double>(EnabledPointsRatioInfo.identifier)) {
_enabledPointsRatio = static_cast<float>(
pointsDictionary.value<double>(EnabledPointsRatioInfo.identifier)
);
}
if (pointsDictionary.hasValue<std::string>("Texture")) {
_pointSpreadFunctionTexturePath =
absPath(pointsDictionary.value<std::string>("Texture"));
_pointSpreadFunctionFile = std::make_unique<ghoul::filesystem::File>(
_pointSpreadFunctionTexturePath
);
}
else {
LERROR("No points filename specified.");
}
_pointsFilename = p.points.filename.string();
_enabledPointsRatio = p.points.enabledPointsRatio.value_or(_enabledPointsRatio);
_pointSpreadFunctionTexturePath = p.points.texture.string();
_pointSpreadFunctionFile = std::make_unique<ghoul::filesystem::File>(
_pointSpreadFunctionTexturePath
);
auto onChange = [&](bool enabled) {
if (enabled) {
@@ -342,8 +301,8 @@ RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary)
addProperty(_emissionMultiply);
addProperty(_starRenderingMethod);
addProperty(_enabledPointsRatio);
addProperty(_translation);
addProperty(_rotation);
_downScaleVolumeRendering.setVisibility(properties::Property::Visibility::Developer);
addProperty(_downScaleVolumeRendering);
addProperty(_numberOfRayCastingSteps);
}
@@ -352,8 +311,8 @@ void RenderableGalaxy::initialize() {
ZoneScoped
// Aspect is currently hardcoded to cubic voxels.
_aspect = static_cast<glm::vec3>(_volumeDimensions);
_aspect /= std::max(std::max(_aspect.x, _aspect.y), _aspect.z);
glm::vec3 d = _volumeDimensions;
_aspect = d / glm::compMax(d);
// The volume
volume::RawVolumeReader<glm::tvec4<GLubyte>> reader(
@@ -363,10 +322,9 @@ void RenderableGalaxy::initialize() {
_volume = reader.read();
std::string cachedPointsFile = FileSys.cacheManager()->cachedFilename(
_pointsFilename,
ghoul::filesystem::CacheManager::Persistent::Yes
_pointsFilename
);
const bool hasCachedFile = FileSys.fileExists(cachedPointsFile);
const bool hasCachedFile = std::filesystem::is_regular_file(cachedPointsFile);
if (hasCachedFile) {
LINFO(fmt::format("Cached file '{}' used for galaxy point file '{}'",
cachedPointsFile, _pointsFilename
@@ -379,7 +337,7 @@ void RenderableGalaxy::initialize() {
}
else {
FileSys.cacheManager()->removeCacheFile(_pointsFilename);
Result resPoint = loadPointFile(_pointsFilename);
Result resPoint = loadPointFile();
_pointPositionsCache = std::move(resPoint.positions);
_pointColorsCache = std::move(resPoint.color);
saveCachedFile(
@@ -392,7 +350,7 @@ void RenderableGalaxy::initialize() {
}
}
else {
Result res = loadPointFile(_pointsFilename);
Result res = loadPointFile();
ghoul_assert(res.success, "Point file loading failed");
_pointPositionsCache = std::move(res.positions);
_pointColorsCache = std::move(res.color);
@@ -455,13 +413,12 @@ void RenderableGalaxy::initializeGL() {
if (!_pointSpreadFunctionTexturePath.empty()) {
_pointSpreadFunctionTexture = ghoul::io::TextureReader::ref().loadTexture(
absPath(_pointSpreadFunctionTexturePath)
absPath(_pointSpreadFunctionTexturePath).string()
);
if (_pointSpreadFunctionTexture) {
LDEBUG(fmt::format(
"Loaded texture from '{}'",
absPath(_pointSpreadFunctionTexturePath)
"Loaded texture from {}", absPath(_pointSpreadFunctionTexturePath)
));
_pointSpreadFunctionTexture->uploadTexture();
}
@@ -485,42 +442,33 @@ void RenderableGalaxy::initializeGL() {
UniformNamesBillboards
);
_pointsProgram->setIgnoreUniformLocationError(
ghoul::opengl::ProgramObject::IgnoreError::Yes
);
GLint positionAttrib = _pointsProgram->attributeLocation("in_position");
GLint colorAttrib = _pointsProgram->attributeLocation("in_color");
glGenVertexArrays(1, &_pointsVao);
glGenBuffers(1, &_positionVbo);
glGenBuffers(1, &_colorVbo);
glBindVertexArray(_pointsVao);
glBindBuffer(GL_ARRAY_BUFFER, _positionVbo);
glBufferData(GL_ARRAY_BUFFER,
glBufferData(
GL_ARRAY_BUFFER,
_pointPositionsCache.size() * sizeof(glm::vec3),
_pointPositionsCache.data(),
GL_STATIC_DRAW
);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
_pointPositionsCache.clear();
glBindBuffer(GL_ARRAY_BUFFER, _colorVbo);
glBufferData(GL_ARRAY_BUFFER,
glBufferData(
GL_ARRAY_BUFFER,
_pointColorsCache.size() * sizeof(glm::vec3),
_pointColorsCache.data(),
GL_STATIC_DRAW
);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
_pointColorsCache.clear();
glBindBuffer(GL_ARRAY_BUFFER, _positionVbo);
glEnableVertexAttribArray(positionAttrib);
glVertexAttribPointer(positionAttrib, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, _colorVbo);
glEnableVertexAttribArray(colorAttrib);
glVertexAttribPointer(colorAttrib, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
@@ -544,7 +492,6 @@ void RenderableGalaxy::update(const UpdateData& data) {
if (!_raycaster) {
return;
}
//glm::mat4 transform = glm::translate(, static_cast<glm::vec3>(_translation));
const glm::vec3 eulerRotation = static_cast<glm::vec3>(_rotation);
glm::mat4 transform = glm::rotate(
glm::mat4(1.f),
@@ -556,14 +503,6 @@ void RenderableGalaxy::update(const UpdateData& data) {
glm::mat4 volumeTransform = glm::scale(transform, _volumeSize);
_pointTransform = transform;
//_pointTransform = glm::scale(transform, _pointScaling);
const glm::vec4 translation = glm::vec4(_translation.value()*_volumeSize, 0.f);
// Todo: handle floating point overflow, to actually support translation.
volumeTransform[3] += translation;
_pointTransform[3] += translation;
_raycaster->setDownscaleRender(_downScaleVolumeRendering);
_raycaster->setMaxSteps(static_cast<int>(_numberOfRayCastingSteps));
@@ -584,7 +523,7 @@ void RenderableGalaxy::render(const RenderData& data, RendererTasks& tasks) {
const float length = safeLength(position);
const glm::vec3 galaxySize = _volumeSize;
const float maxDim = std::max(std::max(galaxySize.x, galaxySize.y), galaxySize.z);
const float maxDim = glm::compMax(galaxySize);
const float lowerRampStart = maxDim * 0.01f;
const float lowerRampEnd = maxDim * 0.1f;
@@ -605,7 +544,7 @@ void RenderableGalaxy::render(const RenderData& data, RendererTasks& tasks) {
}
else if (length < upperRampEnd) {
opacityCoefficient = 1.f - (length - upperRampStart) /
(upperRampEnd - upperRampStart); //fade out
(upperRampEnd - upperRampStart); // fade out
}
else {
opacityCoefficient = 0;
@@ -622,22 +561,23 @@ void RenderableGalaxy::render(const RenderData& data, RendererTasks& tasks) {
}
}
// Render the stars
if (_starRenderingEnabled && _opacityCoefficient > 0.f) {
if (_starRenderingMethod == 1) {
if (!(_starRenderingEnabled && _opacityCoefficient > 0.f)) {
return;
}
if (_starRenderingMethod == 1) {
if (_billboardsProgram) {
renderBillboards(data);
}
else {
}
else {
if (_pointsProgram) {
renderPoints(data);
}
}
}
void RenderableGalaxy::renderPoints(const RenderData& data) {
if (!_pointsProgram) {
return;
}
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glDepthMask(false);
glDisable(GL_DEPTH_TEST);
@@ -680,7 +620,6 @@ void RenderableGalaxy::renderPoints(const RenderData& data) {
glBindVertexArray(_pointsVao);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_nPoints * _enabledPointsRatio));
glBindVertexArray(0);
_pointsProgram->deactivate();
@@ -691,10 +630,6 @@ void RenderableGalaxy::renderPoints(const RenderData& data) {
}
void RenderableGalaxy::renderBillboards(const RenderData& data) {
if (!_billboardsProgram) {
return;
}
// Change OpenGL Blending and Depth states
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glDepthMask(false);
@@ -742,7 +677,6 @@ void RenderableGalaxy::renderBillboards(const RenderData& data) {
glBindVertexArray(_pointsVao);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_nPoints * _enabledPointsRatio));
glBindVertexArray(0);
_billboardsProgram->deactivate();
@@ -752,14 +686,7 @@ void RenderableGalaxy::renderBillboards(const RenderData& data) {
global::renderEngine->openglStateCache().resetDepthState();
}
float RenderableGalaxy::safeLength(const glm::vec3& vector) const {
const float maxComponent = std::max(
std::max(std::abs(vector.x), std::abs(vector.y)), std::abs(vector.z)
);
return glm::length(vector / maxComponent) * maxComponent;
}
RenderableGalaxy::Result RenderableGalaxy::loadPointFile(const std::string&) {
RenderableGalaxy::Result RenderableGalaxy::loadPointFile() {
std::vector<glm::vec3> pointPositions;
std::vector<glm::vec3> pointColors;
int64_t nPoints;
@@ -779,11 +706,11 @@ RenderableGalaxy::Result RenderableGalaxy::loadPointFile(const std::string&) {
_nPoints = static_cast<size_t>(nPoints);
// Read points
float x, y, z, r, g, b, a;
for (size_t i = 0;
i < static_cast<size_t>(_nPoints * _enabledPointsRatio.maxValue()) + 1;
++i)
{
float x, y, z, r, g, b, a;
std::getline(pointFile, line);
std::istringstream issp(line);
issp >> x >> y >> z >> r >> g >> b >> a;
@@ -838,10 +765,7 @@ RenderableGalaxy::Result RenderableGalaxy::loadCachedFile(const std::string& fil
fileStream.read(reinterpret_cast<char*>(&nColors), sizeof(uint64_t));
std::vector<glm::vec3> colors;
colors.resize(nColors);
fileStream.read(
reinterpret_cast<char*>(colors.data()),
nColors * sizeof(glm::vec3)
);
fileStream.read(reinterpret_cast<char*>(colors.data()), nColors * sizeof(glm::vec3));
Result result;
result.success = true;
+1 -3
View File
@@ -57,14 +57,13 @@ public:
private:
void renderPoints(const RenderData& data);
void renderBillboards(const RenderData& data);
float safeLength(const glm::vec3& vector) const;
struct Result {
bool success;
std::vector<glm::vec3> positions;
std::vector<glm::vec3> color;
};
Result loadPointFile(const std::string& file);
Result loadPointFile();
Result loadCachedFile(const std::string& file);
glm::vec3 _volumeSize = glm::vec3(0.f);
@@ -76,7 +75,6 @@ private:
properties::FloatProperty _emissionMultiply;
properties::OptionProperty _starRenderingMethod;
properties::FloatProperty _enabledPointsRatio;
properties::Vec3Property _translation;
properties::Vec3Property _rotation;
properties::FloatProperty _downScaleVolumeRendering;
properties::FloatProperty _numberOfRayCastingSteps;
+4 -8
View File
@@ -29,17 +29,13 @@ uniform float absorptionMultiply#{id} = 50.0;
uniform float emissionMultiply#{id} = 1500.0;
uniform sampler3D galaxyTexture#{id};
void sample#{id}(
vec3 samplePos,
vec3 dir,
inout vec3 accumulatedColor,
inout vec3 accumulatedAlpha,
inout float stepSize
) {
void sample#{id}(vec3 samplePos, vec3 dir, inout vec3 accumulatedColor,
inout vec3 accumulatedAlpha, inout float stepSize)
{
vec3 aspect = aspect#{id};
stepSize = maxStepSize#{id} / length(dir / aspect);
//Early ray termination on black parts of the data
// Early ray termination on black parts of the data
vec3 normalizedPos = samplePos * 2.f - 1.f;
if (normalizedPos.x * normalizedPos.x + normalizedPos.y * normalizedPos.y > 0.7) {
return;
+5 -1
View File
@@ -37,7 +37,11 @@ Fragment getFragment() {
float multipliedOpacityCoefficient = opacityCoefficient*opacityCoefficient;
vec3 extinction = exp(vec3(0.6, 0.2, 0.3) - vs_color);
vec4 fullColor = vec4(vs_color*extinction*vs_starBrightness*multipliedOpacityCoefficient, opacityCoefficient);
// We use the star brightness as the alpha value here to dim the stars as the camera
// moves further away. Otherwise they would occlude the main milkway image even though
// they themselves nolonger have any color contribution left
vec4 fullColor = vec4(vs_color*extinction*vs_starBrightness*multipliedOpacityCoefficient, vs_starBrightness);
frag.color = fullColor;
frag.depth = vs_screenSpaceDepth;
@@ -453,6 +453,7 @@ std::vector<documentation::Documentation> GlobeBrowsingModule::documentations()
globebrowsing::Layer::Documentation(),
globebrowsing::LayerAdjustment::Documentation(),
globebrowsing::LayerManager::Documentation(),
globebrowsing::GlobeTranslation::Documentation(),
GlobeLabelsComponent::Documentation(),
RingsComponent::Documentation(),
ShadowComponent::Documentation()
+5 -2
View File
@@ -105,8 +105,11 @@ GdalWrapper::GdalWrapper(size_t maximumCacheSize, size_t maximumMaximumCacheSize
addProperty(_gdalMaximumCacheSize);
GDALAllRegister();
CPLSetConfigOption("GDAL_DATA", absPath("${MODULE_GLOBEBROWSING}/gdal_data").c_str());
CPLSetConfigOption("CPL_TMPDIR", absPath("${BASE}").c_str());
CPLSetConfigOption(
"GDAL_DATA",
absPath("${MODULE_GLOBEBROWSING}/gdal_data").string().c_str()
);
CPLSetConfigOption("CPL_TMPDIR", absPath("${BASE}").string().c_str());
CPLSetConfigOption("GDAL_HTTP_UNSAFESSL", "YES");
CPLSetConfigOption("GDAL_HTTP_TIMEOUT", "3"); // 3 seconds
@@ -193,7 +193,7 @@ namespace {
std::optional<glm::vec3> labelsColor [[codegen::color()]];
// [[codegen::verbatim(LabelsOpacityInfo.description)]]
std::optional<float> labelsOpacity [[codegen::inrange(0.f, 1.0)]];
std::optional<float> labelsOpacity [[codegen::inrange(0.f, 1.f)]];
// [[codegen::verbatim(LabelsFadeInStartingDistanceInfo.description)]]
std::optional<float> fadeInStartingDistance;
@@ -289,7 +289,7 @@ void GlobeLabelsComponent::initialize(const ghoul::Dictionary& dictionary,
return;
}
const bool loadSuccess = loadLabelsData(absPath(p.fileName->string()));
const bool loadSuccess = loadLabelsData(absPath(p.fileName->string()).string());
if (!loadSuccess) {
return;
}
@@ -338,12 +338,11 @@ void GlobeLabelsComponent::initializeFonts() {
bool GlobeLabelsComponent::loadLabelsData(const std::string& file) {
std::string cachedFile = FileSys.cacheManager()->cachedFilename(
ghoul::filesystem::File(file),
"GlobeLabelsComponent|" + identifier(),
ghoul::filesystem::CacheManager::Persistent::Yes
file,
"GlobeLabelsComponent|" + identifier()
);
bool hasCachedFile = FileSys.fileExists(cachedFile);
bool hasCachedFile = std::filesystem::is_regular_file(cachedFile);
if (hasCachedFile) {
LINFO(fmt::format("Cached file '{}' used for labels file: {}", cachedFile, file));
@@ -476,7 +475,9 @@ bool GlobeLabelsComponent::loadCachedFile(const std::string& file) {
if (version != CurrentCacheVersion) {
LINFO("The format of the cached file has changed: deleting old cache");
fileStream.close();
FileSys.deleteFile(file);
if (std::filesystem::is_regular_file(file)) {
std::filesystem::remove(file);
}
return false;
}
@@ -498,8 +499,7 @@ bool GlobeLabelsComponent::saveCachedFile(const std::string& file) const {
LERROR(fmt::format("Error opening file '{}' for save cache file", file));
return false;
}
fileStream.write(reinterpret_cast<const char*>(&CurrentCacheVersion),
sizeof(int8_t));
fileStream.write(reinterpret_cast<const char*>(&CurrentCacheVersion), sizeof(int8_t));
int32_t nValues = static_cast<int32_t>(_labels.labelsArray.size());
if (nValues == 0) {
@@ -525,11 +525,10 @@ void GlobeLabelsComponent::draw(const RenderData& data) {
viewTransform;
glm::dmat4 mvp = vp * _globe->modelTransform();
glm::dvec3 globePositionWorld = glm::dvec3(_globe->modelTransform() *
glm::vec4(0.f, 0.f, 0.f, 1.f));
glm::dvec3 cameraToGlobeDistanceWorld = globePositionWorld -
data.camera.positionVec3();
double distanceCameraGlobeWorld = glm::length(cameraToGlobeDistanceWorld);
glm::dvec3 globePosWorld =
glm::dvec3(_globe->modelTransform() * glm::vec4(0.f, 0.f, 0.f, 1.f));
glm::dvec3 camToGlobeDistanceWorld = globePosWorld - data.camera.positionVec3();
double distanceCameraGlobeWorld = glm::length(camToGlobeDistanceWorld);
float varyingOpacity = 1.f;
@@ -570,8 +569,7 @@ void GlobeLabelsComponent::draw(const RenderData& data) {
void GlobeLabelsComponent::renderLabels(const RenderData& data,
const glm::dmat4& modelViewProjectionMatrix,
float distToCamera,
float fadeInVariable
float distToCamera, float fadeInVariable
) {
glm::vec4 textColor = glm::vec4(
glm::vec3(_labelsColor),
@@ -736,10 +734,6 @@ bool GlobeLabelsComponent::isLabelInFrustum(const glm::dmat4& MVMatrix,
else if ((glm::dot(nearNormal, position) + nearDistance) < -Radius) {
return false;
}
// The far plane testing is disabled because the atm has no depth.
/*else if ((glm::dot(farNormal, position) + farDistance) < -Radius) {
return false;
}*/
return true;
}
@@ -68,7 +68,6 @@ private:
float distToCamera, float fadeInVariable);
bool isLabelInFrustum(const glm::dmat4& MVMatrix, const glm::dvec3& position) const;
private:
// Labels Structures
struct LabelEntry {
char feature[256];
@@ -99,14 +98,13 @@ private:
properties::FloatProperty _labelsDistanceEPS;
properties::OptionProperty _labelAlignmentOption;
private:
Labels _labels;
// Font
std::shared_ptr<ghoul::fontrendering::Font> _font;
// Globe
globebrowsing::RenderableGlobe* _globe;
globebrowsing::RenderableGlobe* _globe = nullptr;
};
} // namespace openspace
@@ -34,6 +34,7 @@
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/exception.h>
#include <ghoul/misc/profiling.h>
#include <filesystem>
#ifdef _MSC_VER
#pragma warning (push)
@@ -450,7 +451,7 @@ void RawTileDataReader::initialize() {
if (module.isWMSCachingEnabled()) {
ZoneScopedN("WMS Caching")
std::string c;
if (FileSys.fileExists(_datasetFilePath)) {
if (std::filesystem::is_regular_file(_datasetFilePath)) {
// Only replace the 'content' if the dataset is an XML file and we want to do
// caching
std::ifstream t(_datasetFilePath);
@@ -486,7 +487,7 @@ void RawTileDataReader::initialize() {
CPLCreateXMLElementAndValue(
cache,
"Path",
absPath(module.wmsCacheLocation()).c_str()
absPath(module.wmsCacheLocation()).string().c_str()
);
CPLCreateXMLElementAndValue(cache, "Depth", "4");
CPLCreateXMLElementAndValue(cache, "Expires", "315576000"); // 10 years
+69 -124
View File
@@ -34,6 +34,7 @@
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/interaction/sessionrecording.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scene/scene.h>
@@ -107,62 +108,57 @@ namespace {
constexpr openspace::properties::Property::PropertyInfo ShowChunkEdgeInfo = {
"ShowChunkEdges",
"Show chunk edges",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo ShowChunkBoundsInfo = {
"ShowChunkBounds",
"Show chunk bounds",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo HeightResolutionInfo = {
"ShowHeightResolution",
"Show height resolution",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo HeightIntensityInfo = {
"ShowHeightIntensities",
"Show height intensities",
"" // @TODO Missing documentation
"If this value is set to 'true', the borders between chunks are shown using a "
"red highlight"
};
constexpr openspace::properties::Property::PropertyInfo LevelProjectedAreaInfo = {
"LevelByProjectedAreaElseDistance",
"Level by projected area (else distance)",
"" // @TODO Missing documentation
"If this value is set to 'true', the tile level is determined by the area "
"projected on screen. If it is 'false', the distance to the center of the tile "
"is used instead."
};
constexpr openspace::properties::Property::PropertyInfo ResetTileProviderInfo = {
"ResetTileProviders",
"Reset tile providers",
"" // @TODO Missing documentation
"If this property is triggered, all tile provides for the globe are reset and "
"data is reloaded from scratch."
};
constexpr openspace::properties::Property::PropertyInfo ModelSpaceRenderingInfo = {
"ModelSpaceRenderingCutoffLevel",
"Model Space Rendering Cutoff Level",
"" // @TODO Missing documentation
"This value determines the tile level that is used as the cut off between "
"rendering tiles using the globe model rendering vs the flat in-game rendering "
"method. This value is a tradeoff between not having precision errors in the "
"rendering and represting a tile as flat or curved."
};
constexpr openspace::properties::Property::PropertyInfo DynamicLodIterationCountInfo =
{
"DynamicLodIterationCount",
"Data availability checks before LOD factor impact",
"" // @TODO Missing documentation
"The number of checks that have to fail/succeed in a row before the dynamic "
"level-of-detail adjusts the actual level-of-detail up or down during a session "
"recording"
};
constexpr openspace::properties::Property::PropertyInfo PerformShadingInfo = {
"PerformShading",
"Perform shading",
"" // @TODO Missing documentation
"This value determines whether there should be lighting applied to the surface "
"of the globe. Note that if there is an atmosphere attached to the planet, there "
"is a separate setting to control the shadowing induced by the atmosphere part."
};
constexpr openspace::properties::Property::PropertyInfo AccurateNormalsInfo = {
"UseAccurateNormals",
"Use Accurate Normals",
"" // @TODO Missing documentation
"This value determines whether higher-accuracy normals should be used in the "
"rendering. These normals are calculated based on the height field information "
"and are thus only available if the planet has a height map"
};
constexpr openspace::properties::Property::PropertyInfo EclipseInfo = {
@@ -200,25 +196,22 @@ namespace {
constexpr openspace::properties::Property::PropertyInfo TargetLodScaleFactorInfo = {
"TargetLodScaleFactor",
"Target Level of Detail Scale Factor",
"" // @TODO Missing documentation
"Determines the targeted level-of-detail of the tiles for this globe. A higher "
"value means that the tiles rendered are a higher resolution for the same "
"distance of the camera to the planet."
};
constexpr openspace::properties::Property::PropertyInfo CurrentLodScaleFactorInfo = {
"CurrentLodScaleFactor",
"Current Level of Detail Scale Factor (Read Only)",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo CameraMinHeightInfo = {
"CameraMinHeight",
"Camera Minimum Height",
"" // @TODO Missing documentation
"The currently used scale factor whose target value is deteremined by "
"'TargetLodScaleFactor'."
};
constexpr openspace::properties::Property::PropertyInfo OrenNayarRoughnessInfo = {
"OrenNayarRoughness",
"orenNayarRoughness",
"" // @TODO Missing documentation
"The roughness factor that is used for the Oren-Nayar lighting mode"
};
constexpr openspace::properties::Property::PropertyInfo NActiveLayersInfo = {
@@ -506,9 +499,6 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _debugProperties({
BoolProperty(ShowChunkEdgeInfo, false),
BoolProperty(ShowChunkBoundsInfo, false),
BoolProperty(HeightResolutionInfo, false),
BoolProperty(HeightIntensityInfo, false),
BoolProperty(LevelProjectedAreaInfo, true),
BoolProperty(ResetTileProviderInfo, false),
IntProperty(ModelSpaceRenderingInfo, 14, 1, 22),
@@ -524,7 +514,6 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
IntProperty(NumberShadowSamplesInfo, 5, 1, 7),
FloatProperty(TargetLodScaleFactorInfo, 15.f, 1.f, 50.f),
FloatProperty(CurrentLodScaleFactorInfo, 15.f, 1.f, 50.f),
FloatProperty(CameraMinHeightInfo, 100.f, 0.f, 1000.f),
FloatProperty(OrenNayarRoughnessInfo, 0.f, 0.f, 1.f),
IntProperty(NActiveLayersInfo, 0, 0, OpenGLCap.maxTextureUnits() / 3)
})
@@ -544,18 +533,21 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
if (p.radii.has_value()) {
if (std::holds_alternative<glm::dvec3>(*p.radii)) {
_ellipsoid = Ellipsoid(std::get<glm::dvec3>(*p.radii));
setBoundingSphere(static_cast<float>(_ellipsoid.maximumRadius()));
setBoundingSphere(_ellipsoid.maximumRadius());
}
else if (std::holds_alternative<double>(*p.radii)) {
const double radius = std::get<double>(*p.radii);
_ellipsoid = Ellipsoid({ radius, radius, radius });
setBoundingSphere(static_cast<float>(_ellipsoid.maximumRadius()));
setBoundingSphere(_ellipsoid.maximumRadius());
}
else {
throw ghoul::MissingCaseException();
}
}
// For globes, the interaction sphere is always the same as the bounding sphere
setInteractionSphere(boundingSphere());
_generalProperties.performShading =
p.performShading.value_or(_generalProperties.performShading);
@@ -602,16 +594,11 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
});
addProperty(_generalProperties.targetLodScaleFactor);
addProperty(_generalProperties.currentLodScaleFactor);
addProperty(_generalProperties.cameraMinHeight);
addProperty(_generalProperties.orenNayarRoughness);
_generalProperties.nActiveLayers.setReadOnly(true);
addProperty(_generalProperties.nActiveLayers);
_debugPropertyOwner.addProperty(_debugProperties.showChunkEdges);
//_debugPropertyOwner.addProperty(_debugProperties.showChunkBounds);
//_debugPropertyOwner.addProperty(_debugProperties.showChunkAABB);
//_debugPropertyOwner.addProperty(_debugProperties.showHeightResolution);
//_debugPropertyOwner.addProperty(_debugProperties.showHeightIntensities);
_debugPropertyOwner.addProperty(_debugProperties.levelByProjectedAreaElseDistance);
_debugPropertyOwner.addProperty(_debugProperties.resetTileProviders);
_debugPropertyOwner.addProperty(_debugProperties.modelSpaceRenderingCutoffLevel);
@@ -625,8 +612,6 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
_generalProperties.eclipseHardShadows.onChange(notifyShaderRecompilation);
_generalProperties.performShading.onChange(notifyShaderRecompilation);
_debugProperties.showChunkEdges.onChange(notifyShaderRecompilation);
_debugProperties.showHeightResolution.onChange(notifyShaderRecompilation);
_debugProperties.showHeightIntensities.onChange(notifyShaderRecompilation);
_layerManager.onChange([&](Layer* l) {
_shadersNeedRecompilation = true;
@@ -805,13 +790,6 @@ void RenderableGlobe::update(const UpdateData& data) {
_localRenderer.program->setUniform("xSegments", _grid.xSegments);
if (_debugProperties.showHeightResolution) {
_localRenderer.program->setUniform(
"vertexResolution",
glm::vec2(_grid.xSegments, _grid.ySegments)
);
}
ghoul::opengl::updateUniformLocations(
*_localRenderer.program,
_localRenderer.uniformCache,
@@ -824,12 +802,6 @@ void RenderableGlobe::update(const UpdateData& data) {
_globalRenderer.program->setUniform("xSegments", _grid.xSegments);
if (_debugProperties.showHeightResolution) {
_globalRenderer.program->setUniform(
"vertexResolution",
glm::vec2(_grid.xSegments, _grid.ySegments)
);
}
// Ellipsoid Radius (Model Space)
_globalRenderer.program->setUniform(
"radiiSquared",
@@ -843,9 +815,15 @@ void RenderableGlobe::update(const UpdateData& data) {
);
}
setBoundingSphere(static_cast<float>(
_ellipsoid.maximumRadius() * glm::compMax(data.modelTransform.scale)
));
double bs = _ellipsoid.maximumRadius() * glm::compMax(data.modelTransform.scale);
if (_hasRings) {
const double ringSize = _ringsComponent.size();
if (ringSize > bs) {
bs = ringSize;
}
}
setBoundingSphere(bs);
setInteractionSphere(bs);
glm::dmat4 translation =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation);
@@ -1211,48 +1189,32 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&,
}
_localRenderer.program->deactivate();
if (_debugProperties.showChunkBounds) {
for (int i = 0; i < globalCount; ++i) {
debugRenderChunk(
*_globalChunkBuffer[i],
mvp,
_debugProperties.showChunkBounds
);
if (global::sessionRecording->isSavingFramesDuringPlayback()) {
// If our tile cache is very full, we assume we need to adjust the level of detail
// dynamically to not keep rendering frames with unavailable data
// After certain number of iterations(_debugProperties.dynamicLodIterationCount) of
// unavailable/available data in a row, we assume that a change could be made.
const int iterCount = _debugProperties.dynamicLodIterationCount;
const bool exceededIterations =
static_cast<int>(_iterationsOfUnavailableData) > iterCount;
const float clf = _generalProperties.currentLodScaleFactor;
const float clfMin = _generalProperties.currentLodScaleFactor.minValue();
const float targetLod = _generalProperties.targetLodScaleFactor;
const bool validLodFactor = clf > clfMin;
if (exceededIterations && validLodFactor) {
_generalProperties.currentLodScaleFactor =
_generalProperties.currentLodScaleFactor - 0.1f;
_iterationsOfUnavailableData = 0;
_lodScaleFactorDirty = true;
} // Make 2 times the iterations with available data to move it up again
else if (static_cast<int>(_iterationsOfAvailableData) >
(iterCount * 2) && clf < targetLod)
{
_generalProperties.currentLodScaleFactor =
_generalProperties.currentLodScaleFactor + 0.1f;
_iterationsOfAvailableData = 0;
_lodScaleFactorDirty = true;
}
for (int i = 0; i < localCount; ++i) {
debugRenderChunk(
*_localChunkBuffer[i],
mvp,
_debugProperties.showChunkBounds
);
}
}
// If our tile cache is very full, we assume we need to adjust the level of detail
// dynamically to not keep rendering frames with unavailable data
// After certain number of iterations(_debugProperties.dynamicLodIterationCount) of
// unavailable/available data in a row, we assume that a change could be made.
const int iterCount = _debugProperties.dynamicLodIterationCount;
const bool exceededIterations =
static_cast<int>(_iterationsOfUnavailableData) > iterCount;
const float clf = _generalProperties.currentLodScaleFactor;
const float clfMin = _generalProperties.currentLodScaleFactor.minValue();
const float targetLod = _generalProperties.targetLodScaleFactor;
const bool validLodFactor = clf > clfMin;
if (exceededIterations && validLodFactor) {
_generalProperties.currentLodScaleFactor =
_generalProperties.currentLodScaleFactor - 0.1f;
_iterationsOfUnavailableData = 0;
_lodScaleFactorDirty = true;
} // Make 2 times the iterations with available data to move it up again
else if (static_cast<int>(_iterationsOfAvailableData) >
(iterCount * 2) && clf < targetLod)
{
_generalProperties.currentLodScaleFactor =
_generalProperties.currentLodScaleFactor + 0.1f;
_iterationsOfAvailableData = 0;
_lodScaleFactorDirty = true;
}
}
@@ -1649,12 +1611,8 @@ void RenderableGlobe::recompileShaders() {
std::to_string(_generalProperties.shadowMapping)
);
pairs.emplace_back("showChunkEdges", std::to_string(_debugProperties.showChunkEdges));
pairs.emplace_back("showHeightResolution",
std::to_string(_debugProperties.showHeightResolution)
);
pairs.emplace_back("showHeightIntensities",
std::to_string(_debugProperties.showHeightIntensities)
);
pairs.emplace_back("showHeightResolution", "0");
pairs.emplace_back("showHeightIntensities", "0");
pairs.emplace_back("defaultHeight", std::to_string(DefaultHeight));
@@ -1762,13 +1720,6 @@ void RenderableGlobe::recompileShaders() {
_localRenderer.program->setUniform("xSegments", _grid.xSegments);
if (_debugProperties.showHeightResolution) {
_localRenderer.program->setUniform(
"vertexResolution",
glm::vec2(_grid.xSegments, _grid.ySegments)
);
}
ghoul::opengl::updateUniformLocations(
*_localRenderer.program,
_localRenderer.uniformCache,
@@ -1790,12 +1741,6 @@ void RenderableGlobe::recompileShaders() {
_globalRenderer.program->setUniform("xSegments", _grid.xSegments);
if (_debugProperties.showHeightResolution) {
_globalRenderer.program->setUniform(
"vertexResolution",
glm::vec2(_grid.xSegments, _grid.ySegments)
);
}
// Ellipsoid Radius (Model Space)
_globalRenderer.program->setUniform(
"radiiSquared",
@@ -1831,7 +1776,7 @@ SurfacePositionHandle RenderableGlobe::calculateSurfacePositionHandle(
double heightToSurface = getHeight(targetModelSpace);
heightToSurface = glm::isnan(heightToSurface) ? 0.0 : heightToSurface;
centerToEllipsoidSurface = glm::isnan(glm::length(centerToEllipsoidSurface)) ?
(glm::dvec3(0.0, 1.0, 0.0) * static_cast<double>(boundingSphere())) :
(glm::dvec3(0.0, 1.0, 0.0) * interactionSphere()) :
centerToEllipsoidSurface;
ellipsoidSurfaceOutDirection = glm::isnan(glm::length(ellipsoidSurfaceOutDirection)) ?
glm::dvec3(0.0, 1.0, 0.0) : ellipsoidSurfaceOutDirection;
@@ -123,9 +123,6 @@ private:
struct {
properties::BoolProperty showChunkEdges;
properties::BoolProperty showChunkBounds;
properties::BoolProperty showHeightResolution;
properties::BoolProperty showHeightIntensities;
properties::BoolProperty levelByProjectedAreaElseDistance;
properties::BoolProperty resetTileProviders;
properties::IntProperty modelSpaceRenderingCutoffLevel;
@@ -142,7 +139,6 @@ private:
properties::IntProperty nShadowSamples;
properties::FloatProperty targetLodScaleFactor;
properties::FloatProperty currentLodScaleFactor;
properties::FloatProperty cameraMinHeight;
properties::FloatProperty orenNayarRoughness;
properties::IntProperty nActiveLayers;
} _generalProperties;
+69 -68
View File
@@ -56,14 +56,14 @@ namespace {
constexpr const std::array<const char*, 9> UniformNames = {
"modelViewProjectionMatrix", "textureOffset", "colorFilterValue", "_nightFactor",
"sunPosition", "ringTexture", "shadowMatrix", "shadowMapTexture",
"sunPosition", "ringTexture", "shadowMatrix", "shadowMapTexture",
"zFightingPercentage"
};
constexpr const std::array<const char*, 15> UniformNamesAdvancedRings = {
"modelViewProjectionMatrix", "textureOffset", "colorFilterValue", "_nightFactor",
"sunPosition", "sunPositionObj", "camPositionObj", "ringTextureFwrd",
"ringTextureBckwrd", "ringTextureUnlit", "ringTextureColor",
"sunPosition", "sunPositionObj", "camPositionObj", "ringTextureFwrd",
"ringTextureBckwrd", "ringTextureUnlit", "ringTextureColor",
"ringTextureTransparency", "shadowMatrix", "shadowMapTexture", "zFightingPercentage"
};
@@ -251,56 +251,59 @@ void RingsComponent::initialize() {
addProperty(_enabled);
_size.setViewOption(properties::Property::ViewOptions::Logarithmic);
_size = p.size.value_or(_size);
_size.onChange([&]() { _planeIsDirty = true; });
addProperty(_size);
if (p.texture.has_value()) {
_texturePath = absPath(p.texture->string());
_textureFile = std::make_unique<File>(_texturePath);
_texturePath.onChange([&]() { loadTexture(); });
_texturePath = absPath(p.texture->string()).string();
_textureFile = std::make_unique<File>(_texturePath.value());
_texturePath.onChange([this]() { loadTexture(); });
addProperty(_texturePath);
_textureFile->setCallback([&](const File&) { _textureIsDirty = true; });
_textureFile->setCallback([this]() { _textureIsDirty = true; });
}
if (p.textureFwrd.has_value()) {
_textureFwrdPath = absPath(p.textureFwrd->string());
_textureFileForwards = std::make_unique<File>(_textureFwrdPath);
_textureFwrdPath.onChange([&]() { loadTexture(); });
_textureFwrdPath = absPath(p.textureFwrd->string()).string();
_textureFileForwards = std::make_unique<File>(_textureFwrdPath.value());
_textureFwrdPath.onChange([this]() { loadTexture(); });
addProperty(_textureFwrdPath);
_textureFileForwards->setCallback([&](const File&) { _textureIsDirty = true; });
_textureFileForwards->setCallback([this]() { _textureIsDirty = true; });
}
if (p.textureBckwrd.has_value()) {
_textureBckwrdPath = absPath(p.textureBckwrd->string());
_textureFileBackwards = std::make_unique<File>(_textureBckwrdPath);
_textureBckwrdPath.onChange([&]() { loadTexture(); });
_textureBckwrdPath = absPath(p.textureBckwrd->string()).string();
_textureFileBackwards = std::make_unique<File>(_textureBckwrdPath.value());
_textureBckwrdPath.onChange([this]() { loadTexture(); });
addProperty(_textureBckwrdPath);
_textureFileBackwards->setCallback([&](const File&) { _textureIsDirty = true; });
_textureFileBackwards->setCallback([this]() { _textureIsDirty = true; });
}
if (p.textureUnlit.has_value()) {
_textureUnlitPath = absPath(p.textureUnlit->string());
_textureFileUnlit = std::make_unique<File>(_textureUnlitPath);
_textureUnlitPath.onChange([&]() { loadTexture(); });
_textureUnlitPath = absPath(p.textureUnlit->string()).string();
_textureFileUnlit = std::make_unique<File>(_textureUnlitPath.value());
_textureUnlitPath.onChange([this]() { loadTexture(); });
addProperty(_textureUnlitPath);
_textureFileUnlit->setCallback([&](const File&) { _textureIsDirty = true; });
_textureFileUnlit->setCallback([this]() { _textureIsDirty = true; });
}
if (p.textureColor.has_value()) {
_textureColorPath = absPath(p.textureColor->string());
_textureFileColor = std::make_unique<File>(_textureColorPath);
_textureColorPath.onChange([&]() { loadTexture(); });
_textureColorPath = absPath(p.textureColor->string()).string();
_textureFileColor = std::make_unique<File>(_textureColorPath.value());
_textureColorPath.onChange([this]() { loadTexture(); });
addProperty(_textureColorPath);
_textureFileColor->setCallback([&](const File&) { _textureIsDirty = true; });
_textureFileColor->setCallback([this]() { _textureIsDirty = true; });
}
if (p.textureTransparency.has_value()) {
_textureTransparencyPath = absPath(p.textureTransparency->string());
_textureFileTransparency = std::make_unique<File>(_textureTransparencyPath);
_textureTransparencyPath.onChange([&]() { loadTexture(); });
_textureTransparencyPath = absPath(p.textureTransparency->string()).string();
_textureFileTransparency = std::make_unique<File>(
_textureTransparencyPath.value()
);
_textureTransparencyPath.onChange([this]() { loadTexture(); });
addProperty(_textureTransparencyPath);
_textureFileTransparency->setCallback([&](const File&) { _textureIsDirty = true; });
_textureFileTransparency->setCallback([this]() { _textureIsDirty = true; });
}
_offset = p.offset.value_or(_offset);
@@ -423,11 +426,11 @@ void RingsComponent::draw(const RenderData& data,
);
_shader->setUniform(
_uniformCacheAdvancedRings.sunPositionObj,
_uniformCacheAdvancedRings.sunPositionObj,
sunPositionObjectSpace
);
_shader->setUniform(
_uniformCacheAdvancedRings.zFightingPercentage,
_uniformCacheAdvancedRings.zFightingPercentage,
_zFightingPercentage
);
_shader->setUniform(
@@ -438,21 +441,21 @@ void RingsComponent::draw(const RenderData& data,
ringTextureFwrdUnit.activate();
_textureForwards->bind();
_shader->setUniform(
_uniformCacheAdvancedRings.ringTextureFwrd,
_uniformCacheAdvancedRings.ringTextureFwrd,
ringTextureFwrdUnit
);
ringTextureBckwrdUnit.activate();
_textureBackwards->bind();
_shader->setUniform(
_uniformCacheAdvancedRings.ringTextureBckwrd,
_uniformCacheAdvancedRings.ringTextureBckwrd,
ringTextureBckwrdUnit
);
ringTextureUnlitUnit.activate();
_textureUnlit->bind();
_shader->setUniform(
_uniformCacheAdvancedRings.ringTextureUnlit,
_uniformCacheAdvancedRings.ringTextureUnlit,
ringTextureUnlitUnit
);
@@ -488,7 +491,7 @@ void RingsComponent::draw(const RenderData& data,
);
_shader->setUniform(
_uniformCacheAdvancedRings.camPositionObj,
_uniformCacheAdvancedRings.camPositionObj,
_camPositionObjectSpace
);
@@ -513,7 +516,7 @@ void RingsComponent::draw(const RenderData& data,
_shader->setUniform(_uniformCache.sunPosition, _sunPosition);
_shader->setUniform(_uniformCache.zFightingPercentage, _zFightingPercentage);
_shader->setUniform(
_uniformCache.modelViewProjectionMatrix,
_uniformCache.modelViewProjectionMatrix,
modelViewProjectionTransform
);
@@ -533,7 +536,7 @@ void RingsComponent::draw(const RenderData& data,
shadowMapUnit.activate();
glBindTexture(GL_TEXTURE_2D, shadowData.shadowDepthTexture);
_shader->setUniform(_uniformCache.shadowMapTexture, shadowMapUnit);
}
}
glEnable(GL_DEPTH_TEST);
glEnablei(GL_BLEND, 0);
@@ -614,7 +617,7 @@ void RingsComponent::loadTexture() {
if (!_texturePath.value().empty()) {
std::unique_ptr<Texture> texture = TextureReader::ref().loadTexture(
absPath(_texturePath)
absPath(_texturePath).string()
);
if (texture) {
@@ -627,16 +630,16 @@ void RingsComponent::loadTexture() {
_texture->uploadTexture();
_texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
_textureFile = std::make_unique<ghoul::filesystem::File>(_texturePath);
_textureFile->setCallback(
[&](const ghoul::filesystem::File&) { _textureIsDirty = true; }
_textureFile = std::make_unique<ghoul::filesystem::File>(
_texturePath.value()
);
_textureFile->setCallback([this]() { _textureIsDirty = true; });
}
}
if (!_textureFwrdPath.value().empty()) {
std::unique_ptr<Texture> textureForwards = TextureReader::ref().loadTexture(
absPath(_textureFwrdPath)
absPath(_textureFwrdPath).string()
);
if (textureForwards) {
@@ -654,17 +657,15 @@ void RingsComponent::loadTexture() {
ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
_textureFileForwards = std::make_unique<ghoul::filesystem::File>(
_textureFwrdPath
);
_textureFileForwards->setCallback(
[&](const ghoul::filesystem::File&) { _textureIsDirty = true; }
_textureFwrdPath.value()
);
_textureFileForwards->setCallback([this]() { _textureIsDirty = true; });
}
}
if (!_textureBckwrdPath.value().empty()) {
std::unique_ptr<Texture> textureBackwards = TextureReader::ref().loadTexture(
absPath(_textureBckwrdPath)
absPath(_textureBckwrdPath).string()
);
if (textureBackwards) {
@@ -682,17 +683,15 @@ void RingsComponent::loadTexture() {
ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
_textureFileBackwards = std::make_unique<ghoul::filesystem::File>(
_textureBckwrdPath
);
_textureFileBackwards->setCallback(
[&](const ghoul::filesystem::File&) { _textureIsDirty = true; }
_textureBckwrdPath.value()
);
_textureFileBackwards->setCallback([this]() { _textureIsDirty = true; });
}
}
if (!_textureUnlitPath.value().empty()) {
std::unique_ptr<Texture> textureUnlit = TextureReader::ref().loadTexture(
absPath(_textureUnlitPath)
absPath(_textureUnlitPath).string()
);
if (textureUnlit) {
@@ -706,18 +705,18 @@ void RingsComponent::loadTexture() {
_textureUnlit = std::move(textureUnlit);
_textureUnlit->uploadTexture();
_textureUnlit->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
_textureUnlit->setFilter(Texture::FilterMode::AnisotropicMipMap);
_textureFileUnlit = std::make_unique<ghoul::filesystem::File>(_textureUnlitPath);
_textureFileUnlit->setCallback(
[&](const ghoul::filesystem::File&) { _textureIsDirty = true; }
_textureFileUnlit = std::make_unique<ghoul::filesystem::File>(
_textureUnlitPath.value()
);
_textureFileUnlit->setCallback([this]() { _textureIsDirty = true; });
}
}
if (!_textureColorPath.value().empty()) {
std::unique_ptr<Texture> textureColor = TextureReader::ref().loadTexture(
absPath(_textureColorPath)
absPath(_textureColorPath).string()
);
if (textureColor) {
@@ -731,18 +730,18 @@ void RingsComponent::loadTexture() {
_textureColor = std::move(textureColor);
_textureColor->uploadTexture();
_textureColor->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
_textureColor->setFilter(Texture::FilterMode::AnisotropicMipMap);
_textureFileColor = std::make_unique<ghoul::filesystem::File>(_textureColorPath);
_textureFileColor->setCallback(
[&](const ghoul::filesystem::File&) { _textureIsDirty = true; }
_textureFileColor = std::make_unique<ghoul::filesystem::File>(
_textureColorPath.value()
);
_textureFileColor->setCallback([this]() { _textureIsDirty = true; });
}
}
if (!_textureTransparencyPath.value().empty()) {
std::unique_ptr<Texture> textureTransparency = TextureReader::ref().loadTexture(
absPath(_textureTransparencyPath)
absPath(_textureTransparencyPath).string()
);
if (textureTransparency) {
@@ -761,11 +760,9 @@ void RingsComponent::loadTexture() {
);
_textureFileTransparency = std::make_unique<ghoul::filesystem::File>(
_textureTransparencyPath
);
_textureFileTransparency->setCallback(
[&](const ghoul::filesystem::File&) { _textureIsDirty = true; }
_textureTransparencyPath.value()
);
_textureFileTransparency->setCallback([this]() { _textureIsDirty = true; });
}
}
@@ -831,7 +828,7 @@ void RingsComponent::compileShadowShader() {
// Uses multiple textures for the Rings
// See https://bjj.mmedia.is/data/s_rings/index.html for theory behind it
if (_isAdvancedTextureEnabled) {
if (_isAdvancedTextureEnabled) {
_shader = global::renderEngine->buildRenderProgram(
"AdvancedRingsProgram",
absPath("${MODULE_GLOBEBROWSING}/shaders/advanced_rings_vs.glsl"),
@@ -840,8 +837,8 @@ void RingsComponent::compileShadowShader() {
);
ghoul::opengl::updateUniformLocations(
*_shader,
_uniformCacheAdvancedRings,
*_shader,
_uniformCacheAdvancedRings,
UniformNamesAdvancedRings
);
}
@@ -866,4 +863,8 @@ bool RingsComponent::isEnabled() const {
return _enabled;
}
double RingsComponent::size() const {
return _size;
}
} // namespace openspace
@@ -71,6 +71,7 @@ public:
static documentation::Documentation Documentation();
bool isEnabled() const;
double size() const;
private:
void loadTexture();
+50 -12
View File
@@ -44,6 +44,7 @@
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/openglstatecache.h>
#include <filesystem>
#include <fstream>
#include "cpl_minixml.h"
@@ -148,6 +149,21 @@ namespace temporal {
"This is the path to the XML configuration file that describes the temporal tile "
"information."
};
constexpr openspace::properties::Property::PropertyInfo UseFixedTimeInfo = {
"UseFixedTime",
"Use Fixed Time",
"If this value is enabled, the time-varying timevarying dataset will always use "
"the time that is specified in the 'FixedTime' property, rather than using the "
"actual time from OpenSpace"
};
constexpr openspace::properties::Property::PropertyInfo FixedTimeInfo = {
"FixedTime",
"Fixed Time",
"If the 'UseFixedTime' is enabled, this time will be used instead of the actual "
"time taken from OpenSpace for the displayed tiles."
};
} // namespace temporal
@@ -344,12 +360,11 @@ std::unique_ptr<TileProvider> initTileProvider(TemporalTileProvider& t,
const size_t numChars = strlen(temporal::UrlTimePlaceholder);
// @FRAGILE: This will only find the first instance. Dangerous if that instance is
// commented out ---abock
const std::string timeSpecifiedXml = xmlTemplate.replace(pos, numChars, timekey);
std::string gdalDatasetXml = timeSpecifiedXml;
std::string xml = xmlTemplate.replace(pos, numChars, timekey);
FileSys.expandPathTokens(gdalDatasetXml, IgnoredTokens);
xml = FileSys.expandPathTokens(std::move(xml), IgnoredTokens).string();
t.initDict.setValue(KeyFilePath, gdalDatasetXml);
t.initDict.setValue(KeyFilePath, xml);
return std::make_unique<DefaultTileProvider>(t.initDict);
}
@@ -375,18 +390,29 @@ TileProvider* getTileProvider(TemporalTileProvider& t, std::string_view timekey)
TileProvider* getTileProvider(TemporalTileProvider& t, const Time& time) {
ZoneScoped
Time tCopy(time);
if (t.timeQuantizer.quantize(tCopy, true)) {
char Buffer[22];
const int size = timeStringify(t.timeFormat, tCopy, Buffer);
if (t.useFixedTime && !t.fixedTime.value().empty()) {
try {
return getTileProvider(t, std::string_view(Buffer, size));
return getTileProvider(t, t.fixedTime.value());
}
catch (const ghoul::RuntimeError& e) {
LERRORC("TemporalTileProvider", e.message);
return nullptr;
}
}
else {
Time tCopy(time);
if (t.timeQuantizer.quantize(tCopy, true)) {
char Buffer[22];
const int size = timeStringify(t.timeFormat, tCopy, Buffer);
try {
return getTileProvider(t, std::string_view(Buffer, size));
}
catch (const ghoul::RuntimeError& e) {
LERRORC("TemporalTileProvider", e.message);
return nullptr;
}
}
}
return nullptr;
}
@@ -487,9 +513,9 @@ bool readFilePath(TemporalTileProvider& t) {
}
// File path was not a path to a file but a GDAL config or empty
ghoul::filesystem::File f(t.filePath);
if (FileSys.fileExists(f)) {
t.initDict.setValue(temporal::KeyBasePath, f.directoryName());
std::filesystem::path f(t.filePath.value());
if (std::filesystem::is_regular_file(f)) {
t.initDict.setValue(temporal::KeyBasePath, f.parent_path().string());
}
t.gdalXmlTemplate = consumeTemporalMetaData(t, xml);
@@ -831,6 +857,8 @@ TileProviderByLevel::TileProviderByLevel(const ghoul::Dictionary& dictionary) {
TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary)
: initDict(dictionary)
, filePath(temporal::FilePathInfo)
, useFixedTime(temporal::UseFixedTimeInfo, false)
, fixedTime(temporal::FixedTimeInfo)
{
ZoneScoped
@@ -839,6 +867,16 @@ TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary)
filePath = dictionary.value<std::string>(KeyFilePath);
addProperty(filePath);
if (dictionary.hasValue<bool>(temporal::UseFixedTimeInfo.identifier)) {
useFixedTime = dictionary.value<bool>(temporal::UseFixedTimeInfo.identifier);
}
addProperty(useFixedTime);
if (dictionary.hasValue<std::string>(temporal::FixedTimeInfo.identifier)) {
fixedTime = dictionary.value<std::string>(temporal::FixedTimeInfo.identifier);
}
addProperty(fixedTime);
successfulInitialization = readFilePath(*this);
if (!successfulInitialization) {
+3
View File
@@ -34,6 +34,7 @@
#include <modules/globebrowsing/src/tiletextureinitdata.h>
#include <modules/globebrowsing/src/timequantizer.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/intproperty.h>
#include <unordered_map>
@@ -174,6 +175,8 @@ struct TemporalTileProvider : public TileProvider {
ghoul::Dictionary initDict;
properties::StringProperty filePath;
properties::BoolProperty useFixedTime;
properties::StringProperty fixedTime;
std::string gdalXmlTemplate;
std::unordered_map<TimeKey, std::unique_ptr<TileProvider>> tileProviderMap;
+23 -5
View File
@@ -335,8 +335,8 @@ TimeQuantizer::TimeQuantizer(std::string start, std::string end,
: _start(start)
, _timerange(std::move(start), std::move(end))
{
verifyStartTimeRestrictions();
_resolution = parseTimeResolutionStr(resolution);
verifyStartTimeRestrictions();
}
double TimeQuantizer::parseTimeResolutionStr(const std::string& resolutionStr) {
@@ -366,18 +366,36 @@ void TimeQuantizer::setStartEndRange(const std::string& start, const std::string
void TimeQuantizer::setResolution(const std::string& resolutionString) {
_resolution = parseTimeResolutionStr(resolutionString);
verifyStartTimeRestrictions();
}
void TimeQuantizer::verifyStartTimeRestrictions() {
if (_start.day() < 1 || _start.day() > 28) {
//If monthly time resolution then restrict to 28 days so every month is consistent
unsigned int dayUpperLimit;
std::string helpfulDescription = "the selected month";
if (_resolutionUnit == 'M') {
dayUpperLimit = 28;
helpfulDescription = "monthly increment";
}
else if (_resolutionUnit == 'y') {
//Get month sizes using a fixed non-leap year
dayUpperLimit = monthSize(_start.month(), 2001);
helpfulDescription += " on a yearly increment";
}
else {
dayUpperLimit = 31;
}
if (_start.day() < 1 || _start.day() > dayUpperLimit) {
throw ghoul::RuntimeError(fmt::format(
"Invalid start day value of {} for day of month. Valid days are 1 - 28",
_start.day()
"Invalid start day value of {} for {}, valid days are 1 - {}",
_start.day(),
helpfulDescription,
dayUpperLimit
));
}
if (_start.hour() != 0 || _start.minute() != 0 || _start.second() != 0) {
throw ghoul::RuntimeError(fmt::format(
"Invalid start time value of {}:{}:{}. Time must be 00:00:00",
"Invalid start time value of {}:{}:{}, time must be 00:00:00",
_start.hour(), _start.minute(), _start.second()
));
}
+1 -1
View File
@@ -27,7 +27,7 @@
#include <modules/imgui/include/guicomponent.h>
#include <openspace/properties/stringlistproperty.h>
#include <openspace/properties/list/stringlistproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <ghoul/misc/boolean.h>
#include <functional>
+8
View File
@@ -55,6 +55,14 @@ void renderStringProperty(properties::Property* prop, const std::string& ownerNa
IsRegularProperty isRegular = IsRegularProperty::Yes,
ShowToolTip showTooltip = ShowToolTip::Yes, double tooltipDelay = 1.0);
void renderIntListProperty(properties::Property* prop, const std::string& ownerName,
IsRegularProperty isRegular = IsRegularProperty::Yes,
ShowToolTip showTooltip = ShowToolTip::Yes, double tooltipDelay = 1.0);
void renderDoubleListProperty(properties::Property* prop, const std::string& ownerName,
IsRegularProperty isRegular = IsRegularProperty::Yes,
ShowToolTip showTooltip = ShowToolTip::Yes, double tooltipDelay = 1.0);
void renderStringListProperty(properties::Property* prop, const std::string& ownerName,
IsRegularProperty isRegular = IsRegularProperty::Yes,
ShowToolTip showTooltip = ShowToolTip::Yes, double tooltipDelay = 1.0);
+5 -6
View File
@@ -38,7 +38,7 @@
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <filesystem>
namespace {
constexpr const char* _loggerCat = "GUI";
@@ -53,7 +53,7 @@ namespace {
constexpr const std::array<const char*, 2> UniformNames = { "tex", "ortho" };
void addScreenSpaceRenderableLocal(std::string identifier, std::string texturePath) {
if (!FileSys.fileExists(absPath(texturePath))) {
if (!std::filesystem::is_regular_file(absPath(texturePath))) {
LWARNING(fmt::format("Could not find image '{}'", texturePath));
return;
}
@@ -206,8 +206,7 @@ void GUI::deinitialize() {
void GUI::initializeGL() {
std::string cachedFile = FileSys.cacheManager()->cachedFilename(
configurationFile,
"",
ghoul::filesystem::CacheManager::Persistent::Yes
""
);
LDEBUG(fmt::format("Using {} as ImGUI cache location", cachedFile));
@@ -248,9 +247,9 @@ void GUI::initializeGL() {
io.KeyMap[ImGuiKey_Y] = static_cast<int>(Key::Y);
io.KeyMap[ImGuiKey_Z] = static_cast<int>(Key::Z);
io.Fonts->AddFontFromFileTTF(absPath(GuiFont).c_str(), FontSize);
io.Fonts->AddFontFromFileTTF(absPath(GuiFont).string().c_str(), FontSize);
captionFont = io.Fonts->AddFontFromFileTTF(
absPath(GuiFont).c_str(),
absPath(GuiFont).string().c_str(),
FontSize * 1.5f
);
+3 -3
View File
@@ -30,9 +30,9 @@
#include <openspace/engine/openspaceengine.h>
#include <openspace/scene/assetmanager.h>
#include <openspace/scene/asset.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/file.h>
#include <filesystem>
namespace {
std::string assetStateToString(openspace::Asset::State state) {
@@ -90,10 +90,10 @@ void GuiAssetComponent::renderTree(const Asset& asset, const std::string& relati
using namespace ghoul::filesystem;
std::string assetPath = asset.assetFilePath();
const std::string& assetDirectory = File(assetPath).directoryName();
std::string assetDirectory = std::filesystem::path(assetPath).parent_path().string();
if (!relativeToPath.empty()) {
assetPath = FileSys.relativePath(assetPath, relativeToPath);
assetPath = std::filesystem::relative(assetPath, relativeToPath).string();
}
std::string assetText = assetPath + " " + assetStateToString(asset.state());
+3 -1
View File
@@ -477,7 +477,7 @@ void GuiPropertyComponent::renderProperty(properties::Property* prop,
>;
static const std::map<std::string, Func> FunctionMapping = {
{ "BoolProperty", &renderBoolProperty },
{ "DoubleProperty", &renderDoubleProperty},
{ "DoubleProperty", &renderDoubleProperty },
{ "IntProperty", &renderIntProperty },
{ "IVec2Property", &renderIVec2Property },
{ "IVec3Property", &renderIVec3Property },
@@ -493,6 +493,8 @@ void GuiPropertyComponent::renderProperty(properties::Property* prop,
{ "DMat3Property", &renderDMat3Property },
{ "DMat4Property", &renderDMat4Property },
{ "StringProperty", &renderStringProperty },
{ "DoubleListProperty", &renderDoubleListProperty },
{ "IntListProperty", &renderIntListProperty },
{ "StringListProperty", &renderStringListProperty },
{ "OptionProperty", &renderOptionProperty },
{ "TriggerProperty", &renderTriggerProperty },
+109 -51
View File
@@ -29,7 +29,9 @@
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/selectionproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/stringlistproperty.h>
#include <openspace/properties/list/doublelistproperty.h>
#include <openspace/properties/list/intlistproperty.h>
#include <openspace/properties/list/stringlistproperty.h>
#include <openspace/properties/matrix/dmat2property.h>
#include <openspace/properties/matrix/dmat3property.h>
#include <openspace/properties/matrix/dmat4property.h>
@@ -47,6 +49,7 @@
#include <openspace/properties/vector/vec4property.h>
#include <openspace/scripting/scriptengine.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/misc/dictionaryluaformatter.h>
#include <ghoul/misc/misc.h>
namespace openspace {
@@ -101,7 +104,7 @@ void renderBoolProperty(Property* prop, const std::string& ownerName,
BoolProperty* p = static_cast<BoolProperty*>(prop);
const std::string& name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
BoolProperty::ValueType value = *p;
ImGui::Checkbox(name.c_str(), &value);
@@ -123,7 +126,7 @@ void renderOptionProperty(Property* prop, const std::string& ownerName,
OptionProperty* p = static_cast<OptionProperty*>(prop);
const std::string& name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
bool isReadOnly = false;
if (p->metaData().hasValue<bool>("isReadOnly")) {
isReadOnly = p->metaData().value<bool>("isReadOnly");
@@ -186,34 +189,36 @@ void renderSelectionProperty(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
SelectionProperty* p = static_cast<SelectionProperty*>(prop);
const std::string& name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
bool selectionChanged = false;
std::set<std::string> newSelected;
if (ImGui::TreeNode(name.c_str())) {
const std::vector<SelectionProperty::Option>& options = p->options();
std::vector<int> newSelectedIndices;
std::vector<int> selectedIndices = p->value();
std::set<std::string> selected = p->value();
const std::vector<std::string>& options = p->options();
for (int i = 0; i < static_cast<int>(options.size()); ++i) {
std::string description = options[i].description;
bool selected = std::find(
selectedIndices.begin(), selectedIndices.end(), i
) != selectedIndices.end();
const std::string key = options[i];
bool isSelected = p->isSelected(key);
ImGui::Checkbox(description.c_str(), &selected);
selectionChanged |= ImGui::Checkbox(key.c_str(), &isSelected);
if (showTooltip) {
renderTooltip(prop, tooltipDelay);
}
if (selected) {
newSelectedIndices.push_back(i);
if (isSelected) {
newSelected.insert(key);
}
}
if (newSelectedIndices != p->value()) {
if (selectionChanged) {
std::string parameters = "{";
for (int i : newSelectedIndices) {
parameters += std::to_string(i) + ",";
for (const std::string& s : newSelected) {
parameters += fmt::format("'{}',", s);
}
if (!newSelected.empty()) {
parameters.pop_back();
}
parameters += "}";
executeScript(p->fullyQualifiedIdentifier(), parameters, isRegular);
@@ -230,7 +235,7 @@ void renderStringProperty(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
StringProperty* p = static_cast<StringProperty*>(prop);
const std::string& name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
const std::string value = p->value();
@@ -262,18 +267,13 @@ void renderStringProperty(Property* prop, const std::string& ownerName,
ImGui::PopID();
}
void renderStringListProperty(Property* prop, const std::string& ownerName,
IsRegularProperty isRegular, ShowToolTip showTooltip,
double tooltipDelay)
void renderListProperty(const std::string& name, const std::string& fullIdentifier,
const std::string& stringValue, IsRegularProperty isRegular)
{
ghoul_assert(prop, "prop must not be nullptr");
StringListProperty* p = static_cast<StringListProperty*>(prop);
const std::string& name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ghoul_assert(stringValue.size() > 2, "an empty list should have the string value '[]'");
std::string value;
p->getStringValue(value);
// const std::string value = p->value();
// Remove brackets from the string value
std::string value = stringValue.substr(1, stringValue.size() - 2);
static const int bufferSize = 512;
static char buffer[bufferSize];
@@ -288,9 +288,6 @@ void renderStringListProperty(Property* prop, const std::string& ownerName,
bufferSize,
ImGuiInputTextFlags_EnterReturnsTrue
);
if (showTooltip) {
renderTooltip(prop, tooltipDelay);
}
if (hasNewValue) {
std::vector<std::string> tokens = ghoul::tokenizeString(std::string(buffer), ',');
@@ -298,17 +295,78 @@ void renderStringListProperty(Property* prop, const std::string& ownerName,
for (std::string& token : tokens) {
if (!token.empty()) {
ghoul::trimWhitespace(token);
script += "[[" + token + "]],";
script += token + ',';
}
}
script += "}";
script += '}';
executeScript(
p->fullyQualifiedIdentifier(),
fullIdentifier,
std::move(script),
isRegular
);
}
}
void renderDoubleListProperty(Property* prop, const std::string& ownerName,
IsRegularProperty isRegular, ShowToolTip showTooltip,
double tooltipDelay)
{
ghoul_assert(prop, "prop must not be nullptr");
DoubleListProperty* p = static_cast<DoubleListProperty*>(prop);
const std::string& name = p->guiName();
ImGui::PushID((ownerName + '.' + name).c_str());
std::string value;
p->getStringValue(value);
renderListProperty(name, p->fullyQualifiedIdentifier(), value, isRegular);
if (showTooltip) {
renderTooltip(prop, tooltipDelay);
}
ImGui::PopID();
}
void renderIntListProperty(Property* prop, const std::string& ownerName,
IsRegularProperty isRegular, ShowToolTip showTooltip,
double tooltipDelay)
{
ghoul_assert(prop, "prop must not be nullptr");
IntListProperty* p = static_cast<IntListProperty*>(prop);
const std::string& name = p->guiName();
ImGui::PushID((ownerName + '.' + name).c_str());
std::string value;
p->getStringValue(value);
renderListProperty(name, p->fullyQualifiedIdentifier(), value, isRegular);
if (showTooltip) {
renderTooltip(prop, tooltipDelay);
}
ImGui::PopID();
}
void renderStringListProperty(Property* prop, const std::string& ownerName,
IsRegularProperty isRegular, ShowToolTip showTooltip,
double tooltipDelay)
{
ghoul_assert(prop, "prop must not be nullptr");
StringListProperty* p = static_cast<StringListProperty*>(prop);
const std::string& name = p->guiName();
ImGui::PushID((ownerName + '.' + name).c_str());
std::string value;
p->getStringValue(value);
renderListProperty(name, p->fullyQualifiedIdentifier(), value, isRegular);
if (showTooltip) {
renderTooltip(prop, tooltipDelay);
}
ImGui::PopID();
}
@@ -320,7 +378,7 @@ void renderDoubleProperty(properties::Property* prop, const std::string& ownerNa
ghoul_assert(prop, "prop must not be nullptr");
DoubleProperty* p = static_cast<DoubleProperty*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
float value = static_cast<float>(*p);
float min = static_cast<float>(p->minValue());
@@ -352,7 +410,7 @@ void renderIntProperty(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
IntProperty* p = static_cast<IntProperty*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
IntProperty::ValueType value = *p;
int min = p->minValue();
@@ -377,7 +435,7 @@ void renderIVec2Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
IVec2Property* p = static_cast<IVec2Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
IVec2Property::ValueType value = *p;
int min = glm::compMin(p->minValue());
@@ -410,7 +468,7 @@ void renderIVec3Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
IVec3Property* p = static_cast<IVec3Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
IVec3Property::ValueType value = *p;
int min = glm::compMin(p->minValue());
@@ -443,7 +501,7 @@ void renderIVec4Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
IVec4Property* p = static_cast<IVec4Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
IVec4Property::ValueType value = *p;
int min = glm::compMin(p->minValue());
@@ -476,7 +534,7 @@ void renderFloatProperty(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
FloatProperty* p = static_cast<FloatProperty*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
FloatProperty::ValueType value = *p;
float min = p->minValue();
@@ -507,7 +565,7 @@ void renderVec2Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
Vec2Property* p = static_cast<Vec2Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
Vec2Property::ValueType value = *p;
float min = glm::compMin(p->minValue());
@@ -543,7 +601,7 @@ void renderVec3Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
Vec3Property* p = static_cast<Vec3Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
Vec3Property::ValueType value = *p;
float min = glm::compMin(p->minValue());
@@ -588,7 +646,7 @@ void renderVec4Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
Vec4Property* p = static_cast<Vec4Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
Vec4Property::ValueType value = *p;
float min = glm::compMin(p->minValue());
@@ -633,7 +691,7 @@ void renderDVec2Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
DVec2Property* p = static_cast<DVec2Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
glm::vec2 value = glm::dvec2(*p);
float min = static_cast<float>(glm::compMin(p->minValue()));
@@ -668,7 +726,7 @@ void renderDVec3Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
DVec3Property* p = static_cast<DVec3Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
glm::vec3 value = glm::dvec3(*p);
float min = static_cast<float>(glm::compMin(p->minValue()));
@@ -704,7 +762,7 @@ void renderDVec4Property(Property* prop, const std::string& ownerName,
ghoul_assert(prop, "prop must not be nullptr");
DVec4Property* p = static_cast<DVec4Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
glm::vec4 value = glm::dvec4(*p);
float min = static_cast<float>(glm::compMin(p->minValue()));
@@ -741,7 +799,7 @@ void renderDMat2Property(Property* prop, const std::string& ownerName,
DMat2Property* p = static_cast<DMat2Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
ImGui::Text("%s", name.c_str());
glm::mat2 value = glm::dmat2(*p);
@@ -798,7 +856,7 @@ void renderDMat3Property(Property* prop, const std::string& ownerName,
DMat3Property* p = static_cast<DMat3Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
ImGui::Text("%s", name.c_str());
glm::mat3 value = glm::dmat3(*p);
@@ -865,7 +923,7 @@ void renderDMat4Property(Property* prop, const std::string& ownerName,
DMat4Property* p = static_cast<DMat4Property*>(prop);
std::string name = p->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
ImGui::Text("%s", name.c_str());
glm::mat4 value = glm::dmat4(*p);
@@ -940,7 +998,7 @@ void renderTriggerProperty(Property* prop, const std::string& ownerName,
{
ghoul_assert(prop, "prop must not be nullptr");
std::string name = prop->guiName();
ImGui::PushID((ownerName + "." + name).c_str());
ImGui::PushID((ownerName + '.' + name).c_str());
bool pressed = ImGui::Button(name.c_str());
if (pressed) {
+33 -10
View File
@@ -115,9 +115,17 @@ bool DataCygnet::updateTexture() {
}
bool texturesReady = false;
const std::vector<int>& selectedOptions = _dataOptions.value();
const std::set<std::string>& selectedOptions = _dataOptions;
const std::vector<std::string>& options = _dataOptions.options();
std::vector<int> selectedOptionsIndices;
for (const std::string& option : selectedOptions) {
auto it = std::find(options.begin(), options.end(), option);
ghoul_assert(it != options.end(), "Selected option must be in all options");
int idx = static_cast<int>(std::distance(options.begin(), it));
selectedOptionsIndices.push_back(idx);
}
for (int option : selectedOptions) {
for (int option : selectedOptionsIndices) {
float* values = data[option];
if (!values) {
continue;
@@ -191,7 +199,16 @@ bool DataCygnet::readyToRender() const {
* ghoul::TextureUnit needs to be passed as an argument to both.
*/
void DataCygnet::setTextureUniforms() {
const std::vector<int>& selectedOptions = _dataOptions.value();
const std::set<std::string>& selectedOptions = _dataOptions;
const std::vector<std::string>& options = _dataOptions.options();
std::vector<int> selectedOptionsIndices;
for (const std::string& option : selectedOptions) {
auto it = std::find(options.begin(), options.end(), option);
ghoul_assert(it != options.end(), "Selected option must be in all options");
int idx = static_cast<int>(std::distance(options.begin(), it));
selectedOptionsIndices.push_back(idx);
}
int activeTextures = std::min(static_cast<int>(selectedOptions.size()), MaxTextures);
int activeTransferfunctions = std::min(
static_cast<int>(_transferFunctions.size()),
@@ -201,7 +218,7 @@ void DataCygnet::setTextureUniforms() {
// Set Textures
ghoul::opengl::TextureUnit txUnits[MaxTextures];
int j = 0;
for (int option : selectedOptions) {
for (int option : selectedOptionsIndices) {
if (_textures[option]) {
txUnits[j].activate();
_textures[option]->bind();
@@ -215,7 +232,7 @@ void DataCygnet::setTextureUniforms() {
}
if (activeTextures > 0 &&
selectedOptions.back() >= static_cast<int>(_transferFunctions.size()))
selectedOptionsIndices.back() >= static_cast<int>(_transferFunctions.size()))
{
activeTransferfunctions = 1;
}
@@ -230,7 +247,7 @@ void DataCygnet::setTextureUniforms() {
_shader->setUniform("transferFunctions[0]", tfUnits[0]);
}
else {
for (int option : selectedOptions) {
for (int option : selectedOptionsIndices) {
if (static_cast<int>(_transferFunctions.size()) >= option) {
tfUnits[j].activate();
_transferFunctions[option].bind();
@@ -259,7 +276,7 @@ void DataCygnet::readTransferFunctions(std::string tfPath) {
if (tfFile.is_open()) {
std::string line;
while (getline(tfFile, line)) {
tfs.emplace_back(absPath(line));
tfs.emplace_back(absPath(line).string());
}
tfFile.close();
@@ -277,7 +294,7 @@ void DataCygnet::fillOptions(const std::string& source) {
);
for (int i = 0; i < static_cast<int>(options.size()); i++) {
_dataOptions.addOption({ i, options[i] });
_dataOptions.addOption(options[i]);
_textures.push_back(nullptr);
}
@@ -288,7 +305,7 @@ void DataCygnet::fillOptions(const std::string& source) {
_dataOptions.setValue(g->dataOptionsValue());
}
else {
_dataOptions.setValue(std::vector<int>(1, 0));
_dataOptions.setValue({ options.front() });
}
}
@@ -332,7 +349,13 @@ void DataCygnet::subscribeToGroup() {
[&](const ghoul::Dictionary& dict) {
LDEBUG(identifier() + " Event dataOptionsChanged");
if (dict.hasValue<std::vector<int>>("dataOptions")) {
_dataOptions = dict.value<std::vector<int>>("dataOptions");
std::vector<int> idx = dict.value<std::vector<int>>("dataOptions");
std::vector<std::string> opts = _dataOptions.options();
std::set<std::string> selected;
for (int i : idx) {
selected.insert(opts[i]);
}
_dataOptions = selected;
}
}
);
+8 -8
View File
@@ -166,23 +166,23 @@ void IswaDataGroup::registerProperties() {
_dataOptions.onChange([this]() {
LDEBUG("Group " + identifier() + " published dataOptionsChanged");
ghoul::Dictionary dict;
dict.setValue("dataOptions", _dataOptions.value());
std::set<std::string> set = _dataOptions;
std::vector<std::string> vec(set.begin(), set.end());
dict.setValue("dataOptions", vec);
_groupEvent.publish("dataOptionsChanged", dict);
});
}
void IswaDataGroup::registerOptions(
const std::vector<properties::SelectionProperty::Option>& options)
{
void IswaDataGroup::registerOptions(const std::vector<std::string>& options) {
if (!_registered) {
registerProperties();
}
if (_dataOptions.options().empty()) {
for (properties::SelectionProperty::Option option : options) {
_dataOptions.addOption({ option.value, option.description });
for (const std::string& option : options) {
_dataOptions.addOption(option);
}
_dataOptions.setValue(std::vector<int>(1, 0));
_dataOptions.setValue({ options.front() });
}
}
@@ -198,7 +198,7 @@ void IswaDataGroup::createDataProcessor() {
}
}
std::vector<int> IswaDataGroup::dataOptionsValue() const {
std::set<std::string> IswaDataGroup::dataOptionsValue() const {
return _dataOptions;
}
+2 -3
View File
@@ -38,9 +38,8 @@ public:
IswaDataGroup(std::string name, std::string type);
~IswaDataGroup();
void registerOptions(
const std::vector<properties::SelectionProperty::Option>& options);
std::vector<int> dataOptionsValue() const;
void registerOptions(const std::vector<std::string>& options);
std::set<std::string> dataOptionsValue() const;
protected:
void registerProperties();
+6 -4
View File
@@ -67,7 +67,7 @@ void IswaKameleonGroup::clearGroup() {
clearFieldlines();
}
std::vector<int> IswaKameleonGroup::fieldlineValue() const {
std::set<std::string> IswaKameleonGroup::fieldlineValue() const {
return _fieldlines;
}
@@ -118,7 +118,7 @@ void IswaKameleonGroup::readFieldlinePaths(const std::string& indexFile) {
int i = 0;
for (json::iterator it = fieldlines.begin(); it != fieldlines.end(); ++it) {
_fieldlines.addOption({ i, it.key() });
_fieldlines.addOption(it.key());
_fieldlineState[i] = std::make_tuple<std::string, std::string, bool>(
identifier() + "/" + it.key(),
it.value(),
@@ -137,14 +137,16 @@ void IswaKameleonGroup::readFieldlinePaths(const std::string& indexFile) {
}
void IswaKameleonGroup::updateFieldlineSeeds() {
const std::vector<int>& options = _fieldlines.value();
const std::set<std::string>& options = _fieldlines;
std::vector<std::string> opts = _fieldlines.options();
// SeedPath == map<int selectionValue, tuple<string name, string path, bool active>>
using K = int;
using V = std::tuple<std::string, std::string, bool>;
for (std::pair<const K, V>& seedPath : _fieldlineState) {
// if this option was turned off
const auto it = std::find(options.begin(), options.end(), seedPath.first);
std::string o = opts[seedPath.first];
const auto it = std::find(options.begin(), options.end(), o);
if (it == options.end() && std::get<2>(seedPath.second)) {
LDEBUG("Removed fieldlines: " + std::get<0>(seedPath.second));
+1 -1
View File
@@ -36,7 +36,7 @@ public:
virtual void clearGroup() override;
std::vector<int> fieldlineValue() const;
std::set<std::string> fieldlineValue() const;
void setFieldlineInfo(std::string fieldlineIndexFile, std::string kameleonPath);
void changeCdf(std::string path);
+10 -10
View File
@@ -112,7 +112,7 @@ KameleonPlane::~KameleonPlane() {}
void KameleonPlane::deinitializeGL() {
IswaCygnet::deinitialize();
_fieldlines = std::vector<int>();
_fieldlines = std::set<std::string>();
}
void KameleonPlane::initializeGL() {
@@ -131,7 +131,7 @@ void KameleonPlane::initializeGL() {
initializeTime();
createGeometry();
readFieldlinePaths(absPath(_fieldlineIndexFile));
readFieldlinePaths(absPath(_fieldlineIndexFile).string());
if (_group) {
_dataProcessor = _group->dataProcessor();
@@ -268,16 +268,16 @@ void KameleonPlane::setUniforms() {
}
void KameleonPlane::updateFieldlineSeeds() {
std::vector<int> selectedOptions = _fieldlines.value();
std::set<std::string> selectedOptions = _fieldlines;
std::vector<std::string> opts = _fieldlines.options();
// seedPath == map<int selectionValue, tuple<string name, string path, bool active>>
for (auto& seedPath : _fieldlineState) {
using K = int;
using V = std::tuple<std::string, std::string, bool>;
for (std::pair<const K, V>& seedPath : _fieldlineState) {
// if this option was turned off
const auto it = std::find(
selectedOptions.begin(),
selectedOptions.end(),
seedPath.first
);
std::string o = opts[seedPath.first];
const auto it = std::find(selectedOptions.begin(), selectedOptions.end(), o);
if (it == selectedOptions.end() && std::get<2>(seedPath.second)) {
SceneGraphNode* n = global::renderEngine->scene()->sceneGraphNode(
std::get<0>(seedPath.second)
@@ -336,7 +336,7 @@ void KameleonPlane::readFieldlinePaths(const std::string& indexFile) {
const std::string& fullName = identifier();
std::string partName = fullName.substr(0,fullName.find_last_of("-"));
for (json::iterator it = fieldlines.begin(); it != fieldlines.end(); ++it) {
_fieldlines.addOption({i, it.key()});
_fieldlines.addOption(it.key());
_fieldlineState[i] = std::make_tuple<std::string, std::string, bool>(
partName + "/" + it.key(),
it.value(),
+14 -10
View File
@@ -73,11 +73,10 @@ void DataProcessorJson::addDataValues(const std::string& data,
std::vector<float> sum(numOptions, 0.f);
std::vector<std::vector<float>> optionValues(numOptions, std::vector<float>());
const std::vector<properties::SelectionProperty::Option>& options =
dataOptions.options();
const std::vector<std::string>& options = dataOptions.options();
for (int i = 0; i < numOptions; ++i) {
const json& row = variables[options[i].description];
const json& row = variables[options[i]];
// int rowsize = row.size();
for (size_t y = 0; y < row.size(); ++y) {
@@ -108,18 +107,23 @@ std::vector<float*> DataProcessorJson::processData(const std::string& data,
const json& j = json::parse(data);
json variables = j["variables"];
const std::vector<int>& selectedOptions = optionProp;
const std::vector<properties::SelectionProperty::Option>& options =
optionProp.options();
const std::set<std::string>& selectedOptions = optionProp;
const std::vector<std::string>& options = optionProp.options();
std::vector<int> selectedOptionsIndices;
for (const std::string& option : selectedOptions) {
auto it = std::find(options.begin(), options.end(), option);
ghoul_assert(it != options.end(), "Selected option must be in all options");
int idx = static_cast<int>(std::distance(options.begin(), it));
selectedOptionsIndices.push_back(idx);
}
std::vector<float*> dataOptions(options.size(), nullptr);
for (int option : selectedOptions) {
for (int option : selectedOptionsIndices) {
// @CLEANUP: This memory is very easy to lose and should be replaced by some
// other mechanism (std::vector<float> most likely)
dataOptions[option] = new float[dimensions.x * dimensions.y] { 0.f };
json row = variables[options[option].description];
json row = variables[options[option]];
const int rowsize = static_cast<int>(row.size());
for (int y = 0; y < rowsize; ++y) {
@@ -135,7 +139,7 @@ std::vector<float*> DataProcessorJson::processData(const std::string& data,
}
}
calculateFilterValues(selectedOptions);
calculateFilterValues(selectedOptionsIndices);
return dataOptions;
}
+18 -13
View File
@@ -31,6 +31,7 @@
#include <ghoul/filesystem/file.h>
#include <ghoul/filesystem/filesystem.h>
#include <algorithm>
#include <filesystem>
namespace openspace {
@@ -80,15 +81,14 @@ void DataProcessorKameleon::addDataValues(const std::string& path,
std::vector<float> sum(numOptions, 0.f);
std::vector<std::vector<float>> optionValues(numOptions, std::vector<float>());
const std::vector<properties::SelectionProperty::Option>& options =
dataOptions.options();
const std::vector<std::string>& options = dataOptions.options();
const int numValues = static_cast<int>(_dimensions.x * _dimensions.y * _dimensions.z);
for (int i = 0; i < numOptions; ++i) {
//0.5 to gather interesting values for the normalization/histograms.
float* values = _kw->uniformSliceValues(
options[i].description,
options[i],
_dimensions,
0.5f
);
@@ -120,17 +120,22 @@ std::vector<float*> DataProcessorKameleon::processData(const std::string& path,
initializeKameleonWrapper(path);
}
const std::vector<int>& selectedOptions = optionProp;
const std::vector<properties::SelectionProperty::Option>& options =
optionProp.options();
const std::set<std::string>& selectedOptions = optionProp;
const std::vector<std::string>& options = optionProp.options();
std::vector<int> selectedOptionsIndices;
for (const std::string& option : selectedOptions) {
auto it = std::find(options.begin(), options.end(), option);
ghoul_assert(it != options.end(), "Selected option must be in all options");
int idx = static_cast<int>(std::distance(options.begin(), it));
selectedOptionsIndices.push_back(idx);
}
const int numValues = static_cast<int>(glm::compMul(dimensions));
std::vector<float*> dataOptions(numOptions, nullptr);
for (int option : selectedOptions) {
for (int option : selectedOptionsIndices) {
dataOptions[option] = _kw->uniformSliceValues(
options[option].description,
options[option],
dimensions,
_slice
);
@@ -141,7 +146,7 @@ std::vector<float*> DataProcessorKameleon::processData(const std::string& path,
}
}
calculateFilterValues(selectedOptions);
calculateFilterValues(selectedOptionsIndices);
return dataOptions;
}
@@ -154,14 +159,14 @@ void DataProcessorKameleon::setDimensions(glm::size3_t dimensions) {
}
void DataProcessorKameleon::initializeKameleonWrapper(std::string path) {
const std::string& extension = ghoul::filesystem::File(absPath(path)).fileExtension();
if (FileSys.fileExists(absPath(path)) && extension == "cdf") {
std::filesystem::path extension = std::filesystem::path(absPath(path)).extension();
if (std::filesystem::is_regular_file(absPath(path)) && extension == ".cdf") {
if (_kw) {
_kw->close();
}
_kwPath = std::move(path);
_kw = std::make_shared<KameleonWrapper>(absPath(_kwPath));
_kw = std::make_shared<KameleonWrapper>(absPath(_kwPath).string());
}
}
+21 -7
View File
@@ -142,6 +142,10 @@ std::vector<float*> DataProcessorText::processData(const std::string& data,
properties::SelectionProperty& options,
glm::size3_t& dimensions)
{
// The update of the selection properties broke this and we don't have the data to
// actually test whether this update works. So if you are getting a crash around here
// this is why
if (data.empty()) {
return std::vector<float*>();
}
@@ -149,11 +153,20 @@ std::vector<float*> DataProcessorText::processData(const std::string& data,
std::string line;
std::stringstream memorystream(data);
const std::vector<int>& selectedOptions = options.value();
const std::set<std::string>& selectedOptions = options.value();
const std::vector<std::string>& allOptions = options.options();
std::vector<int> selectedOptionsIndices;
std::vector<float*> dataOptions(options.options().size(), nullptr);
for (int o : selectedOptions) {
dataOptions[o] = new float[dimensions.x * dimensions.y] { 0.f };
for (const std::string& o : selectedOptions) {
auto it = std::find(allOptions.begin(), allOptions.end(), o);
ghoul_assert(
it != allOptions.end(),
"Selected option must be in list of all options"
);
int idx = static_cast<int>(std::distance(allOptions.begin(), it));
selectedOptionsIndices.push_back(idx);
dataOptions[idx] = new float[dimensions.x * dimensions.y] { 0.f };
}
int numValues = 0;
@@ -172,11 +185,11 @@ std::vector<float*> DataProcessorText::processData(const std::string& data,
last = (last > 0)? last : lineSize;
const auto it = std::find(
selectedOptions.begin(),
selectedOptions.end(),
selectedOptionsIndices.begin(),
selectedOptionsIndices.end(),
option
);
if (option >= 0 && it != selectedOptions.end()) {
if (option >= 0 && it != selectedOptionsIndices.end()) {
const float value = std::stof(line.substr(first, last));
dataOptions[option][numValues] = processDataPoint(value, option);
}
@@ -187,9 +200,10 @@ std::vector<float*> DataProcessorText::processData(const std::string& data,
numValues++;
}
calculateFilterValues(selectedOptions);
calculateFilterValues(selectedOptionsIndices);
return dataOptions;
//#endif
}
} //namespace openspace
+15 -16
View File
@@ -42,6 +42,7 @@
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/constexpr.h>
#include <filesystem>
#include <fstream>
#include "iswamanager_lua.inl"
@@ -410,10 +411,9 @@ std::string IswaManager::parseKWToLuaTable(const CdfInfo& info, const std::strin
return "";
}
const std::string& extension =
ghoul::filesystem::File(absPath(info.path)).fileExtension();
if (extension == "cdf") {
KameleonWrapper kw = KameleonWrapper(absPath(info.path));
std::filesystem::path ext = std::filesystem::path(absPath(info.path)).extension();
if (ext == ".cdf") {
KameleonWrapper kw = KameleonWrapper(absPath(info.path).string());
std::string parent = kw.parent();
std::string frame = kw.frame();
@@ -587,10 +587,8 @@ void IswaManager::createSphere(MetadataFuture& data) {
}
void IswaManager::createKameleonPlane(CdfInfo info, std::string cut) {
const std::string& extension = ghoul::filesystem::File(
absPath(info.path)
).fileExtension();
if (FileSys.fileExists(absPath(info.path)) && extension == "cdf") {
std::filesystem::path ext = std::filesystem::path(absPath(info.path)).extension();
if (std::filesystem::is_regular_file(absPath(info.path)) && ext == ".cdf") {
if (!info.group.empty()) {
std::string type = typeid(KameleonPlane).name();
registerGroup(info.group, type);
@@ -621,16 +619,17 @@ void IswaManager::createKameleonPlane(CdfInfo info, std::string cut) {
}
}
else {
LWARNING( absPath(info.path) + " is not a cdf file or can't be found.");
LWARNING(
fmt::format("{} is not a cdf file or can't be found", absPath(info.path))
);
}
}
void IswaManager::createFieldline(std::string name, std::string cdfPath,
std::string seedPath)
{
const std::string& ext = ghoul::filesystem::File(absPath(cdfPath)).fileExtension();
if (FileSys.fileExists(absPath(cdfPath)) && ext == "cdf") {
std::filesystem::path ext = std::filesystem::path(absPath(cdfPath)).extension();
if (std::filesystem::is_regular_file(absPath(cdfPath)) && ext == ".cdf") {
std::string luaTable = "{"
"Name = '" + name + "',"
"Parent = 'Earth',"
@@ -700,10 +699,10 @@ ghoul::Event<>& IswaManager::iswaEvent() {
}
void IswaManager::addCdfFiles(std::string cdfpath) {
cdfpath = absPath(cdfpath);
if (FileSys.fileExists(cdfpath)) {
std::filesystem::path cdf = absPath(cdfpath);
if (std::filesystem::is_regular_file(cdf)) {
//std::string basePath = path.substr(0, path.find_last_of("/\\"));
std::ifstream jsonFile(cdfpath);
std::ifstream jsonFile(cdf);
if (jsonFile.is_open()) {
json cdfGroups = json::parse(jsonFile);
@@ -742,7 +741,7 @@ void IswaManager::addCdfFiles(std::string cdfpath) {
}
}
else {
LWARNING(cdfpath + " is not a cdf file or can't be found.");
LWARNING(fmt::format("{} is not a cdf file or can't be found", cdf));
}
}
+2 -1
View File
@@ -31,6 +31,7 @@
#include <ghoul/glm.h>
#include <ghoul/misc/assert.h>
#include <ghoul/misc/misc.h>
#include <filesystem>
#ifdef WIN32
#pragma warning (push)
@@ -113,7 +114,7 @@ KameleonWrapper::~KameleonWrapper() {
bool KameleonWrapper::open(const std::string& filename) {
close();
if (!FileSys.fileExists(filename)) {
if (!std::filesystem::is_regular_file(filename)) {
return false;
}
@@ -31,6 +31,7 @@
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <filesystem>
#ifdef WIN32
#pragma warning (push)
@@ -75,7 +76,7 @@ namespace {
namespace openspace::kameleonvolume {
KameleonVolumeReader::KameleonVolumeReader(std::string path) : _path(std::move(path)) {
if (!FileSys.fileExists(_path)) {
if (!std::filesystem::is_regular_file(_path)) {
throw ghoul::FileNotFoundError(_path);
}
@@ -45,6 +45,7 @@
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/opengl/texture.h>
#include <filesystem>
namespace {
constexpr const char* _loggerCat = "RenderableKameleonVolume";
@@ -172,7 +173,7 @@ RenderableKameleonVolume::RenderableKameleonVolume(const ghoul::Dictionary& dict
}
if (dictionary.hasValue<std::string>(KeySource)) {
_sourcePath = absPath(dictionary.value<std::string>(KeySource));
_sourcePath = absPath(dictionary.value<std::string>(KeySource)).string();
}
if (dictionary.hasValue<std::string>(KeyVariable)) {
@@ -323,7 +324,7 @@ bool RenderableKameleonVolume::isCachingEnabled() const {
}
void RenderableKameleonVolume::load() {
if (!FileSys.fileExists(ghoul::filesystem::File(_sourcePath))) {
if (!std::filesystem::is_regular_file(_sourcePath.value())) {
LERROR(fmt::format("File '{}' does not exist", _sourcePath.value()));
return;
}
@@ -331,13 +332,11 @@ void RenderableKameleonVolume::load() {
loadFromPath(_sourcePath);
return;
}
ghoul::filesystem::File sourceFile(_sourcePath);
std::string cachePath = FileSys.cacheManager()->cachedFilename(
sourceFile.baseName(),
cacheSuffix(),
ghoul::filesystem::CacheManager::Persistent::Yes
std::filesystem::path(_sourcePath.value()).stem(),
cacheSuffix()
);
if (FileSys.fileExists(cachePath)) {
if (std::filesystem::is_regular_file(cachePath)) {
loadRaw(cachePath);
}
else {
@@ -352,15 +351,8 @@ std::string RenderableKameleonVolume::cacheSuffix() const {
}
void RenderableKameleonVolume::loadFromPath(const std::string& path) {
ghoul::filesystem::File file(path);
std::string extension = file.fileExtension();
std::transform(
extension.begin(),
extension.end(),
extension.begin(),
[](char v) { return static_cast<char>(tolower(v)); }
);
if (extension == "cdf") {
std::filesystem::path extension = std::filesystem::path(path).extension();
if (extension == ".cdf" || extension == ".CDF") {
loadCdf(path);
}
else {
@@ -29,30 +29,39 @@
#include <openspace/documentation/verifier.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/misc/dictionaryjsonformatter.h>
#include <filesystem>
#include <fstream>
namespace {
constexpr const char* KeyInput = "Input";
constexpr const char* KeyOutput = "Output";
constexpr const char* MainTemplateFilename = "${WEB}/kameleondocumentation/main.hbs";
constexpr const char* HandlebarsFilename = "${WEB}/common/handlebars-v4.0.5.js";
constexpr const char* JsFilename = "${WEB}/kameleondocumentation/script.js";
constexpr const char* BootstrapFilename = "${WEB}/common/bootstrap.min.css";
constexpr const char* CssFilename = "${WEB}/common/style.css";
struct [[codegen::Dictionary(KameleonDocumentationTask)]] Parameters {
// The CDF file to extract data from
std::filesystem::path input;
// The HTML file to write documentation to
std::string output [[codegen::annotation("A valid filepath")]];
};
#include "kameleondocumentationtask_codegen.cpp"
} // namespace
namespace openspace::kameleonvolume {
documentation::Documentation KameleonDocumentationTask::documentation() {
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "kameleon_documentation_task";
return doc;
}
KameleonDocumentationTask::KameleonDocumentationTask(const ghoul::Dictionary& dictionary)
{
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"KameleonDocumentationTask"
);
_inputPath = absPath(dictionary.value<std::string>(KeyInput));
_outputPath = absPath(dictionary.value<std::string>(KeyOutput));
const Parameters p = codegen::bake<Parameters>(dictionary);
_inputPath = absPath(p.input.string());
_outputPath = absPath(p.output);
}
std::string KameleonDocumentationTask::description() {
@@ -63,14 +72,14 @@ std::string KameleonDocumentationTask::description() {
}
void KameleonDocumentationTask::perform(const Task::ProgressCallback & progressCallback) {
KameleonVolumeReader reader(_inputPath);
KameleonVolumeReader reader(_inputPath.string());
ghoul::Dictionary kameleonDictionary = reader.readMetaData();
progressCallback(0.33f);
ghoul::Dictionary dictionary;
dictionary.setValue("kameleon", std::move(kameleonDictionary));
dictionary.setValue("version", std::string(OPENSPACE_VERSION_NUMBER));
dictionary.setValue("input", _inputPath);
dictionary.setValue("input", _inputPath.string());
std::string json = ghoul::formatJson(dictionary);
progressCallback(0.66f);
@@ -146,26 +155,4 @@ void KameleonDocumentationTask::perform(const Task::ProgressCallback & progressC
progressCallback(1.0f);
}
documentation::Documentation KameleonDocumentationTask::documentation() {
using namespace documentation;
return {
"KameleonDocumentationTask",
"kameleon_documentation_task",
{
{
KeyInput,
new StringAnnotationVerifier("A file path to a cdf file"),
Optional::No,
"The cdf file to extract data from"
},
{
KeyOutput,
new StringAnnotationVerifier("A valid filepath"),
Optional::No,
"The html file to write documentation to"
}
}
};
}
} // namespace openspace::kameleonvolume
@@ -27,6 +27,7 @@
#include <openspace/util/task.h>
#include <filesystem>
#include <string>
namespace openspace::kameleonvolume {
@@ -41,8 +42,8 @@ public:
static documentation::Documentation documentation();
private:
std::string _inputPath;
std::string _outputPath;
std::filesystem::path _inputPath;
std::filesystem::path _outputPath;
};
} // namespace openspace::kameleonvolume
@@ -29,26 +29,34 @@
#include <ghoul/fmt.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/misc/dictionaryjsonformatter.h>
#include <filesystem>
#include <fstream>
namespace {
constexpr const char* KeyInput = "Input";
constexpr const char* KeyOutput = "Output";
struct [[codegen::Dictionary(KameleonMetadataToJsonTask)]] Parameters {
// The CDF file to extract data from
std::filesystem::path input;
// The JSON file to export data into
std::string output [[codegen::annotation("A valid filepath")]];
};
#include "kameleonmetadatatojsontask_codegen.cpp"
} // namespace
namespace openspace::kameleonvolume {
documentation::Documentation KameleonMetadataToJsonTask::documentation() {
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "kameleon_metadata_to_json_task";
return doc;
}
KameleonMetadataToJsonTask::KameleonMetadataToJsonTask(
const ghoul::Dictionary& dictionary)
{
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"KameleonMetadataToJsonTask"
);
_inputPath = absPath(dictionary.value<std::string>(KeyInput));
_outputPath = absPath(dictionary.value<std::string>(KeyOutput));
const Parameters p = codegen::bake<Parameters>(dictionary);
_inputPath = absPath(p.input.string());
_outputPath = absPath(p.output);
}
std::string KameleonMetadataToJsonTask::description() {
@@ -59,7 +67,7 @@ std::string KameleonMetadataToJsonTask::description() {
}
void KameleonMetadataToJsonTask::perform(const Task::ProgressCallback& progressCallback) {
KameleonVolumeReader reader(_inputPath);
KameleonVolumeReader reader(_inputPath.string());
ghoul::Dictionary dictionary = reader.readMetaData();
progressCallback(0.5f);
@@ -69,26 +77,4 @@ void KameleonMetadataToJsonTask::perform(const Task::ProgressCallback& progressC
progressCallback(1.0f);
}
documentation::Documentation KameleonMetadataToJsonTask::documentation() {
using namespace documentation;
return {
"KameleonMetadataToJsonTask",
"kameleon_metadata_to_json_task",
{
{
KeyInput,
new StringAnnotationVerifier("A file path to a cdf file"),
Optional::No,
"The cdf file to extract data from"
},
{
KeyOutput,
new StringAnnotationVerifier("A valid filepath"),
Optional::No,
"The JSON file to export data into"
}
}
};
}
} // namespace openspace::kameleonvolume
@@ -27,6 +27,7 @@
#include <openspace/util/task.h>
#include <filesystem>
#include <string>
namespace openspace::kameleonvolume {
@@ -41,8 +42,8 @@ public:
static documentation::Documentation documentation();
private:
std::string _inputPath;
std::string _outputPath;
std::filesystem::path _inputPath;
std::filesystem::path _outputPath;
};
} // namespace openspace::kameleonvolume
@@ -31,13 +31,11 @@
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/misc/dictionaryluaformatter.h>
#include <array>
#include <filesystem>
#include <optional>
namespace {
constexpr const char* KeyInput = "Input";
constexpr const char* KeyRawVolumeOutput = "RawVolumeOutput";
constexpr const char* KeyDictionaryOutput = "DictionaryOutput";
constexpr const char* KeyDimensions = "Dimensions";
constexpr const char* KeyVariable = "Variable";
constexpr const char* KeyTime = "Time";
constexpr const char* KeyLowerDomainBound = "LowerDomainBound";
constexpr const char* KeyUpperDomainBound = "UpperDomainBound";
@@ -46,92 +44,64 @@ namespace {
constexpr const char* KeyMaxValue = "MaxValue";
constexpr const char* KeyVisUnit = "VisUnit";
struct [[codegen::Dictionary(KameleonVolumeToRawTask)]] Parameters {
// The cdf file to extract data from
std::filesystem::path input;
// The raw volume file to export data to
std::string rawVolumeOutput [[codegen::annotation("A valid filepath")]];
// The Lua dictionary file to export metadata to
std::string dictionaryOutput [[codegen::annotation("A valid filepath")]];
// The variable name to read from the kameleon dataset
std::string variable [[codegen::annotation("A valid kameleon variable")]];
// A vector representing the number of cells in each dimension
glm::ivec3 dimensions;
// A vector representing the lower bound of the domain, in the native kameleon
// grid units
std::optional<glm::vec3> lowerDomainBound;
// A vector representing the lower bound of the domain, in the native kameleon
// grid units
std::optional<glm::vec3> upperDomainBound;
// The unit of the data
std::optional<std::string> visUnit
[[codegen::annotation("A valid kameleon unit")]];
};
#include "kameleonvolumetorawtask_codegen.cpp"
} // namespace
namespace openspace::kameleonvolume {
documentation::Documentation KameleonVolumeToRawTask::documentation() {
using namespace documentation;
return {
"KameleonVolumeToRawTask",
"kameleon_metadata_to_json_task",
{
{
KeyInput,
new StringAnnotationVerifier("A file path to a cdf file"),
Optional::No,
"The cdf file to extract data from",
},
{
KeyRawVolumeOutput,
new StringAnnotationVerifier("A valid filepath"),
Optional::No,
"The raw volume file to export data to",
},
{
KeyDictionaryOutput,
new StringAnnotationVerifier("A valid filepath"),
Optional::No,
"The lua dictionary file to export metadata to",
},
{
KeyVariable,
new StringAnnotationVerifier("A valid kameleon variable"),
Optional::No,
"The variable name to read from the kameleon dataset",
},
{
KeyDimensions,
new DoubleVector3Verifier,
Optional::No,
"A vector representing the number of cells in each dimension",
},
{
KeyLowerDomainBound,
new DoubleVector3Verifier,
Optional::Yes,
"A vector representing the lower bound of the domain, "
"in the native kameleon grid units",
},
{
KeyUpperDomainBound,
new DoubleVector3Verifier,
Optional::Yes,
"A vector representing the lower bound of the domain, "
"in the native kameleon grid units"
},
{
KeyVisUnit,
new StringAnnotationVerifier("A valid kameleon unit"),
Optional::Yes,
"The unit of the data",
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "kameleon_metadata_to_json_task";
return doc;
}
KameleonVolumeToRawTask::KameleonVolumeToRawTask(const ghoul::Dictionary& dictionary) {
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"KameleonVolumeToRawTask"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
_inputPath = absPath(dictionary.value<std::string>(KeyInput));
_rawVolumeOutputPath = absPath(dictionary.value<std::string>(KeyRawVolumeOutput));
_dictionaryOutputPath = absPath(dictionary.value<std::string>(KeyDictionaryOutput));
_variable = dictionary.value<std::string>(KeyVariable);
_dimensions = glm::uvec3(dictionary.value<glm::dvec3>(KeyDimensions));
_inputPath = absPath(p.input.string());
_rawVolumeOutputPath = absPath(p.rawVolumeOutput);
_dictionaryOutputPath = absPath(p.dictionaryOutput);
_variable = p.variable;
_dimensions = p.dimensions;
if (dictionary.hasKey(KeyLowerDomainBound)) {
_lowerDomainBound = dictionary.value<glm::dvec3>(KeyLowerDomainBound);
if (p.lowerDomainBound.has_value()) {
_lowerDomainBound = *p.lowerDomainBound;
}
else {
_autoDomainBounds = true;
}
if (dictionary.hasKey(KeyUpperDomainBound)) {
_upperDomainBound = dictionary.value<glm::dvec3>(KeyUpperDomainBound);
if (p.upperDomainBound.has_value()) {
_upperDomainBound = *p.upperDomainBound;
}
else {
_autoDomainBounds = true;
@@ -147,7 +117,7 @@ std::string KameleonVolumeToRawTask::description() {
}
void KameleonVolumeToRawTask::perform(const Task::ProgressCallback& progressCallback) {
KameleonVolumeReader reader(_inputPath);
KameleonVolumeReader reader(_inputPath.string());
std::array<std::string, 3> variables = reader.gridVariableNames();
@@ -174,7 +144,7 @@ void KameleonVolumeToRawTask::perform(const Task::ProgressCallback& progressCall
progressCallback(0.5f);
volume::RawVolumeWriter<float> writer(_rawVolumeOutputPath);
volume::RawVolumeWriter<float> writer(_rawVolumeOutputPath.string());
writer.write(*rawVolume);
progressCallback(0.9f);
@@ -28,6 +28,7 @@
#include <openspace/util/task.h>
#include <ghoul/glm.h>
#include <filesystem>
#include <string>
namespace openspace::kameleonvolume {
@@ -42,9 +43,9 @@ public:
static documentation::Documentation documentation();
private:
std::string _inputPath;
std::string _rawVolumeOutputPath;
std::string _dictionaryOutputPath;
std::filesystem::path _inputPath;
std::filesystem::path _rawVolumeOutputPath;
std::filesystem::path _dictionaryOutputPath;
std::string _variable;
std::string _units;
@@ -54,6 +54,7 @@
#include <ghoul/opengl/texture.h>
#include <algorithm>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iterator>
@@ -170,7 +171,7 @@ RenderableMultiresVolume::RenderableMultiresVolume(const ghoul::Dictionary& dict
, _scaling(ScalingInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(10.f))
{
if (dictionary.hasValue<std::string>(KeyDataSource)) {
_filename = absPath(dictionary.value<std::string>(KeyDataSource));
_filename = absPath(dictionary.value<std::string>(KeyDataSource)).string();
}
else {
LERROR(fmt::format("Node did not contain a valid '{}'", KeyDataSource));
@@ -222,7 +223,7 @@ RenderableMultiresVolume::RenderableMultiresVolume(const ghoul::Dictionary& dict
if (dictionary.hasValue<std::string>(KeyTransferFunction)) {
_transferFunctionPath = absPath(
dictionary.value<std::string>(KeyTransferFunction)
);
).string();
_transferFunction = std::make_shared<TransferFunction>(_transferFunctionPath);
}
else {
@@ -454,14 +455,12 @@ bool RenderableMultiresVolume::initializeSelector() {
switch (_selector) {
case Selector::TF:
if (_errorHistogramManager) {
std::stringstream cacheName;
ghoul::filesystem::File f = _filename;
cacheName << f.baseName() << "_" << nHistograms << "_errorHistograms";
std::string cacheFilename;
cacheFilename = FileSys.cacheManager()->cachedFilename(
cacheName.str(),
"",
ghoul::filesystem::CacheManager::Persistent::Yes
std::string cacheFilename = FileSys.cacheManager()->cachedFilename(
fmt::format(
"{}_{}_errorHistograms",
std::filesystem::path(_filename).stem().string(), nHistograms
),
""
);
std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary);
if (cacheFile.is_open()) {
@@ -472,12 +471,14 @@ bool RenderableMultiresVolume::initializeSelector() {
);
success &= _errorHistogramManager->loadFromFile(cacheFilename);
}
else if (_errorHistogramsPath != "") {
else if (!_errorHistogramsPath.empty()) {
// Read histograms from scene data.
LINFO(fmt::format(
"Loading histograms from scene data: {}", _errorHistogramsPath
));
success &= _errorHistogramManager->loadFromFile(_errorHistogramsPath);
success &= _errorHistogramManager->loadFromFile(
_errorHistogramsPath.string()
);
}
else {
// Build histograms from tsp file.
@@ -494,14 +495,11 @@ bool RenderableMultiresVolume::initializeSelector() {
case Selector::SIMPLE:
if (_histogramManager) {
std::stringstream cacheName;
ghoul::filesystem::File f = _filename;
cacheName << f.baseName() << "_" << nHistograms << "_histograms";
std::string cacheFilename;
cacheFilename = FileSys.cacheManager()->cachedFilename(
cacheName.str(),
"",
ghoul::filesystem::CacheManager::Persistent::Yes
std::string cacheFilename = FileSys.cacheManager()->cachedFilename(
fmt::format("{}_{}_histogram",
std::filesystem::path(_filename).stem().string(), nHistograms
),
""
);
std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary);
if (cacheFile.is_open()) {
@@ -528,12 +526,12 @@ bool RenderableMultiresVolume::initializeSelector() {
case Selector::LOCAL:
if (_localErrorHistogramManager) {
ghoul::filesystem::File f = _filename;
std::string cacheFilename;
cacheFilename = FileSys.cacheManager()->cachedFilename(
fmt::format("{}_{}_localErrorHistograms", f.baseName(), nHistograms),
"",
ghoul::filesystem::CacheManager::Persistent::Yes
std::string cacheFilename = FileSys.cacheManager()->cachedFilename(
fmt::format(
"{}_{}_localErrorHistograms",
std::filesystem::path(_filename).stem().string(), nHistograms
),
""
);
std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary);
if (cacheFile.is_open()) {
@@ -628,8 +626,9 @@ void RenderableMultiresVolume::update(const UpdateData& data) {
// Make sure that the directory exists
ghoul::filesystem::File file(_statsFileName);
ghoul::filesystem::Directory directory(file.directoryName());
FileSys.createDirectory(directory, ghoul::filesystem::FileSystem::Recursive::Yes);
std::filesystem::path directory =
std::filesystem::path(_statsFileName).parent_path();
std::filesystem::create_directories(directory);
std::ofstream ofs(_statsFileName, std::ofstream::out);
@@ -33,6 +33,7 @@
#include <openspace/properties/scalar/intproperty.h>
#include <openspace/properties/vector/vec3property.h>
#include <chrono>
#include <filesystem>
namespace ghoul { class Dictionary; }
namespace ghoul::filesystem { class File; }
@@ -122,7 +123,7 @@ private:
std::string _volumeName;
std::string _transferFunctionPath;
std::string _errorHistogramsPath;
std::filesystem::path _errorHistogramsPath;
std::shared_ptr<TransferFunction> _transferFunction;
+5 -8
View File
@@ -30,6 +30,7 @@
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/logging/logmanager.h>
#include <filesystem>
#include <numeric>
#include <queue>
@@ -506,11 +507,9 @@ bool TSP::readCache() {
if (!FileSys.cacheManager())
return false;
ghoul::filesystem::File f = _filename;
std::string cacheFilename = FileSys.cacheManager()->cachedFilename(
f.baseName(),
"",
ghoul::filesystem::CacheManager::Persistent::Yes
std::filesystem::path(_filename).stem(),
""
);
std::ifstream file(cacheFilename, std::ios::in | std::ios::binary);
@@ -546,11 +545,9 @@ bool TSP::writeCache() {
return false;
}
ghoul::filesystem::File f = _filename;
std::string cacheFilename = FileSys.cacheManager()->cachedFilename(
f.baseName(),
"",
ghoul::filesystem::CacheManager::Persistent::Yes
std::filesystem::path(_filename).stem(),
""
);
std::ofstream file(cacheFilename, std::ios::out | std::ios::binary);
+1 -1
View File
@@ -27,8 +27,8 @@
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/stringlistproperty.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/list/stringlistproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/intproperty.h>
@@ -78,6 +78,10 @@ namespace {
return std::to_string(value.get<double>());
}
else if (value.is_array()) {
if (value.empty()) {
return "{}";
}
std::string literal = "{";
for (nlohmann::json::iterator it = value.begin(); it != value.end(); ++it) {
literal += luaLiteralFromJson(it.value()) += ",";
@@ -87,6 +91,10 @@ namespace {
return literal;
}
else if (value.is_object()) {
if (value.empty()) {
return "{}";
}
std::string literal = "{";
for (nlohmann::json::iterator it = value.begin(); it != value.end(); ++it) {
literal += it.key() + "=" + luaLiteralFromJson(it.value()) += ",";
+2
View File
@@ -25,6 +25,7 @@
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
set(HEADER_FILES
speckloader.h
rendering/planetgeometry.h
rendering/renderableconstellationbounds.h
rendering/renderablehabitablezone.h
@@ -43,6 +44,7 @@ set(HEADER_FILES
source_group("Header Files" FILES ${HEADER_FILES})
set(SOURCE_FILES
speckloader.cpp
rendering/planetgeometry.cpp
rendering/renderableconstellationbounds.cpp
rendering/renderablehabitablezone.cpp
@@ -31,6 +31,7 @@
#include <openspace/util/updatestructures.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/misc.h>
#include <ghoul/opengl/programobject.h>
#include <fstream>
#include <optional>
@@ -135,18 +136,11 @@ RenderableConstellationBounds::RenderableConstellationBounds(
addProperty(_constellationSelection);
if (p.constellationSelection.has_value()) {
std::vector<properties::SelectionProperty::Option> options =
_constellationSelection.options();
const std::vector<std::string> options = _constellationSelection.options();
std::vector<int> selectedIndices;
std::set<std::string> selectedNames;
for (const std::string& s : *p.constellationSelection) {
const auto it = std::find_if(
options.begin(),
options.end(),
[&s](const properties::SelectionProperty::Option& o) {
return o.description == s;
}
);
const auto it = std::find(options.begin(), options.end(), s);
if (it == options.end()) {
// The user has specified a constellation name that doesn't exist
LWARNINGC(
@@ -155,15 +149,10 @@ RenderableConstellationBounds::RenderableConstellationBounds(
);
}
else {
// If the found the option, we push the index of the found value into the
// array
selectedIndices.push_back(static_cast<int>(
std::distance(options.begin(), it)
));
selectedNames.insert(s);
}
}
_constellationSelection = selectedIndices;
_constellationSelection = selectedNames;
}
}
@@ -244,7 +233,7 @@ bool RenderableConstellationBounds::loadVertexFile() {
return false;
}
std::string fileName = absPath(_vertexFilename);
std::filesystem::path fileName = absPath(_vertexFilename);
std::ifstream file;
file.open(fileName);
if (!file.good()) {
@@ -284,7 +273,7 @@ bool RenderableConstellationBounds::loadVertexFile() {
LERRORC(
"RenderableConstellationBounds",
fmt::format(
"Error reading file '{}' at line #{}", fileName, currentLineNumber
"Error reading file {} at line #{}", fileName, currentLineNumber
)
);
break;
@@ -377,7 +366,11 @@ bool RenderableConstellationBounds::loadConstellationFile() {
}
// Update the constellations full name
s >> it->constellationFullName;
std::string fullName;
std::getline(s, fullName);
ghoul::trimWhitespace(fullName);
it->constellationFullName = std::move(fullName);
++index;
}
@@ -385,32 +378,23 @@ bool RenderableConstellationBounds::loadConstellationFile() {
}
void RenderableConstellationBounds::fillSelectionProperty() {
// Each constellation is associated with its position in the array as this is unique
// and will be constant during the runtime
for (int i = 0 ; i < static_cast<int>(_constellationBounds.size()); ++i) {
const ConstellationBound& bound = _constellationBounds[i];
_constellationSelection.addOption( { i, bound.constellationFullName } );
_constellationSelection.addOption(bound.constellationFullName);
}
}
void RenderableConstellationBounds::selectionPropertyHasChanged() {
const std::vector<int>& values = _constellationSelection;
// If no values are selected (the default), we want to show all constellations
if (values.empty()) {
if (!_constellationSelection.hasSelected()) {
for (ConstellationBound& b : _constellationBounds) {
b.isEnabled = true;
}
}
else {
// In the worst case, this algorithm runs with 2 * nConstellations, which is
// acceptable as the number of constellations is < 100
// First disable all constellations
// Enable all constellations that are selected
for (ConstellationBound& b : _constellationBounds) {
b.isEnabled = false;
}
// then re-enable the ones for which we have indices
for (int value : values) {
_constellationBounds[value].isEnabled = true;
b.isEnabled = _constellationSelection.isSelected(b.constellationFullName);
}
}
}
@@ -31,6 +31,7 @@
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/vector/vec3property.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <vector>
namespace ghoul::opengl { class ProgramObject; }
@@ -143,6 +143,7 @@ RenderableHabitableZone::RenderableHabitableZone(const ghoul::Dictionary& dictio
_width.setReadOnly(true);
computeZone();
setBoundingSphere(_size);
}
void RenderableHabitableZone::render(const RenderData& data, RendererTasks&) {
@@ -476,6 +476,14 @@ void RenderableOrbitalKepler::initializeGL() {
_uniformCache.opacity = _programObject->uniformLocation("opacity");
updateBuffers();
double maxSemiMajorAxis = 0.0;
for (const KeplerParameters& kp : _data) {
if (kp.semiMajorAxis > maxSemiMajorAxis) {
maxSemiMajorAxis = kp.semiMajorAxis;
}
}
setBoundingSphere(maxSemiMajorAxis * 1000);
}
void RenderableOrbitalKepler::deinitializeGL() {

Some files were not shown because too many files have changed in this diff Show More