diff --git a/modules/softwareintegration/network/softwareconnection.cpp b/modules/softwareintegration/network/softwareconnection.cpp index 084ae64cb7..389803ea51 100644 --- a/modules/softwareintegration/network/softwareconnection.cpp +++ b/modules/softwareintegration/network/softwareconnection.cpp @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014-2020 * + * 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 * @@ -33,137 +33,143 @@ namespace { namespace openspace { - const float SoftwareConnection::ProtocolVersion = 1.0; +const float SoftwareConnection::ProtocolVersion = 1.0; - SoftwareConnection::Message::Message(MessageType type, std::vector content) - : type(type) - , content(std::move(content)) - {} +SoftwareConnection::Message::Message(MessageType type, std::vector content) + : type(type), content(std::move(content)) +{} - SoftwareConnection::SoftwareConnectionLostError::SoftwareConnectionLostError() - : ghoul::RuntimeError("Software connection lost", "SoftwareConnection") - {} +SoftwareConnection::SoftwareConnectionLostError::SoftwareConnectionLostError() + : ghoul::RuntimeError("Software connection lost", "SoftwareConnection") +{} - SoftwareConnection::SoftwareConnection(std::unique_ptr socket) - : _socket(std::move(socket)) - {} +SoftwareConnection::SoftwareConnection(std::unique_ptr socket) + : _socket(std::move(socket)) +{} - bool SoftwareConnection::isConnectedOrConnecting() const { - return _socket->isConnected() || _socket->isConnecting(); - } +bool SoftwareConnection::isConnectedOrConnecting() const { + return _socket->isConnected() || _socket->isConnecting(); +} - bool SoftwareConnection::sendMessage(std::string message) { - - if (_isListening) - { - if (!_socket->put(message.data(), message.size())) { - return false; - } - LDEBUG(fmt::format("Message sent: {}", message)); - } - else - { - _isListening = true; +bool SoftwareConnection::sendMessage(std::string message) { + if (_isListening) { + if (!_socket->put(message.data(), message.size())) { return false; } - - return true; + LDEBUG(fmt::format("Message sent: {}", message)); + } + else { + _isListening = true; + return false; } - void SoftwareConnection::disconnect() { - if (_socket) { - _socket->disconnect(); - } + return true; +} + +void SoftwareConnection::disconnect() { + if (_socket) { + _socket->disconnect(); + } +} + +ghoul::io::TcpSocket* SoftwareConnection::socket() { + return _socket.get(); +} + +SoftwareConnection::Message SoftwareConnection::receiveMessage() { + // Header consists of version (3 char), message type (4 char) & subject size (9 char) + size_t HeaderSize = 16 * sizeof(char); + + // Create basic buffer for receiving first part of message + std::vector headerBuffer(HeaderSize); + std::vector subjectBuffer; + + // Receive the header data + if (!_socket->get(headerBuffer.data(), HeaderSize)) { + LERROR("Failed to read header from socket. Disconnecting."); + throw SoftwareConnectionLostError(); } - ghoul::io::TcpSocket* SoftwareConnection::socket() { - return _socket.get(); + // Read and convert version number: Byte 0-2 + std::string version; + for (int i = 0; i < 3; i++) { + version.push_back(headerBuffer[i]); + } + const uint32_t protocolVersionIn = std::stoi(version); + + // Make sure that header matches the protocol version + if (protocolVersionIn != ProtocolVersion) { + LERROR(fmt::format( + "Protocol versions do not match. Remote version: {}, Local version: {}", + protocolVersionIn, + ProtocolVersion + )); + throw SoftwareConnectionLostError(); } - SoftwareConnection::Message SoftwareConnection::receiveMessage() { - // Header consists of version (3 char), message type (4 char) & subject size (9 char) - size_t HeaderSize = 16 * sizeof(char); - - // Create basic buffer for receiving first part of message - std::vector headerBuffer(HeaderSize); - std::vector subjectBuffer; - - // Receive the header data - if (!_socket->get(headerBuffer.data(), HeaderSize)) { - LERROR("Failed to read header from socket. Disconnecting."); - throw SoftwareConnectionLostError(); - } - - // Read and convert version number: Byte 0-2 - std::string version; - for (int i = 0; i < 3; i++) - version.push_back(headerBuffer[i]); - const uint32_t protocolVersionIn = std::stoi(version); - - // Make sure that header matches the protocol version - if (!(protocolVersionIn == ProtocolVersion)) { - LERROR(fmt::format( - "Protocol versions do not match. Remote version: {}, Local version: {}", - protocolVersionIn, - ProtocolVersion - )); - throw SoftwareConnectionLostError(); - } - - // Read message type: Byte 3-6 - std::string type; - for(int i = 3; i < 7; i++) - type.push_back(headerBuffer[i]); - - // Read and convert message size: Byte 7-15 - std::string subjectSizeIn; - for (int i = 7; i < 16; i++) - subjectSizeIn.push_back(headerBuffer[i]); - const size_t subjectSize = stoi(subjectSizeIn); - - // Receive the message data - subjectBuffer.resize(subjectSize); - if (!_socket->get(subjectBuffer.data(), subjectSize)) { - LERROR("Failed to read message from socket. Disconnecting."); - throw SoftwareConnectionLostError(); - } - - // And delegate decoding depending on message type - if (type == "CONN") - return Message(MessageType::Connection, subjectBuffer); - else if (type == "PDAT") - return Message(MessageType::ReadPointData, subjectBuffer); - else if (type == "LUMI") - return Message(MessageType::ReadLuminosityData, subjectBuffer); - else if (type == "VELO") - return Message(MessageType::ReadVelocityData, subjectBuffer); - else if( type == "ASGN") - return Message(MessageType::AddSceneGraphNode, subjectBuffer); - else if (type == "RSGN") - return Message(MessageType::RemoveSceneGraphNode, subjectBuffer); - else if (type == "UPCO") { - _isListening = false; - return Message(MessageType::Color, subjectBuffer); - } - else if (type == "UPOP") { - _isListening = false; - return Message(MessageType::Opacity, subjectBuffer); - } - else if (type == "UPSI") { - _isListening = false; - return Message(MessageType::Size, subjectBuffer); - } - else if (type == "TOVI") { - _isListening = false; - return Message(MessageType::Visibility, subjectBuffer); - } - else if (type == "DISC") - return Message(MessageType::Disconnection, subjectBuffer); - else { - LERROR(fmt::format("Unsupported message type: {}. Disconnecting...", type)); - return Message(MessageType::Disconnection, subjectBuffer); - } + // Read message type: Byte 3-6 + std::string type; + for (int i = 3; i < 7; i++) { + type.push_back(headerBuffer[i]); } + // Read and convert message size: Byte 7-15 + std::string subjectSizeIn; + for (int i = 7; i < 16; i++) { + subjectSizeIn.push_back(headerBuffer[i]); + } + const size_t subjectSize = stoi(subjectSizeIn); + + // Receive the message data + subjectBuffer.resize(subjectSize); + if (!_socket->get(subjectBuffer.data(), subjectSize)) { + LERROR("Failed to read message from socket. Disconnecting."); + throw SoftwareConnectionLostError(); + } + + // And delegate decoding depending on message type + if (type == "CONN") { + return Message(MessageType::Connection, subjectBuffer); + } + else if (type == "PDAT") { + return Message(MessageType::ReadPointData, subjectBuffer); + } + else if (type == "LUMI") { + return Message(MessageType::ReadLuminosityData, subjectBuffer); + } + else if (type == "VELO") { + return Message(MessageType::ReadVelocityData, subjectBuffer); + } + else if (type == "ASGN") { + return Message(MessageType::AddSceneGraphNode, subjectBuffer); + } + else if (type == "RSGN") { + return Message(MessageType::RemoveSceneGraphNode, subjectBuffer); + } + else if (type == "UPCO") { + _isListening = false; + return Message(MessageType::Color, subjectBuffer); + } + else if (type == "UPOP") { + _isListening = false; + return Message(MessageType::Opacity, subjectBuffer); + } + else if (type == "UPSI") { + _isListening = false; + return Message(MessageType::Size, subjectBuffer); + } + else if (type == "TOVI") { + _isListening = false; + return Message(MessageType::Visibility, subjectBuffer); + } + else if (type == "DISC") { + return Message(MessageType::Disconnection, subjectBuffer); + } + else { + LERROR(fmt::format("Unsupported message type: {}. Disconnecting...", type)); + return Message(MessageType::Disconnection, subjectBuffer); + } +} + } // namespace openspace diff --git a/modules/softwareintegration/network/softwareconnection.h b/modules/softwareintegration/network/softwareconnection.h index 2579771ed2..bd42027ee0 100644 --- a/modules/softwareintegration/network/softwareconnection.h +++ b/modules/softwareintegration/network/softwareconnection.h @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014-2020 * + * 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 * @@ -75,10 +75,10 @@ public: SoftwareConnection::Message receiveMessage(); - bool _isListening = true; static const float ProtocolVersion; private: + bool _isListening = true; std::unique_ptr _socket; }; diff --git a/modules/softwareintegration/rendering/renderablepointscloud.cpp b/modules/softwareintegration/rendering/renderablepointscloud.cpp index a705c2ba0b..1924a11ba0 100644 --- a/modules/softwareintegration/rendering/renderablepointscloud.cpp +++ b/modules/softwareintegration/rendering/renderablepointscloud.cpp @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014-2020 * + * 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 * @@ -41,7 +41,7 @@ namespace { constexpr const char* KeyData = "Data"; constexpr const char* KeyLuminosity = "Luminosity"; constexpr const char* KeyVelocity = "Velocity"; - constexpr const char* ProgramName = "shaderProgram"; + constexpr const char* _loggerCat = "PointsCloud"; constexpr int8_t CurrentCacheVersion = 1; @@ -52,13 +52,6 @@ namespace { "The color of the points." }; - constexpr openspace::properties::Property::PropertyInfo OpacityInfo = { - "Opacity", - "Opacity", - "Determines the transparency of the points, where 1 is completely opaque " - "and 0 fully transparent." - }; - constexpr openspace::properties::Property::PropertyInfo SizeInfo = { "Size", "Size", @@ -74,309 +67,282 @@ namespace { namespace openspace { - documentation::Documentation RenderablePointsCloud::Documentation() { - using namespace documentation; - return { - "RenderablePointsCloud", - "softwareintegration_renderable_pointscloud", +documentation::Documentation RenderablePointsCloud::Documentation() { + using namespace documentation; + return { + "RenderablePointsCloud", + "softwareintegration_renderable_pointscloud", + { { - { - "Type", - new StringEqualVerifier("RenderablePointsCloud"), - Optional::No - }, - { - ColorInfo.identifier, - new DoubleVector3Verifier, - Optional::Yes, - ColorInfo.description - }, - { - OpacityInfo.identifier, - new DoubleVerifier, - Optional::Yes, - OpacityInfo.description - }, - { - SizeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - SizeInfo.description - }, - { - ToggleVisibilityInfo.identifier, - new BoolVerifier, - Optional::Yes, - ToggleVisibilityInfo.description - } + ColorInfo.identifier, + new DoubleVector3Verifier, + Optional::Yes, + ColorInfo.description + }, + { + SizeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + SizeInfo.description + }, + { + ToggleVisibilityInfo.identifier, + new BoolVerifier, + Optional::Yes, + ToggleVisibilityInfo.description } - }; + } + }; +} + +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) + , _toggleVisibility(ToggleVisibilityInfo, true) +{ + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "RenderablePointsCloud" + ); + + if (dictionary.hasKey(ColorInfo.identifier)) { + _color = dictionary.value(ColorInfo.identifier); + } + _color.setViewOption(properties::Property::ViewOptions::Color); + addProperty(_color); + + if (dictionary.hasKey(KeyData)) { + ghoul::Dictionary dataDict = dictionary.value(KeyData); + for (int i = 0; i < static_cast(dataDict.size()); ++i) { + _pointData.push_back(dataDict.value(std::to_string(i + 1))); + } + _hasPointData = true; } - RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _color( - ColorInfo, - glm::vec3(0.5f, 0.5, 0.5f), - glm::vec3(0.f), - glm::vec3(1.f) - ) - , _opacity(OpacityInfo, 0.5f, 0.f, 1.f) - , _size(SizeInfo, 1.f, 0.f, 150.f) - , _toggleVisibility(ToggleVisibilityInfo, true) - { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderablePointsCloud" + if (dictionary.hasKey(KeyLuminosity)) { + ghoul::Dictionary lumDict = dictionary.value(KeyLuminosity); + for (int i = 0; i < static_cast(lumDict.size()); ++i) { + float luminosity = static_cast( + lumDict.value(std::to_string(i + 1)) + ); + _luminosityData.push_back(luminosity); + } + _hasLuminosityData = true; + } + + if (dictionary.hasKey(KeyVelocity)) { + ghoul::Dictionary velDict = dictionary.value(KeyVelocity); + for (int i = 0; i < static_cast(velDict.size()); ++i) { + float velocity = static_cast( + velDict.value(std::to_string(i + 1)) + ); + _velocityData.push_back(velocity); + } + _hasVelocityData = true; + } + + addProperty(_opacity); + + if (dictionary.hasKey(SizeInfo.identifier)) { + _size = static_cast(dictionary.value(SizeInfo.identifier)); + } + addProperty(_size); + + if (dictionary.hasKey(ToggleVisibilityInfo.identifier)) { + _toggleVisibility = dictionary.value(ToggleVisibilityInfo.identifier); + } + _toggleVisibility.onChange([&]() { _hasPointData = !_hasPointData; }); + addProperty(_toggleVisibility); +} + +bool RenderablePointsCloud::isReady() const { + return _shaderProgram && (!_fullData.empty()); +} + +void RenderablePointsCloud::initialize() { + bool isSuccessful = loadData(); + if (!isSuccessful) { + throw ghoul::RuntimeError("Error loading data"); + } +} + +void RenderablePointsCloud::initializeGL() { + _shaderProgram = global::renderEngine->buildRenderProgram( + "PointsCloud", + absPath("${MODULE_SOFTWAREINTEGRATION}/shaders/point_vs.glsl"), + absPath("${MODULE_SOFTWAREINTEGRATION}/shaders/point_fs.glsl") + ); +} + +void RenderablePointsCloud::deinitializeGL() { + glDeleteVertexArrays(1, &_vertexArrayObjectID); + _vertexArrayObjectID = 0; + + glDeleteBuffers(1, &_vertexBufferObjectID); + _vertexBufferObjectID = 0; + + if (_shaderProgram) { + global::renderEngine->removeRenderProgram(_shaderProgram.get()); + _shaderProgram = nullptr; + } +} + +void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) { + if (_fullData.empty()) { + return; + } + + if (_hasPointData && _toggleVisibility) { + _shaderProgram->activate(); + + glm::dmat4 modelTransform = + glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation + glm::dmat4(data.modelTransform.rotation) * // Spice rotation + glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)); + + glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform; + + _shaderProgram->setUniform("modelViewTransform", modelViewTransform); + _shaderProgram->setUniform( + "MVPTransform", + glm::dmat4(data.camera.projectionMatrix()) * modelViewTransform ); - if (dictionary.hasKey(ColorInfo.identifier)) { - _color = dictionary.value(ColorInfo.identifier); - } - _color.setViewOption(properties::Property::ViewOptions::Color); - addProperty(_color); + _shaderProgram->setUniform("color", _color); + _shaderProgram->setUniform("opacity", _opacity); + _shaderProgram->setUniform("size", _size); - if (dictionary.hasKey(KeyData)) { - ghoul::Dictionary pointDataDict = dictionary.value( - KeyData - ); - for (int i = 0; i < static_cast(pointDataDict.size()); ++i) { - _pointData.push_back( - { pointDataDict.value(std::to_string(i + 1)) } - ); - } - _hasPointData = true; - } + // Changes GL state: + glEnablei(GL_BLEND, 0); + glDepthMask(false); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_PROGRAM_POINT_SIZE); // Enable gl_PointSize in vertex - if (dictionary.hasKey(KeyLuminosity)) { - ghoul::Dictionary lumDict = dictionary.value(KeyLuminosity); - for (int i = 0; i < static_cast(lumDict.size()); ++i) { - float luminosity = static_cast( - lumDict.value(std::to_string(i + 1)) - ); - _luminosityData.push_back(luminosity); - } - _hasLuminosityData = true; - } - - if (dictionary.hasKey(KeyVelocity)) { - ghoul::Dictionary velDict = dictionary.value(KeyVelocity); - for (int i = 0; i < static_cast(velDict.size()); ++i) { - float velocity = static_cast( - velDict.value(std::to_string(i + 1)) - ); - _velocityData.push_back(velocity); - } - _hasVelocityData = true; - } - - if (dictionary.hasKey(OpacityInfo.identifier)) { - _opacity = static_cast( - dictionary.value(OpacityInfo.identifier)); - } - addProperty(_opacity); - - if (dictionary.hasKey(SizeInfo.identifier)) { - _size = static_cast( - dictionary.value(SizeInfo.identifier)); - } - addProperty(_size); - - if (dictionary.hasKey(ToggleVisibilityInfo.identifier)) { - _toggleVisibility = dictionary.value(ToggleVisibilityInfo.identifier); - } - _toggleVisibility.onChange([&]() { _hasPointData = !_hasPointData; }); - addProperty(_toggleVisibility); - } - - bool RenderablePointsCloud::isReady() const { - return ((_shaderProgram != nullptr) && (!_fullData.empty())); - } - - void RenderablePointsCloud::initialize() { - bool isSuccessful = loadData(); - if (!isSuccessful) { - throw ghoul::RuntimeError("Error loading data"); - } - } - - void RenderablePointsCloud::initializeGL() { - _shaderProgram = global::renderEngine->buildRenderProgram( - "PointsCloud", - absPath("${MODULE_SOFTWAREINTEGRATION}/shaders/point_vs.glsl"), - absPath("${MODULE_SOFTWAREINTEGRATION}/shaders/point_fs.glsl") - ); - } - - void RenderablePointsCloud::deinitializeGL() { - glDeleteVertexArrays(1, &_vertexArrayObjectID); - _vertexArrayObjectID = 0; - - glDeleteBuffers(1, &_vertexBufferObjectID); - _vertexBufferObjectID = 0; - - if (_shaderProgram) { - global::renderEngine->removeRenderProgram(_shaderProgram.get()); - _shaderProgram = nullptr; - } - } - - void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) { - if (_fullData.empty()) { - return; - } - - if (_hasPointData && _toggleVisibility) { - _shaderProgram->activate(); - - glm::dmat4 modelTransform = - glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation - glm::dmat4(data.modelTransform.rotation) * // Spice rotation - glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)); - - glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform; - - _shaderProgram->setUniform("modelViewTransform", modelViewTransform); - _shaderProgram->setUniform( - "MVPTransform", - glm::dmat4(data.camera.projectionMatrix()) * modelViewTransform - ); - - _shaderProgram->setUniform("color", _color); - _shaderProgram->setUniform("opacity", _opacity); - _shaderProgram->setUniform("size", _size); - - // Changes GL state: - glEnablei(GL_BLEND, 0); - //glDepthMask(false); - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_PROGRAM_POINT_SIZE); // Enable gl_PointSize in vertex - - glBindVertexArray(_vertexArrayObjectID); - const GLsizei nPoints = static_cast(_fullData.size() / _nValuesPerPoints); - glDrawArrays(GL_POINTS, 0, nPoints); - - glBindVertexArray(0); - _shaderProgram->deactivate(); - - // Restores GL State - global::renderEngine->openglStateCache().resetBlendState(); - //global::renderEngine->openglStateCache().resetDepthState(); - } - } - - void RenderablePointsCloud::update(const UpdateData&) { - if (!_isDirty) { - return; - } - - if (_hasPointData) { - LDEBUG("Regenerating data"); - - createDataSlice(); - - int size = static_cast(_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(_vertexArrayObjectID); + const GLsizei nPoints = static_cast(_fullData.size() / _nValuesPerPoints); + glDrawArrays(GL_POINTS, 0, nPoints); glBindVertexArray(0); + _shaderProgram->deactivate(); - _isDirty = false; + // Restores GL State + global::renderEngine->openglStateCache().resetBlendState(); + global::renderEngine->openglStateCache().resetDepthState(); + } +} + +void RenderablePointsCloud::update(const UpdateData&) { + if (!_isDirty) { + return; } - void RenderablePointsCloud::createDataSlice() { - _slicedData.clear(); - _slicedData.reserve(4 * (_fullData.size() / _nValuesPerPoints)); + if (_hasPointData) { + LDEBUG("Regenerating data"); - auto addPosition = [&](const glm::vec4& pos) { - for (int j = 0; j < 4; ++j) { - _slicedData.push_back(pos[j]); - } - }; + createDataSlice(); - for (size_t i = 0; i < _fullData.size(); i += _nValuesPerPoints) { - glm::dvec4 transformedPos = _transformationMatrix * glm::dvec4( - _fullData[i + 0], - _fullData[i + 1], - _fullData[i + 2], - 1.0 - ); - // W-normalization - transformedPos /= transformedPos.w; - transformedPos *= openspace::distanceconstants::Parsec; - addPosition(transformedPos); + int size = static_cast(_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() / _nValuesPerPoints)); + + auto addPosition = [&](const glm::vec4& pos) { + for (int j = 0; j < 4; ++j) { + _slicedData.push_back(pos[j]); + } + }; + + for (size_t i = 0; i < _fullData.size(); i += _nValuesPerPoints) { + glm::dvec4 transformedPos = _transformationMatrix * glm::dvec4( + _fullData[i + 0], + _fullData[i + 1], + _fullData[i + 2], + 1.0 + ); + // W-normalization + transformedPos /= transformedPos.w; + transformedPos *= openspace::distanceconstants::Parsec; + addPosition(transformedPos); + } +} + +bool RenderablePointsCloud::loadData() { + bool isSuccessful = true; + _slicedData.clear(); + _fullData.clear(); + + isSuccessful &= readPointData(); + if (!isSuccessful) { + return false; + } + + if (!_hasPointData) { + isSuccessful = true; + } + + return isSuccessful; +} + +bool RenderablePointsCloud::readPointData() { + if (!_hasPointData) { + LERROR("No point data found"); + return false; + } + + _nValuesPerPoints = 3; + + int dataSize = _pointData.size(); + std::vector values(_nValuesPerPoints); + + for (int i = 0; i < dataSize; i++) { + for (int j = 0; j < _nValuesPerPoints; j++) { + values.push_back(_pointData[i][j]); } } - bool RenderablePointsCloud::loadData() { - bool isSuccessful = true; - _slicedData.clear(); - _fullData.clear(); + _fullData.insert(_fullData.end(), values.begin(), values.end()); - isSuccessful &= readPointData(); - if (!isSuccessful) { - return false; - } - - if (!_hasPointData) { - isSuccessful = true; - } - - return isSuccessful; - } - - bool RenderablePointsCloud::readPointData() { - if (!_hasPointData) { - LERROR("No point data found"); - return false; - } - - _nValuesPerPoints = 3; - - int dataSize = _pointData.size(); - std::vector values(_nValuesPerPoints); - - for (int i = 0; i < dataSize; i++) { - - for (int j = 0; j < _nValuesPerPoints; j++) { - values.push_back(_pointData[i][j]); - } - } - - _fullData.insert(_fullData.end(), values.begin(), values.end()); - - return true; - } + return true; +} } // namespace openspace diff --git a/modules/softwareintegration/rendering/renderablepointscloud.h b/modules/softwareintegration/rendering/renderablepointscloud.h index 4b7402ecd6..ee582f1625 100644 --- a/modules/softwareintegration/rendering/renderablepointscloud.h +++ b/modules/softwareintegration/rendering/renderablepointscloud.h @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014-2020 * + * 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 * @@ -68,7 +68,6 @@ protected: std::unique_ptr _shaderProgram = nullptr; properties::BoolProperty _toggleVisibility; - properties::FloatProperty _opacity; properties::FloatProperty _size; properties::Vec3Property _color; diff --git a/modules/softwareintegration/softwareintegrationmodule.cpp b/modules/softwareintegration/softwareintegrationmodule.cpp index d43d54e3fd..68984aa802 100644 --- a/modules/softwareintegration/softwareintegrationmodule.cpp +++ b/modules/softwareintegration/softwareintegrationmodule.cpp @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014-2020 * + * 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 * @@ -45,605 +45,585 @@ namespace { namespace openspace { - SoftwareIntegrationModule::SoftwareIntegrationModule() : OpenSpaceModule(Name) {} +SoftwareIntegrationModule::SoftwareIntegrationModule() : OpenSpaceModule(Name) {} - void SoftwareIntegrationModule::internalInitialize(const ghoul::Dictionary&) { - auto fRenderable = FactoryManager::ref().factory(); - ghoul_assert(fRenderable, "No renderable factory existed"); +void SoftwareIntegrationModule::internalInitialize(const ghoul::Dictionary&) { + auto fRenderable = FactoryManager::ref().factory(); + ghoul_assert(fRenderable, "No renderable factory existed"); - fRenderable->registerClass("RenderablePointsCloud"); + fRenderable->registerClass("RenderablePointsCloud"); - // Open port - start(4700); + // Open port + start(4700); +} + +void SoftwareIntegrationModule::internalDeinitialize() { + stop(); +} + +void SoftwareIntegrationModule::start(int port) { + _socketServer.listen(port); + + _serverThread = std::thread([this]() { handleNewPeers(); }); + _eventLoopThread = std::thread([this]() { eventLoop(); }); +} + +void SoftwareIntegrationModule::stop() { + _shouldStop = true; + _socketServer.close(); + + if (_serverThread.joinable()) { + _serverThread.join(); + } + if (_eventLoopThread.joinable()) { + _eventLoopThread.join(); + } +} + +bool SoftwareIntegrationModule::isConnected(const Peer& peer) const { + return peer.status != SoftwareConnection::Status::Connecting && + peer.status != SoftwareConnection::Status::Disconnected; +} + +std::shared_ptr SoftwareIntegrationModule::peer(size_t id) { + std::lock_guard lock(_peerListMutex); + auto it = _peers.find(id); + if (it == _peers.end()) { + return nullptr; + } + return it->second; +} + +void SoftwareIntegrationModule::disconnect(Peer& peer) { + if (isConnected(peer)) { + _nConnections -= 1; } - void SoftwareIntegrationModule::internalDeinitialize() { - stop(); - } + peer.connection.disconnect(); + peer.thread.join(); + _peers.erase(peer.id); +} - void SoftwareIntegrationModule::internalDeinitializeGL() { - - } - - void SoftwareIntegrationModule::start(int port) - { - _socketServer.listen(port); - - _serverThread = std::thread([this]() { handleNewPeers(); }); - _eventLoopThread = std::thread([this]() { eventLoop(); }); - } - - void SoftwareIntegrationModule::stop() { - _shouldStop = true; - _socketServer.close(); - - if (_serverThread.joinable()) { - _serverThread.join(); - } - if (_eventLoopThread.joinable()) { - _eventLoopThread.join(); +void SoftwareIntegrationModule::eventLoop() { + while (!_shouldStop) { + if (!_incomingMessages.empty()) { + PeerMessage pm = _incomingMessages.pop(); + handlePeerMessage(std::move(pm)); } } +} - bool SoftwareIntegrationModule::isConnected(const Peer& peer) const { - return peer.status != SoftwareConnection::Status::Connecting && - peer.status != SoftwareConnection::Status::Disconnected; - } +void SoftwareIntegrationModule::handleNewPeers() { + while (!_shouldStop) { + std::unique_ptr socket = + _socketServer.awaitPendingTcpSocket(); - std::shared_ptr SoftwareIntegrationModule::peer(size_t id) { - std::lock_guard lock(_peerListMutex); - auto it = _peers.find(id); - if (it == _peers.end()) { - return nullptr; - } - return it->second; - } - - void SoftwareIntegrationModule::disconnect(Peer& peer) { - if (isConnected(peer)) { - _nConnections = nConnections() - 1; - } - - peer.connection.disconnect(); - peer.thread.join(); - _peers.erase(peer.id); - } - - void SoftwareIntegrationModule::eventLoop() { - while (!_shouldStop) { - if (!_incomingMessages.empty()) { - PeerMessage pm = _incomingMessages.pop(); - handlePeerMessage(std::move(pm)); - } - } - } - - void SoftwareIntegrationModule::handleNewPeers() { - while (!_shouldStop) { - std::unique_ptr socket = - _socketServer.awaitPendingTcpSocket(); - - if (socket != nullptr) - socket->startStreams(); - else - return; - - const size_t id = _nextConnectionId++; - std::shared_ptr p = std::make_shared(Peer{ - id, - "", - std::thread(), - SoftwareConnection(std::move(socket)), - SoftwareConnection::Status::Connecting - }); - auto it = _peers.emplace(p->id, p); - it.first->second->thread = std::thread([this, id]() { - handlePeer(id); - }); - } - } - - void SoftwareIntegrationModule::handlePeer(size_t id) { - while (!_shouldStop) { - std::shared_ptr p = peer(id); - if (!p) { - return; - } - - if (!p->connection.isConnectedOrConnecting()) { - return; - } - try { - SoftwareConnection::Message m = p->connection.receiveMessage(); - _incomingMessages.push({ id, m }); - } - catch (const SoftwareConnection::SoftwareConnectionLostError&) { - LERROR(fmt::format("Connection lost to {}", p->id)); - _incomingMessages.push({ - id, - SoftwareConnection::Message( - SoftwareConnection::MessageType::Disconnection, std::vector() - ) - }); - return; - } - } - } - - void SoftwareIntegrationModule::handlePeerMessage(PeerMessage peerMessage) { - const size_t peerId = peerMessage.peerId; - auto it = _peers.find(peerId); - if (it == _peers.end()) { + if (!socket) { return; } - std::shared_ptr& peer = it->second; - const SoftwareConnection::MessageType messageType = peerMessage.message.type; - std::vector& message = peerMessage.message.content; - switch (messageType) { - case SoftwareConnection::MessageType::Connection: { - std::string software(message.begin(), message.end()); - LINFO(fmt::format("OpenSpace has connected with {} through socket.", software)); - break; + socket->startStreams(); + + const size_t id = _nextConnectionId++; + std::shared_ptr p = std::make_shared(Peer{ + id, + "", + std::thread(), + SoftwareConnection(std::move(socket)), + SoftwareConnection::Status::Connecting + }); + auto it = _peers.emplace(p->id, p); + it.first->second->thread = std::thread([this, id]() { + handlePeer(id); + }); + } +} + +void SoftwareIntegrationModule::handlePeer(size_t id) { + while (!_shouldStop) { + std::shared_ptr p = peer(id); + if (!p) { + return; } - case SoftwareConnection::MessageType::ReadPointData: { - messageOffset = 0; // Resets message offset - std::vector xCoordinates = readData(message); - std::vector yCoordinates = readData(message); - std::vector zCoordinates = readData(message); - - int size = xCoordinates.size(); - pointData.clear(); - - for (int i = 0; i < size; i++) { - float x = xCoordinates[i]; - float y = yCoordinates[i]; - float z = zCoordinates[i]; - - pointData.push_back({ x, y, z }); - } - break; + if (!p->connection.isConnectedOrConnecting()) { + return; } - case SoftwareConnection::MessageType::ReadLuminosityData: { - messageOffset = 0; // Resets message offset - - luminosityData.clear(); - luminosityData = readData(message); - break; + try { + SoftwareConnection::Message m = p->connection.receiveMessage(); + _incomingMessages.push({ id, m }); } - case SoftwareConnection::MessageType::ReadVelocityData: { - messageOffset = 0; // Resets message offset - - velocityData.clear(); - velocityData = readData(message); - break; + catch (const SoftwareConnection::SoftwareConnectionLostError&) { + LERROR(fmt::format("Connection lost to {}", p->id)); + _incomingMessages.push({ + id, + SoftwareConnection::Message( + SoftwareConnection::MessageType::Disconnection, std::vector() + ) + }); + return; } - case SoftwareConnection::MessageType::AddSceneGraphNode: { - std::string sgnMessage(message.begin(), message.end()); - LDEBUG(fmt::format("Message recieved.. Scene Graph Node Data: {}", sgnMessage)); + } +} - // The following order of creating variables is the exact order they're received in the message - // If the order is not the same, the global variable 'message offset' will be wrong - std::string identifier = readIdentifier(message); - glm::vec3 color = readColor(message); - float opacity = readFloatValue(message); - float size = readFloatValue(message); - std::string guiName = readGUI(message); +void SoftwareIntegrationModule::handlePeerMessage(PeerMessage peerMessage) { + const size_t peerId = peerMessage.peerId; + auto it = _peers.find(peerId); + if (it == _peers.end()) { + return; + } + std::shared_ptr& peer = it->second; - bool hasLuminosityData = !luminosityData.empty(); - bool hasVelocityData = !velocityData.empty(); + const SoftwareConnection::MessageType messageType = peerMessage.message.type; + std::vector& message = peerMessage.message.content; - ghoul::Dictionary pointDataDictonary; + switch (messageType) { + case SoftwareConnection::MessageType::Connection: { + std::string software(message.begin(), message.end()); + LINFO(fmt::format("OpenSpace has connected with {} through socket.", software)); + break; + } + case SoftwareConnection::MessageType::ReadPointData: { + messageOffset = 0; // Resets message offset + + std::vector xCoordinates = readData(message); + std::vector yCoordinates = readData(message); + std::vector zCoordinates = readData(message); + + int size = xCoordinates.size(); + pointData.clear(); + + for (int i = 0; i < size; i++) { + float x = xCoordinates[i]; + float y = yCoordinates[i]; + float z = zCoordinates[i]; + + pointData.push_back({ x, y, z }); + } + break; + } + case SoftwareConnection::MessageType::ReadLuminosityData: { + messageOffset = 0; // Resets message offset + + luminosityData.clear(); + luminosityData = readData(message); + break; + } + case SoftwareConnection::MessageType::ReadVelocityData: { + messageOffset = 0; // Resets message offset + + velocityData.clear(); + velocityData = readData(message); + break; + } + case SoftwareConnection::MessageType::AddSceneGraphNode: { + std::string sgnMessage(message.begin(), message.end()); + LDEBUG(fmt::format("Message recieved.. Scene Graph Node Data: {}", sgnMessage)); + + // The following order of creating variables is the exact order they're received in the message + // If the order is not the same, the global variable 'message offset' will be wrong + std::string identifier = readIdentifier(message); + glm::vec3 color = readColor(message); + float opacity = readFloatValue(message); + float size = readFloatValue(message); + std::string guiName = readGUI(message); + + ghoul::Dictionary pointDataDictonary; + for (int i = 0; i < pointData.size(); ++i) { + pointDataDictonary.setValue(std::to_string(i + 1), pointData[i]); + } + + // Create a renderable depending on what data was received + ghoul::Dictionary renderable; + renderable.setValue("Type", "RenderablePointsCloud"s); + renderable.setValue("Color", static_cast(color)); + renderable.setValue("Opacity", static_cast(opacity)); + renderable.setValue("Size", static_cast(size)); + renderable.setValue("Data", pointDataDictonary); + + bool hasLuminosityData = !luminosityData.empty(); + bool hasVelocityData = !velocityData.empty(); + + if (hasLuminosityData) { ghoul::Dictionary luminosityDataDictonary; - ghoul::Dictionary velocityDataDictionary; - - for (int i = 0; i < pointData.size(); ++i) { - pointDataDictonary.setValue(std::to_string(i + 1), pointData[i]); + for (int i = 0; i < luminosityData.size(); ++i) { + luminosityDataDictonary.setValue(std::to_string(i + 1), luminosityData[i]); } - if (hasLuminosityData) { - for (int i = 0; i < luminosityData.size(); ++i) { - luminosityDataDictonary.setValue(std::to_string(i + 1), luminosityData[i]); - } - } - if (hasVelocityData) { - for (int i = 0; i < velocityData.size(); ++i) { - velocityDataDictionary.setValue(std::to_string(i + 1), velocityData[i]); - } - } - - // Create a renderable depending on what data was received - ghoul::Dictionary renderable; - renderable.setValue("Type", "RenderablePointsCloud"s); - renderable.setValue("Color", static_cast(color)); - renderable.setValue("Data", pointDataDictonary); - renderable.setValue("Opacity", static_cast(opacity)); - renderable.setValue("Size", static_cast(size)); - - if (hasLuminosityData) { - renderable.setValue("Luminosity", luminosityDataDictonary); - } - - if (hasVelocityData) { - renderable.setValue("Velocity", velocityDataDictionary); - } - - ghoul::Dictionary gui; - gui.setValue("Name", guiName); - gui.setValue("Path", "/Examples"s); - - ghoul::Dictionary node; - node.setValue("Identifier", identifier); - node.setValue("Renderable", renderable); - node.setValue("GUI", gui); - - try { - SceneGraphNode* sgn = global::renderEngine->scene()->loadNode(node); - if (!sgn) { - LERROR("Scene", "Could not load scene graph node"); - break; - } - global::renderEngine->scene()->initializeNode(sgn); - - openspace::global::scriptEngine->queueScript( - "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.RetargetAnchor', nil)" - "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Anchor', '" + identifier + "')" - "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Aim', '')", - scripting::ScriptEngine::RemoteScripting::Yes - ); - } - catch (const documentation::SpecificationError& e) { - return LERROR(fmt::format("Documentation SpecificationError: Error loading scene graph node {}", - e.what()) - ); - } - catch (const ghoul::RuntimeError& e) { - return LERROR(fmt::format("RuntimeError: Error loading scene graph node {}", - e.what()) - ); - } - - handlePeerProperties(identifier, peer); - break; + renderable.setValue("Luminosity", luminosityDataDictonary); } - case SoftwareConnection::MessageType::RemoveSceneGraphNode: { - std::string identifier(message.begin(), message.end()); - LDEBUG(fmt::format("Message recieved.. Delete SGN: {}", identifier)); - SceneGraphNode* sgn = global::renderEngine->scene()->sceneGraphNode(identifier); - - if (global::navigationHandler->orbitalNavigator().anchorNode() == sgn) { - openspace::global::scriptEngine->queueScript( - "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Anchor', 'Sun')" - "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Aim', '')", - scripting::ScriptEngine::RemoteScripting::Yes - ); + if (hasVelocityData) { + ghoul::Dictionary velocityDataDictionary; + for (int i = 0; i < velocityData.size(); ++i) { + velocityDataDictionary.setValue(std::to_string(i + 1), velocityData[i]); } + renderable.setValue("Velocity", velocityDataDictionary); + } + + ghoul::Dictionary gui; + gui.setValue("Name", guiName); + gui.setValue("Path", "/Examples"s); + + ghoul::Dictionary node; + node.setValue("Identifier", identifier); + node.setValue("Renderable", renderable); + node.setValue("GUI", gui); + + try { + SceneGraphNode* sgn = global::renderEngine->scene()->loadNode(node); + if (!sgn) { + LERROR("Scene", "Could not load scene graph node"); + break; + } + global::renderEngine->scene()->initializeNode(sgn); + openspace::global::scriptEngine->queueScript( - "openspace.removeSceneGraphNode('" + identifier + "');", + "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.RetargetAnchor', nil)" + "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Anchor', '" + identifier + "')" + "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Aim', '')", scripting::ScriptEngine::RemoteScripting::Yes ); - LDEBUG(fmt::format("Scengraph {} removed.", identifier)); - break; } - case SoftwareConnection::MessageType::Color: { - std::string colorMessage(message.begin(), message.end()); - LDEBUG(fmt::format("Message recieved.. New Color: {}", colorMessage)); - std::string identifier = readIdentifier(message); - glm::vec3 color = readColor(message); - - // Get color of renderable - const Renderable* myRenderable = renderable(identifier); - properties::Property* colorProperty = myRenderable->property("Color"); - auto propertyAny = colorProperty->get(); - glm::vec3 propertyColor = std::any_cast(propertyAny); - bool isUpdated = (propertyColor != color); - - // Update color of renderable - if (isUpdated) - colorProperty->set(color); - break; + catch (const documentation::SpecificationError& e) { + return LERROR(fmt::format( + "Documentation SpecificationError: Error loading scene graph node {}", + e.what()) + ); } - case SoftwareConnection::MessageType::Opacity: { - std::string opacityMessage(message.begin(), message.end()); - LDEBUG(fmt::format("Message recieved.. New Opacity: {}", opacityMessage)); - std::string identifier = readIdentifier(message); - float opacity = readFloatValue(message); - - // Get opacity of renderable - const Renderable* myRenderable = renderable(identifier); - properties::Property* opacityProperty = myRenderable->property("Opacity"); - auto propertyAny = opacityProperty->get(); - float propertyOpacity = std::any_cast(propertyAny); - bool isUpdated = (propertyOpacity != opacity); - - // Update opacity of renderable - if (isUpdated) - opacityProperty->set(opacity); - break; + catch (const ghoul::RuntimeError& e) { + return LERROR(fmt::format( + "RuntimeError: Error loading scene graph node {}", + e.what()) + ); } - case SoftwareConnection::MessageType::Size: { - std::string sizeMessage(message.begin(), message.end()); - LDEBUG(fmt::format("Message recieved.. New Size: {}", sizeMessage)); - std::string identifier = readIdentifier(message); - float size = readFloatValue(message); - // Get size of renderable - const Renderable* myRenderable = renderable(identifier); - properties::Property* sizeProperty = myRenderable->property("Size"); - auto propertyAny = sizeProperty->get(); - float propertySize = std::any_cast(propertyAny); - bool isUpdated = (propertySize != size); - - // Update size of renderable - if (isUpdated) - sizeProperty->set(size); - break; - } - case SoftwareConnection::MessageType::Visibility: { - std::string visibilityMessage(message.begin(), message.end()); - LDEBUG(fmt::format("Message recieved.. New Visibility: {}", visibilityMessage)); - std::string identifier = readIdentifier(message); - std::string visibility; - visibility.push_back(message[messageOffset]); - - // Toggle visibility of renderable - const Renderable* myRenderable = renderable(identifier); - properties::Property* visibilityProperty = myRenderable->property("ToggleVisibility"); - if(visibility == "F") - visibilityProperty->set(false); - else - visibilityProperty->set(true); - break; - } - case SoftwareConnection::MessageType::Disconnection: { - disconnect(*peer); - break; - } - default: - LERROR(fmt::format( - "Unsupported message type: {}", static_cast(messageType) - )); - break; - } + handlePeerProperties(identifier, peer); + break; } + case SoftwareConnection::MessageType::RemoveSceneGraphNode: { + std::string identifier(message.begin(), message.end()); + LDEBUG(fmt::format("Message recieved.. Delete SGN: {}", identifier)); - void SoftwareIntegrationModule::handlePeerProperties(std::string identifier, const std::shared_ptr& peer) { + if (global::navigationHandler->orbitalNavigator().anchorNode()->identifier() == identifier) { + // If the deleted node is the current anchor, first change focus to the Sun + openspace::global::scriptEngine->queueScript( + "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Anchor', 'Sun')" + "openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Aim', '')", + scripting::ScriptEngine::RemoteScripting::Yes + ); + } + openspace::global::scriptEngine->queueScript( + "openspace.removeSceneGraphNode('" + identifier + "');", + scripting::ScriptEngine::RemoteScripting::Yes + ); + LDEBUG(fmt::format("Scengraph {} removed.", identifier)); + break; + } + case SoftwareConnection::MessageType::Color: { + std::string colorMessage(message.begin(), message.end()); + LDEBUG(fmt::format("Message recieved.. New Color: {}", colorMessage)); + std::string identifier = readIdentifier(message); + glm::vec3 color = readColor(message); + + // Get color of renderable const Renderable* myRenderable = renderable(identifier); properties::Property* colorProperty = myRenderable->property("Color"); - properties::Property* opacityProperty = myRenderable->property("Opacity"); - properties::Property* sizeProperty = myRenderable->property("Size"); - properties::Property* visibilityProperty = myRenderable->property("ToggleVisibility"); + auto propertyAny = colorProperty->get(); + glm::vec3 propertyColor = std::any_cast(propertyAny); + bool isUpdated = (propertyColor != color); // Update color of renderable - auto updateColor = [colorProperty, identifier, peer]() { - std::string lengthOfIdentifier = std::to_string(identifier.length()); - std::string propertyValue = colorProperty->getStringValue(); - std::string lengthOfValue = std::to_string(propertyValue.length()); - std::string messageType = "UPCO"; - std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue; + if (isUpdated) { + colorProperty->set(color); + } + break; + } + case SoftwareConnection::MessageType::Opacity: { + std::string opacityMessage(message.begin(), message.end()); + LDEBUG(fmt::format("Message recieved.. New Opacity: {}", opacityMessage)); + std::string identifier = readIdentifier(message); + float opacity = readFloatValue(message); - // Format length of subject to always be 4 digits - std::ostringstream os; - os << std::setfill('0') << std::setw(4) << subject.length(); - std::string lengthOfSubject = os.str(); - - std::string message = messageType + lengthOfSubject + subject; - peer->connection.sendMessage(message); - }; - colorProperty->onChange(updateColor); + // Get opacity of renderable + const Renderable* myRenderable = renderable(identifier); + properties::Property* opacityProperty = myRenderable->property("Opacity"); + auto propertyAny = opacityProperty->get(); + float propertyOpacity = std::any_cast(propertyAny); + bool isUpdated = (propertyOpacity != opacity); // Update opacity of renderable - auto updateOpacity = [opacityProperty, identifier, peer]() { - std::string lengthOfIdentifier = std::to_string(identifier.length()); - std::string propertyValue = opacityProperty->getStringValue(); - std::string lengthOfValue = std::to_string(propertyValue.length()); - std::string messageType = "UPOP"; - std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue; + if (isUpdated) { + opacityProperty->set(opacity); + } + break; + } + case SoftwareConnection::MessageType::Size: { + std::string sizeMessage(message.begin(), message.end()); + LDEBUG(fmt::format("Message recieved.. New Size: {}", sizeMessage)); + std::string identifier = readIdentifier(message); + float size = readFloatValue(message); - // Format length of subject to always be 4 digits - std::ostringstream os; - os << std::setfill('0') << std::setw(4) << subject.length(); - std::string lengthOfSubject = os.str(); - - std::string message = messageType + lengthOfSubject + subject; - peer->connection.sendMessage(message); - }; - opacityProperty->onChange(updateOpacity); + // Get size of renderable + const Renderable* myRenderable = renderable(identifier); + properties::Property* sizeProperty = myRenderable->property("Size"); + auto propertyAny = sizeProperty->get(); + float propertySize = std::any_cast(propertyAny); + bool isUpdated = (propertySize != size); // Update size of renderable - auto updateSize = [sizeProperty, identifier, peer]() { - std::string lengthOfIdentifier = std::to_string(identifier.length()); - std::string propertyValue = sizeProperty->getStringValue(); - std::string lengthOfValue = std::to_string(propertyValue.length()); - std::string messageType = "UPSI"; - std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue; - - // Format length of subject to always be 4 digits - std::ostringstream os; - os << std::setfill('0') << std::setw(4) << subject.length(); - std::string lengthOfSubject = os.str(); - - std::string message = messageType + lengthOfSubject + subject; - peer->connection.sendMessage(message); - }; - sizeProperty->onChange(updateSize); + if (isUpdated) { + sizeProperty->set(size); + } + break; + } + case SoftwareConnection::MessageType::Visibility: { + std::string visibilityMessage(message.begin(), message.end()); + LDEBUG(fmt::format("Message recieved.. New Visibility: {}", visibilityMessage)); + std::string identifier = readIdentifier(message); + std::string visibility; + visibility.push_back(message[messageOffset]); + bool boolValue = (visibility == "F") ? false : true; // Toggle visibility of renderable - auto toggleVisibility = [visibilityProperty, identifier, peer]() { - std::string lengthOfIdentifier = std::to_string(identifier.length()); - std::string messageType = "TOVI"; + const Renderable* myRenderable = renderable(identifier); + properties::Property* visibilityProperty = myRenderable->property("ToggleVisibility"); + visibilityProperty->set(boolValue); + break; + } + case SoftwareConnection::MessageType::Disconnection: { + disconnect(*peer); + break; + } + default: + LERROR(fmt::format( + "Unsupported message type: {}", static_cast(messageType) + )); + break; + } +} - std::string propertyValue; - if (visibilityProperty->getStringValue() == "false") - propertyValue = "F"; - else - propertyValue = "T"; +void SoftwareIntegrationModule::handlePeerProperties(std::string identifier, const std::shared_ptr& peer) { + const Renderable* myRenderable = renderable(identifier); + properties::Property* colorProperty = myRenderable->property("Color"); + properties::Property* opacityProperty = myRenderable->property("Opacity"); + properties::Property* sizeProperty = myRenderable->property("Size"); + properties::Property* visibilityProperty = myRenderable->property("ToggleVisibility"); - std::string subject = lengthOfIdentifier + identifier + propertyValue; - // We don't need a lengthOfValue here because it will always be 1 character + // Update color of renderable + auto updateColor = [colorProperty, identifier, peer]() { + std::string lengthOfIdentifier = std::to_string(identifier.length()); + std::string propertyValue = colorProperty->getStringValue(); + std::string lengthOfValue = std::to_string(propertyValue.length()); + std::string messageType = "UPCO"; + std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue; - // Format length of subject to always be 4 digits - std::ostringstream os; - os << std::setfill('0') << std::setw(4) << subject.length(); - std::string lengthOfSubject = os.str(); + // Format length of subject to always be 4 digits + std::ostringstream os; + os << std::setfill('0') << std::setw(4) << subject.length(); + std::string lengthOfSubject = os.str(); - std::string message = messageType + lengthOfSubject + subject; - peer->connection.sendMessage(message); - }; - visibilityProperty->onChange(toggleVisibility); + std::string message = messageType + lengthOfSubject + subject; + peer->connection.sendMessage(message); + }; + colorProperty->onChange(updateColor); + + // Update opacity of renderable + auto updateOpacity = [opacityProperty, identifier, peer]() { + std::string lengthOfIdentifier = std::to_string(identifier.length()); + std::string propertyValue = opacityProperty->getStringValue(); + std::string lengthOfValue = std::to_string(propertyValue.length()); + std::string messageType = "UPOP"; + std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue; + + // Format length of subject to always be 4 digits + std::ostringstream os; + os << std::setfill('0') << std::setw(4) << subject.length(); + std::string lengthOfSubject = os.str(); + + std::string message = messageType + lengthOfSubject + subject; + peer->connection.sendMessage(message); + }; + opacityProperty->onChange(updateOpacity); + + // Update size of renderable + auto updateSize = [sizeProperty, identifier, peer]() { + std::string lengthOfIdentifier = std::to_string(identifier.length()); + std::string propertyValue = sizeProperty->getStringValue(); + std::string lengthOfValue = std::to_string(propertyValue.length()); + std::string messageType = "UPSI"; + std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue; + + // Format length of subject to always be 4 digits + std::ostringstream os; + os << std::setfill('0') << std::setw(4) << subject.length(); + std::string lengthOfSubject = os.str(); + + std::string message = messageType + lengthOfSubject + subject; + peer->connection.sendMessage(message); + }; + sizeProperty->onChange(updateSize); + + // Toggle visibility of renderable + auto toggleVisibility = [visibilityProperty, identifier, peer]() { + std::string lengthOfIdentifier = std::to_string(identifier.length()); + std::string messageType = "TOVI"; + + bool isVisible = visibilityProperty->getStringValue() == "true"; + + std::string visibilityFlag = isVisible ? "T" : "F"; + std::string subject = lengthOfIdentifier + identifier + visibilityFlag; + // We don't need a lengthOfValue here because it will always be 1 character + + // Format length of subject to always be 4 digits + std::ostringstream os; + os << std::setfill('0') << std::setw(4) << subject.length(); + std::string lengthOfSubject = os.str(); + + std::string message = messageType + lengthOfSubject + subject; + peer->connection.sendMessage(message); + }; + visibilityProperty->onChange(toggleVisibility); +} + +// Read size value or opacity value +float SoftwareIntegrationModule::readFloatValue(std::vector& message) { + std::string length; + length.push_back(message[messageOffset]); + messageOffset++; + + int lengthOfValue = stoi(length); + std::string value; + int counter = 0; + while (counter != lengthOfValue) { + value.push_back(message[messageOffset]); + messageOffset++; + counter++; + } + return std::stof(value); +} + +glm::vec3 SoftwareIntegrationModule::readColor(std::vector& message) { + std::string lengthOfColor; // Not used for now, but sent in message + lengthOfColor.push_back(message[messageOffset]); + messageOffset++; + lengthOfColor.push_back(message[messageOffset]); + messageOffset++; + + // Color is recieved in a string-format of (redValue, greenValue, blueValue) + // Therefore, we have to iterate through the message and ignore characters + // "( , )" and separate the values in the string + std::string red; + while (message[messageOffset] != ',') { + if (message[messageOffset] == '(') { + messageOffset++; + } + else { + red.push_back(message[messageOffset]); + messageOffset++; + } + } + messageOffset++; + + std::string green; + while (message[messageOffset] != ',') { + green.push_back(message[messageOffset]); + messageOffset++; + } + messageOffset++; + + std::string blue; + while (message[messageOffset] != ')') { + blue.push_back(message[messageOffset]); + messageOffset++; + } + messageOffset++; + + // Convert red, green, blue strings to floats + float r = std::stof(red); + float g = std::stof(green); + float b = std::stof(blue); + + return glm::vec3(r, g, b); +} + +std::string SoftwareIntegrationModule::readGUI(std::vector& message) { + std::string length; + length.push_back(message[messageOffset]); + messageOffset++; + length.push_back(message[messageOffset]); + messageOffset++; + + int lengthOfGuiName = stoi(length); + std::string guiName; + int counter = 0; + while (counter != lengthOfGuiName) { + guiName.push_back(message[messageOffset]); + messageOffset++; + counter++; } - // Read size value or opacity value - float SoftwareIntegrationModule::readFloatValue(std::vector& message) { - std::string length; - length.push_back(message[messageOffset]); - messageOffset++; + return guiName; +} - int lengthOfValue = stoi(length); - std::string value; - int counter = 0; - while (counter != lengthOfValue) - { +std::string SoftwareIntegrationModule::readIdentifier(std::vector& message) { + std::string length; + length.push_back(message[0]); + length.push_back(message[1]); + + int lengthOfIdentifier = stoi(length); + int counter = 0; + messageOffset = 2; // Resets messageOffset + + std::string identifier; + while (counter != lengthOfIdentifier) { + identifier.push_back(message[messageOffset]); + messageOffset++; + counter++; + } + + return identifier; +} + +std::vector SoftwareIntegrationModule::readData(std::vector& message) { + std::string length; + int lengthOffset = messageOffset + 9; // 9 first bytes is the length of the data + + for (int i = messageOffset; i < lengthOffset; i++) { + length.push_back(message[i]); + messageOffset++; + } + + int lengthOfData = stoi(length); + int counter = 0; + + std::vector data; + std::string value; + + while (counter != lengthOfData) { + while (message[messageOffset] != ',') { value.push_back(message[messageOffset]); messageOffset++; counter++; } - float floatValue = std::stof(value); - - return floatValue; + float dataValue = stof(value); + data.push_back(dataValue); + value = ""; + messageOffset++; + counter++; } - glm::vec3 SoftwareIntegrationModule::readColor(std::vector& message) { - std::string lengthOfColor; // Not used for now, but sent in message - lengthOfColor.push_back(message[messageOffset]); - messageOffset++; - lengthOfColor.push_back(message[messageOffset]); - messageOffset++; + return data; +} - // Color is recieved in a string-format of (redValue, greenValue, blueValue) - // Therefore, we have to iterate through the message and ignore characters - // "( , )" and separate the values in the string - std::string red; - while (message[messageOffset] != ',') - { - if (message[messageOffset] == '(') - messageOffset++; - else { - red.push_back(message[messageOffset]); - messageOffset++; - } - } - messageOffset++; +size_t SoftwareIntegrationModule::nConnections() const { + return _nConnections; +} - std::string green; - while (message[messageOffset] != ',') - { - green.push_back(message[messageOffset]); - messageOffset++; - } - messageOffset++; - - std::string blue; - while (message[messageOffset] != ')') - { - blue.push_back(message[messageOffset]); - messageOffset++; - } - messageOffset++; - - // Convert red, green, blue strings to floats - float r = std::stof(red); - float g = std::stof(green); - float b = std::stof(blue); - glm::vec3 color(r, g, b); - - return color; - } - - std::string SoftwareIntegrationModule::readGUI(std::vector& message) { - std::string length; - length.push_back(message[messageOffset]); - messageOffset++; - length.push_back(message[messageOffset]); - messageOffset++; - - int lengthOfGUI = stoi(length); - std::string GUI; - int counter = 0; - while (counter != lengthOfGUI) - { - GUI.push_back(message[messageOffset]); - messageOffset++; - counter++; - } - - return GUI; - } - - std::string SoftwareIntegrationModule::readIdentifier(std::vector& message) { - std::string length; - length.push_back(message[0]); - length.push_back(message[1]); - - int lengthOfIdentifier = stoi(length); - int counter = 0; - messageOffset = 2; // Resets messageOffset - - std::string identifier; - while (counter != lengthOfIdentifier) - { - identifier.push_back(message[messageOffset]); - messageOffset++; - counter++; - } - - return identifier; - } - - std::vector SoftwareIntegrationModule::readData(std::vector& message) { - std::string length; - int lengthOffset = messageOffset + 9; // 9 first bytes is the length of the data - - for (int i = messageOffset; i < lengthOffset; i++) - { - length.push_back(message[i]); - messageOffset++; - } - - int lengthOfData = stoi(length); - int counter = 0; - - std::vector< float > data; - std::string value; - - while (counter != lengthOfData) - { - while (message[messageOffset] != ',') - { - value.push_back(message[messageOffset]); - messageOffset++; - counter++; - } - float dataValue = stof(value); - data.push_back(dataValue); - value = ""; - messageOffset++; - counter++; - } - - return data; - } - - size_t SoftwareIntegrationModule::nConnections() const { - return _nConnections; - } - - std::vector SoftwareIntegrationModule::documentations() const { - return { - RenderablePointsCloud::Documentation(), - }; - } +std::vector SoftwareIntegrationModule::documentations() const { + return { + RenderablePointsCloud::Documentation(), + }; +} } // namespace openspace diff --git a/modules/softwareintegration/softwareintegrationmodule.h b/modules/softwareintegration/softwareintegrationmodule.h index 258ec40177..9d8e12335c 100644 --- a/modules/softwareintegration/softwareintegrationmodule.h +++ b/modules/softwareintegration/softwareintegrationmodule.h @@ -2,7 +2,7 @@ * * * OpenSpace * * * - * Copyright (c) 2014-2020 * + * 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 * @@ -68,19 +68,18 @@ private: void internalInitialize(const ghoul::Dictionary&) override; void internalDeinitialize() override; - void internalDeinitializeGL() override; bool isConnected(const Peer& peer) const; std::shared_ptr peer(size_t id); - void disconnect(Peer& peer); + void disconnect(Peer& peer); void eventLoop(); void handleNewPeers(); void handlePeer(size_t id); void handlePeerMessage(PeerMessage peerMessage); void handlePeerProperties(std::string identifier, const std::shared_ptr& peer); - + float readFloatValue(std::vector& message); glm::vec3 readColor(std::vector& message); std::string readGUI(std::vector& message);