Full support of colormaps. Revamp of entire softwareintegration module. Update to SIMP version 1.8.

Co-authored-by: Jacob Molin <jacobmolin@users.noreply.github.com>
This commit is contained in:
Victor Lindquist
2022-05-20 00:23:00 -06:00
parent d62b9a49e5
commit 47a5c8e299
20 changed files with 1743 additions and 1064 deletions

View File

@@ -36,17 +36,17 @@
#include <ghoul/opengl/openglstatecache.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <fstream>
#include <optional>
namespace {
constexpr const char* _loggerCat = "PointsCloud";
constexpr const std::array<const char*, 8> UniformNames = {
"color", "opacity", "size", "modelMatrix",
"cameraUp", "cameraViewProjectionMatrix", "eyePosition", "sizeOption"
constexpr const std::array<const char*, 12> UniformNames = {
"color", "opacity", "size", "modelMatrix", "cameraUp", "cameraViewProjectionMatrix",
"eyePosition", "sizeOption", "colormapTexture", "colormapMin", "colormapMax",
"colormapEnabled"
};
//"colorMapTexture",
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
"Color",
@@ -60,28 +60,16 @@ namespace {
"The size of the points."
};
constexpr openspace::properties::Property::PropertyInfo ToggleVisibilityInfo = {
"ToggleVisibility",
"Toggle Visibility",
"Enables/Disables the drawing of points."
};
constexpr openspace::properties::Property::PropertyInfo DataInfo = {
"Data",
"Data",
"Data to use for the positions of the points, given in Parsec."
};
constexpr openspace::properties::Property::PropertyInfo DataStorageKeyInfo = {
"DataStorageKey",
"Data Storage Key",
"Key used to access a dataset in the module's centralized storage, which is synced to all nodes."
};
constexpr openspace::properties::Property::PropertyInfo IdentifierInfo = {
"Identifier",
"Identifier",
"Identifier used as part of key to access data in syncable central storage."
"Identifier used as part of key to access data in centralized central storage."
};
constexpr openspace::properties::Property::PropertyInfo SizeOptionInfo = {
@@ -90,44 +78,47 @@ namespace {
"This value determines how the size of the data points are rendered."
};
constexpr openspace::properties::Property::PropertyInfo ColorMapEnabledInfo = {
"ColorMapEnabled",
"ColorMap Enabled",
constexpr openspace::properties::Property::PropertyInfo ColormapEnabledInfo = {
"ColormapEnabled",
"Colormap Enabled",
"Boolean to determine whether to use colormap or not."
};
constexpr openspace::properties::Property::PropertyInfo LoadNewColorMapInfo = {
"LoadNewColorMap",
"Load New ColorMap",
"Boolean to determine whether to load new colormap or not."
constexpr openspace::properties::Property::PropertyInfo ColormapMinInfo = {
"ColormapMin",
"Colormap min",
"Minimum value to sample from color map."
};
constexpr openspace::properties::Property::PropertyInfo ColormapMaxInfo = {
"ColormapMax",
"Colormap max",
"Maximum value to sample from color map."
};
struct [[codegen::Dictionary(RenderablePointsCloud)]] Parameters {
// [[codegen::verbatim(ColorInfo.description)]]
std::optional<glm::vec3> color;
std::optional<glm::vec4> color;
// [[codegen::verbatim(SizeInfo.description)]]
std::optional<float> size;
// [[codegen::verbatim(ToggleVisibilityInfo.description)]]
std::optional<bool> toggleVisiblity;
// [[codegen::verbatim(DataInfo.description)]]
std::optional<std::vector<glm::vec3>> data;
// [[codegen::verbatim(DataStorageKeyInfo.description)]]
std::optional<std::string> dataStorageKey;
// [[codegen::verbatim(IdentifierInfo.description)]]
std::optional<std::string> identifier;
// [[codegen::verbatim(ColorMapEnabledInfo.description)]]
std::optional<std::string> colorMapEnabled;
// [[codegen::verbatim(ColormapMinInfo.description)]]
std::optional<float> colormapMin;
// [[codegen::verbatim(LoadNewColorMapInfo.description)]]
std::optional<std::string> loadNewColorMap;
// [[codegen::verbatim(ColormapMaxInfo.description)]]
std::optional<float> colormapMax;
enum class SizeOption {
// [[codegen::verbatim(ColormapEnabledInfo.description)]]
std::optional<bool> colormapEnabled;
enum class SizeOption : uint32_t {
Uniform,
NonUniform
};
@@ -145,12 +136,12 @@ documentation::Documentation RenderablePointsCloud::Documentation() {
RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _color(ColorInfo, glm::vec3(0.5f), glm::vec3(0.f), glm::vec3(1.f))
, _size(SizeInfo, 1.f, 0.f, 150.f)
, _isVisible(ToggleVisibilityInfo, true)
, _color(ColorInfo, glm::vec4(glm::vec3(0.5f), 1.f), glm::vec4(0.f), glm::vec4(1.f), glm::vec4(.001f))
, _size(SizeInfo, 1.f, 0.f, 30.f, .001f)
, _sizeOption(SizeOptionInfo, properties::OptionProperty::DisplayType::Dropdown)
, _colorMapEnabled(ColorMapEnabledInfo, false)
, _loadNewColorMap(LoadNewColorMapInfo, false)
, _colormapEnabled(ColormapEnabledInfo, false)
, _colormapMin(ColormapMinInfo)
, _colormapMax(ColormapMaxInfo)
{
const Parameters p = codegen::bake<Parameters>(dictionary);
@@ -158,27 +149,37 @@ RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary
_color.setViewOption(properties::Property::ViewOptions::Color);
addProperty(_color);
// Check if a key to data stored in the module's centralized memory was included
_nValuesPerPoint = 3;
_dataStorageKey = p.dataStorageKey.value();
_identifier = p.identifier.value();
_size = p.size.value_or(_size);
addProperty(_size);
_isVisible = p.toggleVisiblity.value_or(_isVisible);
addProperty(_isVisible);
addProperty(_opacity);
addProperty(_colorMapEnabled);
auto colormapMinMaxChecker = [this] {
if (_colormapMin.value() > _colormapMax.value()) {
auto temp = _colormapMin.value();
_colormapMin = _colormapMax.value();
_colormapMax = temp;
}
};
addProperty(_loadNewColorMap);
_colormapMin = p.colormapMin.value_or(_colormapMin);
_colormapMin.setVisibility(properties::Property::Visibility::Hidden);
_colormapMin.onChange(colormapMinMaxChecker);
addProperty(_colormapMin);
_colormapMax = p.colormapMax.value_or(_colormapMax);
_colormapMax.setVisibility(properties::Property::Visibility::Hidden);
_colormapMax.onChange(colormapMinMaxChecker);
addProperty(_colormapMax);
_colormapEnabled = p.colormapEnabled.value_or(_colormapEnabled);
addProperty(_colormapEnabled);
_sizeOption.addOptions({
{ SizeOption::Uniform, "Uniform" },
{ SizeOption::NonUniform, "NonUniform" }
{ SizeOption::NonUniform, "Non Uniform" }
});
if (p.sizeOption.has_value()) {
switch (*p.sizeOption) {
@@ -190,16 +191,11 @@ RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary
break;
}
}
_sizeOption.onChange([&] { _isDirty = true; });
addProperty(_sizeOption);
}
bool RenderablePointsCloud::isReady() const {
return _shaderProgram && (!_fullData.empty());
}
void RenderablePointsCloud::initialize() {
loadData();
return _shaderProgram && _identifier.has_value() && _identifier.value() != "";
}
void RenderablePointsCloud::initializeGL() {
@@ -214,11 +210,12 @@ void RenderablePointsCloud::initializeGL() {
}
void RenderablePointsCloud::deinitializeGL() {
glDeleteVertexArrays(1, &_vertexArrayObjectID);
_vertexArrayObjectID = 0;
glDeleteBuffers(1, &_vbo);
_vbo = 0;
glDeleteVertexArrays(1, &_vao);
_vao = 0;
glDeleteBuffers(1, &_vertexBufferObjectID);
_vertexBufferObjectID = 0;
_colormapTexture.reset();
if (_shaderProgram) {
global::renderEngine->removeRenderProgram(_shaderProgram.get());
@@ -227,13 +224,8 @@ void RenderablePointsCloud::deinitializeGL() {
}
void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) {
if (_fullData.empty()) {
return;
}
if (!_isVisible) {
return;
}
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
if (pointDataSlice->empty()) return;
_shaderProgram->activate();
@@ -259,17 +251,28 @@ void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) {
cameraViewProjectionMatrix
);
if (_loadNewColorMap) loadColorMap();
if (_colorMapEnabled && _colorMapTexture) {
// TODO: Set _colorMapTexture in shader. A trasnfer function similar to
ghoul::opengl::TextureUnit colorUnit;
if (_colormapTexture) {
// _colormapAttributeData
// TODO: Set _colormapTextre in shader. A trasnfer function similar to
// 'bv2rgb' in C:\OpenSpace\SoftwareIntegration\modules\space\shaders\star_fs.glsl
// should probably be used.
colorUnit.activate();
_colormapTexture->bind();
_shaderProgram->setUniform(_uniformCache.colormapTexture, colorUnit);
}
else {
_shaderProgram->setUniform(_uniformCache.color, _color);
// We need to set the uniform to something, or the shader doesn't work
_shaderProgram->setUniform(_uniformCache.colormapTexture, colorUnit);
}
_shaderProgram->setUniform(_uniformCache.colormapMin, _colormapMin);
_shaderProgram->setUniform(_uniformCache.colormapMax, _colormapMax);
_shaderProgram->setUniform(_uniformCache.colormapEnabled, _colormapEnabled);
_shaderProgram->setUniform(_uniformCache.color, _color);
_shaderProgram->setUniform(_uniformCache.opacity, _opacity);
_shaderProgram->setUniform(_uniformCache.size, _size);
_shaderProgram->setUniform(_uniformCache.sizeOption, _sizeOption);
@@ -279,8 +282,8 @@ void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(false);
glBindVertexArray(_vertexArrayObjectID);
const GLsizei nPoints = static_cast<GLsizei>(_fullData.size() / _nValuesPerPoint);
glBindVertexArray(_vao);
const GLsizei nPoints = static_cast<GLsizei>(pointDataSlice->size() / 3);
glDrawArrays(GL_POINTS, 0, nPoints);
glBindVertexArray(0);
@@ -297,130 +300,228 @@ void RenderablePointsCloud::update(const UpdateData&) {
ghoul::opengl::updateUniformLocations(*_shaderProgram, _uniformCache, UniformNames);
}
if (!_isDirty) {
bool updatedDataSlices = checkDataStorage();
if (updatedDataSlices) {
if (_vao == 0) {
glGenVertexArrays(1, &_vao);
LDEBUG(fmt::format("Generating Vertex Array id '{}'", _vao));
}
if (_vbo == 0) {
glGenBuffers(1, &_vbo);
LDEBUG(fmt::format("Generating Vertex Buffer Object id '{}'", _vbo));
}
glBindVertexArray(_vao);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
auto colormapAttrDataSlice = getDataSlice(DataSliceKey::ColormapAttributes);
if (pointDataSlice->empty()) return;
// ========================== Create resulting data slice and buffer it ==========================
std::vector<float> bufferData;
bufferData.reserve(pointDataSlice->size() / 3);
for(size_t i = 0, j = 0; j < pointDataSlice->size(); ++i, j += 3) {
bufferData.push_back(pointDataSlice->at(j));
bufferData.push_back(pointDataSlice->at(j + 1));
bufferData.push_back(pointDataSlice->at(j + 2));
if (colormapAttrDataSlice->size() > i) {
bufferData.push_back(colormapAttrDataSlice->at(i));
}
else {
bufferData.push_back(0.0);
}
}
glBufferData(
GL_ARRAY_BUFFER,
bufferData.size() * sizeof(GLfloat),
bufferData.data(),
GL_STATIC_DRAW
);
// ==============================================================================================
// ========================================= VAO stuff =========================================
GLsizei stride = static_cast<GLsizei>(sizeof(GLfloat) * 4);
GLint positionAttribute = _shaderProgram->attributeLocation("in_position");
glEnableVertexAttribArray(positionAttribute);
glVertexAttribPointer(
positionAttribute,
3,
GL_FLOAT,
GL_FALSE,
stride,
nullptr
);
if (_hasLoadedColormapAttributeData) {
GLint colormapScalarsAttribute = _shaderProgram->attributeLocation("in_colormapAttributeScalar");
glEnableVertexAttribArray(colormapScalarsAttribute);
glVertexAttribPointer(
colormapScalarsAttribute,
1,
GL_FLOAT,
GL_FALSE,
stride,
reinterpret_cast<void*>(sizeof(GLfloat) * 3)
);
}
// ==============================================================================================
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
// This can happen if the user checks the "_colormapEnabled" checkbox in the GUI
auto colormapAttributeData = getDataSlice(DataSliceKey::ColormapAttributes);
if (_colormapEnabled.value() && (!_colormapTexture || colormapAttributeData->empty())) {
if (!_colormapTexture) {
LINFO("Color map not loaded. Has it been sent from external software?");
}
else {
LINFO("Color map attribute data not loaded. Has it been sent from external software?");
}
_colormapEnabled = false;
}
}
bool RenderablePointsCloud::checkDataStorage() {
if (!_identifier.has_value()) {
LERROR("No identifier found in renderable");
return false;
}
bool updatedDataSlices = false;
auto softwareIntegrationModule = global::moduleEngine->module<SoftwareIntegrationModule>();
std::string dataPointsKey = _identifier.value() + "-DataPoints";
std::string colorMapKey = _identifier.value() + "-Colormap";
std::string colorMapScalarsKey = _identifier.value() + "-CmapAttributeData";
if (softwareIntegrationModule->isDataDirty(dataPointsKey)) {
loadData(dataPointsKey, softwareIntegrationModule);
updatedDataSlices = true;
}
if (softwareIntegrationModule->isDataDirty(colorMapKey)) {
loadColormap(colorMapKey, softwareIntegrationModule);
}
if (softwareIntegrationModule->isDataDirty(colorMapScalarsKey)) {
loadCmapAttributeData(colorMapScalarsKey, softwareIntegrationModule);
updatedDataSlices = true;
}
return updatedDataSlices;
}
void RenderablePointsCloud::loadData(
const std::string& storageKey, SoftwareIntegrationModule* softwareIntegrationModule
) {
// Fetch data from module's centralized storage
auto fullPointData = softwareIntegrationModule->fetchData(storageKey);
if (fullPointData.empty()) {
LWARNING("There was an issue trying to fetch the point data from the centralized storage.");
return;
}
LDEBUG("Regenerating data");
// @TODO (emmbr26 2021-02-11) This 'createDataSlice'step doesn't really seem
// necessary, but could rather be combined with the loadData() step?
createDataSlice();
int size = static_cast<int>(_slicedData.size());
if (_vertexArrayObjectID == 0) {
glGenVertexArrays(1, &_vertexArrayObjectID);
LDEBUG(fmt::format("Generating Vertex Array id '{}'", _vertexArrayObjectID));
}
if (_vertexBufferObjectID == 0) {
glGenBuffers(1, &_vertexBufferObjectID);
LDEBUG(fmt::format("Generating Vertex Buffer Object id '{}'", _vertexBufferObjectID));
}
glBindVertexArray(_vertexArrayObjectID);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
glBufferData(
GL_ARRAY_BUFFER,
size * sizeof(float),
_slicedData.data(),
GL_STATIC_DRAW
);
GLint positionAttribute = _shaderProgram->attributeLocation("in_position");
glEnableVertexAttribArray(positionAttribute);
glVertexAttribPointer(
positionAttribute,
4,
GL_FLOAT,
GL_FALSE,
0,
nullptr
);
glBindVertexArray(0);
_isDirty = false;
}
void RenderablePointsCloud::createDataSlice() {
_slicedData.clear();
_slicedData.reserve(4 * (_fullData.size() / _nValuesPerPoint));
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
pointDataSlice->clear();
pointDataSlice->reserve(fullPointData.size() * 3);
// Create data slice
auto addPosition = [&](const glm::vec4& pos) {
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(pos[j]);
for (glm::vec4::length_type j = 0; j < glm::vec4::length() - 1; ++j) {
pointDataSlice->push_back(pos[j]);
}
};
for (size_t i = 0; i < _fullData.size(); i += _nValuesPerPoint) {
glm::dvec4 transformedPos = glm::dvec4(
_fullData[i + 0],
_fullData[i + 1],
_fullData[i + 2],
for (size_t i = 0; i < fullPointData.size(); i += 3) {
glm::dvec4 transformedPos = {
fullPointData[i + 0],
fullPointData[i + 1],
fullPointData[i + 2],
1.0
);
};
// W-normalization
transformedPos /= transformedPos.w;
transformedPos *= openspace::distanceconstants::Parsec;
transformedPos *= distanceconstants::Parsec;
addPosition(transformedPos);
}
}
void RenderablePointsCloud::loadData() {
if (!_dataStorageKey.has_value()) {
LWARNING("No point data found");
return;
}
// @TODO: potentially combine point data with additional data about the points,
// such as luminosity
// Fetch data from module's centralized storage
auto module = global::moduleEngine->module<SoftwareIntegrationModule>();
_fullData = module->fetchData(_dataStorageKey.value());
_isDirty = true;
}
void RenderablePointsCloud::loadColorMap() {
if (!_identifier.has_value()) {
LWARNING("No data storage identifier found");
return;
}
// Fetch data from module's centralized storage
auto module = global::moduleEngine->module<SoftwareIntegrationModule>();
std::vector<float> colorMap = module->fetchData(_identifier.value() + "-ColorMap");
void RenderablePointsCloud::loadColormap(
const std::string& storageKey, SoftwareIntegrationModule* softwareIntegrationModule
) {
std::vector<float> colorMap = softwareIntegrationModule->fetchData(storageKey);
if (colorMap.empty()) {
LWARNING("There was an issue trying to fetch the colormap data from the syncable storage.");
LWARNING("There was an issue trying to fetch the colormap data from the centralized storage.");
return;
}
int width = colorMap.size();
uint8_t* values = new uint8_t[width];
size_t nValues = colorMap.size();
uint8_t* values = new uint8_t[nValues];
int i = 0;
while (i < width) {
values[i++] = static_cast<uint8_t>(colorMap[i] * 255);
values[i++] = static_cast<uint8_t>(colorMap[i] * 255);
values[i++] = static_cast<uint8_t>(colorMap[i] * 255);
values[i++] = static_cast<uint8_t>(colorMap[i] * 255);
for (size_t i = 0; i < nValues; ++i) {
values[i] = static_cast<uint8_t>(colorMap[i] * 255);
}
GLenum type = GL_TEXTURE_1D;
_colorMapTexture = std::make_unique<ghoul::opengl::Texture>(
_colormapTexture.reset();
_colormapTexture = std::make_unique<ghoul::opengl::Texture>(
values,
glm::uvec3(width, 1, 1),
type,
glm::size3_t(nValues / 4, 1, 1),
GL_TEXTURE_1D,
ghoul::opengl::Texture::Format::RGBA
);
_colormapTexture->uploadTexture();
_isDirty = true;
_loadNewColorMap = false;
_colorMapEnabled = true;
_hasLoadedColormap = true;
}
void RenderablePointsCloud::loadCmapAttributeData(
const std::string& storageKey, SoftwareIntegrationModule* softwareIntegrationModule
) {
auto colormapAttributeData = softwareIntegrationModule->fetchData(storageKey);
if (colormapAttributeData.empty()) {
LWARNING("There was an issue trying to fetch the colormap data from the centralized storage.");
return;
}
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
if (pointDataSlice->size() / 3 != colormapAttributeData.size()) {
LWARNING(fmt::format(
"There is a mismatch in the amount of colormap attribute scalars ({}) and the amount of points ({})",
colormapAttributeData.size(), pointDataSlice->size() / 3
));
_colormapEnabled = false;
return;
}
auto colormapAttributeDataSlice = getDataSlice(DataSliceKey::ColormapAttributes);
colormapAttributeDataSlice->clear();
colormapAttributeDataSlice->reserve(colormapAttributeData.size());
for (size_t i = 0; i < (pointDataSlice->size() / 3); ++i) {
colormapAttributeDataSlice->push_back(colormapAttributeData[i]);
}
_hasLoadedColormapAttributeData = true;
LDEBUG("Rerendering colormap attribute data");
}
std::shared_ptr<RenderablePointsCloud::DataSlice> RenderablePointsCloud::getDataSlice(DataSliceKey key) {
if (!_dataSlices.count(key)) {
_dataSlices.insert({ key, std::make_shared<DataSlice>() });
}
return _dataSlices.at(key);
}
} // namespace openspace