mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2025-12-31 00:10:44 -06:00
* Adapting to the changes in Ghoul * First step of moving filesystem functions to std * Remove persistence flag from cachemanager
1637 lines
56 KiB
C++
1637 lines
56 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2021 *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
|
* conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies *
|
|
* or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
****************************************************************************************/
|
|
|
|
#include <modules/space/rendering/renderablestars.h>
|
|
|
|
#include <openspace/documentation/documentation.h>
|
|
#include <openspace/documentation/verifier.h>
|
|
#include <openspace/util/updatestructures.h>
|
|
#include <openspace/util/distanceconstants.h>
|
|
#include <openspace/engine/openspaceengine.h>
|
|
#include <openspace/engine/globals.h>
|
|
#include <openspace/rendering/renderengine.h>
|
|
#include <ghoul/filesystem/cachemanager.h>
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/templatefactory.h>
|
|
#include <ghoul/io/texture/texturereader.h>
|
|
#include <ghoul/opengl/openglstatecache.h>
|
|
#include <ghoul/opengl/programobject.h>
|
|
#include <ghoul/opengl/texture.h>
|
|
#include <ghoul/opengl/textureunit.h>
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
|
|
namespace {
|
|
constexpr const char* _loggerCat = "RenderableStars";
|
|
|
|
constexpr const std::array<const char*, 17> UniformNames = {
|
|
"modelMatrix", "cameraUp", "cameraViewProjectionMatrix",
|
|
"colorOption", "magnitudeExponent", "eyePosition", "psfParamConf",
|
|
"lumCent", "radiusCent", "brightnessCent", "colorTexture",
|
|
"alphaValue", "psfTexture", "otherDataTexture", "otherDataRange",
|
|
"filterOutOfRange", "fixedColor"
|
|
};
|
|
|
|
constexpr int8_t CurrentCacheVersion = 3;
|
|
|
|
constexpr const int RenderOptionPointSpreadFunction = 0;
|
|
constexpr const int RenderOptionTexture = 1;
|
|
|
|
constexpr const int PsfMethodSpencer = 0;
|
|
constexpr const int PsfMethodMoffat = 1;
|
|
|
|
constexpr double PARSEC = 0.308567756E17;
|
|
|
|
struct ColorVBOLayout {
|
|
std::array<float, 3> position;
|
|
float value;
|
|
float luminance;
|
|
float absoluteMagnitude;
|
|
float apparentMagnitude;
|
|
};
|
|
|
|
struct VelocityVBOLayout {
|
|
std::array<float, 3> position;
|
|
float value;
|
|
float luminance;
|
|
float absoluteMagnitude;
|
|
float apparentMagnitude;
|
|
|
|
float vx; // v_x
|
|
float vy; // v_y
|
|
float vz; // v_z
|
|
};
|
|
|
|
struct SpeedVBOLayout {
|
|
std::array<float, 3> position;
|
|
float value;
|
|
float luminance;
|
|
float absoluteMagnitude;
|
|
float apparentMagnitude;
|
|
|
|
float speed;
|
|
};
|
|
|
|
struct OtherDataLayout {
|
|
std::array<float, 3> position;
|
|
float value;
|
|
float luminance;
|
|
float absoluteMagnitude;
|
|
float apparentMagnitude;
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo SpeckFileInfo = {
|
|
"SpeckFile",
|
|
"Speck File",
|
|
"The speck file that is loaded to get the data for rendering these stars."
|
|
};
|
|
|
|
static const openspace::properties::Property::PropertyInfo ColorTextureInfo = {
|
|
"ColorMap",
|
|
"ColorBV Texture",
|
|
"The path to the texture that is used to convert from the B-V value of the star "
|
|
"to its color. The texture is used as a one dimensional lookup function."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ColorOptionInfo = {
|
|
"ColorOption",
|
|
"Color Option",
|
|
"This value determines which quantity is used for determining the color of the "
|
|
"stars."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo OtherDataOptionInfo = {
|
|
"OtherData",
|
|
"Other Data Column",
|
|
"The index of the speck file data column that is used as the color input"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo OtherDataValueRangeInfo = {
|
|
"OtherDataValueRange",
|
|
"Range of the other data values",
|
|
"This value is the min/max value range that is used to normalize the other data "
|
|
"values so they can be used by the specified color map."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo FixedColorInfo = {
|
|
"FixedColorValue",
|
|
"Color used for fixed star colors",
|
|
"The color that should be used if the 'Fixed Color' value is used."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo OtherDataColorMapInfo = {
|
|
"OtherDataColorMap",
|
|
"Other Data Color Map",
|
|
"The color map that is used if the 'Other Data' rendering method is selected"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo FilterOutOfRangeInfo = {
|
|
"FilterOutOfRange",
|
|
"Filter Out of Range",
|
|
"Determines whether other data values outside the value range should be visible "
|
|
"or filtered away"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo EnableTestGridInfo = {
|
|
"EnableTestGrid",
|
|
"Enable Test Grid",
|
|
"Set it to true for rendering the test grid."
|
|
};
|
|
|
|
// Old Method
|
|
constexpr openspace::properties::Property::PropertyInfo PsfTextureInfo = {
|
|
"Texture",
|
|
"Point Spread Function Texture",
|
|
"The path to the texture that should be used as a point spread function for the "
|
|
"stars."
|
|
};
|
|
|
|
/*constexpr openspace::properties::Property::PropertyInfo ShapeTextureInfo = {
|
|
"ShapeTexture",
|
|
"Shape Texture to be convolved",
|
|
"The path to the texture that should be used as the base shape for the stars."
|
|
};*/
|
|
|
|
// PSF
|
|
constexpr openspace::properties::Property::PropertyInfo MagnitudeExponentInfo = {
|
|
"MagnitudeExponent",
|
|
"Magnitude Exponent",
|
|
"Adjust star magnitude by 10^MagnitudeExponent. "
|
|
"Stars closer than this distance are given full opacity. "
|
|
"Farther away, stars dim proportionally to the logarithm of their distance."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo RenderMethodOptionInfo = {
|
|
"RenderMethod",
|
|
"Render Method",
|
|
"Render method for the stars."
|
|
};
|
|
|
|
openspace::properties::PropertyOwner::PropertyOwnerInfo
|
|
UserProvidedTextureOptionInfo =
|
|
{
|
|
"UserProvidedTexture",
|
|
"User Provided Texture",
|
|
""
|
|
};
|
|
|
|
openspace::properties::PropertyOwner::PropertyOwnerInfo ParametersOwnerOptionInfo = {
|
|
"ParametersOwner",
|
|
"Parameters Options",
|
|
""
|
|
};
|
|
|
|
openspace::properties::PropertyOwner::PropertyOwnerInfo MoffatMethodOptionInfo = {
|
|
"MoffatMethodOption",
|
|
"Moffat Method",
|
|
""
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo PSFMethodOptionInfo = {
|
|
"PSFMethodOptionInfo",
|
|
"PSF Method Option",
|
|
"Debug option for PSF main function: Spencer or Moffat."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo SizeCompositionOptionInfo = {
|
|
"SizeComposition",
|
|
"Size Composition Option",
|
|
"Base multiplyer for the final stars' sizes."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo LumPercentInfo = {
|
|
"LumPercent",
|
|
"Luminosity Contribution",
|
|
"Luminosity Contribution."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo RadiusPercentInfo = {
|
|
"RadiusPercent",
|
|
"Radius Contribution",
|
|
"Radius Contribution."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo BrightnessPercentInfo = {
|
|
"BrightnessPercen",
|
|
"App Brightness Contribution",
|
|
"App Brightness Contribution."
|
|
};
|
|
|
|
openspace::properties::PropertyOwner::PropertyOwnerInfo SpencerPSFParamOwnerInfo = {
|
|
"SpencerPSFParamOwner",
|
|
"Spencer PSF Paramameters",
|
|
"PSF parameters for Spencer"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo P0ParamInfo = {
|
|
"P0Param",
|
|
"P0",
|
|
"P0 parameter contribution."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo P1ParamInfo = {
|
|
"P1Param",
|
|
"P1",
|
|
"P1 parameter contribution."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo P2ParamInfo = {
|
|
"P2Param",
|
|
"P2",
|
|
"P2 parameter contribution."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo AlphaConstInfo = {
|
|
"AlphaConst",
|
|
"Alpha",
|
|
"Empirical Alpha Constant."
|
|
};
|
|
|
|
openspace::properties::PropertyOwner::PropertyOwnerInfo MoffatPSFParamOwnerInfo = {
|
|
"MoffatPSFParam",
|
|
"Moffat PSF Parameters",
|
|
"PSF parameters for Moffat"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo FWHMInfo = {
|
|
"FWHM",
|
|
"FWHM",
|
|
"Moffat's FWHM"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo BetaInfo = {
|
|
"Beta",
|
|
"Beta",
|
|
"Moffat's Beta Constant."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo FadeInDistancesInfo = {
|
|
"FadeInDistances",
|
|
"Fade-In Start and End Distances",
|
|
"These values determine the initial and final distances from the center of "
|
|
"our galaxy from which the astronomical object will start and end "
|
|
"fading-in."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo DisableFadeInInfo = {
|
|
"DisableFadeIn",
|
|
"Disable Fade-in effect",
|
|
"Enables/Disables the Fade-in effect."
|
|
};
|
|
|
|
struct [[codegen::Dictionary(RenderableStars)]] Parameters {
|
|
// The path to the SPECK file containing information about the stars being
|
|
// rendered
|
|
std::filesystem::path speckFile [[codegen::key("File")]];
|
|
|
|
// [[codegen::verbatim(ColorTextureInfo.description)]]
|
|
std::filesystem::path colorMap;
|
|
|
|
enum class ColorOption {
|
|
Color,
|
|
Velocity,
|
|
Speed,
|
|
OtherData [[codegen::key("Other Data")]],
|
|
FixedColor [[codegen::key("Fixed Color")]]
|
|
};
|
|
// [[codegen::verbatim(ColorOptionInfo.description)]]
|
|
std::optional<ColorOption> colorOption;
|
|
|
|
// [[codegen::verbatim(OtherDataOptionInfo.description)]]
|
|
std::optional<std::string> otherData;
|
|
|
|
// [[codegen::verbatim(OtherDataColorMapInfo.description)]]
|
|
std::optional<std::string> otherDataColorMap;
|
|
|
|
// [[codegen::verbatim(FilterOutOfRangeInfo.description)]]
|
|
std::optional<bool> filterOutOfRange;
|
|
|
|
// This value specifies a value that is always filtered out of the value ranges on
|
|
// loading. This can be used to trim the dataset's automatic value range
|
|
std::optional<float> staticFilter;
|
|
|
|
// This is the value that is used to replace statically filtered values. Setting
|
|
// this value only makes sense if 'StaticFilter' is 'true', as well
|
|
std::optional<float> staticFilterReplacement;
|
|
|
|
// [[codegen::verbatim(MagnitudeExponentInfo.description)]]
|
|
std::optional<float> magnitudeExponent;
|
|
|
|
// [[codegen::verbatim(EnableTestGridInfo.description)]]
|
|
std::optional<bool> enableTestGrid;
|
|
|
|
// [[codegen::verbatim(RenderMethodOptionInfo.description)]]
|
|
std::string renderMethod;
|
|
|
|
// [[codegen::verbatim(PsfTextureInfo.description)]]
|
|
std::filesystem::path texture;
|
|
|
|
// [[codegen::verbatim(SizeCompositionOptionInfo.description)]]
|
|
std::optional<std::string> sizeComposition;
|
|
|
|
// [[codegen::verbatim(FadeInDistancesInfo.description)]]
|
|
std::optional<glm::dvec2> fadeInDistances;
|
|
|
|
// [[codegen::verbatim(DisableFadeInInfo.description)]]
|
|
std::optional<bool> distableFadeIn;
|
|
};
|
|
#include "renderablestars_codegen.cpp"
|
|
} // namespace
|
|
|
|
namespace openspace {
|
|
|
|
documentation::Documentation RenderableStars::Documentation() {
|
|
documentation::Documentation doc = codegen::doc<Parameters>();
|
|
doc.id = "space_renderablestars";
|
|
return doc;
|
|
}
|
|
|
|
RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary)
|
|
: Renderable(dictionary)
|
|
, _speckFile(SpeckFileInfo)
|
|
, _colorTexturePath(ColorTextureInfo)
|
|
//, _shapeTexturePath(ShapeTextureInfo)
|
|
, _colorOption(ColorOptionInfo, properties::OptionProperty::DisplayType::Dropdown)
|
|
, _otherDataOption(
|
|
OtherDataOptionInfo,
|
|
properties::OptionProperty::DisplayType::Dropdown
|
|
)
|
|
, _otherDataColorMapPath(OtherDataColorMapInfo)
|
|
, _otherDataRange(
|
|
OtherDataValueRangeInfo,
|
|
glm::vec2(0.f, 1.f),
|
|
glm::vec2(-10.f, -10.f),
|
|
glm::vec2(10.f, 10.f)
|
|
)
|
|
, _fixedColor(FixedColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
|
|
, _filterOutOfRange(FilterOutOfRangeInfo, false)
|
|
, _pointSpreadFunctionTexturePath(PsfTextureInfo)
|
|
, _psfMethodOption(
|
|
PSFMethodOptionInfo,
|
|
properties::OptionProperty::DisplayType::Dropdown
|
|
)
|
|
, _psfMultiplyOption(
|
|
SizeCompositionOptionInfo,
|
|
properties::OptionProperty::DisplayType::Dropdown
|
|
)
|
|
, _lumCent(LumPercentInfo, 0.5f, 0.f, 3.f)
|
|
, _radiusCent(RadiusPercentInfo, 0.5f, 0.f, 3.f)
|
|
, _brightnessCent(BrightnessPercentInfo, 0.5f, 0.f, 3.f)
|
|
, _magnitudeExponent(MagnitudeExponentInfo, 4.f, 0.f, 8.f)
|
|
, _spencerPSFParamOwner(SpencerPSFParamOwnerInfo)
|
|
, _p0Param(P0ParamInfo, 0.384f, 0.f, 1.f)
|
|
, _p1Param(P1ParamInfo, 0.478f, 0.f, 1.f)
|
|
, _p2Param(P2ParamInfo, 0.138f, 0.f, 1.f)
|
|
, _spencerAlphaConst(AlphaConstInfo, 0.02f, 0.000001f, 5.f)
|
|
, _moffatPSFParamOwner(MoffatPSFParamOwnerInfo)
|
|
, _FWHMConst(FWHMInfo, 10.4f, -100.f, 1000.f)
|
|
, _moffatBetaConst(BetaInfo, 4.765f, 0.f, 100.f)
|
|
, _renderingMethodOption(
|
|
RenderMethodOptionInfo,
|
|
properties::OptionProperty::DisplayType::Dropdown
|
|
)
|
|
, _userProvidedTextureOwner(UserProvidedTextureOptionInfo)
|
|
, _parametersOwner(ParametersOwnerOptionInfo)
|
|
, _moffatMethodOwner(MoffatMethodOptionInfo)
|
|
, _fadeInDistance(
|
|
FadeInDistancesInfo,
|
|
glm::vec2(0.f),
|
|
glm::vec2(0.f),
|
|
glm::vec2(100.f)
|
|
)
|
|
, _disableFadeInDistance(DisableFadeInInfo, true)
|
|
{
|
|
using File = ghoul::filesystem::File;
|
|
|
|
const Parameters p = codegen::bake<Parameters>(dictionary);
|
|
|
|
addProperty(_opacity);
|
|
registerUpdateRenderBinFromOpacity();
|
|
|
|
_speckFile = p.speckFile.string();
|
|
_speckFile.onChange([&]() { _speckFileIsDirty = true; });
|
|
addProperty(_speckFile);
|
|
|
|
_colorTexturePath = p.colorMap.string();
|
|
_colorTextureFile = std::make_unique<File>(_colorTexturePath.value());
|
|
|
|
//_shapeTexturePath = absPath(dictionary.value<std::string>(
|
|
// ShapeTextureInfo.identifier
|
|
// ));
|
|
//_shapeTextureFile = std::make_unique<File>(_shapeTexturePath);
|
|
|
|
if (p.otherDataColorMap.has_value()) {
|
|
_otherDataColorMapPath = absPath(*p.otherDataColorMap);
|
|
}
|
|
|
|
_fixedColor.setViewOption(properties::Property::ViewOptions::Color, true);
|
|
addProperty(_fixedColor);
|
|
|
|
_colorOption.addOptions({
|
|
{ ColorOption::Color, "Color" },
|
|
{ ColorOption::Velocity, "Velocity" },
|
|
{ ColorOption::Speed, "Speed" },
|
|
{ ColorOption::OtherData, "Other Data" },
|
|
{ ColorOption::FixedColor, "Fixed Color" }
|
|
});
|
|
if (p.colorOption.has_value()) {
|
|
switch (*p.colorOption) {
|
|
case Parameters::ColorOption::Color:
|
|
_colorOption = ColorOption::Color;
|
|
break;
|
|
case Parameters::ColorOption::Velocity:
|
|
_colorOption = ColorOption::Velocity;
|
|
break;
|
|
case Parameters::ColorOption::Speed:
|
|
_colorOption = ColorOption::Speed;
|
|
break;
|
|
case Parameters::ColorOption::OtherData:
|
|
_colorOption = ColorOption::OtherData;
|
|
break;
|
|
case Parameters::ColorOption::FixedColor:
|
|
_colorOption = ColorOption::FixedColor;
|
|
break;
|
|
}
|
|
}
|
|
_colorOption.onChange([&] { _dataIsDirty = true; });
|
|
addProperty(_colorOption);
|
|
|
|
_colorTexturePath.onChange([&] { _colorTextureIsDirty = true; });
|
|
_colorTextureFile->setCallback([this]() { _colorTextureIsDirty = true; });
|
|
addProperty(_colorTexturePath);
|
|
|
|
/*_shapeTexturePath.onChange([&] { _shapeTextureIsDirty = true; });
|
|
_shapeTextureFile->setCallback([&](const File&) {
|
|
_shapeTextureIsDirty = true;
|
|
});
|
|
addProperty(_shapeTexturePath);*/
|
|
|
|
_enableTestGrid = p.enableTestGrid.value_or(_enableTestGrid);
|
|
_queuedOtherData = p.otherData.value_or(_queuedOtherData);
|
|
|
|
_otherDataOption.onChange([&]() { _dataIsDirty = true; });
|
|
addProperty(_otherDataOption);
|
|
|
|
addProperty(_otherDataRange);
|
|
|
|
addProperty(_otherDataColorMapPath);
|
|
_otherDataColorMapPath.onChange([&]() { _otherDataColorMapIsDirty = true; });
|
|
|
|
_staticFilterValue = p.staticFilter;
|
|
_staticFilterReplacementValue =
|
|
p.staticFilterReplacement.value_or(_staticFilterReplacementValue);
|
|
|
|
addProperty(_filterOutOfRange);
|
|
|
|
_renderingMethodOption.addOption(
|
|
RenderOptionPointSpreadFunction,
|
|
"Point Spread Function Based"
|
|
);
|
|
_renderingMethodOption.addOption(RenderOptionTexture, "Textured Based");
|
|
addProperty(_renderingMethodOption);
|
|
|
|
if (p.renderMethod == "PSF") {
|
|
_renderingMethodOption = RenderOptionPointSpreadFunction;
|
|
}
|
|
else if (p.renderMethod == "Texture Based") {
|
|
_renderingMethodOption = RenderOptionTexture;
|
|
}
|
|
|
|
_pointSpreadFunctionTexturePath = absPath(p.texture.string());
|
|
_pointSpreadFunctionFile = std::make_unique<File>(
|
|
_pointSpreadFunctionTexturePath.value()
|
|
);
|
|
_pointSpreadFunctionTexturePath.onChange([this]() {
|
|
_pointSpreadFunctionTextureIsDirty = true;
|
|
});
|
|
_pointSpreadFunctionFile->setCallback([this]() {
|
|
_pointSpreadFunctionTextureIsDirty = true;
|
|
});
|
|
_userProvidedTextureOwner.addProperty(_pointSpreadFunctionTexturePath);
|
|
|
|
_psfMethodOption.addOption(PsfMethodSpencer, "Spencer's Function");
|
|
_psfMethodOption.addOption(PsfMethodMoffat, "Moffat's Function");
|
|
_psfMethodOption = PsfMethodSpencer;
|
|
_psfMethodOption.onChange([&]() { renderPSFToTexture(); });
|
|
_parametersOwner.addProperty(_psfMethodOption);
|
|
|
|
_psfMultiplyOption.addOption(0, "Use Star's Apparent Brightness");
|
|
_psfMultiplyOption.addOption(1, "Use Star's Luminosity and Size");
|
|
_psfMultiplyOption.addOption(2, "Luminosity, Size, App Brightness");
|
|
_psfMultiplyOption.addOption(3, "Absolute Magnitude");
|
|
_psfMultiplyOption.addOption(4, "Apparent Magnitude");
|
|
_psfMultiplyOption.addOption(5, "Distance Modulus");
|
|
|
|
|
|
if (p.sizeComposition.has_value()) {
|
|
if (*p.sizeComposition == "App Brightness") {
|
|
_psfMultiplyOption = 0;
|
|
}
|
|
else if (*p.sizeComposition == "Lum and Size") {
|
|
_psfMultiplyOption = 1;
|
|
}
|
|
else if (*p.sizeComposition == "Lum, Size and App Brightness") {
|
|
_psfMultiplyOption = 2;
|
|
}
|
|
else if (*p.sizeComposition == "Abs Magnitude") {
|
|
_psfMultiplyOption = 3;
|
|
}
|
|
else if (*p.sizeComposition == "App Maginitude") {
|
|
_psfMultiplyOption = 4;
|
|
}
|
|
else if (*p.sizeComposition == "Distance Modulus") {
|
|
_psfMultiplyOption = 5;
|
|
}
|
|
}
|
|
else {
|
|
_psfMultiplyOption = 5;
|
|
}
|
|
|
|
_parametersOwner.addProperty(_psfMultiplyOption);
|
|
_parametersOwner.addProperty(_lumCent);
|
|
_parametersOwner.addProperty(_radiusCent);
|
|
_parametersOwner.addProperty(_brightnessCent);
|
|
|
|
_magnitudeExponent = p.magnitudeExponent.value_or(_magnitudeExponent);
|
|
_parametersOwner.addProperty(_magnitudeExponent);
|
|
|
|
auto renderPsf = [&]() { renderPSFToTexture(); };
|
|
|
|
_spencerPSFParamOwner.addProperty(_p0Param);
|
|
_p0Param.onChange(renderPsf);
|
|
_spencerPSFParamOwner.addProperty(_p1Param);
|
|
_p1Param.onChange(renderPsf);
|
|
_spencerPSFParamOwner.addProperty(_p2Param);
|
|
_p2Param.onChange(renderPsf);
|
|
_spencerPSFParamOwner.addProperty(_spencerAlphaConst);
|
|
_spencerAlphaConst.onChange(renderPsf);
|
|
|
|
_moffatPSFParamOwner.addProperty(_FWHMConst);
|
|
_FWHMConst.onChange(renderPsf);
|
|
_moffatPSFParamOwner.addProperty(_moffatBetaConst);
|
|
_moffatBetaConst.onChange(renderPsf);
|
|
|
|
_parametersOwner.addPropertySubOwner(_spencerPSFParamOwner);
|
|
_parametersOwner.addPropertySubOwner(_moffatPSFParamOwner);
|
|
|
|
addPropertySubOwner(_userProvidedTextureOwner);
|
|
addPropertySubOwner(_parametersOwner);
|
|
addPropertySubOwner(_moffatMethodOwner);
|
|
|
|
if (p.fadeInDistances.has_value()) {
|
|
glm::vec2 v = *p.fadeInDistances;
|
|
_fadeInDistance = v;
|
|
_disableFadeInDistance = false;
|
|
addProperty(_fadeInDistance);
|
|
addProperty(_disableFadeInDistance);
|
|
}
|
|
}
|
|
|
|
RenderableStars::~RenderableStars() {}
|
|
|
|
bool RenderableStars::isReady() const {
|
|
return _program && _pointSpreadFunctionTexture;
|
|
}
|
|
|
|
void RenderableStars::initializeGL() {
|
|
_program = global::renderEngine->buildRenderProgram(
|
|
"Star",
|
|
absPath("${MODULE_SPACE}/shaders/star_vs.glsl"),
|
|
absPath("${MODULE_SPACE}/shaders/star_fs.glsl"),
|
|
absPath("${MODULE_SPACE}/shaders/star_ge.glsl")
|
|
);
|
|
|
|
ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames);
|
|
|
|
loadData();
|
|
|
|
if (!_queuedOtherData.empty()) {
|
|
auto it = std::find(_dataNames.begin(), _dataNames.end(), _queuedOtherData);
|
|
if (it == _dataNames.end()) {
|
|
LERROR(fmt::format("Could not find other data column {}", _queuedOtherData));
|
|
}
|
|
else {
|
|
_otherDataOption = static_cast<int>(std::distance(_dataNames.begin(), it));
|
|
_queuedOtherData.clear();
|
|
}
|
|
}
|
|
_speckFileIsDirty = false;
|
|
|
|
LDEBUG("Creating Polygon Texture");
|
|
|
|
glGenVertexArrays(1, &_psfVao);
|
|
glGenBuffers(1, &_psfVbo);
|
|
glBindVertexArray(_psfVao);
|
|
glBindBuffer(GL_ARRAY_BUFFER, _psfVbo);
|
|
|
|
const GLfloat vertexData[] = {
|
|
//x y s t
|
|
-1.f, -1.f, 0.f, 0.f,
|
|
1.f, 1.f, 1.f, 1.f,
|
|
-1.f, 1.f, 0.f, 1.f,
|
|
-1.f, -1.f, 0.f, 0.f,
|
|
1.f, -1.f, 1.f, 0.f,
|
|
1.f, 1.f, 1.f, 1.f
|
|
};
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
|
|
glVertexAttribPointer(
|
|
0,
|
|
4,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
sizeof(GLfloat) * 4,
|
|
nullptr
|
|
);
|
|
glEnableVertexAttribArray(0);
|
|
glBindVertexArray(0);
|
|
|
|
glGenTextures(1, &_psfTexture);
|
|
glBindTexture(GL_TEXTURE_2D, _psfTexture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
// Stopped using a buffer object for GL_PIXEL_UNPACK_BUFFER
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
glTexImage2D(
|
|
GL_TEXTURE_2D,
|
|
0,
|
|
GL_RGBA8,
|
|
_psfTextureSize,
|
|
_psfTextureSize,
|
|
0,
|
|
GL_RGBA,
|
|
GL_BYTE,
|
|
nullptr
|
|
);
|
|
|
|
|
|
LDEBUG("Creating Convolution Texture");
|
|
|
|
glGenTextures(1, &_convolvedTexture);
|
|
glBindTexture(GL_TEXTURE_2D, _convolvedTexture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
// Stopped using a buffer object for GL_PIXEL_UNPACK_BUFFER
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
glTexImage2D(
|
|
GL_TEXTURE_2D,
|
|
0,
|
|
GL_RGBA8,
|
|
_convolvedfTextureSize,
|
|
_convolvedfTextureSize,
|
|
0,
|
|
GL_RGBA,
|
|
GL_BYTE,
|
|
nullptr
|
|
);
|
|
|
|
//loadShapeTexture();
|
|
loadPSFTexture();
|
|
renderPSFToTexture();
|
|
}
|
|
|
|
void RenderableStars::deinitializeGL() {
|
|
glDeleteBuffers(1, &_vbo);
|
|
_vbo = 0;
|
|
glDeleteVertexArrays(1, &_vao);
|
|
_vao = 0;
|
|
|
|
_colorTexture = nullptr;
|
|
//_shapeTexture = nullptr;
|
|
|
|
if (_program) {
|
|
global::renderEngine->removeRenderProgram(_program.get());
|
|
_program = nullptr;
|
|
}
|
|
}
|
|
|
|
void RenderableStars::loadPSFTexture() {
|
|
_pointSpreadFunctionTexture = nullptr;
|
|
if (!_pointSpreadFunctionTexturePath.value().empty() &&
|
|
std::filesystem::exists(_pointSpreadFunctionTexturePath.value()))
|
|
{
|
|
_pointSpreadFunctionTexture = ghoul::io::TextureReader::ref().loadTexture(
|
|
absPath(_pointSpreadFunctionTexturePath)
|
|
);
|
|
|
|
if (_pointSpreadFunctionTexture) {
|
|
LDEBUG(fmt::format(
|
|
"Loaded texture from '{}'",
|
|
absPath(_pointSpreadFunctionTexturePath)
|
|
));
|
|
_pointSpreadFunctionTexture->uploadTexture();
|
|
}
|
|
_pointSpreadFunctionTexture->setFilter(
|
|
ghoul::opengl::Texture::FilterMode::AnisotropicMipMap
|
|
);
|
|
|
|
_pointSpreadFunctionFile = std::make_unique<ghoul::filesystem::File>(
|
|
_pointSpreadFunctionTexturePath.value()
|
|
);
|
|
_pointSpreadFunctionFile->setCallback(
|
|
[this]() { _pointSpreadFunctionTextureIsDirty = true; }
|
|
);
|
|
}
|
|
_pointSpreadFunctionTextureIsDirty = false;
|
|
|
|
}
|
|
|
|
void RenderableStars::renderPSFToTexture() {
|
|
// Saves current FBO first
|
|
// GLint defaultFBO;
|
|
// defaultFBO = global::renderEngine->openglStateCache().defaultFramebuffer();
|
|
|
|
// GLint m_viewport[4];
|
|
// global::renderEngine.openglStateCache().viewPort(m_viewport);
|
|
|
|
// Creates the FBO for the calculations
|
|
GLuint psfFBO;
|
|
glGenFramebuffers(1, &psfFBO);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, psfFBO);
|
|
GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
|
|
glDrawBuffers(1, drawBuffers);
|
|
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _psfTexture, 0);
|
|
|
|
glViewport(0, 0, _psfTextureSize, _psfTextureSize);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
std::unique_ptr<ghoul::opengl::ProgramObject> program =
|
|
ghoul::opengl::ProgramObject::Build(
|
|
"RenderStarPSFToTexture",
|
|
absPath("${MODULE_SPACE}/shaders/psfToTexture_vs.glsl"),
|
|
absPath("${MODULE_SPACE}/shaders/psfToTexture_fs.glsl")
|
|
);
|
|
|
|
program->activate();
|
|
constexpr const float black[] = { 0.f, 0.f, 0.f, 0.f };
|
|
glClearBufferfv(GL_COLOR, 0, black);
|
|
|
|
program->setUniform("psfMethod", _psfMethodOption.value());
|
|
program->setUniform("p0Param", _p0Param);
|
|
program->setUniform("p1Param", _p1Param);
|
|
program->setUniform("p2Param", _p2Param);
|
|
program->setUniform("alphaConst", _spencerAlphaConst);
|
|
program->setUniform("FWHM", _FWHMConst);
|
|
program->setUniform("betaConstant", _moffatBetaConst);
|
|
|
|
// Draws psf to texture
|
|
glBindVertexArray(_psfVao);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
glBindVertexArray(0);
|
|
|
|
program->deactivate();
|
|
|
|
// JCC: Convolution is disabled while FFT is not enabled
|
|
//// Now convolves with a disc shape for final shape
|
|
|
|
//GLuint convolveFBO;
|
|
//glGenFramebuffers(1, &convolveFBO);
|
|
//glBindFramebuffer(GL_FRAMEBUFFER, convolveFBO);
|
|
//glDrawBuffers(1, drawBuffers);
|
|
|
|
//glFramebufferTexture(
|
|
// GL_FRAMEBUFFER,
|
|
// GL_COLOR_ATTACHMENT0,
|
|
// _convolvedTexture,
|
|
// 0
|
|
//);
|
|
|
|
//glViewport(0, 0, _convolvedfTextureSize, _convolvedfTextureSize);
|
|
|
|
//std::unique_ptr<ghoul::opengl::ProgramObject> programConvolve =
|
|
// ghoul::opengl::ProgramObject::Build("ConvolvePSFandStarShape",
|
|
// absPath("${MODULE_SPACE}/shaders/convolution_vs.glsl"),
|
|
// absPath("${MODULE_SPACE}/shaders/convolution_fs.glsl")
|
|
// );
|
|
|
|
//programConvolve->activate();
|
|
//glClearBufferfv(GL_COLOR, 0, black);
|
|
|
|
//ghoul::opengl::TextureUnit psfTextureUnit;
|
|
//psfTextureUnit.activate();
|
|
//glBindTexture(GL_TEXTURE_2D, _psfTexture);
|
|
//programConvolve->setUniform("psfTexture", psfTextureUnit);
|
|
//
|
|
//ghoul::opengl::TextureUnit shapeTextureUnit;
|
|
//shapeTextureUnit.activate();
|
|
//_shapeTexture->bind();
|
|
//programConvolve->setUniform("shapeTexture", shapeTextureUnit);
|
|
|
|
//programConvolve->setUniform("psfTextureSize", _psfTextureSize);
|
|
//programConvolve->setUniform(
|
|
// "convolvedfTextureSize",
|
|
// _convolvedfTextureSize
|
|
//);
|
|
|
|
//// Convolves to texture
|
|
//glBindVertexArray(_psfVao);
|
|
//glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
//glBindVertexArray(0);
|
|
|
|
//programConvolve->deactivate();
|
|
|
|
//// Restores system state
|
|
//glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
|
//glViewport(
|
|
// m_viewport[0],
|
|
// m_viewport[1],
|
|
// m_viewport[2],
|
|
// m_viewport[3]
|
|
//);
|
|
//glDeleteFramebuffers(1, &psfFBO);
|
|
//glDeleteFramebuffers(1, &convolveFBO);
|
|
|
|
// Restores OpenGL blending state
|
|
global::renderEngine->openglStateCache().resetBlendState();
|
|
}
|
|
|
|
void RenderableStars::render(const RenderData& data, RendererTasks&) {
|
|
if (_fullData.empty()) {
|
|
return;
|
|
}
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
glDepthMask(false);
|
|
|
|
_program->activate();
|
|
|
|
glm::dvec3 eyePosition = glm::dvec3(
|
|
glm::inverse(data.camera.combinedViewMatrix()) * glm::dvec4(0.0, 0.0, 0.0, 1.0)
|
|
);
|
|
_program->setUniform(_uniformCache.eyePosition, eyePosition);
|
|
|
|
glm::dvec3 cameraUp = data.camera.lookUpVectorWorldSpace();
|
|
_program->setUniform(_uniformCache.cameraUp, cameraUp);
|
|
|
|
glm::dmat4 modelMatrix =
|
|
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
|
glm::dmat4(data.modelTransform.rotation) *
|
|
glm::scale(glm::dmat4(1.0), data.modelTransform.scale);
|
|
|
|
glm::dmat4 projectionMatrix = glm::dmat4(data.camera.projectionMatrix());
|
|
|
|
glm::dmat4 cameraViewProjectionMatrix = projectionMatrix *
|
|
data.camera.combinedViewMatrix();
|
|
|
|
_program->setUniform(_uniformCache.modelMatrix, modelMatrix);
|
|
_program->setUniform(
|
|
_uniformCache.cameraViewProjectionMatrix,
|
|
cameraViewProjectionMatrix
|
|
);
|
|
_program->setUniform(_uniformCache.colorOption, _colorOption);
|
|
_program->setUniform(_uniformCache.magnitudeExponent, _magnitudeExponent);
|
|
|
|
_program->setUniform(_uniformCache.psfParamConf, _psfMultiplyOption.value());
|
|
_program->setUniform(_uniformCache.lumCent, _lumCent);
|
|
_program->setUniform(_uniformCache.radiusCent, _radiusCent);
|
|
_program->setUniform(_uniformCache.brightnessCent, _brightnessCent);
|
|
|
|
if (_colorOption == ColorOption::FixedColor) {
|
|
if (_uniformCache.fixedColor == -1) {
|
|
_uniformCache.fixedColor = _program->uniformLocation("fixedColor");
|
|
}
|
|
_program->setUniform(_uniformCache.fixedColor, _fixedColor);
|
|
}
|
|
|
|
float fadeInVariable = 1.f;
|
|
if (!_disableFadeInDistance) {
|
|
float distCamera = static_cast<float>(glm::length(data.camera.positionVec3()));
|
|
const glm::vec2 fadeRange = _fadeInDistance;
|
|
const double a = 1.f / ((fadeRange.y - fadeRange.x) * PARSEC);
|
|
const double b = -(fadeRange.x / (fadeRange.y - fadeRange.x));
|
|
const double funcValue = a * distCamera + b;
|
|
fadeInVariable *= static_cast<float>(funcValue > 1.f ? 1.f : funcValue);
|
|
|
|
_program->setUniform(_uniformCache.alphaValue, _opacity * fadeInVariable);
|
|
}
|
|
else {
|
|
_program->setUniform(_uniformCache.alphaValue, _opacity);
|
|
}
|
|
|
|
ghoul::opengl::TextureUnit psfUnit;
|
|
psfUnit.activate();
|
|
|
|
if (_renderingMethodOption.value() == 0) { // PSF Based Methods
|
|
glBindTexture(GL_TEXTURE_2D, _psfTexture);\
|
|
// Convolutioned texture
|
|
//glBindTexture(GL_TEXTURE_2D, _convolvedTexture);
|
|
}
|
|
else if (_renderingMethodOption.value() == 1) { // Textured based Method
|
|
_pointSpreadFunctionTexture->bind();
|
|
}
|
|
|
|
_program->setUniform(_uniformCache.psfTexture, psfUnit);
|
|
|
|
ghoul::opengl::TextureUnit colorUnit;
|
|
if (_colorTexture) {
|
|
colorUnit.activate();
|
|
_colorTexture->bind();
|
|
_program->setUniform(_uniformCache.colorTexture, colorUnit);
|
|
}
|
|
|
|
ghoul::opengl::TextureUnit otherDataUnit;
|
|
if (_colorOption == ColorOption::OtherData && _otherDataColorMapTexture) {
|
|
otherDataUnit.activate();
|
|
_otherDataColorMapTexture->bind();
|
|
_program->setUniform(_uniformCache.otherDataTexture, otherDataUnit);
|
|
}
|
|
else {
|
|
// We need to set the uniform to something, or the shader doesn't work
|
|
_program->setUniform(_uniformCache.otherDataTexture, colorUnit);
|
|
}
|
|
// Same here, if we don't set this value, the rendering disappears even if we don't
|
|
// use this color mode --- abock 2018-11-19
|
|
_program->setUniform(_uniformCache.otherDataRange, _otherDataRange);
|
|
_program->setUniform(_uniformCache.filterOutOfRange, _filterOutOfRange);
|
|
|
|
|
|
glBindVertexArray(_vao);
|
|
const GLsizei nStars = static_cast<GLsizei>(_fullData.size() / _nValuesPerStar);
|
|
glDrawArrays(GL_POINTS, 0, nStars);
|
|
|
|
glBindVertexArray(0);
|
|
_program->deactivate();
|
|
|
|
// Restores OpenGL blending state
|
|
global::renderEngine->openglStateCache().resetBlendState();
|
|
global::renderEngine->openglStateCache().resetDepthState();
|
|
}
|
|
|
|
void RenderableStars::update(const UpdateData&) {
|
|
if (_speckFileIsDirty) {
|
|
loadData();
|
|
_speckFileIsDirty = false;
|
|
_dataIsDirty = true;
|
|
}
|
|
|
|
if (_fullData.empty()) {
|
|
return;
|
|
}
|
|
|
|
if (_dataIsDirty) {
|
|
const int value = _colorOption;
|
|
LDEBUG("Regenerating data");
|
|
|
|
createDataSlice(ColorOption(value));
|
|
|
|
int size = static_cast<int>(_slicedData.size());
|
|
|
|
if (_vao == 0) {
|
|
glGenVertexArrays(1, &_vao);
|
|
}
|
|
if (_vbo == 0) {
|
|
glGenBuffers(1, &_vbo);
|
|
}
|
|
glBindVertexArray(_vao);
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
|
|
glBufferData(
|
|
GL_ARRAY_BUFFER,
|
|
size * sizeof(GLfloat),
|
|
_slicedData.data(),
|
|
GL_STATIC_DRAW
|
|
);
|
|
|
|
GLint positionAttrib = _program->attributeLocation("in_position");
|
|
// bvLumAbsMagAppMag = bv color, luminosity, abs magnitude and app magnitude
|
|
GLint bvLumAbsMagAppMagAttrib = _program->attributeLocation(
|
|
"in_bvLumAbsMagAppMag"
|
|
);
|
|
|
|
const size_t nStars = _fullData.size() / _nValuesPerStar;
|
|
const size_t nValues = _slicedData.size() / nStars;
|
|
|
|
GLsizei stride = static_cast<GLsizei>(sizeof(GLfloat) * nValues);
|
|
|
|
glEnableVertexAttribArray(positionAttrib);
|
|
glEnableVertexAttribArray(bvLumAbsMagAppMagAttrib);
|
|
const int colorOption = _colorOption;
|
|
switch (colorOption) {
|
|
case ColorOption::Color:
|
|
case ColorOption::FixedColor:
|
|
glVertexAttribPointer(
|
|
positionAttrib,
|
|
3,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
stride,
|
|
nullptr // = offsetof(ColorVBOLayout, position)
|
|
);
|
|
glVertexAttribPointer(
|
|
bvLumAbsMagAppMagAttrib,
|
|
4,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
stride,
|
|
reinterpret_cast<void*>(offsetof(ColorVBOLayout, value))
|
|
);
|
|
|
|
break;
|
|
case ColorOption::Velocity:
|
|
{
|
|
glVertexAttribPointer(
|
|
positionAttrib,
|
|
3,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
stride,
|
|
nullptr // = offsetof(VelocityVBOLayout, position)
|
|
);
|
|
glVertexAttribPointer(
|
|
bvLumAbsMagAppMagAttrib,
|
|
4,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
stride,
|
|
reinterpret_cast<void*>(offsetof(VelocityVBOLayout, value))
|
|
);
|
|
|
|
GLint velocityAttrib = _program->attributeLocation("in_velocity");
|
|
glEnableVertexAttribArray(velocityAttrib);
|
|
glVertexAttribPointer(
|
|
velocityAttrib,
|
|
3,
|
|
GL_FLOAT,
|
|
GL_TRUE,
|
|
stride,
|
|
reinterpret_cast<void*>(offsetof(VelocityVBOLayout, vx)) // NOLINT
|
|
);
|
|
|
|
break;
|
|
}
|
|
case ColorOption::Speed:
|
|
{
|
|
glVertexAttribPointer(
|
|
positionAttrib,
|
|
3,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
stride,
|
|
nullptr // = offsetof(SpeedVBOLayout, position)
|
|
);
|
|
glVertexAttribPointer(
|
|
bvLumAbsMagAppMagAttrib,
|
|
4,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
stride,
|
|
reinterpret_cast<void*>(offsetof(SpeedVBOLayout, value))
|
|
);
|
|
|
|
GLint speedAttrib = _program->attributeLocation("in_speed");
|
|
glEnableVertexAttribArray(speedAttrib);
|
|
glVertexAttribPointer(
|
|
speedAttrib,
|
|
1,
|
|
GL_FLOAT,
|
|
GL_TRUE,
|
|
stride,
|
|
reinterpret_cast<void*>(offsetof(SpeedVBOLayout, speed))
|
|
);
|
|
break;
|
|
}
|
|
case ColorOption::OtherData:
|
|
{
|
|
glVertexAttribPointer(
|
|
positionAttrib,
|
|
3,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
stride,
|
|
nullptr // = offsetof(OtherDataLayout, position)
|
|
);
|
|
glVertexAttribPointer(
|
|
bvLumAbsMagAppMagAttrib,
|
|
4,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
stride,
|
|
reinterpret_cast<void*>(offsetof(OtherDataLayout, value)) // NOLINT
|
|
);
|
|
}
|
|
}
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindVertexArray(0);
|
|
|
|
_dataIsDirty = false;
|
|
}
|
|
|
|
if (_pointSpreadFunctionTextureIsDirty) {
|
|
LDEBUG("Reloading Point Spread Function texture");
|
|
loadPSFTexture();
|
|
}
|
|
|
|
if (_colorTextureIsDirty) {
|
|
LDEBUG("Reloading Color Texture");
|
|
_colorTexture = nullptr;
|
|
if (_colorTexturePath.value() != "") {
|
|
_colorTexture = ghoul::io::TextureReader::ref().loadTexture(
|
|
absPath(_colorTexturePath)
|
|
);
|
|
if (_colorTexture) {
|
|
LDEBUG(fmt::format(
|
|
"Loaded texture from '{}'",
|
|
absPath(_colorTexturePath)
|
|
));
|
|
_colorTexture->uploadTexture();
|
|
}
|
|
|
|
_colorTextureFile = std::make_unique<ghoul::filesystem::File>(
|
|
_colorTexturePath.value()
|
|
);
|
|
_colorTextureFile->setCallback([this]() { _colorTextureIsDirty = true; });
|
|
}
|
|
_colorTextureIsDirty = false;
|
|
}
|
|
|
|
//loadShapeTexture();
|
|
|
|
if (_otherDataColorMapIsDirty) {
|
|
LDEBUG("Reloading Color Texture");
|
|
_otherDataColorMapTexture = nullptr;
|
|
if (!_otherDataColorMapPath.value().empty()) {
|
|
_otherDataColorMapTexture = ghoul::io::TextureReader::ref().loadTexture(
|
|
absPath(_otherDataColorMapPath)
|
|
);
|
|
if (_otherDataColorMapTexture) {
|
|
LDEBUG(fmt::format(
|
|
"Loaded texture from '{}'",
|
|
absPath(_otherDataColorMapPath)
|
|
));
|
|
_otherDataColorMapTexture->uploadTexture();
|
|
}
|
|
}
|
|
_otherDataColorMapIsDirty = false;
|
|
|
|
}
|
|
|
|
if (_program->isDirty()) {
|
|
_program->rebuildFromFile();
|
|
ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames);
|
|
}
|
|
}
|
|
|
|
/*
|
|
void RenderableStars::loadShapeTexture() {
|
|
if (_shapeTextureIsDirty) {
|
|
LDEBUG("Reloading Shape Texture");
|
|
_shapeTexture = nullptr;
|
|
if (_shapeTexturePath.value() != "") {
|
|
_shapeTexture = ghoul::io::TextureReader::ref().loadTexture(
|
|
absPath(_shapeTexturePath)
|
|
);
|
|
if (_shapeTexture) {
|
|
LDEBUG(fmt::format(
|
|
"Loaded texture from '{}'",
|
|
absPath(_shapeTexturePath)
|
|
));
|
|
_shapeTexture->uploadTexture();
|
|
}
|
|
|
|
_shapeTextureFile = std::make_unique<ghoul::filesystem::File>(
|
|
_shapeTexturePath
|
|
);
|
|
_shapeTextureFile->setCallback(
|
|
[&](const ghoul::filesystem::File&) { _shapeTextureIsDirty = true; }
|
|
);
|
|
}
|
|
_shapeTextureIsDirty = false;
|
|
}
|
|
}
|
|
*/
|
|
|
|
void RenderableStars::loadData() {
|
|
std::string file = absPath(_speckFile);
|
|
if (!std::filesystem::is_regular_file(file)) {
|
|
return;
|
|
}
|
|
|
|
_nValuesPerStar = 0;
|
|
_slicedData.clear();
|
|
_fullData.clear();
|
|
_dataNames.clear();
|
|
|
|
std::string cachedFile = FileSys.cacheManager()->cachedFilename(file);
|
|
bool hasCachedFile = std::filesystem::is_regular_file(cachedFile);
|
|
if (hasCachedFile) {
|
|
LINFO(fmt::format("Cached file '{}' used for Speck file '{}'",
|
|
cachedFile, file
|
|
));
|
|
|
|
bool success = loadCachedFile(cachedFile);
|
|
if (success) {
|
|
return;
|
|
}
|
|
else {
|
|
FileSys.cacheManager()->removeCacheFile(file);
|
|
// Intentional fall-through to the 'else' computation to generate the cache
|
|
// file for the next run
|
|
}
|
|
}
|
|
else {
|
|
LINFO(fmt::format("Cache for Speck file '{}' not found", file));
|
|
}
|
|
LINFO(fmt::format("Loading Speck file '{}'", file));
|
|
|
|
readSpeckFile();
|
|
|
|
LINFO("Saving cache");
|
|
saveCachedFile(cachedFile);
|
|
}
|
|
|
|
void RenderableStars::readSpeckFile() {
|
|
std::string _file = _speckFile;
|
|
std::ifstream file(_file);
|
|
if (!file.good()) {
|
|
LERROR(fmt::format("Failed to open Speck file '{}'", _file));
|
|
return;
|
|
}
|
|
|
|
// 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, '\n');
|
|
|
|
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
|
|
if (_enableTestGrid) {
|
|
file.seekg(position - std::streamoff(8));
|
|
}
|
|
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 >> _nValuesPerStar;
|
|
|
|
std::string name;
|
|
str >> name;
|
|
_dataNames.push_back(name);
|
|
|
|
// +3 because the position x, y, z
|
|
if (name == "lum") {
|
|
_lumArrayPos = _nValuesPerStar + 3;
|
|
}
|
|
else if (name == "absmag") {
|
|
_absMagArrayPos = _nValuesPerStar + 3;
|
|
}
|
|
else if (name == "appmag") {
|
|
_appMagArrayPos = _nValuesPerStar + 3;
|
|
}
|
|
else if (name == "colorb_v") {
|
|
_bvColorArrayPos = _nValuesPerStar + 3;
|
|
}
|
|
else if (name == "vx") {
|
|
_velocityArrayPos = _nValuesPerStar + 3;
|
|
}
|
|
else if (name == "speed") {
|
|
_speedArrayPos = _nValuesPerStar + 3;
|
|
}
|
|
_nValuesPerStar += 1; // We want the number, but the index is 0 based
|
|
}
|
|
}
|
|
|
|
_nValuesPerStar += 3; // X Y Z are not counted in the Speck file indices
|
|
_otherDataOption.addOptions(_dataNames);
|
|
|
|
float minLumValue = std::numeric_limits<float>::max();
|
|
float maxLumValue = std::numeric_limits<float>::min();
|
|
|
|
do {
|
|
std::vector<float> values(_nValuesPerStar);
|
|
std::stringstream str(line);
|
|
|
|
for (int i = 0; i < _nValuesPerStar; ++i) {
|
|
str >> values[i];
|
|
}
|
|
|
|
bool nullArray = true;
|
|
for (float v : values) {
|
|
if (v != 0.0) {
|
|
nullArray = false;
|
|
break;
|
|
}
|
|
}
|
|
minLumValue = values[_lumArrayPos] < minLumValue ?
|
|
values[_lumArrayPos] : minLumValue;
|
|
maxLumValue = values[_lumArrayPos] > maxLumValue ?
|
|
values[_lumArrayPos] : maxLumValue;
|
|
if (!nullArray) {
|
|
_fullData.insert(_fullData.end(), values.begin(), values.end());
|
|
}
|
|
|
|
std::getline(file, line, '\n');
|
|
|
|
} while (!file.eof());
|
|
|
|
// Normalize Luminosity:
|
|
for (size_t i = 0; i < _fullData.size(); i += _nValuesPerStar) {
|
|
_fullData[i + _lumArrayPos] =
|
|
(_fullData[i + _lumArrayPos] - minLumValue) / (maxLumValue - minLumValue);
|
|
}
|
|
}
|
|
|
|
bool RenderableStars::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();
|
|
if (std::filesystem::is_regular_file(file)) {
|
|
std::filesystem::remove(file);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int32_t nValues = 0;
|
|
fileStream.read(reinterpret_cast<char*>(&nValues), sizeof(int32_t));
|
|
fileStream.read(reinterpret_cast<char*>(&_nValuesPerStar), sizeof(int32_t));
|
|
|
|
fileStream.read(reinterpret_cast<char*>(&_lumArrayPos), sizeof(int32_t));
|
|
fileStream.read(reinterpret_cast<char*>(&_absMagArrayPos), sizeof(int32_t));
|
|
fileStream.read(reinterpret_cast<char*>(&_appMagArrayPos), sizeof(int32_t));
|
|
fileStream.read(reinterpret_cast<char*>(&_bvColorArrayPos), sizeof(int32_t));
|
|
fileStream.read(reinterpret_cast<char*>(&_velocityArrayPos), sizeof(int32_t));
|
|
fileStream.read(reinterpret_cast<char*>(&_speedArrayPos), sizeof(int32_t));
|
|
|
|
for (int i = 0; i < _nValuesPerStar - 3; ++i) {
|
|
uint16_t len;
|
|
fileStream.read(reinterpret_cast<char*>(&len), sizeof(uint16_t));
|
|
std::vector<char> buffer(len);
|
|
fileStream.read(buffer.data(), len);
|
|
std::string value(buffer.begin(), buffer.end());
|
|
_dataNames.push_back(value);
|
|
}
|
|
_otherDataOption.addOptions(_dataNames);
|
|
|
|
_fullData.resize(nValues);
|
|
fileStream.read(reinterpret_cast<char*>(
|
|
_fullData.data()),
|
|
nValues * sizeof(_fullData[0])
|
|
);
|
|
|
|
bool success = fileStream.good();
|
|
return success;
|
|
}
|
|
else {
|
|
LERROR(fmt::format("Error opening file '{}' for loading cache file", file));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void RenderableStars::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;
|
|
}
|
|
|
|
fileStream.write(
|
|
reinterpret_cast<const char*>(&CurrentCacheVersion),
|
|
sizeof(int8_t)
|
|
);
|
|
|
|
int32_t nValues = static_cast<int32_t>(_fullData.size());
|
|
if (nValues == 0) {
|
|
throw ghoul::RuntimeError("Error writing cache: No values were loaded");
|
|
}
|
|
fileStream.write(reinterpret_cast<const char*>(&nValues), sizeof(int32_t));
|
|
|
|
int32_t nValuesPerStar = static_cast<int32_t>(_nValuesPerStar);
|
|
fileStream.write(reinterpret_cast<const char*>(&nValuesPerStar), sizeof(int32_t));
|
|
fileStream.write(reinterpret_cast<const char*>(&_lumArrayPos), sizeof(int32_t));
|
|
fileStream.write(reinterpret_cast<const char*>(&_absMagArrayPos), sizeof(int32_t));
|
|
fileStream.write(reinterpret_cast<const char*>(&_appMagArrayPos), sizeof(int32_t));
|
|
fileStream.write(reinterpret_cast<const char*>(&_bvColorArrayPos), sizeof(int32_t));
|
|
fileStream.write(reinterpret_cast<const char*>(&_velocityArrayPos), sizeof(int32_t));
|
|
fileStream.write(reinterpret_cast<const char*>(&_speedArrayPos), sizeof(int32_t));
|
|
|
|
// -3 as we don't want to save the xyz values that are in the beginning of the file
|
|
for (int i = 0; i < _nValuesPerStar - 3; ++i) {
|
|
uint16_t len = static_cast<uint16_t>(_dataNames[i].size());
|
|
fileStream.write(reinterpret_cast<const char*>(&len), sizeof(uint16_t));
|
|
fileStream.write(_dataNames[i].c_str(), len);
|
|
}
|
|
|
|
size_t nBytes = nValues * sizeof(_fullData[0]);
|
|
fileStream.write(reinterpret_cast<const char*>(_fullData.data()), nBytes);
|
|
}
|
|
|
|
void RenderableStars::createDataSlice(ColorOption option) {
|
|
_slicedData.clear();
|
|
|
|
_otherDataRange = glm::vec2(
|
|
std::numeric_limits<float>::max(),
|
|
-std::numeric_limits<float>::max()
|
|
);
|
|
|
|
double maxRadius = 0.0;
|
|
|
|
for (size_t i = 0; i < _fullData.size(); i += _nValuesPerStar) {
|
|
glm::dvec3 position = glm::dvec3(
|
|
_fullData[i + 0],
|
|
_fullData[i + 1],
|
|
_fullData[i + 2]
|
|
);
|
|
position *= openspace::distanceconstants::Parsec;
|
|
|
|
const double r = glm::length(position);
|
|
if (r > maxRadius) {
|
|
maxRadius = r;
|
|
}
|
|
|
|
switch (option) {
|
|
case ColorOption::Color:
|
|
case ColorOption::FixedColor:
|
|
{
|
|
union {
|
|
ColorVBOLayout value;
|
|
std::array<float, sizeof(ColorVBOLayout) / sizeof(float)> data;
|
|
} layout;
|
|
|
|
layout.value.position = { {
|
|
static_cast<float>(position[0]),
|
|
static_cast<float>(position[1]),
|
|
static_cast<float>(position[2])
|
|
}};
|
|
|
|
if (_enableTestGrid) {
|
|
float sunColor = 0.650f;
|
|
layout.value.value = sunColor;// _fullData[i + 3];
|
|
}
|
|
else {
|
|
layout.value.value = _fullData[i + _bvColorArrayPos];
|
|
}
|
|
|
|
layout.value.luminance = _fullData[i + _lumArrayPos];
|
|
layout.value.absoluteMagnitude = _fullData[i + _absMagArrayPos];
|
|
layout.value.apparentMagnitude = _fullData[i + _appMagArrayPos];
|
|
|
|
_slicedData.insert(
|
|
_slicedData.end(),
|
|
layout.data.begin(),
|
|
layout.data.end());
|
|
|
|
break;
|
|
}
|
|
case ColorOption::Velocity:
|
|
{
|
|
union {
|
|
VelocityVBOLayout value;
|
|
std::array<float, sizeof(VelocityVBOLayout) / sizeof(float)> data;
|
|
} layout;
|
|
|
|
layout.value.position = {{
|
|
static_cast<float>(position[0]),
|
|
static_cast<float>(position[1]),
|
|
static_cast<float>(position[2])
|
|
}};
|
|
|
|
layout.value.value = _fullData[i + _bvColorArrayPos];
|
|
layout.value.luminance = _fullData[i + _lumArrayPos];
|
|
layout.value.absoluteMagnitude = _fullData[i + _absMagArrayPos];
|
|
layout.value.apparentMagnitude = _fullData[i + _appMagArrayPos];
|
|
|
|
layout.value.vx = _fullData[i + _velocityArrayPos];
|
|
layout.value.vy = _fullData[i + _velocityArrayPos + 1];
|
|
layout.value.vz = _fullData[i + _velocityArrayPos + 2];
|
|
|
|
_slicedData.insert(
|
|
_slicedData.end(),
|
|
layout.data.begin(),
|
|
layout.data.end()
|
|
);
|
|
break;
|
|
}
|
|
case ColorOption::Speed:
|
|
{
|
|
union {
|
|
SpeedVBOLayout value;
|
|
std::array<float, sizeof(SpeedVBOLayout) / sizeof(float)> data;
|
|
} layout;
|
|
|
|
layout.value.position = {{
|
|
static_cast<float>(position[0]),
|
|
static_cast<float>(position[1]),
|
|
static_cast<float>(position[2])
|
|
}};
|
|
|
|
layout.value.value = _fullData[i + _bvColorArrayPos];
|
|
layout.value.luminance = _fullData[i + _lumArrayPos];
|
|
layout.value.absoluteMagnitude = _fullData[i + _absMagArrayPos];
|
|
layout.value.apparentMagnitude = _fullData[i + _appMagArrayPos];
|
|
|
|
layout.value.speed = _fullData[i + _speedArrayPos];
|
|
|
|
_slicedData.insert(
|
|
_slicedData.end(),
|
|
layout.data.begin(),
|
|
layout.data.end()
|
|
);
|
|
break;
|
|
}
|
|
case ColorOption::OtherData:
|
|
{
|
|
union {
|
|
OtherDataLayout value;
|
|
std::array<float, sizeof(OtherDataLayout)> data;
|
|
} layout = {};
|
|
|
|
layout.value.position = {{
|
|
static_cast<float>(position[0]),
|
|
static_cast<float>(position[1]),
|
|
static_cast<float>(position[2])
|
|
}};
|
|
|
|
int index = _otherDataOption.value();
|
|
// plus 3 because of the position
|
|
layout.value.value = _fullData[i + index + 3];
|
|
|
|
if (_staticFilterValue.has_value() &&
|
|
layout.value.value == _staticFilterValue)
|
|
{
|
|
layout.value.value = _staticFilterReplacementValue;
|
|
}
|
|
|
|
glm::vec2 range = _otherDataRange.value();
|
|
range.x = std::min(range.x, layout.value.value);
|
|
range.y = std::max(range.y, layout.value.value);
|
|
_otherDataRange = range;
|
|
_otherDataRange.setMinValue(glm::vec2(range.x));
|
|
_otherDataRange.setMaxValue(glm::vec2(range.y));
|
|
|
|
layout.value.luminance = _fullData[i + _lumArrayPos];
|
|
layout.value.absoluteMagnitude = _fullData[i + _absMagArrayPos];
|
|
layout.value.apparentMagnitude = _fullData[i + _appMagArrayPos];
|
|
|
|
_slicedData.insert(
|
|
_slicedData.end(),
|
|
layout.data.begin(),
|
|
layout.data.end()
|
|
);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
setBoundingSphere(maxRadius);
|
|
}
|
|
|
|
} // namespace openspace
|