Working on sending edits.

This commit is contained in:
Andrzej Kapolka 2014-01-24 16:03:25 -08:00
parent f73f16b0fb
commit 7f20750e80
10 changed files with 172 additions and 79 deletions

View file

@ -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)

View file

@ -10,7 +10,6 @@
#include <SharedUtil.h>
#include <MetavoxelMessages.h>
#include <MetavoxelUtil.h>
#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() };

View file

@ -19,6 +19,7 @@
#include <DatagramSequencer.h>
#include <MetavoxelData.h>
#include <MetavoxelMessages.h>
#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);

View file

@ -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<AttributePointer>(), QVector<AttributePointer>() << 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 {

View file

@ -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;
}

View file

@ -25,6 +25,7 @@ class QObject;
class Attribute;
class AttributeValue;
class Bitstream;
class OwnedAttributeValue;
class TypeStreamer;
typedef QSharedPointer<Attribute> 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<class T> Bitstream& operator<<(const QList<T>& list);
template<class T> Bitstream& operator>>(QList<T>& list);

View file

@ -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<ReceiveRecord>::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) {

View file

@ -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<int> _offsetsReceived;
int _remainingBytes;
QList<HighPriorityMessage> _highPriorityMessages;
int _receivedHighPriorityMessages;
};
#endif /* defined(__interface__DatagramSequencer__) */

View file

@ -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<AttributePointer>(), QVector<AttributePointer>() << 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);
}

View file

@ -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)