From 7f20750e80980d9918688d1e684e90e3c7699dc0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 24 Jan 2014 16:03:25 -0800 Subject: [PATCH] Working on sending edits. --- cmake/macros/AutoMTC.cmake | 4 +- interface/src/MetavoxelSystem.cpp | 15 +++- interface/src/MetavoxelSystem.h | 5 +- interface/src/ui/MetavoxelEditor.cpp | 50 +------------ libraries/metavoxels/src/Bitstream.cpp | 17 +++-- libraries/metavoxels/src/Bitstream.h | 5 +- .../metavoxels/src/DatagramSequencer.cpp | 71 ++++++++++++++----- libraries/metavoxels/src/DatagramSequencer.h | 19 +++++ .../metavoxels/src/MetavoxelMessages.cpp | 54 ++++++++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 11 ++- 10 files changed, 172 insertions(+), 79 deletions(-) create mode 100644 libraries/metavoxels/src/MetavoxelMessages.cpp diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake index fca8170e64..f0c9ebc6a0 100644 --- a/cmake/macros/AutoMTC.cmake +++ b/cmake/macros/AutoMTC.cmake @@ -9,10 +9,12 @@ macro(AUTO_MTC TARGET ROOT_DIR) ${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES}) find_package(Qt5Core REQUIRED) + find_package(Qt5Script REQUIRED) + find_package(Qt5Widgets REQUIRED) add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp) - qt5_use_modules(${TARGET}_automtc Core) + qt5_use_modules(${TARGET}_automtc Core Script Widgets) target_link_libraries(${TARGET} ${TARGET}_automtc) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index cf0c29ff15..66d9444ed0 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -10,7 +10,6 @@ #include -#include #include #include "Application.h" @@ -42,6 +41,12 @@ void MetavoxelSystem::init() { _buffer.create(); } +void MetavoxelSystem::applyEdit(const MetavoxelEdit& edit) { + foreach (MetavoxelClient* client, _clients) { + client->applyEdit(edit); + } +} + void MetavoxelSystem::processData(const QByteArray& data, const HifiSockAddr& sender) { QMetaObject::invokeMethod(this, "receivedData", Q_ARG(const QByteArray&, data), Q_ARG(const HifiSockAddr&, sender)); } @@ -187,6 +192,14 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : _receiveRecords.append(record); } +void MetavoxelClient::applyEdit(const MetavoxelEdit& edit) { + // apply immediately to local tree + edit.apply(_data); + + // start sending it out + _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); +} + void MetavoxelClient::simulate(float deltaTime, MetavoxelVisitor& visitor) { Bitstream& out = _sequencer.startPacket(); ClientStateMessage state = { Application::getInstance()->getCamera()->getPosition() }; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 7692f0d30d..f758b6e938 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -19,6 +19,7 @@ #include #include +#include #include "renderer/ProgramObject.h" @@ -34,7 +35,7 @@ public: void init(); - MetavoxelData& getData() { return _data; } + void applyEdit(const MetavoxelEdit& edit); void processData(const QByteArray& data, const HifiSockAddr& sender); @@ -90,6 +91,8 @@ public: const QUuid& getSessionID() const { return _sessionID; } + void applyEdit(const MetavoxelEdit& edit); + void simulate(float deltaTime, MetavoxelVisitor& visitor); void receivedData(const QByteArray& data, const HifiSockAddr& sender); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 9a8b951522..df77deb714 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -339,60 +339,14 @@ void MetavoxelEditor::resetState() { _height = 0.0f; } -class Applier : public MetavoxelVisitor { -public: - - Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value); - - virtual bool visit(MetavoxelInfo& info); - -protected: - - glm::vec3 _minimum; - glm::vec3 _maximum; - float _granularity; - AttributeValue _value; -}; - -Applier::Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value) : - MetavoxelVisitor(QVector(), QVector() << value.getAttribute()), - _minimum(minimum), - _maximum(maximum), - _granularity(granularity), - _value(value) { -} - -bool Applier::visit(MetavoxelInfo& info) { - // find the intersection between volume and voxel - glm::vec3 minimum = glm::max(info.minimum, _minimum); - glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _maximum); - glm::vec3 size = maximum - minimum; - if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { - return false; // disjoint - } - float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); - if (volume >= 1.0f) { - info.outputValues[0] = _value; - return false; // entirely contained - } - if (info.size <= _granularity) { - if (volume >= 0.5f) { - info.outputValues[0] = _value; - } - return false; // reached granularity limit; take best guess - } - return true; // subdivide -} - void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(getSelectedAttribute()); if (!attribute) { return; } OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue())); - - Applier applier(minimum, maximum, _gridSpacing->value(), value); - Application::getInstance()->getMetavoxels()->getData().guide(applier); + MetavoxelEdit edit = { minimum, maximum, _gridSpacing->value(), value }; + Application::getInstance()->getMetavoxels()->applyEdit(edit); } QVariant MetavoxelEditor::getValue() const { diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 77273fea9c..4f23c79d78 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -237,20 +237,25 @@ Bitstream& Bitstream::operator>>(QVariant& value) { return *this; } -Bitstream& Bitstream::operator<<(const AttributeValue& value) { - _attributeStreamer << value.getAttribute(); - if (value.getAttribute()) { - value.getAttribute()->write(*this, value.getValue(), true); +Bitstream& Bitstream::operator<<(const AttributeValue& attributeValue) { + _attributeStreamer << attributeValue.getAttribute(); + if (attributeValue.getAttribute()) { + attributeValue.getAttribute()->write(*this, attributeValue.getValue(), true); } return *this; } -Bitstream& Bitstream::operator>>(AttributeValue& value) { +Bitstream& Bitstream::operator>>(OwnedAttributeValue& attributeValue) { AttributePointer attribute; _attributeStreamer >> attribute; if (attribute) { - void* value; + void* value = attribute->create(); attribute->read(*this, value, true); + attributeValue = AttributeValue(attribute, value); + attribute->destroy(value); + + } else { + attributeValue = AttributeValue(); } return *this; } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index c8793f9673..4d6ea8efe5 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -25,6 +25,7 @@ class QObject; class Attribute; class AttributeValue; class Bitstream; +class OwnedAttributeValue; class TypeStreamer; typedef QSharedPointer AttributePointer; @@ -232,8 +233,8 @@ public: Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); - Bitstream& operator<<(const AttributeValue& value); - Bitstream& operator>>(AttributeValue& value); + Bitstream& operator<<(const AttributeValue& attributeValue); + Bitstream& operator>>(OwnedAttributeValue& attributeValue); template Bitstream& operator<<(const QList& list); template Bitstream& operator>>(QList& list); diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 028a01e2e8..1e246b9818 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -25,7 +25,8 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : _outgoingDatagramStream(&_outgoingDatagramBuffer), _incomingPacketNumber(0), _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), - _inputStream(_incomingPacketStream) { + _inputStream(_incomingPacketStream), + _receivedHighPriorityMessages(0) { _outgoingPacketStream.setByteOrder(QDataStream::LittleEndian); _incomingDatagramStream.setByteOrder(QDataStream::LittleEndian); @@ -35,6 +36,34 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); } +void DatagramSequencer::sendHighPriorityMessage(const QVariant& data) { + HighPriorityMessage message = { data, _outgoingPacketNumber + 1 }; + _highPriorityMessages.append(message); +} + +Bitstream& DatagramSequencer::startPacket() { + // start with the list of acknowledgements + _outgoingPacketStream << (quint32)_receiveRecords.size(); + foreach (const ReceiveRecord& record, _receiveRecords) { + _outgoingPacketStream << (quint32)record.packetNumber; + } + + // write the high-priority messages + _outgoingPacketStream << (quint32)_highPriorityMessages.size(); + foreach (const HighPriorityMessage& message, _highPriorityMessages) { + _outputStream << message.data; + } + + // return the stream, allowing the caller to write the rest + return _outputStream; +} + +void DatagramSequencer::endPacket() { + _outputStream.flush(); + sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos())); + _outgoingPacketStream.device()->seek(0); +} + /// Simple RAII-style object to keep a device open when in scope. class QIODeviceOpener { public: @@ -47,21 +76,6 @@ private: QIODevice* _device; }; -Bitstream& DatagramSequencer::startPacket() { - // start with the list of acknowledgements - _outgoingPacketStream << (quint32)_receiveRecords.size(); - foreach (const ReceiveRecord& record, _receiveRecords) { - _outgoingPacketStream << (quint32)record.packetNumber; - } - return _outputStream; -} - -void DatagramSequencer::endPacket() { - _outputStream.flush(); - sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos())); - _outgoingPacketStream.device()->seek(0); -} - void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { _incomingDatagramBuffer.setData(datagram.constData() + _datagramHeaderSize, datagram.size() - _datagramHeaderSize); QIODeviceOpener opener(&_incomingDatagramBuffer, QIODevice::ReadOnly); @@ -123,12 +137,26 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { _sendRecords.erase(_sendRecords.begin(), it + 1); } + // read and dispatch the high-priority messages + quint32 highPriorityMessageCount; + _incomingPacketStream >> highPriorityMessageCount; + int newHighPriorityMessages = highPriorityMessageCount - _receivedHighPriorityMessages; + for (int i = 0; i < highPriorityMessageCount; i++) { + QVariant data; + _inputStream >> data; + if (i >= _receivedHighPriorityMessages) { + emit receivedHighPriorityMessage(data); + } + } + _receivedHighPriorityMessages = highPriorityMessageCount; + + // alert external parties so that they can read the rest emit readyToRead(_inputStream); _incomingPacketStream.device()->seek(0); _inputStream.reset(); // record the receipt - ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings() }; + ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings(), newHighPriorityMessages }; _receiveRecords.append(record); } @@ -138,10 +166,19 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { QList::iterator it = qBinaryFind(_receiveRecords.begin(), _receiveRecords.end(), compare); if (it != _receiveRecords.end()) { _inputStream.persistReadMappings(it->mappings); + _receivedHighPriorityMessages -= it->newHighPriorityMessages; emit receiveAcknowledged(it - _receiveRecords.begin()); _receiveRecords.erase(_receiveRecords.begin(), it + 1); } _outputStream.persistWriteMappings(record.mappings); + + // remove the received high priority messages + for (int i = _highPriorityMessages.size() - 1; i >= 0; i--) { + if (_highPriorityMessages.at(i).firstPacketNumber <= record.packetNumber) { + _highPriorityMessages.erase(_highPriorityMessages.begin(), _highPriorityMessages.begin() + i + 1); + break; + } + } } void DatagramSequencer::sendPacket(const QByteArray& packet) { diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index e5e4b8c4be..a7159bd9de 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -31,6 +31,12 @@ public: /// Returns the packet number of the last packet received (or the packet currently being assembled). int getIncomingPacketNumber() const { return _incomingPacketNumber; } + /// Returns the packet number of the sent packet at the specified index. + int getSentPacketNumber(int index) const { return _sendRecords.at(index).packetNumber; } + + /// Adds a message to the high priority queue. Will be sent with every outgoing packet until received. + void sendHighPriorityMessage(const QVariant& data); + /// Starts a new packet for transmission. /// \return a reference to the Bitstream to use for writing to the packet Bitstream& startPacket(); @@ -50,6 +56,9 @@ signals: /// Emitted when a packet is available to read. void readyToRead(Bitstream& input); + /// Emitted when we've received a high-priority message + void receivedHighPriorityMessage(const QVariant& data); + /// Emitted when a sent packet has been acknowledged by the remote side. /// \param index the index of the packet in our list of send records void sendAcknowledged(int index); @@ -71,10 +80,17 @@ private: public: int packetNumber; Bitstream::ReadMappings mappings; + int newHighPriorityMessages; bool operator<(const ReceiveRecord& other) const { return packetNumber < other.packetNumber; } }; + class HighPriorityMessage { + public: + QVariant data; + int firstPacketNumber; + }; + /// Notes that the described send was acknowledged by the other party. void sendRecordAcknowledged(const SendRecord& record); @@ -104,6 +120,9 @@ private: Bitstream _inputStream; QSet _offsetsReceived; int _remainingBytes; + + QList _highPriorityMessages; + int _receivedHighPriorityMessages; }; #endif /* defined(__interface__DatagramSequencer__) */ diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp new file mode 100644 index 0000000000..46ec4fd781 --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -0,0 +1,54 @@ +// +// MetavoxelMessages.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 1/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "MetavoxelMessages.h" + +class EditVisitor : public MetavoxelVisitor { +public: + + EditVisitor(const MetavoxelEdit& edit); + + virtual bool visit(MetavoxelInfo& info); + +private: + + const MetavoxelEdit& _edit; +}; + +EditVisitor::EditVisitor(const MetavoxelEdit& edit) : + MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), + _edit(edit) { +} + +bool EditVisitor::visit(MetavoxelInfo& info) { + // find the intersection between volume and voxel + glm::vec3 minimum = glm::max(info.minimum, _edit.minimum); + glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.maximum); + glm::vec3 size = maximum - minimum; + if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { + return false; // disjoint + } + float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); + if (volume >= 1.0f) { + info.outputValues[0] = _edit.value; + return false; // entirely contained + } + if (info.size <= _edit.granularity) { + if (volume >= 0.5f) { + info.outputValues[0] = _edit.value; + } + return false; // reached granularity limit; take best guess + } + return true; // subdivide +} + +void MetavoxelEdit::apply(MetavoxelDataPointer& data) const { + //data.detach(); + EditVisitor visitor(*this); + data->guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index e3550acaa5..9b711737e0 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -9,7 +9,9 @@ #ifndef __interface__MetavoxelMessages__ #define __interface__MetavoxelMessages__ +#include "AttributeRegistry.h" #include "Bitstream.h" +#include "MetavoxelData.h" /// A message containing the state of a client. class ClientStateMessage { @@ -35,9 +37,12 @@ class MetavoxelEdit { public: - glm::vec3 minimum; - glm::vec3 maximum; - float granularity; + STREAM glm::vec3 minimum; + STREAM glm::vec3 maximum; + STREAM float granularity; + STREAM OwnedAttributeValue value; + + void apply(MetavoxelDataPointer& data) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelEdit)