/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2023 * * * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string STRING_NOT_SET = ""; namespace { constexpr const char* _loggerCat = "PointsCloud"; constexpr const std::array UniformNames = { "color", "fade", "size", "modelMatrix", "cameraUp", "screenSize", "cameraViewProjectionMatrix", "eyePosition", "sizeOption", "colormapTexture", "colormapMin", "colormapMax", "colormapNanMode", "colormapNanColor", "colormapEnabled", "linearSizeMin", "linearSizeMax", "linearSizeEnabled","velocityNanMode", "motionEnabled", "time" }; constexpr openspace::properties::Property::PropertyInfo ColorInfo = { "Color", "Color", "The color of the points." }; constexpr openspace::properties::Property::PropertyInfo SizeInfo = { "Size", "Size", "The size of the points." }; constexpr openspace::properties::Property::PropertyInfo IdentifierInfo = { "Identifier", "Identifier", "Identifier used as part of key to access data in centralized central storage." }; constexpr openspace::properties::Property::PropertyInfo PointUnitInfo = { "PointUnit", "Point Unit", "The distance unit of the point data." }; constexpr openspace::properties::Property::PropertyInfo SizeOptionInfo = { "SizeOption", "Size option", "This value determines how the size of the data points are rendered." }; constexpr openspace::properties::Property::PropertyInfo ColormapMinInfo = { "ColormapMin", "Colormap min", "Minimum value to sample from colormap." }; constexpr openspace::properties::Property::PropertyInfo ColormapMaxInfo = { "ColormapMax", "Colormap max", "Maximum value to sample from colormap." }; constexpr openspace::properties::Property::PropertyInfo ColormapNanModeInfo = { "ColormapNanMode", "Colormap NaN Mode", "How points with NaN value in colormap attribute should be represented." }; constexpr openspace::properties::Property::PropertyInfo ColormapNanColorInfo = { "ColormapNanColor", "Colormap NaN Color", "The color of the points where the colormap scalar is NaN." }; constexpr openspace::properties::Property::PropertyInfo ColormapEnabledInfo = { "ColormapEnabled", "Colormap enabled", "Boolean to determine whether to use colormap or not." }; constexpr openspace::properties::Property::PropertyInfo LinearSizeMinInfo = { "LinearSizeMin", "Linear size min", "Minimum value to use for linear size." }; constexpr openspace::properties::Property::PropertyInfo LinearSizeMaxInfo = { "LinearSizeMax", "Linear size max", "Maximum value to use for linear size." }; constexpr openspace::properties::Property::PropertyInfo LinearSizeEnabledInfo = { "LinearSizeEnabled", "Linear size enabled", "Boolean to determine whether to use linear size or not." }; constexpr openspace::properties::Property::PropertyInfo VelocityDistanceUnitInfo = { "VelocityDistanceUnit", "Velocity Distance Unit", "The distance unit of the velocity data." }; constexpr openspace::properties::Property::PropertyInfo VelocityTimeUnitInfo = { "VelocityTimeUnit", "Velocity Time Unit", "The time unit of the velocity data." }; constexpr openspace::properties::Property::PropertyInfo VelocityDateRecordedInfo = { "VelocityDateRecorded", "Velocity Date Recorded", "The date the velocity data was recorded." }; constexpr openspace::properties::Property::PropertyInfo VelocityNanModeInfo = { "VelocityNanMode", "Velocity NaN Mode", "How points with NaN value in colormap attribute should be represented." }; constexpr openspace::properties::Property::PropertyInfo MotionEnabledInfo = { "MotionEnabled", "Motion enabled", "Boolean to determine whether to use motion or not." }; constexpr openspace::properties::Property::PropertyInfo NameInfo = { "Name", "Name", "The name of the points cloud" }; struct [[codegen::Dictionary(RenderablePointsCloud)]] Parameters { // [[codegen::verbatim(ColorInfo.description)]] std::optional color; // [[codegen::verbatim(SizeInfo.description)]] std::optional size; // [[codegen::verbatim(IdentifierInfo.description)]] std::optional identifier; // [[codegen::verbatim(PointUnitInfo.description)]] std::optional pointUnit; // [[codegen::verbatim(ColormapMinInfo.description)]] std::optional colormapMin; // [[codegen::verbatim(ColormapMaxInfo.description)]] std::optional colormapMax; // [[codegen::verbatim(ColormapNanModeInfo.description)]] std::optional colormapNanMode; // [[codegen::verbatim(ColormapNanColorInfo.description)]] std::optional colormapNanColor; // [[codegen::verbatim(ColormapEnabledInfo.description)]] std::optional colormapEnabled; // [[codegen::verbatim(LinearSizeMinInfo.description)]] std::optional linearSizeMin; // [[codegen::verbatim(LinearSizeMaxInfo.description)]] std::optional linearSizeMax; // [[codegen::verbatim(LinearSizeEnabledInfo.description)]] std::optional linearSizeEnabled; // [[codegen::verbatim(VelocityDistanceUnitInfo.description)]] std::optional velocityDistanceUnit; // [[codegen::verbatim(VelocityTimeUnitInfo.description)]] std::optional velocityTimeUnit; // [[codegen::verbatim(VelocityDateRecordedInfo.description)]] std::optional velocityDateRecorded; // [[codegen::verbatim(VelocityNanModeInfo.description)]] std::optional velocityNanMode; // [[codegen::verbatim(MotionEnabledInfo.description)]] std::optional motionEnabled; // [[codegen::verbatim(NameInfo.description)]] std::optional name; // VOLATILE: Keep in sync with SizeOption in `./renderablepointscloud.h` enum class SizeOption { Uniform = 0, NonUniform }; // [[codegen::verbatim(SizeOptionInfo.description)]] std::optional sizeOption; }; #include "renderablepointscloud_codegen.cpp" } // namespace namespace openspace { using namespace softwareintegration; documentation::Documentation RenderablePointsCloud::Documentation() { return codegen::doc("softwareintegration_renderable_pointscloud"); } RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _color(ColorInfo, glm::vec4(glm::vec3(0.5f), 1.f), glm::vec4(0.f), glm::vec4(1.f), glm::vec4(.01f)) , _size(SizeInfo, 1.f, 0.f, 500.f, .1f) , _pointUnit(PointUnitInfo, STRING_NOT_SET) , _sizeOption(SizeOptionInfo, properties::OptionProperty::DisplayType::Dropdown) , _colormapMin(ColormapMinInfo) , _colormapMax(ColormapMaxInfo) , _colormapNanMode(ColormapNanModeInfo) , _colormapNanColor(ColormapNanColorInfo, glm::vec4(glm::vec3(0.5f), 1.f), glm::vec4(1.0f), glm::vec4(0.f), glm::vec4(0.f)) , _colormapEnabled(ColormapEnabledInfo, false) , _linearSizeMax(LinearSizeMinInfo) , _linearSizeMin(LinearSizeMaxInfo) , _linearSizeEnabled(LinearSizeEnabledInfo, false) , _velocityDistanceUnit(VelocityDistanceUnitInfo, STRING_NOT_SET) , _velocityTimeUnit(VelocityTimeUnitInfo, STRING_NOT_SET) , _velocityDateRecorded(VelocityDateRecordedInfo, glm::ivec3{ -1 }) , _velocityNanMode(VelocityNanModeInfo) , _name(NameInfo) , _motionEnabled(MotionEnabledInfo, false) { const Parameters p = codegen::bake(dictionary); _identifier = p.identifier.value(); _name = p.name.value_or(_name); _name.setVisibility(properties::Property::Visibility::Hidden); addProperty(_name); _color = p.color.value_or(_color); _color.setViewOption(properties::Property::ViewOptions::Color); addProperty(_color); _size = p.size.value_or(_size); addProperty(_size); _sizeOption.addOptions({ { SizeOption::Uniform, "Uniform" }, { SizeOption::NonUniform, "Non uniform" } }); if (p.sizeOption.has_value()) { switch (*p.sizeOption) { case Parameters::SizeOption::Uniform: _sizeOption = SizeOption::Uniform; break; case Parameters::SizeOption::NonUniform: _sizeOption = SizeOption::NonUniform; break; } } addProperty(_sizeOption); _pointUnit = p.pointUnit.value_or(_pointUnit); _pointUnit.setVisibility(properties::Property::Visibility::Hidden); _pointUnit.onChange([this] { _pointUnitIsDirty = true; }); addProperty(_pointUnit); // =============== Colormap =============== _colormapMin = p.colormapMin.value_or(_colormapMin); _colormapMin.setVisibility(properties::Property::Visibility::Hidden); _colormapMin.onChange([this] { checkColormapMinMax(); }); addProperty(_colormapMin); _colormapMax = p.colormapMax.value_or(_colormapMax); _colormapMax.setVisibility(properties::Property::Visibility::Hidden); _colormapMax.onChange([this] { checkColormapMinMax(); }); addProperty(_colormapMax); _colormapNanMode = p.colormapNanMode.value_or(_colormapNanMode); _colormapNanMode.setVisibility(properties::Property::Visibility::Hidden); addProperty(_colormapNanMode); _colormapNanColor = p.colormapNanColor.value_or(_colormapNanColor); _colormapNanColor.setVisibility(properties::Property::Visibility::Hidden); addProperty(_colormapNanColor); _colormapEnabled = p.colormapEnabled.value_or(_colormapEnabled); _colormapEnabled.onChange([this] { checkIfColormapCanBeEnabled(); }); addProperty(_colormapEnabled); // =============== Linear size =============== _linearSizeEnabled = p.linearSizeEnabled.value_or(_linearSizeEnabled); _linearSizeEnabled.onChange([this] { checkIfLinearSizeCanBeEnabled(); }); addProperty(_linearSizeEnabled); auto linearSizeMinMaxChecker = [this] { if (_linearSizeMin.value() > _linearSizeMax.value()) { auto temp = _linearSizeMin.value(); _linearSizeMin = _linearSizeMax.value(); _linearSizeMax = temp; } }; _linearSizeMin = p.linearSizeMin.value_or(_linearSizeMin); _linearSizeMin.setVisibility(properties::Property::Visibility::Hidden); _linearSizeMin.onChange(linearSizeMinMaxChecker); addProperty(_linearSizeMin); _linearSizeMax = p.linearSizeMax.value_or(_linearSizeMax); _linearSizeMax.setVisibility(properties::Property::Visibility::Hidden); _linearSizeMax.onChange(linearSizeMinMaxChecker); addProperty(_linearSizeMax); _velocityDistanceUnit = p.velocityDistanceUnit.value_or(_velocityDistanceUnit); _velocityDistanceUnit.setVisibility(properties::Property::Visibility::Hidden); _velocityDistanceUnit.onChange([this] { _velocityUnitsAreDirty = true; }); addProperty(_velocityDistanceUnit); _velocityTimeUnit = p.velocityTimeUnit.value_or(_velocityTimeUnit); _velocityTimeUnit.setVisibility(properties::Property::Visibility::Hidden); _velocityTimeUnit.onChange([this] { _velocityUnitsAreDirty = true; }); addProperty(_velocityTimeUnit); _velocityDateRecorded = p.velocityDateRecorded.value_or(_velocityDateRecorded); _velocityDateRecorded.setVisibility(properties::Property::Visibility::Hidden); _velocityDateRecorded.onChange([this] { updateVelocityT0(); }); addProperty(_velocityDateRecorded); _velocityNanMode = p.velocityNanMode.value_or(_velocityNanMode); _velocityNanMode.setVisibility(properties::Property::Visibility::Hidden); addProperty(_velocityNanMode); _motionEnabled = p.motionEnabled.value_or(_motionEnabled); _motionEnabled.onChange([this] { checkIfMotionCanBeEnabled(); }); addProperty(_motionEnabled); } bool RenderablePointsCloud::isReady() const { return _shaderProgram && _identifier.has_value() && _identifier.value() != ""; } void RenderablePointsCloud::initializeGL() { _shaderProgram = global::renderEngine->buildRenderProgram( "PointsCloud", absPath("${MODULE_SOFTWAREINTEGRATION}/shaders/point_vs.glsl"), absPath("${MODULE_SOFTWAREINTEGRATION}/shaders/point_fs.glsl"), absPath("${MODULE_SOFTWAREINTEGRATION}/shaders/point_ge.glsl") ); ghoul::opengl::updateUniformLocations(*_shaderProgram, _uniformCache, UniformNames); } void RenderablePointsCloud::deinitializeGL() { glDeleteBuffers(1, &_vbo); _vbo = 0; glDeleteVertexArrays(1, &_vao); _vao = 0; _colormapTexture = nullptr; if (_shaderProgram) { global::renderEngine->removeRenderProgram(_shaderProgram.get()); _shaderProgram = nullptr; } } void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) { auto pointDataSlice = getDataSlice(DataSliceKey::Points); if (pointDataSlice->empty()) return; _shaderProgram->activate(); auto eyePosition = glm::dvec3{ glm::inverse(data.camera.combinedViewMatrix()) * glm::dvec4(0.0, 0.0, 0.0, 1.0) }; _shaderProgram->setUniform(_uniformCache.eyePosition, eyePosition); // _shaderProgram->setUniform(_uniformCache.cameraPosition, data.camera.positionVec3()); _shaderProgram->setUniform( _uniformCache.cameraUp, glm::dvec3(data.camera.lookUpVectorWorldSpace()) ); GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); _shaderProgram->setUniform(_uniformCache.screenSize, glm::ivec2(viewport[2], viewport[3])); auto modelMatrix = glm::dmat4{ glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * glm::dmat4(data.modelTransform.rotation) * glm::scale(glm::dmat4(1.0), data.modelTransform.scale) }; _shaderProgram->setUniform(_uniformCache.modelMatrix, modelMatrix); _shaderProgram->setUniform( _uniformCache.cameraViewProjectionMatrix, glm::dmat4( glm::dmat4(data.camera.projectionMatrix()) * data.camera.combinedViewMatrix() ) ); ghoul::opengl::TextureUnit colorUnit; if (_colormapTexture) { colorUnit.activate(); _colormapTexture->bind(); _shaderProgram->setUniform(_uniformCache.colormapTexture, colorUnit); } else { // 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.colormapNanMode, _colormapNanMode); _shaderProgram->setUniform(_uniformCache.colormapNanColor, _colormapNanColor); _shaderProgram->setUniform(_uniformCache.colormapEnabled, _colormapEnabled); _shaderProgram->setUniform(_uniformCache.linearSizeMin, _linearSizeMin); _shaderProgram->setUniform(_uniformCache.linearSizeMax, _linearSizeMax); _shaderProgram->setUniform(_uniformCache.linearSizeEnabled, _linearSizeEnabled); _shaderProgram->setUniform(_uniformCache.velocityNanMode, _velocityNanMode); _shaderProgram->setUniform(_uniformCache.motionEnabled, _motionEnabled); _shaderProgram->setUniform( _uniformCache.time, static_cast(data.time.j2000Seconds() - _t0) ); _shaderProgram->setUniform(_uniformCache.color, _color); _shaderProgram->setUniform(_uniformCache.fade, _fade); _shaderProgram->setUniform(_uniformCache.size, _size); _shaderProgram->setUniform(_uniformCache.sizeOption, _sizeOption); // Changes GL state: glEnablei(GL_BLEND, 0); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_FALSE); glBindVertexArray(_vao); const GLsizei nPoints = static_cast(pointDataSlice->size() / 3); glDrawArrays(GL_POINTS, 0, nPoints); glBindVertexArray(0); _shaderProgram->deactivate(); // Restores GL State glDepthMask(GL_TRUE); global::renderEngine->openglStateCache().resetBlendState(); global::renderEngine->openglStateCache().resetDepthState(); } void RenderablePointsCloud::update(const UpdateData&) { if (_shaderProgram->isDirty()) { _shaderProgram->rebuildFromFile(); ghoul::opengl::updateUniformLocations(*_shaderProgram, _uniformCache, UniformNames); } bool updatedDataSlices = checkDataStorage(); if (updatedDataSlices) { if (_vao == 0) { glGenVertexArrays(1, &_vao); LDEBUG(std::format("Generating Vertex Array id '{}'", _vao)); } if (_vbo == 0) { glGenBuffers(1, &_vbo); LDEBUG(std::format("Generating Vertex Buffer Object id '{}'", _vbo)); } glBindVertexArray(_vao); glBindBuffer(GL_ARRAY_BUFFER, _vbo); auto pointDataSlice = getDataSlice(DataSliceKey::Points); auto colormapAttrDataSlice = getDataSlice(DataSliceKey::ColormapAttributes); auto linearSizeAttrDataSlice = getDataSlice(DataSliceKey::LinearSizeAttributes); auto velocityDataSlice = getDataSlice(DataSliceKey::Velocity); if (pointDataSlice->empty()) return; // ========================== Create resulting data slice and buffer it ========================== std::vector 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(std::nanf("0")); } if (linearSizeAttrDataSlice->size() > i) { bufferData.push_back(linearSizeAttrDataSlice->at(i)); } else { bufferData.push_back(std::nanf("0")); } if (velocityDataSlice->size() > (j + 2)) { bufferData.push_back(velocityDataSlice->at(j)); bufferData.push_back(velocityDataSlice->at(j + 1)); bufferData.push_back(velocityDataSlice->at(j + 2)); } else { bufferData.push_back(std::nanf("0")); bufferData.push_back(std::nanf("0")); bufferData.push_back(std::nanf("0")); } } glBufferData( GL_ARRAY_BUFFER, bufferData.size() * sizeof(GLfloat), bufferData.data(), GL_STATIC_DRAW ); // ============================================================================================== // ========================================= VAO stuff ========================================= GLsizei stride = static_cast(sizeof(GLfloat) * _nValuesForVAOStride); 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(sizeof(GLfloat) * 3) ); } if (_hasLoadedLinearSizeAttributeData) { GLint linearSizeAttributeScalar = _shaderProgram->attributeLocation("in_linearSizeAttributeScalar"); glEnableVertexAttribArray(linearSizeAttributeScalar); glVertexAttribPointer( linearSizeAttributeScalar, 1, GL_FLOAT, GL_FALSE, stride, reinterpret_cast(sizeof(GLfloat) * 4) ); } if (_hasLoadedVelocityData) { GLint velocityAttribute = _shaderProgram->attributeLocation("in_velocity"); glEnableVertexAttribArray(velocityAttribute); glVertexAttribPointer( velocityAttribute, 3, GL_FLOAT, GL_FALSE, stride, reinterpret_cast(sizeof(GLfloat) * 5) ); } // ============================================================================================== glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } } bool RenderablePointsCloud::checkDataStorage() { if (!_identifier.has_value()) { LERROR("No identifier found in renderable"); return false; } bool updatedDataSlices = false; auto softwareIntegrationModule = global::moduleEngine->module(); if (shouldLoadPointData(softwareIntegrationModule)) { loadPointData(softwareIntegrationModule); updatedDataSlices = true; _pointUnitIsDirty = false; } if (shouldLoadColormap(softwareIntegrationModule)) { loadColormap(softwareIntegrationModule); } if (shouldLoadColormapAttrData(softwareIntegrationModule)) { loadColormapAttributeData(softwareIntegrationModule); updatedDataSlices = true; } if (shouldLoadLinearSizeAttrData(softwareIntegrationModule)) { loadLinearSizeAttributeData(softwareIntegrationModule); updatedDataSlices = true; } if (shouldLoadVelocityData(softwareIntegrationModule)) { loadVelocityData(softwareIntegrationModule); updatedDataSlices = true; _velocityUnitsAreDirty = false; } return updatedDataSlices; } void RenderablePointsCloud::loadPointData(SoftwareIntegrationModule* softwareIntegrationModule) { // Fetch point data from module's centralized storage std::vector pointData; if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::DataPoints, pointData)) { LERROR("There was an issue trying to fetch the point data from the centralized storage."); return; } // Parse unit DistanceUnit pointUnit; try { pointUnit = distanceUnitFromString(_pointUnit.value().c_str()); } catch (const ghoul::MissingCaseException& ) { LERROR(std::format( "Error when parsing point unit. OpenSpace doesn't support the distance unit '{}'.", _pointUnit.stringValue() )); return; } // Convert to meters if needed int nNans = 0; if (pointUnit != DistanceUnit::Meter) { float toMeters = static_cast(toMeter(pointUnit)); for (auto& value : pointData) { if (isnan(value)) { nNans++; continue; } value *= toMeters; } } // Assign point data to point data slice auto pointDataSlice = getDataSlice(DataSliceKey::Points); pointDataSlice->clear(); pointDataSlice->assign(pointData.begin(), pointData.end()); softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::DataPoints); LINFO(std::format( "New point data ({} points) has loaded. {} values are NaN values. " "Points with at least one NaN value are hidden.", (pointData.size() / 3), nNans )); } void RenderablePointsCloud::loadVelocityData(SoftwareIntegrationModule* softwareIntegrationModule) { // Fetch velocity data from module's centralized storage std::vector velocityData; if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::VelocityData, velocityData)) { LWARNING("There was an issue trying to fetch the velocity data from the centralized storage."); return; } // Check that velocity data is same length as point data auto pointDataSlice = getDataSlice(DataSliceKey::Points); // if (pointDataSlice->size() != velocityData.size()) { // LWARNING(std::format( // "There is a mismatch in the amount of velocity data ({}) and the amount of points ({})", // velocityData.size() / 3, pointDataSlice->size() / 3 // )); // _motionEnabled = false; // return; // } // Parse units DistanceUnit velocityDistanceUnit; TimeUnit velocityTimeUnit; try { velocityDistanceUnit = distanceUnitFromString(_velocityDistanceUnit.value().c_str()); velocityTimeUnit = timeUnitFromString(_velocityTimeUnit.value().c_str()); } catch (const ghoul::MissingCaseException& ) { LERROR(std::format( "Error when parsing velocity units." "OpenSpace doesn't support the velocity unit '{}/{}'.", _velocityDistanceUnit.stringValue(), _velocityTimeUnit.stringValue() )); return; } // Units conversion bool conversionNeeded = false; // Meters multiplier float toMeters = 1.0; if (velocityDistanceUnit != DistanceUnit::Meter) { toMeters = static_cast(toMeter(velocityDistanceUnit)); conversionNeeded = true; } // Seconds divider float toSeconds = 1.0; if (velocityTimeUnit != TimeUnit::Second) { toSeconds = static_cast(convertTime(1.0, velocityTimeUnit, TimeUnit::Second)); conversionNeeded = true; } // Check for NaN values and convert to m/s if needed int nNans = 0; for (auto& v : velocityData) { if (isnan(v)) { nNans++; continue; } if (conversionNeeded) { v *= toMeters / toSeconds; } } // Assign velocity data to velocity data slice auto velocityDataSlice = getDataSlice(DataSliceKey::Velocity); velocityDataSlice->clear(); velocityDataSlice->assign(velocityData.begin(), velocityData.end()); LINFO( std::format( "Viewing {} points with velocity ({} points in total). " "{} points have NaN-value velocity and are hidden or static.", velocityData.size()/3 - nNans/3, pointDataSlice->size()/3, nNans/3 ) ); _hasLoadedVelocityData = true; softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::VelocityData); LDEBUG("New velocity data has loaded"); } void RenderablePointsCloud::loadColormap(SoftwareIntegrationModule* softwareIntegrationModule) { // Fetch colormap data from module's centralized storage std::vector colormap; if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::Colormap, colormap)) { LWARNING("There was an issue trying to fetch the colormap data from the centralized storage."); return; } size_t nValues = colormap.size(); uint8_t* values = new uint8_t[nValues]; for (size_t i = 0; i < nValues; ++i) { values[i] = static_cast(colormap[i] * 255); } _colormapTexture = nullptr; _colormapTexture = std::make_unique( values, glm::size3_t(nValues / 4, 1, 1), GL_TEXTURE_1D, ghoul::opengl::Texture::Format::RGBA ); _colormapTexture->uploadTexture(); _hasLoadedColormap = true; LDEBUG("New colormap has loaded"); softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::Colormap); } void RenderablePointsCloud::loadColormapAttributeData(SoftwareIntegrationModule* softwareIntegrationModule) { // Fetch colormap attribute data from module's centralized storage std::vector colormapAttributeData; if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::ColormapAttrData, colormapAttributeData)) { LWARNING("There was an issue trying to fetch the colormap attribute data from the centralized storage."); return; } // Check that colormap attribute data is same length as point data auto pointDataSlice = getDataSlice(DataSliceKey::Points); // if (pointDataSlice->size() / 3 != colormapAttributeData.size()) { // LWARNING(std::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->assign(colormapAttributeData.begin(), colormapAttributeData.end()); _hasLoadedColormapAttributeData = true; softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::ColormapAttrData); LDEBUG("New colormap attribute data has loaded"); } void RenderablePointsCloud::loadLinearSizeAttributeData(SoftwareIntegrationModule* softwareIntegrationModule) { // Fetch linear size attribute data from module's centralized storage std::vector linearSizeAttributeData; if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::LinearSizeAttrData, linearSizeAttributeData)) { LWARNING("There was an issue trying to fetch the linear size attribute data from the centralized storage."); return; } // Check that linear size attribute data is same length as point data auto pointDataSlice = getDataSlice(DataSliceKey::Points); // if (pointDataSlice->size() / 3 != linearSizeAttributeData.size()) { // LWARNING(std::format( // "There is a mismatch in the amount of linear size attribute scalars ({}) and the amount of points ({})", // linearSizeAttributeData.size(), pointDataSlice->size() / 3 // )); // _linearSizeEnabled = false; // return; // } auto linearSizeAttributeDataSlice = getDataSlice(DataSliceKey::LinearSizeAttributes); linearSizeAttributeDataSlice->clear(); linearSizeAttributeDataSlice->assign(linearSizeAttributeData.begin(), linearSizeAttributeData.end()); _hasLoadedLinearSizeAttributeData = true; softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::LinearSizeAttrData); LDEBUG("New colormap attribute data has loaded"); } std::shared_ptr RenderablePointsCloud::getDataSlice(DataSliceKey key) { if (!_dataSlices.count(key)) { _dataSlices.insert({ key, std::make_shared() }); } return _dataSlices.at(key); } void RenderablePointsCloud::checkIfColormapCanBeEnabled() { if (!global::windowDelegate->isMaster()) return; // This can happen if the user checks the "ColormapEnabled" checkbox in the GUI auto colormapAttributeData = getDataSlice(DataSliceKey::ColormapAttributes); if (_colormapEnabled.value()) { if ((!_colormapTexture || colormapAttributeData->empty())) { if (!_colormapTexture) { LINFO("Colormap not loaded. Has it been sent from external software?"); } if (colormapAttributeData->empty()) { LINFO("Colormap attribute data not loaded. Has it been sent from external software?"); } global::scriptEngine->queueScript( std::format( "openspace.setPropertyValueSingle('Scene.{}.Renderable.ColormapEnabled', {});", _identifier.value(), "false" ) ); } } } void RenderablePointsCloud::checkIfLinearSizeCanBeEnabled() { if (!global::windowDelegate->isMaster()) return; // This can happen if the user checks the "LinearSizeEnabled" checkbox in the GUI auto linearSizeAttributeData = getDataSlice(DataSliceKey::LinearSizeAttributes); if (_linearSizeEnabled.value() && linearSizeAttributeData->empty()) { LINFO("Linear size attribute data not loaded. Has it been sent from external software?"); global::scriptEngine->queueScript( std::format( "openspace.setPropertyValueSingle('Scene.{}.Renderable.LinearSizeEnabled', {});", _identifier.value(), "false" ) ); } } void RenderablePointsCloud::checkIfMotionCanBeEnabled() { if (!global::windowDelegate->isMaster()) return; // This can happen if the user checks the "MotionEnabled" checkbox in the GUI auto velocityData = getDataSlice(DataSliceKey::Velocity); if (_motionEnabled.value() && velocityData->empty()) { LINFO("Velocity data not loaded. Has it been sent from external software?"); global::scriptEngine->queueScript( std::format( "openspace.setPropertyValueSingle('Scene.{}.Renderable.MotionEnabled', {});", _identifier.value(), "false" ) ); } } void RenderablePointsCloud::checkColormapMinMax() { float min = std::any_cast(_colormapMin.get()); float max = std::any_cast(_colormapMax.get()); if (min > max) { float temp = min; _colormapMin = max; _colormapMax = temp; } } bool RenderablePointsCloud::shouldLoadPointData(SoftwareIntegrationModule* softwareIntegrationModule) { return ( ( _pointUnitIsDirty || softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::DataPoints) ) && _pointUnit.value() != STRING_NOT_SET ); } bool RenderablePointsCloud::shouldLoadVelocityData(SoftwareIntegrationModule* softwareIntegrationModule) { return ( ( _velocityUnitsAreDirty || softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::VelocityData) ) && _velocityDistanceUnit.value() != STRING_NOT_SET && _velocityTimeUnit.value() != STRING_NOT_SET ); } bool RenderablePointsCloud::shouldLoadColormap(SoftwareIntegrationModule* softwareIntegrationModule) { return softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::Colormap); } bool RenderablePointsCloud::shouldLoadColormapAttrData(SoftwareIntegrationModule* softwareIntegrationModule) { return softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::ColormapAttrData); } bool RenderablePointsCloud::shouldLoadLinearSizeAttrData(SoftwareIntegrationModule* softwareIntegrationModule) { return softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::LinearSizeAttrData); } void RenderablePointsCloud::updateVelocityT0() { _t0 = Time::convertTime(simp::toDateString(_velocityDateRecorded)); } } // namespace openspace