mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-07 04:00:37 -06:00
Merge branch 'thesis/2022/software-integration_velocity-support' into thesis/2022/software-integration_dop_test
This commit is contained in:
@@ -65,7 +65,7 @@ void NetworkEngine::stop() {
|
||||
}
|
||||
|
||||
_incomingMessages.interrupt();
|
||||
|
||||
|
||||
_shouldStopEventThread = true;
|
||||
_socketServer.close();
|
||||
_softwareConnections.clear();
|
||||
@@ -90,7 +90,7 @@ void NetworkEngine::handleNewSoftwareConnections() {
|
||||
|
||||
socket->startStreams();
|
||||
|
||||
auto p = std::make_shared<SoftwareConnection>(SoftwareConnection{ std::move(socket) });
|
||||
auto p = std::make_shared<SoftwareConnection>(std::move(socket));
|
||||
std::lock_guard guard(_softwareConnectionsMutex);
|
||||
auto [it, peerInserted] = _softwareConnections.emplace(p->id(), p);
|
||||
|
||||
@@ -122,7 +122,7 @@ void NetworkEngine::peerEventLoop(size_t connection_id) {
|
||||
LDEBUG(fmt::format("Connection lost to {}: {}", connection_id, err.message));
|
||||
_incomingMessages.push({
|
||||
connection_id,
|
||||
SoftwareConnection::Message{ simp::MessageType::Disconnection }
|
||||
SoftwareConnection::Message{ simp::MessageType::InternalDisconnection }
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -164,6 +164,7 @@ void NetworkEngine::handleIncomingMessage(IncomingMessage incomingMessage) {
|
||||
|
||||
switch (messageType) {
|
||||
case simp::MessageType::Connection: {
|
||||
LDEBUG(fmt::format("Message recieved... Connection: {}", incomingMessage.connection_id));
|
||||
size_t offset = 0;
|
||||
const std::string software = simp::readString(message, offset);
|
||||
|
||||
@@ -173,57 +174,63 @@ void NetworkEngine::handleIncomingMessage(IncomingMessage incomingMessage) {
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::PointData: {
|
||||
LDEBUG("Message recieved.. Point data");
|
||||
LDEBUG("Message recieved... Point data");
|
||||
_pointDataMessageHandler.handlePointDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::VelocityData: {
|
||||
LDEBUG("Message recieved... Velocity data");
|
||||
_pointDataMessageHandler.handleVelocityDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::RemoveSceneGraphNode: {
|
||||
LDEBUG(fmt::format("Message recieved.. Remove SGN"));
|
||||
LDEBUG(fmt::format("Message recieved... Remove SGN"));
|
||||
_pointDataMessageHandler.handleRemoveSGNMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Color: {
|
||||
LDEBUG(fmt::format("Message recieved.. New color"));
|
||||
LDEBUG(fmt::format("Message recieved... Color"));
|
||||
_pointDataMessageHandler.handleFixedColorMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Colormap: {
|
||||
LDEBUG(fmt::format("Message recieved.. New colormap"));
|
||||
LDEBUG(fmt::format("Message recieved... Colormap"));
|
||||
_pointDataMessageHandler.handleColormapMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::AttributeData: {
|
||||
LDEBUG(fmt::format("Message recieved.. New attribute data"));
|
||||
LDEBUG(fmt::format("Message recieved... Attribute data"));
|
||||
_pointDataMessageHandler.handleAttributeDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Opacity: {
|
||||
LDEBUG(fmt::format("Message recieved.. New Opacity"));
|
||||
LDEBUG(fmt::format("Message recieved... Opacity"));
|
||||
_pointDataMessageHandler.handleOpacityMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::FixedSize: {
|
||||
LDEBUG(fmt::format("Message recieved.. New size"));
|
||||
LDEBUG(fmt::format("Message recieved... Size"));
|
||||
_pointDataMessageHandler.handleFixedPointSizeMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::LinearSize: {
|
||||
LDEBUG(fmt::format("Message recieved.. New linear size"));
|
||||
LDEBUG(fmt::format("Message recieved... Linear size"));
|
||||
_pointDataMessageHandler.handleLinearPointSizeMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Visibility: {
|
||||
LDEBUG(fmt::format("Message recieved.. New visibility"));
|
||||
LDEBUG(fmt::format("Message recieved... Visibility"));
|
||||
_pointDataMessageHandler.handleVisibilityMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Disconnection: {
|
||||
LDEBUG(fmt::format("Message recieved.. Disconnect software connection: {}", incomingMessage.connection_id));
|
||||
case simp::MessageType::InternalDisconnection: {
|
||||
LDEBUG(fmt::format("Message recieved... Disconnection from software connection: {}", incomingMessage.connection_id));
|
||||
std::lock_guard guard(_softwareConnectionsMutex);
|
||||
SoftwareConnection::NetworkEngineFriends::stopThread(connectionPtr);
|
||||
|
||||
if (_softwareConnections.count(incomingMessage.connection_id)) {
|
||||
_softwareConnections.erase(incomingMessage.connection_id);
|
||||
}
|
||||
SoftwareConnection::NetworkEngineFriends::stopThread(connectionPtr);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
~NetworkEngine();
|
||||
|
||||
struct IncomingMessage {
|
||||
size_t connection_id{1};
|
||||
size_t connection_id;
|
||||
SoftwareConnection::Message message{ softwareintegration::simp::MessageType::Unknown };
|
||||
};
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@ SoftwareConnection::SoftwareConnectionLostError::SoftwareConnectionLostError(con
|
||||
SoftwareConnection::SoftwareConnection(std::unique_ptr<ghoul::io::TcpSocket> socket)
|
||||
: _id{ _nextConnectionId++ }, _socket{ std::move(socket) }, _sceneGraphNodes{},
|
||||
_thread{}, _shouldStopThread{ false }
|
||||
{}
|
||||
{
|
||||
LDEBUG(fmt::format("Adding software connection {}", _id));
|
||||
}
|
||||
|
||||
SoftwareConnection::SoftwareConnection(SoftwareConnection&& sc)
|
||||
: _id{ std::move(sc._id) }, _socket{ std::move(sc._socket) },
|
||||
@@ -55,16 +57,17 @@ SoftwareConnection::SoftwareConnection(SoftwareConnection&& sc)
|
||||
{}
|
||||
|
||||
SoftwareConnection::~SoftwareConnection() {
|
||||
// TODO: Destructor being called on Connection for some reason
|
||||
LINFO(fmt::format("Removing software connection {}", _id));
|
||||
// When adding new features, always make sure that the
|
||||
// destructor is called when disconnecting external
|
||||
// since NetworkEngine and MessageHandler has
|
||||
// shared_ptrs to SoftwareConnection, which can cause
|
||||
// bugs if not handled properly.
|
||||
// Tips: use weak_ptr instead of shared_ptr in callbacks.
|
||||
LDEBUG(fmt::format("Removing software connection {}", _id));
|
||||
|
||||
if (!_isConnected) return;
|
||||
_isConnected = false;
|
||||
|
||||
for (auto& identifier : _sceneGraphNodes) {
|
||||
removePropertySubscriptions(identifier);
|
||||
}
|
||||
|
||||
if (_socket) {
|
||||
_socket->disconnect();
|
||||
}
|
||||
@@ -94,6 +97,8 @@ void SoftwareConnection::addPropertySubscription(
|
||||
}
|
||||
|
||||
auto property = r->property(propertyName);
|
||||
|
||||
// Set new onChange handler
|
||||
OnChangeHandle onChangeHandle = property->onChange(newHandler);
|
||||
|
||||
auto propertySubscriptions = _subscribedProperties.find(identifier);
|
||||
@@ -102,7 +107,11 @@ void SoftwareConnection::addPropertySubscription(
|
||||
auto propertySubscription = propertySubscriptions->second.find(propertyName);
|
||||
if (propertySubscription != propertySubscriptions->second.end()) {
|
||||
// Property subscription already exists
|
||||
removeExistingPropertySubscription(identifier, property, propertySubscription->second);
|
||||
|
||||
// Remove old handle
|
||||
property->removeOnChange(propertySubscription->second);
|
||||
|
||||
// Save new handle
|
||||
propertySubscription->second = onChangeHandle;
|
||||
}
|
||||
else {
|
||||
@@ -169,7 +178,10 @@ void SoftwareConnection::removePropertySubscriptions(const std::string& identifi
|
||||
|
||||
if (propertySubscriptions == _subscribedProperties.end()) return;
|
||||
|
||||
for (auto& [propertyName, onChangeHandle] : propertySubscriptions->second) {
|
||||
auto propertySubscriptionIt = std::begin(propertySubscriptions->second);
|
||||
while (propertySubscriptionIt != std::end(propertySubscriptions->second)) {
|
||||
auto& [propertyName, onChangeHandle] = *propertySubscriptionIt;
|
||||
|
||||
if (!r->hasProperty(propertyName)) {
|
||||
LWARNING(fmt::format(
|
||||
"Couldn't remove property subscription. Property \"{}\" doesn't exist on \"{}\"",
|
||||
@@ -178,27 +190,15 @@ void SoftwareConnection::removePropertySubscriptions(const std::string& identifi
|
||||
continue;
|
||||
}
|
||||
|
||||
auto propertySubscription = propertySubscriptions->second.find(propertyName);
|
||||
if (propertySubscription == propertySubscriptions->second.end()) continue;
|
||||
|
||||
auto property = r->property(propertyName);
|
||||
removeExistingPropertySubscription(identifier, property, onChangeHandle);
|
||||
property->removeOnChange(onChangeHandle);
|
||||
|
||||
propertySubscriptionIt = propertySubscriptions->second.erase(propertySubscriptionIt);
|
||||
}
|
||||
|
||||
_subscribedProperties.erase(propertySubscriptions);
|
||||
}
|
||||
|
||||
void SoftwareConnection::removeExistingPropertySubscription(
|
||||
const std::string& identifier,
|
||||
properties::Property *property,
|
||||
OnChangeHandle onChangeHandle
|
||||
) {
|
||||
property->removeOnChange(onChangeHandle);
|
||||
|
||||
auto propertySubscriptions = _subscribedProperties.find(identifier);
|
||||
propertySubscriptions->second.erase(property->identifier());
|
||||
}
|
||||
|
||||
void SoftwareConnection::PointDataMessageHandlerFriends::removePropertySubscription(
|
||||
std::shared_ptr<SoftwareConnection> connectionPtr,
|
||||
const std::string& propertyName,
|
||||
@@ -230,13 +230,19 @@ void SoftwareConnection::PointDataMessageHandlerFriends::removePropertySubscript
|
||||
auto propertySubscription = propertySubscriptions->second.find(propertyName);
|
||||
if (propertySubscription != propertySubscriptions->second.end()) {
|
||||
// Property subscription already exists
|
||||
connectionPtr->removeExistingPropertySubscription(identifier, property, propertySubscription->second);
|
||||
|
||||
// Remove onChange handle
|
||||
property->removeOnChange(propertySubscription->second);
|
||||
|
||||
// Remove property subscription
|
||||
propertySubscriptions->second.erase(propertySubscription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareConnection::disconnect() {
|
||||
_socket->disconnect();
|
||||
LINFO(fmt::format("OpenSpace has disconnected with external software through socket"));
|
||||
}
|
||||
|
||||
bool SoftwareConnection::isConnected() const {
|
||||
@@ -279,11 +285,6 @@ void SoftwareConnection::removeSceneGraphNode(const std::string& identifier) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function is only called on the server node, i.e. the node connected to the external software
|
||||
*
|
||||
* @return SoftwareConnection::Message
|
||||
*/
|
||||
SoftwareConnection::Message SoftwareConnection::receiveMessageFromSoftware() {
|
||||
// Header consists of version (3 char), message type (4 char) & subject size (15 char)
|
||||
size_t headerSize = 22 * sizeof(char);
|
||||
@@ -325,17 +326,10 @@ SoftwareConnection::Message SoftwareConnection::receiveMessageFromSoftware() {
|
||||
}
|
||||
const size_t subjectSize = std::stoi(subjectSizeIn);
|
||||
|
||||
// TODO: Remove this
|
||||
// std::string rawHeader;
|
||||
// for (int i = 0; i < 22; i++) {
|
||||
// rawHeader.push_back(headerBuffer[i]);
|
||||
// }
|
||||
// LDEBUG(fmt::format("Message received with header: {}", rawHeader));
|
||||
|
||||
auto typeEnum = softwareintegration::simp::getMessageType(type);
|
||||
|
||||
// Receive the message data
|
||||
if (typeEnum != softwareintegration::simp::MessageType::Disconnection && typeEnum != softwareintegration::simp::MessageType::Unknown) {
|
||||
if (typeEnum != softwareintegration::simp::MessageType::InternalDisconnection && typeEnum != softwareintegration::simp::MessageType::Unknown) {
|
||||
subjectBuffer.resize(subjectSize);
|
||||
if (!_socket->get(subjectBuffer.data(), subjectSize)) {
|
||||
throw SoftwareConnectionLostError("Failed to read message from socket. Disconnecting.");
|
||||
|
||||
@@ -104,11 +104,6 @@ public:
|
||||
|
||||
private:
|
||||
void removePropertySubscriptions(const std::string& identifier);
|
||||
void removeExistingPropertySubscription(
|
||||
const std::string& identifier,
|
||||
properties::Property *property,
|
||||
OnChangeHandle onChangeHandle
|
||||
);
|
||||
|
||||
SubscribedProperties _subscribedProperties;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <openspace/rendering/renderable.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/util/distanceconstants.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/dictionaryluaformatter.h>
|
||||
@@ -85,6 +86,102 @@ void PointDataMessageHandler::handlePointDataMessage(const std::vector<char>& me
|
||||
addCallback(identifier, { reanchorCallback, { storage::Key::DataPoints }, "reanchorCallback" });
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleVelocityDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
LWARNING(fmt::format("PointDataMessageHandler::handleVelocityDataMessage()"));
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
checkRenderable(message, messageOffset, connection, identifier);
|
||||
|
||||
simp::LengthUnit length_unit;
|
||||
simp::NaNRenderMode velNaNMode;
|
||||
glm::vec4 velNaNColor;
|
||||
size_t nVelocities;
|
||||
size_t dimensionality;
|
||||
std::vector<float> velocities;
|
||||
|
||||
try {
|
||||
// The following order of creating variables is the exact order they are received
|
||||
// in the message. If the order is not the same, the global variable
|
||||
// 'message offset' will be wrong
|
||||
length_unit = simp::getLengthUnit(simp::readString(message, messageOffset));
|
||||
if (length_unit == simp::LengthUnit::Unknown) {
|
||||
// TODO: Write message
|
||||
throw simp::SimpError("");
|
||||
}
|
||||
velNaNMode = simp::getNaNRenderMode(simp::readString(message, messageOffset));
|
||||
switch (velNaNMode) {
|
||||
case simp::NaNRenderMode::Color:
|
||||
velNaNColor = simp::readColor(message, messageOffset);
|
||||
break;
|
||||
case simp::NaNRenderMode::Hide: // Nothing to read
|
||||
break;
|
||||
default: // simp::NaNRenderMode::Unknown
|
||||
// TODO: Write message
|
||||
throw simp::SimpError("");
|
||||
break;
|
||||
}
|
||||
nVelocities = static_cast<size_t>(simp::readIntValue(message, messageOffset));
|
||||
dimensionality = static_cast<size_t>(simp::readIntValue(message, messageOffset));
|
||||
simp::readPointData(message, messageOffset, nVelocities, dimensionality, velocities);
|
||||
}
|
||||
catch (const simp::SimpError& err) {
|
||||
LERROR(fmt::format("Error when reading point data message: {}", err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert data to m/s (assuming time unit is seconds for now)
|
||||
if (length_unit != simp::LengthUnit::m) {
|
||||
convertToMeterPerSecond(length_unit, velocities);
|
||||
}
|
||||
|
||||
// Use the renderable identifier as the data key
|
||||
auto module = global::moduleEngine->module<SoftwareIntegrationModule>();
|
||||
module->storeData(identifier, storage::Key::VelocityData, std::move(velocities));
|
||||
|
||||
auto velNaNModeCallback = [this, identifier, velNaNMode, velNaNColor, connection] {
|
||||
if (velNaNMode == simp::NaNRenderMode::Color) {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
|
||||
// Get velNaNColor of renderable
|
||||
properties::Property* velNaNColorProperty = r->property("VelNaNColor");
|
||||
glm::vec4 propertyVelNaNColor = std::any_cast<glm::vec4>(velNaNColorProperty->get());
|
||||
|
||||
// Update velNaNColor of renderable
|
||||
if (propertyVelNaNColor != velNaNColor) {
|
||||
global::scriptEngine->queueScript(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.VelNaNColor', {});",
|
||||
identifier, ghoul::to_string(velNaNColor)
|
||||
),
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
global::scriptEngine->queueScript(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.VelNaNMode', {});",
|
||||
identifier, static_cast<int>(velNaNMode)
|
||||
),
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
};
|
||||
addCallback(identifier, { velNaNModeCallback, {}, "velNaNModeCallback" });
|
||||
|
||||
auto enableMotionCallback = [identifier] {
|
||||
global::scriptEngine->queueScript(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.MotionEnabled', {});",
|
||||
identifier, "true"
|
||||
),
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
};
|
||||
addCallback(identifier, { enableMotionCallback, { storage::Key::VelocityData }, "Enable motion mode, wait for VelocityData" });
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleFixedColorMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
@@ -100,13 +197,19 @@ void PointDataMessageHandler::handleFixedColorMessage(const std::vector<char>& m
|
||||
return;
|
||||
}
|
||||
|
||||
auto callback = [this, identifier, color, connection] {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
if (!r) return;
|
||||
|
||||
// Get color of renderable
|
||||
properties::Property* colorProperty = r->property("Color");
|
||||
glm::vec4 propertyColor = std::any_cast<glm::vec4>(colorProperty->get());
|
||||
// Get color of renderable
|
||||
properties::Property* colorProperty = r->property("Color");
|
||||
|
||||
// Create weak_ptr, safer than shared_ptr for lambdas
|
||||
std::weak_ptr<SoftwareConnection> connWeakPtr{ connection };
|
||||
|
||||
auto setFixedColorCallback = [this, identifier, color, colorProperty, connWeakPtr] {
|
||||
if (!colorProperty || connWeakPtr.expired()) return;
|
||||
// auto conn = connWeakPtr.lock()
|
||||
|
||||
// auto propertySub = connection->getPropertySubscription(identifier, colorProperty->identifier());
|
||||
// if (propertySub) {
|
||||
@@ -114,7 +217,8 @@ void PointDataMessageHandler::handleFixedColorMessage(const std::vector<char>& m
|
||||
// }
|
||||
|
||||
// Update color of renderable
|
||||
if (propertyColor != color) {
|
||||
glm::vec4 currentColor = std::any_cast<glm::vec4>(colorProperty->get());
|
||||
if (currentColor != color) {
|
||||
global::scriptEngine->queueScript(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.Color', {});",
|
||||
@@ -132,7 +236,14 @@ void PointDataMessageHandler::handleFixedColorMessage(const std::vector<char>& m
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
};
|
||||
addCallback(identifier, { callback, {}, "handleFixedColorMessage" });
|
||||
addCallback(identifier, { setFixedColorCallback, {}, "handleFixedColorMessage" });
|
||||
|
||||
// Create and set onChange for color
|
||||
auto updateColor = [this, colorProperty, identifier, connWeakPtr] {
|
||||
if (!colorProperty || connWeakPtr.expired()) return;
|
||||
onFixedColorChange(colorProperty, identifier, connWeakPtr.lock());
|
||||
};
|
||||
connection->addPropertySubscription(colorProperty->identifier(), identifier, updateColor);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleColormapMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
@@ -143,22 +254,23 @@ void PointDataMessageHandler::handleColormapMessage(const std::vector<char>& mes
|
||||
|
||||
float min;
|
||||
float max;
|
||||
simp::CmapNaNMode cmapNaNMode;
|
||||
simp::NaNRenderMode cmapNaNMode;
|
||||
glm::vec4 cmapNaNColor;
|
||||
size_t nColors;
|
||||
std::vector<float> colorMap;
|
||||
try {
|
||||
min = simp::readFloatValue(message, messageOffset);
|
||||
max = simp::readFloatValue(message, messageOffset);
|
||||
cmapNaNMode = simp::getCmapNaNMode(simp::readString(message, messageOffset));
|
||||
cmapNaNMode = simp::getNaNRenderMode(simp::readString(message, messageOffset));
|
||||
switch (cmapNaNMode) {
|
||||
case simp::CmapNaNMode::Color:
|
||||
case simp::NaNRenderMode::Color:
|
||||
cmapNaNColor = simp::readColor(message, messageOffset);
|
||||
break;
|
||||
case simp::CmapNaNMode::Hide: // Nothing to read
|
||||
case simp::NaNRenderMode::Hide: // Nothing to read
|
||||
break;
|
||||
default: // simp::CmapNaNMode::Unknown
|
||||
// TODO: Throw SimpError
|
||||
default: // simp::NaNRenderMode::Unknown
|
||||
// TODO: Write message
|
||||
throw simp::SimpError("");
|
||||
break;
|
||||
}
|
||||
nColors = static_cast<size_t>(simp::readIntValue(message, messageOffset));
|
||||
@@ -213,8 +325,7 @@ void PointDataMessageHandler::handleColormapMessage(const std::vector<char>& mes
|
||||
addCallback(identifier, { colormapLimitsCallback, {}, "colormapLimitsCallback" });
|
||||
|
||||
auto cmapNaNModeCallback = [this, identifier, cmapNaNMode, cmapNaNColor, connection] {
|
||||
|
||||
if (cmapNaNMode == simp::CmapNaNMode::Color) {
|
||||
if (cmapNaNMode == simp::NaNRenderMode::Color) {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
|
||||
@@ -343,14 +454,20 @@ void PointDataMessageHandler::handleOpacityMessage(const std::vector<char>& mess
|
||||
return;
|
||||
}
|
||||
|
||||
auto callback = [this, identifier, opacity, connection] {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
if (!r) return;
|
||||
|
||||
// Get opacity of renderable
|
||||
properties::Property* opacityProperty = r->property("Opacity");
|
||||
|
||||
// Create weak_ptr, safer than shared_ptr for lambdas
|
||||
std::weak_ptr<SoftwareConnection> connWeakPtr{ connection };
|
||||
|
||||
// Get opacity from renderable
|
||||
properties::Property* opacityProperty = r->property("Opacity");
|
||||
auto propertyAny = opacityProperty->get();
|
||||
float propertyOpacity = std::any_cast<float>(propertyAny);
|
||||
auto callback = [this, identifier, opacity, opacityProperty, connWeakPtr] {
|
||||
if (!opacityProperty || connWeakPtr.expired()) return;
|
||||
// auto conn = connWeakPtr.lock()
|
||||
|
||||
|
||||
// auto propertySub = connection->getPropertySubscription(identifier, opacityProperty->identifier());
|
||||
// if (propertySub) {
|
||||
@@ -358,7 +475,8 @@ void PointDataMessageHandler::handleOpacityMessage(const std::vector<char>& mess
|
||||
// }
|
||||
|
||||
// Update opacity of renderable
|
||||
if (propertyOpacity != opacity) {
|
||||
float currentOpacity = std::any_cast<float>(opacityProperty->get());
|
||||
if (currentOpacity != opacity) {
|
||||
std::string script = fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.Opacity', {});",
|
||||
identifier, ghoul::to_string(opacity)
|
||||
@@ -370,6 +488,13 @@ void PointDataMessageHandler::handleOpacityMessage(const std::vector<char>& mess
|
||||
}
|
||||
};
|
||||
addCallback(identifier, { callback, {}, "handleOpacityMessage" });
|
||||
|
||||
// Create and set onChange for opacity
|
||||
auto updateOpacity = [this, opacityProperty, identifier, connWeakPtr] {
|
||||
if (!opacityProperty || connWeakPtr.expired()) return;
|
||||
onOpacityChange(opacityProperty, identifier, connWeakPtr.lock());
|
||||
};
|
||||
connection->addPropertySubscription(opacityProperty->identifier(), identifier, updateOpacity);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleFixedPointSizeMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
@@ -387,14 +512,19 @@ void PointDataMessageHandler::handleFixedPointSizeMessage(const std::vector<char
|
||||
return;
|
||||
}
|
||||
|
||||
auto callback = [this, identifier, size, connection] {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
if (!r) return;
|
||||
|
||||
// Get size from renderable
|
||||
properties::Property* sizeProperty = r->property("Size");
|
||||
auto propertyAny = sizeProperty->get();
|
||||
float propertySize = std::any_cast<float>(propertyAny);
|
||||
// Get size of renderable
|
||||
properties::Property* sizeProperty = r->property("Size");
|
||||
|
||||
// Create weak_ptr, safer than shared_ptr for lambdas
|
||||
std::weak_ptr<SoftwareConnection> connWeakPtr{ connection };
|
||||
|
||||
auto callback = [this, identifier, size, sizeProperty, connWeakPtr] {
|
||||
if (!sizeProperty || connWeakPtr.expired()) return;
|
||||
// auto conn = connWeakPtr.lock()
|
||||
|
||||
// auto propertySub = connection->getPropertySubscription(identifier, sizeProperty->identifier());
|
||||
// if (propertySub) {
|
||||
@@ -402,7 +532,8 @@ void PointDataMessageHandler::handleFixedPointSizeMessage(const std::vector<char
|
||||
// }
|
||||
|
||||
// Update size of renderable
|
||||
if (propertySize != size) {
|
||||
float currentSize = std::any_cast<float>(sizeProperty->get());
|
||||
if (currentSize != size) {
|
||||
// TODO: Add interpolation to script, but do not send back
|
||||
// updates to external software until the interpolation is done
|
||||
// Use this: "openspace.setPropertyValueSingle('Scene.{}.Renderable.Size', {}, 1);",
|
||||
@@ -424,6 +555,12 @@ void PointDataMessageHandler::handleFixedPointSizeMessage(const std::vector<char
|
||||
);
|
||||
};
|
||||
addCallback(identifier, { callback, {}, "handleFixedPointSizeMessage" });
|
||||
|
||||
auto updateSize = [this, sizeProperty, identifier, connWeakPtr] {
|
||||
if (!sizeProperty || connWeakPtr.expired()) return;
|
||||
onFixedPointSizeChange(sizeProperty, identifier, connWeakPtr.lock());
|
||||
};
|
||||
connection->addPropertySubscription(sizeProperty->identifier(), identifier, updateSize);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleLinearPointSizeMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
@@ -525,9 +662,21 @@ void PointDataMessageHandler::handleVisibilityMessage(const std::vector<char>& m
|
||||
return;
|
||||
}
|
||||
|
||||
auto callback = [this, identifier, visibilityMessage, connection] {
|
||||
// Get renderable
|
||||
// auto r = getRenderable(identifier);
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
if (!r) return;
|
||||
|
||||
// Get visibility of renderable
|
||||
properties::Property* visibilityProperty = r->property("Enabled");
|
||||
|
||||
// Create weak_ptr, safer than shared_ptr for lambdas
|
||||
std::weak_ptr<SoftwareConnection> connWeakPtr{ connection };
|
||||
|
||||
const bool visibility = visibilityMessage == "T";
|
||||
|
||||
auto callback = [this, identifier, visibility, visibilityProperty, connWeakPtr] {
|
||||
if (!visibilityProperty || connWeakPtr.expired()) return;
|
||||
// auto conn = connWeakPtr.lock()
|
||||
|
||||
// Get visibility from renderable
|
||||
// properties::Property* enabledProperty = r->property("Enabled");
|
||||
@@ -536,18 +685,28 @@ void PointDataMessageHandler::handleVisibilityMessage(const std::vector<char>& m
|
||||
// propertySub->shouldSendMessage = false;
|
||||
// }
|
||||
|
||||
// Toggle visibility from renderable
|
||||
const std::string visability = visibilityMessage == "T" ? "true" : "false";
|
||||
std::string script = fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.Enabled', {});",
|
||||
identifier, visability
|
||||
);
|
||||
global::scriptEngine->queueScript(
|
||||
script,
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
// Toggle visibility of renderable
|
||||
bool currentVisibility = std::any_cast<bool>(visibilityProperty->get());
|
||||
if (currentVisibility != visibility) {
|
||||
const std::string vis = visibility ? "true" : "false";
|
||||
std::string script = fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.Enabled', {});",
|
||||
identifier, vis
|
||||
);
|
||||
global::scriptEngine->queueScript(
|
||||
script,
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
};
|
||||
addCallback(identifier, { callback, {}, "handleVisibilityMessage" });
|
||||
|
||||
// Create and set onChange for visibility
|
||||
auto toggleVisibility = [this, visibilityProperty, identifier, connWeakPtr] {
|
||||
if (!visibilityProperty || connWeakPtr.expired()) return;
|
||||
onVisibilityChange(visibilityProperty, identifier, connWeakPtr.lock());
|
||||
};
|
||||
connection->addPropertySubscription(visibilityProperty->identifier(), identifier, toggleVisibility);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleRemoveSGNMessage(const std::vector<char>& message,std::shared_ptr<SoftwareConnection> connection) {
|
||||
@@ -689,66 +848,6 @@ void PointDataMessageHandler::checkRenderable(
|
||||
"openspace.setPropertyValueSingle('Modules.CefWebGui.Reload', nil)", // Reload WebGUI so that SoftwareIntegration GUI appears
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
auto subscriptionCallback = [this, identifier, connection] {
|
||||
subscribeToRenderableUpdates(identifier, connection);
|
||||
};
|
||||
addCallback(identifier, { subscriptionCallback, {}, "subscriptionCallback" });
|
||||
}
|
||||
else {
|
||||
subscribeToRenderableUpdates(identifier, connection);
|
||||
}
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::subscribeToRenderableUpdates(
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
// Get renderable
|
||||
auto aRenderable = getRenderable(identifier);
|
||||
if (!aRenderable) return;
|
||||
|
||||
if (!connection->isConnected()) {
|
||||
LERROR(fmt::format(
|
||||
"Could not subscribe to updates for renderable '{}' due to lost connection",
|
||||
identifier
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// Update color of renderable
|
||||
properties::Property* colorProperty = aRenderable->property("Color");
|
||||
if (colorProperty) {
|
||||
auto updateColor = [this, colorProperty, identifier, connection]() {
|
||||
onFixedColorChange(colorProperty, identifier, connection);
|
||||
};
|
||||
connection->addPropertySubscription(colorProperty->identifier(), identifier, updateColor);
|
||||
}
|
||||
|
||||
// Update opacity of renderable
|
||||
properties::Property* opacityProperty = aRenderable->property("Opacity");
|
||||
if (opacityProperty) {
|
||||
auto updateOpacity = [this, opacityProperty, identifier, connection]() {
|
||||
onOpacityChange(opacityProperty, identifier, connection);
|
||||
};
|
||||
connection->addPropertySubscription(opacityProperty->identifier(), identifier, updateOpacity);
|
||||
}
|
||||
|
||||
// Update size of renderable
|
||||
properties::Property* sizeProperty = aRenderable->property("Size");
|
||||
if (sizeProperty) {
|
||||
auto updateSize = [this, sizeProperty, identifier, connection] {
|
||||
onFixedPointSizeChange(sizeProperty, identifier, connection);
|
||||
};
|
||||
connection->addPropertySubscription(sizeProperty->identifier(), identifier, updateSize);
|
||||
}
|
||||
|
||||
// Toggle visibility of renderable
|
||||
properties::Property* visibilityProperty = aRenderable->property("Enabled");
|
||||
if (visibilityProperty) {
|
||||
auto toggleVisibility = [this, visibilityProperty, identifier, connection] {
|
||||
onVisibilityChange(visibilityProperty, identifier, connection);
|
||||
};
|
||||
connection->addPropertySubscription(visibilityProperty->identifier(), identifier, toggleVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,4 +969,35 @@ void PointDataMessageHandler::onVisibilityChange(
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
// TODO: Move to SIMP / use distanceconversion
|
||||
void PointDataMessageHandler::convertToMeterPerSecond(simp::LengthUnit currLengthUnit, std::vector<float>& data) {
|
||||
// distanceconversion::convertDistance
|
||||
float multiplier = 1.0;
|
||||
switch (currLengthUnit) {
|
||||
case simp::LengthUnit::km:
|
||||
multiplier = 1000.0;
|
||||
break;
|
||||
case simp::LengthUnit::AU:
|
||||
multiplier = static_cast<float>(distanceconstants::AstronomicalUnit);
|
||||
break;
|
||||
case simp::LengthUnit::lyr:
|
||||
multiplier = static_cast<float>(distanceconstants::LightYear);
|
||||
break;
|
||||
case simp::LengthUnit::pc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec);
|
||||
break;
|
||||
case simp::LengthUnit::kpc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec * 1.0e3);
|
||||
break;
|
||||
case simp::LengthUnit::Mpc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec * 1.0e6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::transform(std::begin(data), std::end(data), std::begin(data),
|
||||
[multiplier] (float f) {return f * multiplier;});
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -46,6 +46,7 @@ class PointDataMessageHandler {
|
||||
|
||||
public:
|
||||
void handlePointDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleVelocityDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleFixedColorMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleColormapMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleAttributeDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
@@ -64,8 +65,6 @@ private:
|
||||
std::shared_ptr<SoftwareConnection> connection, std::string& identifier
|
||||
);
|
||||
|
||||
void subscribeToRenderableUpdates(const std::string& identifier, std::shared_ptr<SoftwareConnection> connection);
|
||||
|
||||
void addCallback(
|
||||
const std::string& identifier,
|
||||
const Callback& newCallback
|
||||
@@ -95,6 +94,11 @@ private:
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
);
|
||||
|
||||
void convertToMeterPerSecond(
|
||||
softwareintegration::simp::LengthUnit currLengthUnit,
|
||||
std::vector<float>& data
|
||||
);
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -42,16 +42,19 @@
|
||||
#include <ghoul/opengl/textureunit.h>
|
||||
#include <fstream>
|
||||
|
||||
int TIME_COUNTER = 0;
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "PointsCloud";
|
||||
|
||||
constexpr const std::array<const char*, 18> UniformNames = {
|
||||
constexpr const std::array<const char*, 20> UniformNames = {
|
||||
"color", "opacity", "size", "modelMatrix", "cameraUp", "screenSize",
|
||||
"cameraViewProjectionMatrix", "eyePosition", "sizeOption",
|
||||
"colormapTexture", "colormapMin", "colormapMax", "cmapNaNMode",
|
||||
"cmapNaNColor", "colormapEnabled", "linearSizeMin", "linearSizeMax",
|
||||
"linearSizeEnabled"
|
||||
"linearSizeEnabled", "motionEnabled", "theTime"
|
||||
};
|
||||
// "velNaNMode", "velNaNColor",
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
|
||||
"Color",
|
||||
@@ -130,6 +133,24 @@ namespace {
|
||||
"Linear size enabled",
|
||||
"Boolean to determine whether to use linear size or not."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo VelNaNModeInfo = {
|
||||
"VelNaNMode",
|
||||
"Vel NaN Mode",
|
||||
"How points with NaN value in colormap attribute should be represented."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo VelNaNColorInfo = {
|
||||
"VelNaNColor",
|
||||
"Vel NaN Color",
|
||||
"The color of the points where the colormap scalar is NaN."
|
||||
};
|
||||
|
||||
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",
|
||||
@@ -144,6 +165,7 @@ namespace {
|
||||
// [[codegen::verbatim(SizeInfo.description)]]
|
||||
std::optional<float> size;
|
||||
|
||||
// TODO: This can be removed? Not in use anymore?
|
||||
// [[codegen::verbatim(DataInfo.description)]]
|
||||
std::optional<std::vector<glm::vec3>> data;
|
||||
|
||||
@@ -177,6 +199,15 @@ namespace {
|
||||
// [[codegen::verbatim(NameInfo.description)]]
|
||||
std::optional<std::string> name;
|
||||
|
||||
// [[codegen::verbatim(VelNaNModeInfo.description)]]
|
||||
std::optional<int> velNaNMode;
|
||||
|
||||
// [[codegen::verbatim(VelNaNColorInfo.description)]]
|
||||
std::optional<glm::vec4> velNaNColor;
|
||||
|
||||
// [[codegen::verbatim(MotionEnabledInfo.description)]]
|
||||
std::optional<bool> motionEnabled;
|
||||
|
||||
enum class SizeOption : uint32_t {
|
||||
Uniform,
|
||||
NonUniform
|
||||
@@ -207,8 +238,11 @@ RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary
|
||||
, _colormapEnabled(ColormapEnabledInfo, false)
|
||||
, _linearSizeMax(LinearSizeMinInfo)
|
||||
, _linearSizeMin(LinearSizeMaxInfo)
|
||||
, _linearSizeEnabled(LinearSizeEnabledInfo)
|
||||
, _linearSizeEnabled{LinearSizeEnabledInfo, false}
|
||||
, _name(NameInfo)
|
||||
, _velNaNMode(VelNaNModeInfo)
|
||||
, _velNaNColor(VelNaNColorInfo, glm::vec4(glm::vec3(0.5f), 1.f), glm::vec4(1.0f), glm::vec4(0.f), glm::vec4(0.f))
|
||||
, _motionEnabled(MotionEnabledInfo, false)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
@@ -289,6 +323,19 @@ RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary
|
||||
_linearSizeMax.setVisibility(properties::Property::Visibility::Hidden);
|
||||
_linearSizeMax.onChange(linearSizeMinMaxChecker);
|
||||
addProperty(_linearSizeMax);
|
||||
|
||||
_velNaNMode = p.velNaNMode.value_or(_velNaNMode);
|
||||
_velNaNMode.setVisibility(properties::Property::Visibility::Hidden);
|
||||
addProperty(_velNaNMode);
|
||||
|
||||
_velNaNColor = p.velNaNColor.value_or(_velNaNColor);
|
||||
// _velNaNColor.setViewOption(properties::Property::ViewOptions::Color); // TODO: CHECK WHAT THIS IS
|
||||
_velNaNColor.setVisibility(properties::Property::Visibility::Hidden);
|
||||
addProperty(_velNaNColor);
|
||||
|
||||
_motionEnabled = p.motionEnabled.value_or(_motionEnabled);
|
||||
_motionEnabled.onChange([this] { checkIfMotionCanBeEnabled(); });
|
||||
addProperty(_motionEnabled);
|
||||
}
|
||||
|
||||
bool RenderablePointsCloud::isReady() const {
|
||||
@@ -381,6 +428,14 @@ void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) {
|
||||
_shaderProgram->setUniform(_uniformCache.linearSizeMax, _linearSizeMax);
|
||||
_shaderProgram->setUniform(_uniformCache.linearSizeEnabled, _linearSizeEnabled);
|
||||
|
||||
// _shaderProgram->setUniform(_uniformCache.velNaNMode, _velNaNMode);
|
||||
// _shaderProgram->setUniform(_uniformCache.velNaNColor, _velNaNColor);
|
||||
_shaderProgram->setUniform(_uniformCache.motionEnabled, _motionEnabled);
|
||||
_shaderProgram->setUniform(
|
||||
_uniformCache.theTime,
|
||||
static_cast<float>(data.time.j2000Seconds())
|
||||
);
|
||||
|
||||
_shaderProgram->setUniform(_uniformCache.color, _color);
|
||||
|
||||
_shaderProgram->setUniform(_uniformCache.opacity, _opacity);
|
||||
@@ -429,6 +484,7 @@ void RenderablePointsCloud::update(const UpdateData&) {
|
||||
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
|
||||
auto colormapAttrDataSlice = getDataSlice(DataSliceKey::ColormapAttributes);
|
||||
auto linearSizeAttrDataSlice = getDataSlice(DataSliceKey::LinearSizeAttributes);
|
||||
auto velocityDataSlice = getDataSlice(DataSliceKey::Velocity);
|
||||
|
||||
if (pointDataSlice->empty()) return;
|
||||
|
||||
@@ -445,14 +501,28 @@ void RenderablePointsCloud::update(const UpdateData&) {
|
||||
bufferData.push_back(colormapAttrDataSlice->at(i));
|
||||
}
|
||||
else {
|
||||
bufferData.push_back(0.0);
|
||||
bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered?
|
||||
}
|
||||
|
||||
if (linearSizeAttrDataSlice->size() > i) {
|
||||
bufferData.push_back(linearSizeAttrDataSlice->at(i));
|
||||
}
|
||||
else {
|
||||
bufferData.push_back(0.0);
|
||||
bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered?
|
||||
}
|
||||
|
||||
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")); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
|
||||
bufferData.push_back(std::nanf("0")); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
|
||||
bufferData.push_back(std::nanf("0")); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
|
||||
// bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
|
||||
// bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
|
||||
// bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,7 +535,7 @@ void RenderablePointsCloud::update(const UpdateData&) {
|
||||
// ==============================================================================================
|
||||
|
||||
// ========================================= VAO stuff =========================================
|
||||
GLsizei stride = static_cast<GLsizei>(sizeof(GLfloat) * 5);
|
||||
GLsizei stride = static_cast<GLsizei>(sizeof(GLfloat) * _nValuesForVAOStride);
|
||||
GLint positionAttribute = _shaderProgram->attributeLocation("in_position");
|
||||
glEnableVertexAttribArray(positionAttribute);
|
||||
glVertexAttribPointer(
|
||||
@@ -502,6 +572,19 @@ void RenderablePointsCloud::update(const UpdateData&) {
|
||||
reinterpret_cast<void*>(sizeof(GLfloat) * 4)
|
||||
);
|
||||
}
|
||||
|
||||
if (_hasLoadedVelocityData) {
|
||||
GLint velocityAttribute = _shaderProgram->attributeLocation("in_velocity");
|
||||
glEnableVertexAttribArray(velocityAttribute);
|
||||
glVertexAttribPointer(
|
||||
velocityAttribute,
|
||||
3,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
stride,
|
||||
reinterpret_cast<void*>(sizeof(GLfloat) * 5)
|
||||
);
|
||||
}
|
||||
// ==============================================================================================
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
@@ -536,6 +619,11 @@ bool RenderablePointsCloud::checkDataStorage() {
|
||||
loadLinearSizeAttributeData(softwareIntegrationModule);
|
||||
updatedDataSlices = true;
|
||||
}
|
||||
|
||||
if (softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::VelocityData)) {
|
||||
loadVelocityData(softwareIntegrationModule);
|
||||
updatedDataSlices = true;
|
||||
}
|
||||
|
||||
return updatedDataSlices;
|
||||
}
|
||||
@@ -551,7 +639,7 @@ void RenderablePointsCloud::loadData(SoftwareIntegrationModule* softwareIntegrat
|
||||
|
||||
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
|
||||
pointDataSlice->clear();
|
||||
pointDataSlice->reserve(fullPointData.size() * 3);
|
||||
pointDataSlice->reserve(fullPointData.size() * 3); // TODO: Do we really need *3 here?
|
||||
|
||||
// Create data slice
|
||||
auto addPosition = [&](const glm::vec4& pos) {
|
||||
@@ -630,7 +718,7 @@ void RenderablePointsCloud::loadCmapAttributeData(SoftwareIntegrationModule* sof
|
||||
}
|
||||
|
||||
_hasLoadedColormapAttributeData = true;
|
||||
LDEBUG("Rerendering colormap attribute data");
|
||||
LDEBUG("Rerendering with colormap attribute data");
|
||||
}
|
||||
|
||||
void RenderablePointsCloud::loadLinearSizeAttributeData(SoftwareIntegrationModule* softwareIntegrationModule) {
|
||||
@@ -648,7 +736,7 @@ void RenderablePointsCloud::loadLinearSizeAttributeData(SoftwareIntegrationModul
|
||||
"There is a mismatch in the amount of linear size attribute scalars ({}) and the amount of points ({})",
|
||||
linearSizeAttributeData.size(), pointDataSlice->size() / 3
|
||||
));
|
||||
_colormapEnabled = false;
|
||||
_linearSizeEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -661,9 +749,108 @@ void RenderablePointsCloud::loadLinearSizeAttributeData(SoftwareIntegrationModul
|
||||
}
|
||||
|
||||
_hasLoadedLinearSizeAttributeData = true;
|
||||
LDEBUG("Rerendering linear size attribute data");
|
||||
LDEBUG("Rerendering with linear size attribute data");
|
||||
}
|
||||
|
||||
void RenderablePointsCloud::loadVelocityData(SoftwareIntegrationModule* softwareIntegrationModule) {
|
||||
// Fetch data from module's centralized storage
|
||||
auto velocityData = softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::VelocityData);
|
||||
|
||||
if (velocityData.empty()) {
|
||||
LWARNING("There was an issue trying to fetch the velocity data from the centralized storage.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
|
||||
|
||||
if (pointDataSlice->size() != velocityData.size()) {
|
||||
LWARNING(fmt::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;
|
||||
}
|
||||
|
||||
auto velocityDataSlice = getDataSlice(DataSliceKey::Velocity);
|
||||
velocityDataSlice->clear();
|
||||
velocityDataSlice->reserve(velocityData.size() * 3); // TODO: Do we really need *3 here?
|
||||
|
||||
// =========================================
|
||||
int nNans = 0;
|
||||
int prevNNans = 0;
|
||||
int nPointsContainingNans = 0;
|
||||
// Create velocity data slice
|
||||
auto addPosition = [&](const glm::vec4& pos) {
|
||||
for (glm::vec4::length_type j = 0; j < glm::vec4::length() - 1; ++j) {
|
||||
if (isnan(pos[j])) {
|
||||
nNans++;
|
||||
}
|
||||
velocityDataSlice->push_back(pos[j]);
|
||||
}
|
||||
if (prevNNans < nNans) {
|
||||
nPointsContainingNans++;
|
||||
prevNNans = nNans;
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < velocityData.size(); i += 3) {
|
||||
glm::dvec4 transformedPos = {
|
||||
velocityData[i + 0],
|
||||
velocityData[i + 1],
|
||||
velocityData[i + 2],
|
||||
1.0
|
||||
};
|
||||
// W-normalization
|
||||
// transformedPos /= transformedPos.w;
|
||||
// transformedPos *= distanceconstants::Parsec; // TODO: Is this converting parsec => meter?
|
||||
|
||||
addPosition(transformedPos);
|
||||
}
|
||||
|
||||
LWARNING(fmt::format("Points: {}. Velocity points: {}. Velocity points w/ nans {}. Velocity nans in total: {}", pointDataSlice->size()/3, velocityData.size()/3, nPointsContainingNans, nNans));
|
||||
// =========================================
|
||||
|
||||
|
||||
_hasLoadedVelocityData = true;
|
||||
LDEBUG("Rerendering with motion based on velocity data");
|
||||
}
|
||||
|
||||
// void RenderablePointsCloud::loadData(SoftwareIntegrationModule* softwareIntegrationModule) {
|
||||
// // Fetch data from module's centralized storage
|
||||
// auto fullPointData = softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::DataPoints);
|
||||
|
||||
// if (fullPointData.empty()) {
|
||||
// LWARNING("There was an issue trying to fetch the point data from the centralized storage.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// auto pointDataSlice = getDataSlice(DataSliceKey::Points);
|
||||
// pointDataSlice->clear();
|
||||
// pointDataSlice->reserve(fullPointData.size() * 3);
|
||||
|
||||
// // Create data slice
|
||||
// auto addPosition = [&](const glm::vec4& pos) {
|
||||
// for (glm::vec4::length_type j = 0; j < glm::vec4::length() - 1; ++j) {
|
||||
// pointDataSlice->push_back(pos[j]);
|
||||
// }
|
||||
// };
|
||||
|
||||
// for (size_t i = 0; i < fullPointData.size(); i += 3) {
|
||||
// glm::dvec4 transformedPos = {
|
||||
// fullPointData[i + 0],
|
||||
// fullPointData[i + 1],
|
||||
// fullPointData[i + 2],
|
||||
// 1.0
|
||||
// };
|
||||
// // W-normalization
|
||||
// transformedPos /= transformedPos.w;
|
||||
// transformedPos *= distanceconstants::Parsec;
|
||||
|
||||
// addPosition(transformedPos);
|
||||
// }
|
||||
// }
|
||||
|
||||
std::shared_ptr<RenderablePointsCloud::DataSlice> RenderablePointsCloud::getDataSlice(DataSliceKey key) {
|
||||
if (!_dataSlices.count(key)) {
|
||||
_dataSlices.insert({ key, std::make_shared<DataSlice>() });
|
||||
@@ -713,6 +900,24 @@ void RenderablePointsCloud::checkIfLinearSizeCanBeEnabled() {
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.MotionEnabled', {});",
|
||||
_identifier.value(), "false"
|
||||
),
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
LINFO(fmt::format("checkIfMotionCanBeEnabled(): MotionEnabled = {}", _motionEnabled));
|
||||
}
|
||||
|
||||
void RenderablePointsCloud::checkColormapMinMax() {
|
||||
float min = std::any_cast<float>(_colormapMin.get());
|
||||
float max = std::any_cast<float>(_colormapMax.get());
|
||||
|
||||
@@ -67,12 +67,14 @@ private:
|
||||
void loadColormap(SoftwareIntegrationModule* softwareIntegrationModule);
|
||||
void loadCmapAttributeData(SoftwareIntegrationModule* softwareIntegrationModule);
|
||||
void loadLinearSizeAttributeData(SoftwareIntegrationModule* softwareIntegrationModule);
|
||||
void loadVelocityData(SoftwareIntegrationModule* softwareIntegrationModule);
|
||||
|
||||
bool checkDataStorage();
|
||||
|
||||
void checkColormapMinMax();
|
||||
void checkIfColormapCanBeEnabled();
|
||||
void checkIfLinearSizeCanBeEnabled();
|
||||
void checkColormapMinMax();
|
||||
void checkIfMotionCanBeEnabled();
|
||||
|
||||
std::unique_ptr<ghoul::opengl::ProgramObject> _shaderProgram = nullptr;
|
||||
UniformCache(
|
||||
@@ -80,8 +82,9 @@ private:
|
||||
cameraViewProjectionMatrix, eyePosition, sizeOption,
|
||||
colormapTexture, colormapMin, colormapMax, cmapNaNMode,
|
||||
cmapNaNColor, colormapEnabled, linearSizeMin, linearSizeMax,
|
||||
linearSizeEnabled
|
||||
linearSizeEnabled,motionEnabled, theTime
|
||||
) _uniformCache;
|
||||
// velNaNMode, velNaNColor,
|
||||
|
||||
properties::FloatProperty _size;
|
||||
properties::Vec4Property _color;
|
||||
@@ -98,17 +101,27 @@ private:
|
||||
properties::Vec4Property _cmapNaNColor;
|
||||
properties::StringProperty _name;
|
||||
|
||||
properties::BoolProperty _motionEnabled;
|
||||
properties::IntProperty _velNaNMode;
|
||||
properties::Vec4Property _velNaNColor;
|
||||
|
||||
std::optional<std::string> _identifier = std::nullopt;
|
||||
|
||||
bool _hasLoadedColormapAttributeData = false;
|
||||
bool _hasLoadedColormap = false;
|
||||
|
||||
bool _hasLoadedLinearSizeAttributeData = false;
|
||||
|
||||
bool _hasLoadedVelocityData = false;
|
||||
|
||||
// This is determined by nrAttributes + size for each attribute
|
||||
const size_t _nValuesForVAOStride = 8;
|
||||
|
||||
enum class DataSliceKey : size_t {
|
||||
Points = 0,
|
||||
ColormapAttributes,
|
||||
LinearSizeAttributes
|
||||
LinearSizeAttributes,
|
||||
Velocity
|
||||
};
|
||||
using DataSlice = std::vector<float>;
|
||||
std::unordered_map<DataSliceKey, std::shared_ptr<DataSlice>> _dataSlices;
|
||||
|
||||
@@ -33,6 +33,7 @@ in vec2 coords;
|
||||
flat in float ge_screenSpaceDepth;
|
||||
flat in vec4 ge_positionViewSpace;
|
||||
in float ta;
|
||||
// in vec4 ge_gPosition;
|
||||
|
||||
uniform vec4 color;
|
||||
uniform float opacity;
|
||||
@@ -44,6 +45,9 @@ uniform vec4 cmapNaNColor;
|
||||
uniform bool colormapEnabled;
|
||||
uniform sampler1D colormapTexture;
|
||||
|
||||
// uniform int velNaNMode;
|
||||
// uniform vec4 velNaNColor;
|
||||
|
||||
vec4 attributeScalarToRgb(float attributeData) {
|
||||
float t = (attributeData - colormapMin) / (colormapMax - colormapMin);
|
||||
|
||||
@@ -60,7 +64,7 @@ Fragment getFragment() {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Don't show points with no value for that attribute, if CmapNaNMode is Hidden
|
||||
// Don't show points with no value for that attribute, if NaNRenderMode is Hidden
|
||||
if (colormapEnabled && isnan(ge_colormapAttributeScalar) && cmapNaNMode == CMAPNANMODE_HIDDEN) {
|
||||
discard;
|
||||
}
|
||||
@@ -82,6 +86,11 @@ Fragment getFragment() {
|
||||
vec4 colorFromColormap = attributeScalarToRgb(ge_colormapAttributeScalar);
|
||||
outputColor = vec4(colorFromColormap.rgb, colorFromColormap.a * color.a * opacity);
|
||||
}
|
||||
|
||||
// // TODO: Remove, Just referencing so it doesn't crash
|
||||
// if (velNaNMode == 0) {
|
||||
// velNaNColor = vec4(1.0, 1.0, 1.0);
|
||||
// }
|
||||
}
|
||||
|
||||
Fragment frag;
|
||||
|
||||
@@ -26,12 +26,17 @@
|
||||
|
||||
#include "PowerScaling/powerScalingMath.hglsl"
|
||||
|
||||
// const float EPS = 1e-5;
|
||||
|
||||
layout(points) in;
|
||||
layout(triangle_strip, max_vertices = 4) out;
|
||||
|
||||
in float vs_colormapAttributeScalar[];
|
||||
flat in float vs_linearSizeAttributeScalar[];
|
||||
|
||||
// in vec4 vs_gPosition[];
|
||||
// out vec4 ge_gPosition;
|
||||
|
||||
flat out float ge_screenSpaceDepth;
|
||||
out float ta;
|
||||
out vec4 ge_positionViewSpace;
|
||||
@@ -96,6 +101,15 @@ void main() {
|
||||
|
||||
ta = 1.0;
|
||||
|
||||
|
||||
// // Discard geometry if star has no position (but wasn't a nullArray).
|
||||
// // Or if observed distance is above threshold set by cutOffThreshold.
|
||||
// // By discarding in gs instead of fs we save computations for when nothing is visible.
|
||||
// vec4 position = gl_in[0].gl_Position;
|
||||
// if (length(position) < EPS) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
vec3 pos = gl_in[0].gl_Position.xyz;
|
||||
|
||||
dvec4 modelPos = modelMatrix * dvec4(pos, 1.0);
|
||||
@@ -104,7 +118,7 @@ void main() {
|
||||
|
||||
if (linearSizeEnabled) {
|
||||
float interpolatedSizeAtt = 1.0;
|
||||
// TODO: colormapAttributeScalar should be linearSizeAttributeScalar???
|
||||
// TODO: "colormapAttributeScalar" should be "linearSizeAttributeScalar"???
|
||||
float colormapAttributeScalar = vs_linearSizeAttributeScalar[0];
|
||||
if (colormapAttributeScalar < linearSizeMin) {
|
||||
interpolatedSizeAtt = 0.0;
|
||||
|
||||
@@ -26,16 +26,42 @@
|
||||
|
||||
#include "PowerScaling/powerScaling_vs.hglsl"
|
||||
|
||||
const float Parsec = 3.0856776e16;
|
||||
|
||||
layout(location = 0) in vec3 in_position;
|
||||
in float in_colormapAttributeScalar;
|
||||
in float in_linearSizeAttributeScalar;
|
||||
|
||||
in vec3 in_velocity;
|
||||
// out vec4 vs_gPosition;
|
||||
|
||||
out float vs_colormapAttributeScalar;
|
||||
flat out float vs_linearSizeAttributeScalar;
|
||||
|
||||
uniform bool motionEnabled;
|
||||
uniform float theTime;
|
||||
|
||||
void main() {
|
||||
vs_colormapAttributeScalar = in_colormapAttributeScalar;
|
||||
vs_linearSizeAttributeScalar = in_linearSizeAttributeScalar;
|
||||
|
||||
// Don't show points with no value for velocity
|
||||
// if (motionEnabled && (isnan(in_velocity[0]) || isnan(in_velocity[1]) || isnan(in_velocity[2]))) {
|
||||
// vs_gPosition = vec4(0.0);
|
||||
// gl_Position = vec4(0.0);
|
||||
// return;
|
||||
// }
|
||||
|
||||
gl_Position = vec4(in_position, 1.0);
|
||||
vec4 objectPosition = vec4(in_position, 1.0);
|
||||
|
||||
// Add velocity if applicable
|
||||
if (motionEnabled) {
|
||||
// TODO: How to handle NaN velocity values???
|
||||
if (!isnan(in_velocity[0]) || !isnan(in_velocity[1]) || !isnan(in_velocity[2])) {
|
||||
objectPosition.xyz += 10*theTime/Parsec * in_velocity;
|
||||
}
|
||||
// objectPosition.xyz += theTime * vec3(500000.0, 500000.0, 500000.0);
|
||||
}
|
||||
|
||||
gl_Position = objectPosition;
|
||||
}
|
||||
|
||||
@@ -80,12 +80,21 @@ const std::unordered_map<std::string, MessageType> _messageTypeFromSIMPType{
|
||||
{ "FPSI", MessageType::FixedSize },
|
||||
{ "LPSI", MessageType::LinearSize },
|
||||
{ "TOVI", MessageType::Visibility },
|
||||
{ "DISC", MessageType::Disconnection },
|
||||
};
|
||||
|
||||
const std::unordered_map<std::string, CmapNaNMode> _cmapNaNModeFromString{
|
||||
{"Hide", CmapNaNMode::Hide},
|
||||
{"Color", CmapNaNMode::Color}
|
||||
const std::unordered_map<std::string, NaNRenderMode> _naNRenderModeFromString{
|
||||
{ "Hide", NaNRenderMode::Hide},
|
||||
{ "Color", NaNRenderMode::Color },
|
||||
};
|
||||
|
||||
const std::unordered_map<std::string, LengthUnit> _lengthUnitFromString {
|
||||
{ "m", LengthUnit::m },
|
||||
{ "km", LengthUnit::km },
|
||||
{ "AU", LengthUnit::AU },
|
||||
{ "lyr", LengthUnit::lyr },
|
||||
{ "pc", LengthUnit::pc },
|
||||
{ "kpc", LengthUnit::kpc },
|
||||
{ "Mpc", LengthUnit::Mpc },
|
||||
};
|
||||
|
||||
glm::vec4 readSingleColor(const std::vector<char>& message, size_t& offset) {
|
||||
@@ -137,6 +146,10 @@ SimpError::SimpError(const tools::ErrorCode _errorCode, const std::string& msg)
|
||||
: errorCode{errorCode}, ghoul::RuntimeError(fmt::format("{}: Error Code: {} - {}", "SIMP error", static_cast<uint32_t>(_errorCode), msg), "Software Integration Messaging Protocol error")
|
||||
{}
|
||||
|
||||
SimpError::SimpError(const std::string& msg)
|
||||
: errorCode{tools::ErrorCode::Generic}, ghoul::RuntimeError(fmt::format("{}: Error Code: {} - {}", "SIMP error", static_cast<uint32_t>(errorCode), msg), "Software Integration Messaging Protocol error")
|
||||
{}
|
||||
|
||||
MessageType getMessageType(const std::string& type) {
|
||||
if (_messageTypeFromSIMPType.count(type) == 0) return MessageType::Unknown;
|
||||
return _messageTypeFromSIMPType.at(type);
|
||||
@@ -154,9 +167,14 @@ std::string getSIMPType(const MessageType& type) {
|
||||
return it->first;
|
||||
}
|
||||
|
||||
CmapNaNMode getCmapNaNMode(const std::string& type) {
|
||||
if (_cmapNaNModeFromString.count(type) == 0) return CmapNaNMode::Unknown;
|
||||
return _cmapNaNModeFromString.at(type);
|
||||
NaNRenderMode getNaNRenderMode(const std::string& type) {
|
||||
if (_naNRenderModeFromString.count(type) == 0) return NaNRenderMode::Unknown;
|
||||
return _naNRenderModeFromString.at(type);
|
||||
}
|
||||
|
||||
LengthUnit getLengthUnit(const std::string& type) {
|
||||
if (_lengthUnitFromString.count(type) == 0) return LengthUnit::Unknown;
|
||||
return _lengthUnitFromString.at(type);
|
||||
}
|
||||
|
||||
std::string formatLengthOfSubject(size_t lengthOfSubject) {
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace storage {
|
||||
|
||||
enum class Key : uint8_t {
|
||||
DataPoints = 0,
|
||||
VelocityData,
|
||||
Colormap,
|
||||
ColormapAttrData,
|
||||
LinearSizeAttrData,
|
||||
@@ -63,6 +64,7 @@ const char SEP = ';';
|
||||
enum class MessageType : uint32_t {
|
||||
Connection = 0,
|
||||
PointData,
|
||||
VelocityData,
|
||||
RemoveSceneGraphNode,
|
||||
Color,
|
||||
Colormap,
|
||||
@@ -71,16 +73,27 @@ enum class MessageType : uint32_t {
|
||||
FixedSize,
|
||||
LinearSize,
|
||||
Visibility,
|
||||
Disconnection,
|
||||
InternalDisconnection,
|
||||
Unknown
|
||||
};
|
||||
|
||||
enum class CmapNaNMode : uint32_t {
|
||||
enum class NaNRenderMode : uint32_t {
|
||||
Hide = 0,
|
||||
Color,
|
||||
Unknown
|
||||
};
|
||||
|
||||
enum class LengthUnit : uint32_t {
|
||||
m = 0,
|
||||
km,
|
||||
AU,
|
||||
lyr,
|
||||
pc,
|
||||
kpc,
|
||||
Mpc,
|
||||
Unknown
|
||||
};
|
||||
|
||||
namespace tools {
|
||||
|
||||
enum class ErrorCode : uint32_t {
|
||||
@@ -95,6 +108,7 @@ enum class ErrorCode : uint32_t {
|
||||
class SimpError : public ghoul::RuntimeError {
|
||||
public:
|
||||
tools::ErrorCode errorCode;
|
||||
explicit SimpError(const std::string& msg);
|
||||
explicit SimpError(const tools::ErrorCode _errorCode, const std::string& msg);
|
||||
};
|
||||
|
||||
@@ -102,7 +116,9 @@ MessageType getMessageType(const std::string& type);
|
||||
|
||||
std::string getSIMPType(const MessageType& type);
|
||||
|
||||
CmapNaNMode getCmapNaNMode(const std::string& type);
|
||||
NaNRenderMode getNaNRenderMode(const std::string& type);
|
||||
|
||||
LengthUnit getLengthUnit(const std::string& type);
|
||||
|
||||
std::string formatLengthOfSubject(size_t lengthOfSubject);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user