mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-04-23 12:39:24 -05:00
Fixed merge conflicts with master
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()) += ",";
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user