From 36f1e59201d3056d67c7d375a86bc5221a085819 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Feb 2014 20:26:09 -0800 Subject: [PATCH 01/48] Working on support for sending messages through reliable channels. --- .../metavoxels/src/DatagramSequencer.cpp | 70 ++++++++++++++++--- libraries/metavoxels/src/DatagramSequencer.h | 16 +++++ tests/metavoxels/src/MetavoxelTests.cpp | 10 ++- 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index fcbe6b5e87..77f893ccb6 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -343,16 +343,32 @@ void CircularBuffer::remove(int length) { } QByteArray CircularBuffer::readBytes(int offset, int length) const { - // write in up to two segments + QByteArray bytes(length, 0); + readBytes(offset, length, bytes.data()); + return bytes; +} + +void CircularBuffer::readBytes(int offset, int length, char* data) const { + // read in up to two segments QByteArray array; int start = (_position + offset) % _data.size(); int firstSegment = qMin(length, _data.size() - start); - array.append(_data.constData() + start, firstSegment); + memcpy(data, _data.constData() + start, firstSegment); int secondSegment = length - firstSegment; if (secondSegment > 0) { - array.append(_data.constData(), secondSegment); + memcpy(data + firstSegment, _data.constData(), secondSegment); + } +} + +void CircularBuffer::writeBytes(int offset, int length, const char* data) { + // write in up to two segments + int start = (_position + offset) % _data.size(); + int firstSegment = qMin(length, _data.size() - start); + memcpy(_data.data() + start, data, firstSegment); + int secondSegment = length - firstSegment; + if (secondSegment > 0) { + memcpy(_data.data(), data + firstSegment, secondSegment); } - return array; } void CircularBuffer::writeToStream(int offset, int length, QDataStream& out) const { @@ -561,7 +577,14 @@ int ReliableChannel::getBytesAvailable() const { } void ReliableChannel::sendMessage(const QVariant& message) { + // write a placeholder for the length, then fill it in when we know what it is + int placeholder = _buffer.pos(); + _dataStream << (quint32)0; _bitstream << message; + _bitstream.flush(); + + quint32 length = _buffer.pos() - placeholder; + _buffer.writeBytes(placeholder, sizeof(quint32), (const char*)&length); } void ReliableChannel::sendClearSharedObjectMessage(int id) { @@ -576,7 +599,8 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o _bitstream(_dataStream), _priority(1.0f), _offset(0), - _writePosition(0) { + _writePosition(0), + _expectingMessage(true) { _buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); @@ -688,10 +712,32 @@ void ReliableChannel::readData(QDataStream& in) { readSome = true; } } + if (!readSome) { + return; + } - // let listeners know that there's data to read - if (readSome) { - emit _buffer.readyRead(); + forever { + // if we're expecting a message, peek into the buffer to see if we have the whole thing. + // if so, read it in, handle it, and loop back around in case there are more + if (_expectingMessage) { + int available = _buffer.bytesAvailable(); + if (available >= sizeof(quint32)) { + quint32 length; + _buffer.readBytes(_buffer.pos(), sizeof(quint32), (char*)&length); + if (available >= length) { + _dataStream.skipRawData(sizeof(quint32)); + QVariant message; + _bitstream >> message; + _bitstream.reset(); + handleMessage(message); + continue; + } + } + // otherwise, just let whoever's listening know that data is available + } else { + emit _buffer.readyRead(); + } + break; } // prune any read data from the buffer @@ -701,3 +747,11 @@ void ReliableChannel::readData(QDataStream& in) { } } +void ReliableChannel::handleMessage(const QVariant& message) { + if (message.userType() == ClearSharedObjectMessage::Type) { + _bitstream.clearSharedObject(message.value().id); + + } else { + emit receivedMessage(message); + } +} diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 27a4f05379..9adebfbfa4 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -185,6 +185,12 @@ public: /// Reads part of the data from the buffer. QByteArray readBytes(int offset, int length) const; + /// Reads part of the data from the buffer. + void readBytes(int offset, int length, char* data) const; + + /// Writes to part of the data in the buffer. + void writeBytes(int offset, int length, const char* data); + /// Writes part of the buffer to the supplied stream. void writeToStream(int offset, int length, QDataStream& out) const; @@ -267,8 +273,16 @@ public: int getBytesAvailable() const; + /// Sends a framed message on this channel. void sendMessage(const QVariant& message); + /// For input channels, sets whether the channel is expecting a framed message. + void setExpectingMessage(bool expectingMessage) { _expectingMessage = expectingMessage; } + +signals: + + void receivedMessage(const QVariant& message); + private slots: void sendClearSharedObjectMessage(int id); @@ -286,6 +300,7 @@ private: void spanAcknowledged(const DatagramSequencer::ChannelSpan& span); void readData(QDataStream& in); + void handleMessage(const QVariant& message); int _index; CircularBuffer _buffer; @@ -297,6 +312,7 @@ private: int _offset; int _writePosition; SpanList _acknowledged; + bool _expectingMessage; }; #endif /* defined(__interface__DatagramSequencer__) */ diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 15d7463742..49ff5714c0 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -83,8 +83,14 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) : connect(_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&))); connect(_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleHighPriorityMessage(const QVariant&))); - connect(&_sequencer->getReliableInputChannel()->getBuffer(), SIGNAL(readyRead()), SLOT(readReliableChannel())); - connect(&_sequencer->getReliableInputChannel(1)->getBuffer(), SIGNAL(readyRead()), SLOT(readLowPriorityReliableChannel())); + + ReliableChannel* firstInput = _sequencer->getReliableInputChannel(); + firstInput->setExpectingMessage(false); + connect(&firstInput->getBuffer(), SIGNAL(readyRead()), SLOT(readReliableChannel())); + + ReliableChannel* secondInput = _sequencer->getReliableInputChannel(1); + secondInput->setExpectingMessage(false); + connect(&secondInput->getBuffer(), SIGNAL(readyRead()), SLOT(readLowPriorityReliableChannel())); // enqueue a large amount of data in a low-priority channel ReliableChannel* output = _sequencer->getReliableOutputChannel(1); From f9b0ff0608bbffc91f89f71bff70840ca61bb25e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Feb 2014 12:33:40 -0800 Subject: [PATCH 02/48] Various bits of streaming work: send delete messages for main channel on reliable channel, test messages on main channel. --- .../metavoxels/src/DatagramSequencer.cpp | 56 ++++++++------ libraries/metavoxels/src/DatagramSequencer.h | 16 ++-- libraries/metavoxels/src/MetavoxelMessages.h | 11 +++ tests/metavoxels/src/MetavoxelTests.cpp | 75 ++++++++++--------- tests/metavoxels/src/MetavoxelTests.h | 9 ++- tools/mtc/src/main.cpp | 4 +- 6 files changed, 96 insertions(+), 75 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 77f893ccb6..a60d4e9df3 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -42,6 +42,7 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader, QObject* _outgoingDatagramStream.setByteOrder(QDataStream::LittleEndian); connect(&_outputStream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int))); + connect(this, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleHighPriorityMessage(const QVariant&))); memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); } @@ -182,7 +183,7 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { QVariant data; _inputStream >> data; if ((int)i >= _receivedHighPriorityMessages) { - handleHighPriorityMessage(data); + emit receivedHighPriorityMessage(data); } } _receivedHighPriorityMessages = highPriorityMessageCount; @@ -208,9 +209,22 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { } void DatagramSequencer::sendClearSharedObjectMessage(int id) { - // for now, high priority - ClearSharedObjectMessage message = { id }; - sendHighPriorityMessage(QVariant::fromValue(message)); + // send it low-priority unless the channel has messages disabled + ReliableChannel* channel = getReliableOutputChannel(); + if (channel->getMessagesEnabled()) { + ClearMainChannelSharedObjectMessage message = { id }; + channel->sendMessage(QVariant::fromValue(message)); + + } else { + ClearSharedObjectMessage message = { id }; + sendHighPriorityMessage(QVariant::fromValue(message)); + } +} + +void DatagramSequencer::handleHighPriorityMessage(const QVariant& data) { + if (data.userType() == ClearSharedObjectMessage::Type) { + _inputStream.clearSharedObject(data.value().id); + } } void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { @@ -303,15 +317,6 @@ void DatagramSequencer::sendPacket(const QByteArray& packet, const QVector().id); - - } else { - emit receivedHighPriorityMessage(data); - } -} - const int INITIAL_CIRCULAR_BUFFER_CAPACITY = 16; CircularBuffer::CircularBuffer(QObject* parent) : @@ -592,6 +597,16 @@ void ReliableChannel::sendClearSharedObjectMessage(int id) { sendMessage(QVariant::fromValue(message)); } +void ReliableChannel::handleMessage(const QVariant& message) { + if (message.userType() == ClearSharedObjectMessage::Type) { + _bitstream.clearSharedObject(message.value().id); + + } else if (message.userType() == ClearMainChannelSharedObjectMessage::Type) { + static_cast(parent())->_inputStream.clearSharedObject( + message.value().id); + } +} + ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool output) : QObject(sequencer), _index(index), @@ -600,12 +615,13 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o _priority(1.0f), _offset(0), _writePosition(0), - _expectingMessage(true) { + _messagesEnabled(true) { _buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); connect(&_bitstream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int))); + connect(this, SIGNAL(receivedMessage(const QVariant&)), SLOT(handleMessage(const QVariant&))); } void ReliableChannel::writeData(QDataStream& out, int bytes, QVector& spans) { @@ -719,7 +735,7 @@ void ReliableChannel::readData(QDataStream& in) { forever { // if we're expecting a message, peek into the buffer to see if we have the whole thing. // if so, read it in, handle it, and loop back around in case there are more - if (_expectingMessage) { + if (_messagesEnabled) { int available = _buffer.bytesAvailable(); if (available >= sizeof(quint32)) { quint32 length; @@ -729,7 +745,7 @@ void ReliableChannel::readData(QDataStream& in) { QVariant message; _bitstream >> message; _bitstream.reset(); - handleMessage(message); + emit receivedMessage(message); continue; } } @@ -747,11 +763,3 @@ void ReliableChannel::readData(QDataStream& in) { } } -void ReliableChannel::handleMessage(const QVariant& message) { - if (message.userType() == ClearSharedObjectMessage::Type) { - _bitstream.clearSharedObject(message.value().id); - - } else { - emit receivedMessage(message); - } -} diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 9adebfbfa4..7156175f51 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -94,6 +94,7 @@ signals: private slots: void sendClearSharedObjectMessage(int id); + void handleHighPriorityMessage(const QVariant& data); private: @@ -133,8 +134,6 @@ private: /// readyToWrite) as necessary. void sendPacket(const QByteArray& packet, const QVector& spans); - void handleHighPriorityMessage(const QVariant& data); - QList _sendRecords; QList _receiveRecords; @@ -273,12 +272,13 @@ public: int getBytesAvailable() const; + /// Sets whether we expect to write/read framed messages. + void setMessagesEnabled(bool enabled) { _messagesEnabled = enabled; } + bool getMessagesEnabled() const { return _messagesEnabled; } + /// Sends a framed message on this channel. void sendMessage(const QVariant& message); - /// For input channels, sets whether the channel is expecting a framed message. - void setExpectingMessage(bool expectingMessage) { _expectingMessage = expectingMessage; } - signals: void receivedMessage(const QVariant& message); @@ -286,7 +286,8 @@ signals: private slots: void sendClearSharedObjectMessage(int id); - + void handleMessage(const QVariant& message); + private: friend class DatagramSequencer; @@ -300,7 +301,6 @@ private: void spanAcknowledged(const DatagramSequencer::ChannelSpan& span); void readData(QDataStream& in); - void handleMessage(const QVariant& message); int _index; CircularBuffer _buffer; @@ -312,7 +312,7 @@ private: int _offset; int _writePosition; SpanList _acknowledged; - bool _expectingMessage; + bool _messagesEnabled; }; #endif /* defined(__interface__DatagramSequencer__) */ diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index c3cc78c5bc..1c547809fb 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -32,6 +32,17 @@ public: DECLARE_STREAMABLE_METATYPE(ClearSharedObjectMessage) +/// Clears the mapping for a shared object on the main channel (as opposed to the one on which the message was sent). +class ClearMainChannelSharedObjectMessage { + STREAMABLE + +public: + + STREAM int id; +}; + +DECLARE_STREAMABLE_METATYPE(ClearMainChannelSharedObjectMessage) + /// A message containing the state of a client. class ClientStateMessage { STREAMABLE diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 49ff5714c0..530f7e3108 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -22,10 +22,10 @@ static int highPriorityMessagesSent = 0; static int highPriorityMessagesReceived = 0; static int unreliableMessagesSent = 0; static int unreliableMessagesReceived = 0; +static int reliableMessagesSent = 0; +static int reliableMessagesReceived = 0; static int streamedBytesSent = 0; static int streamedBytesReceived = 0; -static int lowPriorityStreamedBytesSent = 0; -static int lowPriorityStreamedBytesReceived = 0; bool MetavoxelTests::run() { @@ -51,9 +51,8 @@ bool MetavoxelTests::run() { qDebug() << "Sent" << highPriorityMessagesSent << "high priority messages, received" << highPriorityMessagesReceived; qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived; + qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived; qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived; - qDebug() << "Sent" << lowPriorityStreamedBytesSent << "low-priority streamed bytes, received" << - lowPriorityStreamedBytesReceived; qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived; qDebug() << "All tests passed!"; @@ -77,30 +76,31 @@ static QByteArray createRandomBytes() { Endpoint::Endpoint(const QByteArray& datagramHeader) : _sequencer(new DatagramSequencer(datagramHeader, this)), - _highPriorityMessagesToSend(0.0f) { + _highPriorityMessagesToSend(0.0f), + _reliableMessagesToSend(0.0f) { connect(_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&))); connect(_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&))); connect(_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleHighPriorityMessage(const QVariant&))); - ReliableChannel* firstInput = _sequencer->getReliableInputChannel(); - firstInput->setExpectingMessage(false); - connect(&firstInput->getBuffer(), SIGNAL(readyRead()), SLOT(readReliableChannel())); + connect(_sequencer->getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)), + SLOT(handleReliableMessage(const QVariant&))); ReliableChannel* secondInput = _sequencer->getReliableInputChannel(1); - secondInput->setExpectingMessage(false); - connect(&secondInput->getBuffer(), SIGNAL(readyRead()), SLOT(readLowPriorityReliableChannel())); + secondInput->setMessagesEnabled(false); + connect(&secondInput->getBuffer(), SIGNAL(readyRead()), SLOT(readReliableChannel())); // enqueue a large amount of data in a low-priority channel ReliableChannel* output = _sequencer->getReliableOutputChannel(1); output->setPriority(0.25f); - const int MIN_LOW_PRIORITY_DATA = 100000; - const int MAX_LOW_PRIORITY_DATA = 200000; - QByteArray bytes = createRandomBytes(MIN_LOW_PRIORITY_DATA, MAX_LOW_PRIORITY_DATA); - _lowPriorityDataStreamed.append(bytes); + output->setMessagesEnabled(false); + const int MIN_STREAM_BYTES = 100000; + const int MAX_STREAM_BYTES = 200000; + QByteArray bytes = createRandomBytes(MIN_STREAM_BYTES, MAX_STREAM_BYTES); + _dataStreamed.append(bytes); output->getBuffer().write(bytes); - lowPriorityStreamedBytesSent += bytes.size(); + streamedBytesSent += bytes.size(); } static QVariant createRandomMessage() { @@ -166,13 +166,17 @@ bool Endpoint::simulate(int iterationNumber) { _highPriorityMessagesToSend -= 1.0f; } - // stream some random data - const int MIN_BYTES_TO_STREAM = 10; - const int MAX_BYTES_TO_STREAM = 100; - QByteArray bytes = createRandomBytes(MIN_BYTES_TO_STREAM, MAX_BYTES_TO_STREAM); - _dataStreamed.append(bytes); - streamedBytesSent += bytes.size(); - _sequencer->getReliableOutputChannel()->getDataStream().writeRawData(bytes.constData(), bytes.size()); + // and some number of reliable messages + const float MIN_RELIABLE_MESSAGES = 0.0f; + const float MAX_RELIABLE_MESSAGES = 4.0f; + _reliableMessagesToSend += randFloatInRange(MIN_RELIABLE_MESSAGES, MAX_RELIABLE_MESSAGES); + while (_reliableMessagesToSend >= 1.0f) { + QVariant message = createRandomMessage(); + _reliableMessagesSent.append(message); + _sequencer->getReliableOutputChannel()->sendMessage(message); + reliableMessagesSent++; + _reliableMessagesToSend -= 1.0f; + } // send a packet try { @@ -249,8 +253,19 @@ void Endpoint::readMessage(Bitstream& in) { throw QString("Received unsent/already sent unreliable message."); } +void Endpoint::handleReliableMessage(const QVariant& message) { + if (_other->_reliableMessagesSent.isEmpty()) { + throw QString("Received unsent/already sent reliable message."); + } + QVariant sentMessage = _other->_reliableMessagesSent.takeFirst(); + if (!messagesEqual(message, sentMessage)) { + throw QString("Sent/received reliable message mismatch."); + } + reliableMessagesReceived++; +} + void Endpoint::readReliableChannel() { - CircularBuffer& buffer = _sequencer->getReliableInputChannel()->getBuffer(); + CircularBuffer& buffer = _sequencer->getReliableInputChannel(1)->getBuffer(); QByteArray bytes = buffer.read(buffer.bytesAvailable()); if (_other->_dataStreamed.size() < bytes.size()) { throw QString("Received unsent/already sent streamed data."); @@ -262,17 +277,3 @@ void Endpoint::readReliableChannel() { } streamedBytesReceived += bytes.size(); } - -void Endpoint::readLowPriorityReliableChannel() { - CircularBuffer& buffer = _sequencer->getReliableInputChannel(1)->getBuffer(); - QByteArray bytes = buffer.read(buffer.bytesAvailable()); - if (_other->_lowPriorityDataStreamed.size() < bytes.size()) { - throw QString("Received unsent/already sent low-priority streamed data."); - } - QByteArray compare = _other->_lowPriorityDataStreamed.readBytes(0, bytes.size()); - _other->_lowPriorityDataStreamed.remove(bytes.size()); - if (compare != bytes) { - throw QString("Sent/received low-priority streamed data mismatch."); - } - lowPriorityStreamedBytesReceived += bytes.size(); -} diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index b73f7eb07e..f19870ac15 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -48,9 +48,9 @@ private slots: void sendDatagram(const QByteArray& datagram); void handleHighPriorityMessage(const QVariant& message); void readMessage(Bitstream& in); + void handleReliableMessage(const QVariant& message); void readReliableChannel(); - void readLowPriorityReliableChannel(); - + private: DatagramSequencer* _sequencer; @@ -59,8 +59,9 @@ private: float _highPriorityMessagesToSend; QVariantList _highPriorityMessagesSent; QList _unreliableMessagesSent; + float _reliableMessagesToSend; + QVariantList _reliableMessagesSent; CircularBuffer _dataStreamed; - CircularBuffer _lowPriorityDataStreamed; }; /// A simple test message. @@ -88,7 +89,7 @@ public: DECLARE_STREAMABLE_METATYPE(TestMessageB) // A test message that demonstrates inheritance and composition. -class TestMessageC : public TestMessageA { +class TestMessageC : STREAM public TestMessageA { STREAMABLE public: diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index 050fe0e418..248c2ddd2d 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -121,7 +121,7 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << " &&\n"; out << " "; } - out << "static_cast<" << base << "&>(first) == static_cast<" << base << "&>(second)"; + out << "static_cast(first) == static_cast(second)"; first = false; } foreach (const QString& field, str.fields) { @@ -147,7 +147,7 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << " ||\n"; out << " "; } - out << "static_cast<" << base << "&>(first) != static_cast<" << base << "&>(second)"; + out << "static_cast(first) != static_cast(second)"; first = false; } foreach (const QString& field, str.fields) { From d6d95a586d6da16b68eec21fa54210ba4720d6c0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Feb 2014 14:16:14 -0800 Subject: [PATCH 03/48] Working on deleting attributes. --- interface/src/ui/MetavoxelEditor.cpp | 22 +++- interface/src/ui/MetavoxelEditor.h | 5 +- libraries/metavoxels/src/AttributeRegistry.h | 4 +- libraries/metavoxels/src/Bitstream.h | 2 +- libraries/metavoxels/src/SharedObject.cpp | 56 ---------- libraries/metavoxels/src/SharedObject.h | 110 ++++++++++++++----- 6 files changed, 104 insertions(+), 95 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index c97832d791..e37669e907 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -45,12 +45,19 @@ MetavoxelEditor::MetavoxelEditor() : attributeGroup->setLayout(attributeLayout); attributeLayout->addWidget(_attributes = new QListWidget()); - connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(updateValueEditor())); + connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(selectedAttributeChanged())); + + QHBoxLayout* attributeButtonLayout = new QHBoxLayout(); + attributeLayout->addLayout(attributeButtonLayout); QPushButton* newAttribute = new QPushButton("New..."); - attributeLayout->addWidget(newAttribute); + attributeButtonLayout->addWidget(newAttribute); connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute())); + attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete")); + _deleteAttribute->setEnabled(false); + connect(_deleteAttribute, SIGNAL(clicked()), SLOT(deleteSelectedAttribute())); + QFormLayout* formLayout = new QFormLayout(); topLayout->addLayout(formLayout); @@ -141,19 +148,20 @@ bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { return false; } -void MetavoxelEditor::updateValueEditor() { +void MetavoxelEditor::selectedAttributeChanged() { QString selected = getSelectedAttribute(); if (selected.isNull()) { + _deleteAttribute->setEnabled(false); _value->setVisible(false); return; } + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); + _value->setVisible(true); if (_valueArea->widget()) { delete _valueArea->widget(); } - - AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); QWidget* editor = attribute->createEditor(); if (editor) { _valueArea->setWidget(editor); @@ -188,6 +196,10 @@ void MetavoxelEditor::createNewAttribute() { updateAttributes(nameText); } +void MetavoxelEditor::deleteSelectedAttribute() { + +} + void MetavoxelEditor::centerGridPosition() { const float CENTER_OFFSET = 0.625f; float eyePosition = (glm::inverse(getGridRotation()) * Application::getInstance()->getCamera()->getPosition()).z - diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 9024837757..5e941f3540 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -17,6 +17,7 @@ class QComboBox; class QDoubleSpinBox; class QGroupBox; class QListWidget; +class QPushButton; class QScrollArea; /// Allows editing metavoxels. @@ -31,8 +32,9 @@ public: private slots: - void updateValueEditor(); + void selectedAttributeChanged(); void createNewAttribute(); + void deleteSelectedAttribute(); void centerGridPosition(); void alignGridPosition(); @@ -49,6 +51,7 @@ private: QVariant getValue() const; QListWidget* _attributes; + QPushButton* _deleteAttribute; QComboBox* _gridPlane; QDoubleSpinBox* _gridSpacing; QDoubleSpinBox* _gridPosition; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 56cff7eeb4..431fc59fe2 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -24,7 +24,7 @@ class QScriptValue; class Attribute; -typedef QSharedPointer AttributePointer; +typedef SharedObjectPointerTemplate AttributePointer; /// Maintains information about metavoxel attribute types. class AttributeRegistry { @@ -141,7 +141,7 @@ public: }; /// Represents a registered attribute. -class Attribute : public QObject { +class Attribute : public SharedObject { Q_OBJECT public: diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index cc776a742a..87ee66c661 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -31,7 +31,7 @@ class Bitstream; class OwnedAttributeValue; class TypeStreamer; -typedef QSharedPointer AttributePointer; +typedef SharedObjectPointerTemplate AttributePointer; /// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that /// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index f97e285bcf..0b93826aa5 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -72,62 +72,6 @@ bool SharedObject::equals(const SharedObject* other) const { return true; } -SharedObjectPointer::SharedObjectPointer(SharedObject* data) : _data(data) { - if (_data) { - _data->incrementReferenceCount(); - } -} - -SharedObjectPointer::SharedObjectPointer(const SharedObjectPointer& other) : _data(other._data) { - if (_data) { - _data->incrementReferenceCount(); - } -} - -SharedObjectPointer::~SharedObjectPointer() { - if (_data) { - _data->decrementReferenceCount(); - } -} - -void SharedObjectPointer::detach() { - if (_data && _data->getReferenceCount() > 1) { - _data->decrementReferenceCount(); - (_data = _data->clone())->incrementReferenceCount(); - } -} - -void SharedObjectPointer::reset() { - if (_data) { - _data->decrementReferenceCount(); - } - _data = NULL; -} - -SharedObjectPointer& SharedObjectPointer::operator=(SharedObject* data) { - if (_data) { - _data->decrementReferenceCount(); - } - if ((_data = data)) { - _data->incrementReferenceCount(); - } - return *this; -} - -SharedObjectPointer& SharedObjectPointer::operator=(const SharedObjectPointer& other) { - if (_data) { - _data->decrementReferenceCount(); - } - if ((_data = other._data)) { - _data->incrementReferenceCount(); - } - return *this; -} - -uint qHash(const SharedObjectPointer& pointer, uint seed) { - return qHash(pointer.data(), seed); -} - SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); layout->setAlignment(Qt::AlignTop); diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index e439c4c7f0..fedbeb50d8 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -44,49 +44,99 @@ private: }; /// A pointer to a shared object. -class SharedObjectPointer { +template class SharedObjectPointerTemplate { public: - SharedObjectPointer(SharedObject* data = NULL); - SharedObjectPointer(const SharedObjectPointer& other); - ~SharedObjectPointer(); - - SharedObject* data() { return _data; } - const SharedObject* data() const { return _data; } - const SharedObject* constData() const { return _data; } + SharedObjectPointerTemplate(T* data = NULL); + SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other); + ~SharedObjectPointerTemplate(); + T* data() const { return _data; } + void detach(); - void swap(SharedObjectPointer& other) { qSwap(_data, other._data); } + void swap(SharedObjectPointerTemplate& other) { qSwap(_data, other._data); } void reset(); - operator SharedObject*() { return _data; } - operator const SharedObject*() const { return _data; } - bool operator!() const { return !_data; } - - bool operator!=(const SharedObjectPointer& other) const { return _data != other._data; } - - SharedObject& operator*() { return *_data; } - const SharedObject& operator*() const { return *_data; } - - SharedObject* operator->() { return _data; } - const SharedObject* operator->() const { return _data; } - - SharedObjectPointer& operator=(SharedObject* data); - SharedObjectPointer& operator=(const SharedObjectPointer& other); - - bool operator==(const SharedObjectPointer& other) const { return _data == other._data; } - -private: + operator T*() const { return _data; } + T& operator*() const { return *_data; } + T* operator->() const { return _data; } - SharedObject* _data; + SharedObjectPointerTemplate& operator=(T* data); + SharedObjectPointerTemplate& operator=(const SharedObjectPointerTemplate& other); + + bool operator==(const SharedObjectPointerTemplate& other) const { return _data == other._data; } + bool operator!=(const SharedObjectPointerTemplate& other) const { return _data != other._data; } + +private: + + T* _data; }; -Q_DECLARE_METATYPE(SharedObjectPointer) +template inline SharedObjectPointerTemplate::SharedObjectPointerTemplate(T* data) : _data(data) { + if (_data) { + _data->incrementReferenceCount(); + } +} -uint qHash(const SharedObjectPointer& pointer, uint seed = 0); +template inline SharedObjectPointerTemplate::SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other) : + _data(other._data) { + + if (_data) { + _data->incrementReferenceCount(); + } +} + +template inline SharedObjectPointerTemplate::~SharedObjectPointerTemplate() { + if (_data) { + _data->decrementReferenceCount(); + } +} + +template inline void SharedObjectPointerTemplate::detach() { + if (_data && _data->getReferenceCount() > 1) { + _data->decrementReferenceCount(); + (_data = _data->clone())->incrementReferenceCount(); + } +} + +template inline void SharedObjectPointerTemplate::reset() { + if (_data) { + _data->decrementReferenceCount(); + } + _data = NULL; +} + +template inline SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=(T* data) { + if (_data) { + _data->decrementReferenceCount(); + } + if ((_data = data)) { + _data->incrementReferenceCount(); + } + return *this; +} + +template inline SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=( + const SharedObjectPointerTemplate& other) { + if (_data) { + _data->decrementReferenceCount(); + } + if ((_data = other._data)) { + _data->incrementReferenceCount(); + } + return *this; +} + +template uint qHash(const SharedObjectPointerTemplate& pointer, uint seed = 0) { + return qHash(pointer.data(), seed); +} + +typedef SharedObjectPointerTemplate SharedObjectPointer; + +Q_DECLARE_METATYPE(SharedObjectPointer) /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { From 5b207e4f8e90c6fee98091429fc93bc9ac095ed5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Feb 2014 17:10:32 -0800 Subject: [PATCH 04/48] Work on creating new attributes. --- interface/src/ui/MetavoxelEditor.cpp | 17 +++++++--- .../metavoxels/src/AttributeRegistry.cpp | 4 +++ libraries/metavoxels/src/AttributeRegistry.h | 6 +++- libraries/metavoxels/src/Bitstream.cpp | 4 +-- libraries/metavoxels/src/MetavoxelUtil.cpp | 31 +++++++++++++++++++ libraries/metavoxels/src/MetavoxelUtil.h | 28 +++++++++++++++++ libraries/metavoxels/src/SharedObject.cpp | 13 ++++++-- libraries/metavoxels/src/SharedObject.h | 14 +++++++-- 8 files changed, 103 insertions(+), 14 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index e37669e907..9012248484 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -155,14 +155,13 @@ void MetavoxelEditor::selectedAttributeChanged() { _value->setVisible(false); return; } - AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); - + _deleteAttribute->setEnabled(true); _value->setVisible(true); if (_valueArea->widget()) { delete _valueArea->widget(); } - QWidget* editor = attribute->createEditor(); + QWidget* editor = AttributeRegistry::getInstance()->getAttribute(selected)->createEditor(); if (editor) { _valueArea->setWidget(editor); } @@ -181,6 +180,10 @@ void MetavoxelEditor::createNewAttribute() { QLineEdit name; form.addRow("Name:", &name); + SharedObjectEditor editor(&Attribute::staticMetaObject, false); + editor.setObject(new QRgbAttribute()); + layout.addWidget(&editor); + QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); dialog.connect(&buttons, SIGNAL(accepted()), SLOT(accept())); dialog.connect(&buttons, SIGNAL(rejected()), SLOT(reject())); @@ -191,13 +194,17 @@ void MetavoxelEditor::createNewAttribute() { return; } QString nameText = name.text().trimmed(); - AttributeRegistry::getInstance()->registerAttribute(new QRgbAttribute(nameText)); + SharedObjectPointer attribute = editor.getObject(); + attribute->setObjectName(nameText); + AttributeRegistry::getInstance()->registerAttribute(attribute.staticCast()); updateAttributes(nameText); } void MetavoxelEditor::deleteSelectedAttribute() { - + AttributeRegistry::getInstance()->deregisterAttribute(getSelectedAttribute()); + _attributes->selectionModel()->clear(); + updateAttributes(); } void MetavoxelEditor::centerGridPosition() { diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9595d96e1f..8483174dac 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -53,6 +53,10 @@ AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute return pointer; } +void AttributeRegistry::deregisterAttribute(const QString& name) { + _attributes.remove(name); +} + QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) { return engine->newQObject(getInstance()->getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 431fc59fe2..1235cefbb5 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -48,6 +48,9 @@ public: /// attribute AttributePointer registerAttribute(AttributePointer attribute); + /// Deregisters an attribute. + void deregisterAttribute(const QString& name); + /// Retrieves an attribute by name. AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); } @@ -260,7 +263,8 @@ class SharedObjectAttribute : public InlineAttribute { public: - Q_INVOKABLE SharedObjectAttribute(const QString& name = QString(), const QMetaObject* metaObject = NULL, + Q_INVOKABLE SharedObjectAttribute(const QString& name = QString(), + const QMetaObject* metaObject = &SharedObject::staticMetaObject, const SharedObjectPointer& defaultValue = SharedObjectPointer()); virtual void read(Bitstream& in, void*& value, bool isLeaf) const; diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 262f2df7f5..61e300eae5 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -68,8 +68,8 @@ IDStreamer& IDStreamer::operator>>(int& value) { int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { getMetaObjects().insert(className, metaObject); - // register it as a subclass of all of its superclasses - for (const QMetaObject* superClass = metaObject->superClass(); superClass != NULL; superClass = superClass->superClass()) { + // register it as a subclass of itself and all of its superclasses + for (const QMetaObject* superClass = metaObject; superClass != NULL; superClass = superClass->superClass()) { getMetaObjectSubClasses().insert(superClass, metaObject); } return 0; diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index d6b42b11fb..8b33416dc3 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -104,6 +105,12 @@ static QItemEditorCreatorBase* createDoubleEditorCreator() { return creator; } +static QItemEditorCreatorBase* createQMetaObjectEditorCreator() { + QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + static QItemEditorCreatorBase* createQColorEditorCreator() { QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); @@ -123,6 +130,7 @@ static QItemEditorCreatorBase* createParameterizedURLEditorCreator() { } static QItemEditorCreatorBase* doubleEditorCreator = createDoubleEditorCreator(); +static QItemEditorCreatorBase* qMetaObjectEditorCreator = createQMetaObjectEditorCreator(); static QItemEditorCreatorBase* qColorEditorCreator = createQColorEditorCreator(); static QItemEditorCreatorBase* vec3EditorCreator = createVec3EditorCreator(); static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator(); @@ -153,6 +161,29 @@ bool Box::contains(const Box& other) const { other.minimum.z >= minimum.z && other.maximum.z <= maximum.z; } +QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) { + QVBoxLayout* layout = new QVBoxLayout(); + layout->setContentsMargins(QMargins()); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + layout->addWidget(_box = new QComboBox()); + connect(_box, SIGNAL(currentIndexChanged(int)), SLOT(updateMetaObject())); + + foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(&SharedObject::staticMetaObject)) { + _box->addItem(metaObject->className(), QVariant::fromValue(metaObject)); + } +} + +void QMetaObjectEditor::setMetaObject(const QMetaObject* metaObject) { + _metaObject = metaObject; + _box->setCurrentIndex(_metaObject ? _box->findText(_metaObject->className()) : -1); +} + +void QMetaObjectEditor::updateMetaObject() { + int index = _box->currentIndex(); + emit metaObjectChanged(_metaObject = (index == -1) ? NULL : _box->itemData(index).value()); +} + QColorEditor::QColorEditor(QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(QMargins()); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 7cfedd7a62..49039090a0 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -21,6 +21,7 @@ #include "Bitstream.h" class QByteArray; +class QComboBox; class QDoubleSpinBox; class QLineEdit; class QPushButton; @@ -50,6 +51,33 @@ public: DECLARE_STREAMABLE_METATYPE(Box) +/// Editor for meta-object values. +class QMetaObjectEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(const QMetaObject* metaObject MEMBER _metaObject WRITE setMetaObject NOTIFY metaObjectChanged USER true) + +public: + + QMetaObjectEditor(QWidget* parent); + +signals: + + void metaObjectChanged(const QMetaObject* metaObject); + +public slots: + + void setMetaObject(const QMetaObject* metaObject); + +private slots: + + void updateMetaObject(); + +private: + + QComboBox* _box; + const QMetaObject* _metaObject; +}; + /// Editor for color values. class QColorEditor : public QWidget { Q_OBJECT diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 0b93826aa5..37f7be54c7 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -16,6 +16,8 @@ #include "MetavoxelUtil.h" #include "SharedObject.h" +REGISTER_META_OBJECT(SharedObject) + SharedObject::SharedObject() : _referenceCount(0) { } @@ -72,7 +74,7 @@ bool SharedObject::equals(const SharedObject* other) const { return true; } -SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent) : QWidget(parent) { +SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nullable, QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); layout->setAlignment(Qt::AlignTop); setLayout(layout); @@ -81,9 +83,14 @@ SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* p layout->addLayout(form); form->addRow("Type:", _type = new QComboBox()); - _type->addItem("(none)"); + if (nullable) { + _type->addItem("(none)"); + } foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) { - _type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); + // add add constructable subclasses + if (metaObject->constructorCount() > 0) { + _type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); + } } connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); } diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index fedbeb50d8..b62b88d2ee 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -21,7 +21,7 @@ class SharedObject : public QObject { public: - SharedObject(); + Q_INVOKABLE SharedObject(); int getReferenceCount() const { return _referenceCount; } void incrementReferenceCount(); @@ -64,6 +64,8 @@ public: T& operator*() const { return *_data; } T* operator->() const { return _data; } + template SharedObjectPointerTemplate staticCast() const; + SharedObjectPointerTemplate& operator=(T* data); SharedObjectPointerTemplate& operator=(const SharedObjectPointerTemplate& other); @@ -109,6 +111,10 @@ template inline void SharedObjectPointerTemplate::reset() { _data = NULL; } +template template inline SharedObjectPointerTemplate SharedObjectPointerTemplate::staticCast() const { + return SharedObjectPointerTemplate(static_cast(_data)); +} + template inline SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=(T* data) { if (_data) { _data->decrementReferenceCount(); @@ -141,11 +147,13 @@ Q_DECLARE_METATYPE(SharedObjectPointer) /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { Q_OBJECT - Q_PROPERTY(SharedObjectPointer object MEMBER _object WRITE setObject USER true) + Q_PROPERTY(SharedObjectPointer object READ getObject WRITE setObject USER true) public: - SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent); + SharedObjectEditor(const QMetaObject* metaObject, bool nullable = true, QWidget* parent = NULL); + + const SharedObjectPointer& getObject() const { return _object; } public slots: From bb56e2847f35c384f5423d52122b5c86056deb98 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Feb 2014 20:00:07 -0800 Subject: [PATCH 05/48] Global edits, edits use virtual function. --- cmake/macros/AutoMTC.cmake | 15 +- interface/src/ui/MetavoxelEditor.cpp | 343 +++++++++++------- interface/src/ui/MetavoxelEditor.h | 74 +++- libraries/metavoxels/CMakeLists.txt | 6 +- libraries/metavoxels/src/MetavoxelData.cpp | 3 +- .../metavoxels/src/MetavoxelMessages.cpp | 57 ++- libraries/metavoxels/src/MetavoxelMessages.h | 46 ++- libraries/metavoxels/src/MetavoxelUtil.cpp | 4 + libraries/metavoxels/src/MetavoxelUtil.h | 2 + tests/metavoxels/CMakeLists.txt | 6 +- 10 files changed, 381 insertions(+), 175 deletions(-) diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake index f0c9ebc6a0..f29c3400bb 100644 --- a/cmake/macros/AutoMTC.cmake +++ b/cmake/macros/AutoMTC.cmake @@ -3,20 +3,11 @@ macro(AUTO_MTC TARGET ROOT_DIR) add_subdirectory(${ROOT_DIR}/tools/mtc ${ROOT_DIR}/tools/mtc) endif (NOT TARGET mtc) + set(AUTOMTC_SRC ${TARGET}_automtc.cpp) + file(GLOB INCLUDE_FILES src/*.h) - add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp - ${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 Script Widgets) - - target_link_libraries(${TARGET} ${TARGET}_automtc) + add_custom_command(OUTPUT ${AUTOMTC_SRC} COMMAND mtc -o ${AUTOMTC_SRC} ${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES}) endmacro() diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 9012248484..0ba0b08633 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -81,6 +81,9 @@ MetavoxelEditor::MetavoxelEditor() : alignGridPosition(); centerGridPosition(); + formLayout->addRow("Tool:", _toolBox = new QComboBox()); + connect(_toolBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTool())); + _value = new QGroupBox(); _value->setTitle("Value"); topLayout->addWidget(_value); @@ -91,14 +94,20 @@ MetavoxelEditor::MetavoxelEditor() : valueLayout->addWidget(_valueArea = new QScrollArea()); _valueArea->setWidgetResizable(true); + BoxSetTool* boxSetTool = new BoxSetTool(this); + topLayout->addWidget(boxSetTool); + _toolBox->addItem("Set Value (Box)", QVariant::fromValue(boxSetTool)); + + GlobalSetTool* globalSetTool = new GlobalSetTool(this); + topLayout->addWidget(globalSetTool); + _toolBox->addItem("Set Value (Global)", QVariant::fromValue(globalSetTool)); + updateAttributes(); connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); Application::getInstance()->getGLWidget()->installEventFilter(this); - resetState(); - show(); if (_gridProgram.isLinked()) { @@ -109,43 +118,42 @@ MetavoxelEditor::MetavoxelEditor() : _gridProgram.link(); } -bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { - switch (_state) { - case HOVERING_STATE: - if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) { - _state = DRAGGING_STATE; - return true; - } - break; +QString MetavoxelEditor::getSelectedAttribute() const { + QList selectedItems = _attributes->selectedItems(); + return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); +} + +double MetavoxelEditor::getGridSpacing() const { + return pow(2.0, _gridSpacing->value()); +} + +double MetavoxelEditor::getGridPosition() const { + return _gridPosition->value(); +} + +glm::quat MetavoxelEditor::getGridRotation() const { + // for simplicity, we handle the other two planes by rotating them onto X/Y and performing computation there + switch (_gridPlane->currentIndex()) { + case GRID_PLANE_XY: + return glm::quat(); - case DRAGGING_STATE: - if (event->type() == QEvent::MouseButtonRelease) { - _state = RAISING_STATE; - return true; - } - break; + case GRID_PLANE_XZ: + return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f); - case RAISING_STATE: - if (event->type() == QEvent::MouseButtonPress) { - if (_height != 0) { - // find the start and end corners in X/Y - float base = _gridPosition->value(); - float top = base + _height; - glm::quat rotation = getGridRotation(); - glm::vec3 start = rotation * glm::vec3(glm::min(_startPosition, _endPosition), glm::min(base, top)); - float spacing = getGridSpacing(); - glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) + - glm::vec2(spacing, spacing), glm::max(base, top)); - - // find the minimum and maximum extents after rotation - applyValue(glm::min(start, end), glm::max(start, end)); - } - resetState(); - return true; - } - break; + case GRID_PLANE_YZ: + default: + return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f); } - return false; +} + +QVariant MetavoxelEditor::getValue() const { + QWidget* editor = _valueArea->widget(); + return editor ? editor->metaObject()->userProperty().read(editor) : QVariant(); +} + +bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { + // pass along to the active tool + return getActiveTool()->eventFilter(watched, event); } void MetavoxelEditor::selectedAttributeChanged() { @@ -222,13 +230,15 @@ void MetavoxelEditor::alignGridPosition() { _gridPosition->setValue(step * floor(_gridPosition->value() / step)); } -void MetavoxelEditor::render() { - QString selected = getSelectedAttribute(); - if (selected.isNull()) { - resetState(); - return; +void MetavoxelEditor::updateTool() { + for (int i = 0; i < _toolBox->count(); i++) { + _toolBox->itemData(i).value()->setVisible(i == _toolBox->currentIndex()); } +} +const float GRID_BRIGHTNESS = 0.5f; + +void MetavoxelEditor::render() { glDisable(GL_LIGHTING); glDepthMask(GL_FALSE); @@ -238,85 +248,22 @@ void MetavoxelEditor::render() { glm::vec3 axis = glm::axis(rotation); glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); - glm::quat inverseRotation = glm::inverse(rotation); - glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); - glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); - float spacing = getGridSpacing(); - float position = _gridPosition->value(); - if (_state == RAISING_STATE) { - // find the plane at the mouse position, orthogonal to the plane, facing the eye position - glLineWidth(4.0f); - glm::vec3 eyePosition = inverseRotation * Application::getInstance()->getViewFrustum()->getOffsetPosition(); - glm::vec3 mousePoint = glm::vec3(_mousePosition, position); - glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint); - glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f)); - float divisor = glm::dot(normal, rayDirection); - if (fabs(divisor) > EPSILON) { - float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor; - float projection = rayOrigin.z + distance * rayDirection.z; - _height = spacing * roundf(projection / spacing) - position; - } - } else if (fabs(rayDirection.z) > EPSILON) { - // find the intersection of the rotated mouse ray with the plane - float distance = (position - rayOrigin.z) / rayDirection.z; - _mousePosition = glm::vec2(rayOrigin + rayDirection * distance); - glm::vec2 snappedPosition = spacing * glm::floor(_mousePosition / spacing); - - if (_state == HOVERING_STATE) { - _startPosition = _endPosition = snappedPosition; - glLineWidth(2.0f); - - } else if (_state == DRAGGING_STATE) { - _endPosition = snappedPosition; - glLineWidth(4.0f); - } - } else { - // cancel any operation in progress - resetState(); - } - - const float GRID_BRIGHTNESS = 0.5f; - if (_startPosition != INVALID_VECTOR) { - glm::vec2 minimum = glm::min(_startPosition, _endPosition); - glm::vec2 maximum = glm::max(_startPosition, _endPosition); - - glPushMatrix(); - glTranslatef(minimum.x, minimum.y, position); - glScalef(maximum.x + spacing - minimum.x, maximum.y + spacing - minimum.y, _height); - - glTranslatef(0.5f, 0.5f, 0.5f); - if (_state != HOVERING_STATE) { - const float BOX_ALPHA = 0.25f; - QColor color = getValue().value(); - if (color.isValid()) { - glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA); - } else { - glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA); - } - glEnable(GL_CULL_FACE); - glutSolidCube(1.0); - glDisable(GL_CULL_FACE); - } - glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); - glutWireCube(1.0); - - glPopMatrix(); - - } else { - glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); - } + getActiveTool()->render(); glLineWidth(1.0f); // center the grid around the camera position on the plane - glm::vec3 rotated = inverseRotation * Application::getInstance()->getCamera()->getPosition(); + glm::vec3 rotated = glm::inverse(rotation) * Application::getInstance()->getCamera()->getPosition(); + float spacing = getGridSpacing(); const int GRID_DIVISIONS = 300; glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position); + spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), _gridPosition->value()); float scale = GRID_DIVISIONS * spacing; glScalef(scale, scale, scale); + glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); + _gridProgram.bind(); Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); @@ -348,49 +295,173 @@ void MetavoxelEditor::updateAttributes(const QString& select) { } } -QString MetavoxelEditor::getSelectedAttribute() const { - QList selectedItems = _attributes->selectedItems(); - return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); +MetavoxelTool* MetavoxelEditor::getActiveTool() const { + return static_cast(_toolBox->itemData(_toolBox->currentIndex()).value()); } -double MetavoxelEditor::getGridSpacing() const { - return pow(2.0, _gridSpacing->value()); +ProgramObject MetavoxelEditor::_gridProgram; + +MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor) : + _editor(editor) { + + QVBoxLayout* layout = new QVBoxLayout(); + setLayout(layout); + + setVisible(false); } -glm::quat MetavoxelEditor::getGridRotation() const { - // for simplicity, we handle the other two planes by rotating them onto X/Y and performing computation there - switch (_gridPlane->currentIndex()) { - case GRID_PLANE_XY: - return glm::quat(); +void MetavoxelTool::render() { + // nothing by default +} + +BoxSetTool::BoxSetTool(MetavoxelEditor* editor) : + MetavoxelTool(editor) { + + resetState(); +} + +void BoxSetTool::render() { + QString selected = _editor->getSelectedAttribute(); + if (selected.isNull()) { + resetState(); + return; + } + glm::quat rotation = _editor->getGridRotation(); + glm::quat inverseRotation = glm::inverse(rotation); + glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); + glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); + float spacing = _editor->getGridSpacing(); + float position = _editor->getGridPosition(); + if (_state == RAISING_STATE) { + // find the plane at the mouse position, orthogonal to the plane, facing the eye position + glLineWidth(4.0f); + glm::vec3 eyePosition = inverseRotation * Application::getInstance()->getViewFrustum()->getOffsetPosition(); + glm::vec3 mousePoint = glm::vec3(_mousePosition, position); + glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint); + glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f)); + float divisor = glm::dot(normal, rayDirection); + if (fabs(divisor) > EPSILON) { + float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor; + float projection = rayOrigin.z + distance * rayDirection.z; + _height = spacing * roundf(projection / spacing) - position; + } + } else if (fabs(rayDirection.z) > EPSILON) { + // find the intersection of the rotated mouse ray with the plane + float distance = (position - rayOrigin.z) / rayDirection.z; + _mousePosition = glm::vec2(rayOrigin + rayDirection * distance); + glm::vec2 snappedPosition = spacing * glm::floor(_mousePosition / spacing); + + if (_state == HOVERING_STATE) { + _startPosition = _endPosition = snappedPosition; + glLineWidth(2.0f); - case GRID_PLANE_XZ: - return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f); - - case GRID_PLANE_YZ: - default: - return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f); + } else if (_state == DRAGGING_STATE) { + _endPosition = snappedPosition; + glLineWidth(4.0f); + } + } else { + // cancel any operation in progress + resetState(); + } + + if (_startPosition != INVALID_VECTOR) { + glm::vec2 minimum = glm::min(_startPosition, _endPosition); + glm::vec2 maximum = glm::max(_startPosition, _endPosition); + + glPushMatrix(); + glTranslatef(minimum.x, minimum.y, position); + glScalef(maximum.x + spacing - minimum.x, maximum.y + spacing - minimum.y, _height); + + glTranslatef(0.5f, 0.5f, 0.5f); + if (_state != HOVERING_STATE) { + const float BOX_ALPHA = 0.25f; + QColor color = _editor->getValue().value(); + if (color.isValid()) { + glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA); + } else { + glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA); + } + glEnable(GL_CULL_FACE); + glutSolidCube(1.0); + glDisable(GL_CULL_FACE); + } + glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); + glutWireCube(1.0); + + glPopMatrix(); } } -void MetavoxelEditor::resetState() { +bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) { + switch (_state) { + case HOVERING_STATE: + if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) { + _state = DRAGGING_STATE; + return true; + } + break; + + case DRAGGING_STATE: + if (event->type() == QEvent::MouseButtonRelease) { + _state = RAISING_STATE; + return true; + } + break; + + case RAISING_STATE: + if (event->type() == QEvent::MouseButtonPress) { + if (_height != 0) { + // find the start and end corners in X/Y + float base = _editor->getGridPosition(); + float top = base + _height; + glm::quat rotation = _editor->getGridRotation(); + glm::vec3 start = rotation * glm::vec3(glm::min(_startPosition, _endPosition), glm::min(base, top)); + float spacing = _editor->getGridSpacing(); + glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) + + glm::vec2(spacing, spacing), glm::max(base, top)); + + // find the minimum and maximum extents after rotation + applyValue(glm::min(start, end), glm::max(start, end)); + } + resetState(); + return true; + } + break; + } + return false; +} + +void BoxSetTool::resetState() { _state = HOVERING_STATE; _startPosition = INVALID_VECTOR; _height = 0.0f; } -void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { - AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(getSelectedAttribute()); +void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); if (!attribute) { return; } - OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue())); - MetavoxelEditMessage edit = { { minimum, maximum }, getGridSpacing(), value }; - Application::getInstance()->getMetavoxels()->applyEdit(edit); + OwnedAttributeValue value(attribute, attribute->createFromVariant(_editor->getValue())); + MetavoxelEditMessage message = { QVariant::fromValue(BoxSetEdit(Box(minimum, maximum), + _editor->getGridSpacing(), value)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message); } -QVariant MetavoxelEditor::getValue() const { - QWidget* editor = _valueArea->widget(); - return editor ? editor->metaObject()->userProperty().read(editor) : QVariant(); +GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) : + MetavoxelTool(editor) { + + QPushButton* button = new QPushButton("Apply"); + layout()->addWidget(button); + connect(button, SIGNAL(clicked()), SLOT(apply())); } -ProgramObject MetavoxelEditor::_gridProgram; +void GlobalSetTool::apply() { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); + if (!attribute) { + return; + } + OwnedAttributeValue value(attribute, attribute->createFromVariant(_editor->getValue())); + MetavoxelEditMessage message = { QVariant::fromValue(GlobalSetEdit(value)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 5e941f3540..be2afd0a36 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -20,6 +20,8 @@ class QListWidget; class QPushButton; class QScrollArea; +class MetavoxelTool; + /// Allows editing metavoxels. class MetavoxelEditor : public QDialog { Q_OBJECT @@ -28,6 +30,14 @@ public: MetavoxelEditor(); + QString getSelectedAttribute() const; + + double getGridSpacing() const; + double getGridPosition() const; + glm::quat getGridRotation() const; + + QVariant getValue() const; + virtual bool eventFilter(QObject* watched, QEvent* event); private slots: @@ -37,38 +47,84 @@ private slots: void deleteSelectedAttribute(); void centerGridPosition(); void alignGridPosition(); + void updateTool(); void render(); private: - void updateAttributes(const QString& select = QString()); - QString getSelectedAttribute() const; - double getGridSpacing() const; - glm::quat getGridRotation() const; - void resetState(); - void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); - QVariant getValue() const; + void updateAttributes(const QString& select = QString()); + MetavoxelTool* getActiveTool() const; QListWidget* _attributes; QPushButton* _deleteAttribute; + QComboBox* _gridPlane; QDoubleSpinBox* _gridSpacing; QDoubleSpinBox* _gridPosition; + + QComboBox* _toolBox; + QGroupBox* _value; QScrollArea* _valueArea; + static ProgramObject _gridProgram; +}; + +/// Base class for editor tools. +class MetavoxelTool : public QWidget { + Q_OBJECT + +public: + + MetavoxelTool(MetavoxelEditor* editor); + + /// Renders the tool's interface, if any. + virtual void render(); + +protected: + + MetavoxelEditor* _editor; +}; + +/// Allows setting the value of a region by dragging out a box. +class BoxSetTool : public MetavoxelTool { + Q_OBJECT + +public: + + BoxSetTool(MetavoxelEditor* editor); + + virtual void render(); + + virtual bool eventFilter(QObject* watched, QEvent* event); + +private: + + void resetState(); + void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); + enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE }; State _state; glm::vec2 _mousePosition; ///< the position of the mouse in rotated space - glm::vec2 _startPosition; ///< the first corner of the selection base glm::vec2 _endPosition; ///< the second corner of the selection base float _height; ///< the selection height +}; + +/// Allows setting the value across the entire space. +class GlobalSetTool : public MetavoxelTool { + Q_OBJECT + +public: - static ProgramObject _gridProgram; + GlobalSetTool(MetavoxelEditor* editor); + +private slots: + + void apply(); }; #endif /* defined(__interface__MetavoxelEditor__) */ diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt index 989ed6d4a7..491d537b1a 100644 --- a/libraries/metavoxels/CMakeLists.txt +++ b/libraries/metavoxels/CMakeLists.txt @@ -11,12 +11,12 @@ set(TARGET_NAME metavoxels) find_package(Qt5Network REQUIRED) find_package(Qt5Widgets REQUIRED) -include(${MACRO_DIR}/SetupHifiLibrary.cmake) -setup_hifi_library(${TARGET_NAME}) - include(${MACRO_DIR}/AutoMTC.cmake) auto_mtc(${TARGET_NAME} ${ROOT_DIR}) +include(${MACRO_DIR}/SetupHifiLibrary.cmake) +setup_hifi_library(${TARGET_NAME} ${AUTOMTC_SRC}) + qt5_use_modules(${TARGET_NAME} Network Script Widgets) include(${MACRO_DIR}/IncludeGLM.cmake) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index b0e24405e6..6296b93693 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -43,8 +43,7 @@ MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) { Box MetavoxelData::getBounds() const { float halfSize = _size * 0.5f; - Box bounds = { glm::vec3(-halfSize, -halfSize, -halfSize), glm::vec3(halfSize, halfSize, halfSize) }; - return bounds; + return Box(glm::vec3(-halfSize, -halfSize, -halfSize), glm::vec3(halfSize, halfSize, halfSize)); } void MetavoxelData::guide(MetavoxelVisitor& visitor) { diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 380df4cac1..9f3ccedc0a 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -9,24 +9,35 @@ #include "MetavoxelData.h" #include "MetavoxelMessages.h" -class EditVisitor : public MetavoxelVisitor { +void MetavoxelEditMessage::apply(MetavoxelData& data) const { + static_cast(edit.data())->apply(data); +} + +MetavoxelEdit::~MetavoxelEdit() { +} + +BoxSetEdit::BoxSetEdit(const Box& region, float granularity, const OwnedAttributeValue& value) : + region(region), granularity(granularity), value(value) { +} + +class BoxSetEditVisitor : public MetavoxelVisitor { public: - EditVisitor(const MetavoxelEditMessage& edit); + BoxSetEditVisitor(const BoxSetEdit& edit); virtual bool visit(MetavoxelInfo& info); private: - const MetavoxelEditMessage& _edit; + const BoxSetEdit& _edit; }; -EditVisitor::EditVisitor(const MetavoxelEditMessage& edit) : +BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) : MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), _edit(edit) { } -bool EditVisitor::visit(MetavoxelInfo& info) { +bool BoxSetEditVisitor::visit(MetavoxelInfo& info) { // find the intersection between volume and voxel glm::vec3 minimum = glm::max(info.minimum, _edit.region.minimum); glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.region.maximum); @@ -48,12 +59,44 @@ bool EditVisitor::visit(MetavoxelInfo& info) { return true; // subdivide } -void MetavoxelEditMessage::apply(MetavoxelData& data) const { +void BoxSetEdit::apply(MetavoxelData& data) const { // expand to fit the entire edit while (!data.getBounds().contains(region)) { data.expand(); } - EditVisitor visitor(*this); + BoxSetEditVisitor visitor(*this); data.guide(visitor); } + +GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) : + value(value) { +} + +class GlobalSetEditVisitor : public MetavoxelVisitor { +public: + + GlobalSetEditVisitor(const GlobalSetEdit& edit); + + virtual bool visit(MetavoxelInfo& info); + +private: + + const GlobalSetEdit& _edit; +}; + +GlobalSetEditVisitor::GlobalSetEditVisitor(const GlobalSetEdit& edit) : + MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), + _edit(edit) { +} + +bool GlobalSetEditVisitor::visit(MetavoxelInfo& info) { + info.outputValues[0] = _edit.value; + return false; // entirely contained +} + +void GlobalSetEdit::apply(MetavoxelData& data) const { + GlobalSetEditVisitor visitor(*this); + data.guide(visitor); +} + diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 1c547809fb..165accbbb1 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -67,13 +67,53 @@ class MetavoxelEditMessage { public: - STREAM Box region; - STREAM float granularity; - STREAM OwnedAttributeValue value; + STREAM QVariant edit; void apply(MetavoxelData& data) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelEditMessage) +/// Abstract base class for edits. +class MetavoxelEdit { +public: + + virtual ~MetavoxelEdit(); + + virtual void apply(MetavoxelData& data) const = 0; +}; + +/// An edit that sets the region within a box to a value. +class BoxSetEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM Box region; + STREAM float granularity; + STREAM OwnedAttributeValue value; + + BoxSetEdit(const Box& region = Box(), float granularity = 0.0f, + const OwnedAttributeValue& value = OwnedAttributeValue()); + + virtual void apply(MetavoxelData& data) const; +}; + +DECLARE_STREAMABLE_METATYPE(BoxSetEdit) + +/// An edit that sets the entire tree to a value. +class GlobalSetEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM OwnedAttributeValue value; + + GlobalSetEdit(const OwnedAttributeValue& value = OwnedAttributeValue()); + + virtual void apply(MetavoxelData& data) const; +}; + +DECLARE_STREAMABLE_METATYPE(GlobalSetEdit) + #endif /* defined(__interface__MetavoxelMessages__) */ diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 8b33416dc3..f7c2a92b75 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -155,6 +155,10 @@ QByteArray signal(const char* signature) { return signal.replace("dummyMethod()", signature); } +Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) : + minimum(minimum), maximum(maximum) { +} + bool Box::contains(const Box& other) const { return other.minimum.x >= minimum.x && other.maximum.x <= maximum.x && other.minimum.y >= minimum.y && other.maximum.y <= maximum.y && diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 49039090a0..37a6c8bcda 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -46,6 +46,8 @@ public: STREAM glm::vec3 minimum; STREAM glm::vec3 maximum; + Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); + bool contains(const Box& other) const; }; diff --git a/tests/metavoxels/CMakeLists.txt b/tests/metavoxels/CMakeLists.txt index 416f398470..9d21dd2a44 100644 --- a/tests/metavoxels/CMakeLists.txt +++ b/tests/metavoxels/CMakeLists.txt @@ -12,12 +12,12 @@ find_package(Qt5Network REQUIRED) find_package(Qt5Script REQUIRED) find_package(Qt5Widgets REQUIRED) -include(${MACRO_DIR}/SetupHifiProject.cmake) -setup_hifi_project(${TARGET_NAME} TRUE) - include(${MACRO_DIR}/AutoMTC.cmake) auto_mtc(${TARGET_NAME} ${ROOT_DIR}) +include(${MACRO_DIR}/SetupHifiProject.cmake) +setup_hifi_project(${TARGET_NAME} TRUE ${AUTOMTC_SRC}) + qt5_use_modules(${TARGET_NAME} Network Script Widgets) #include glm From 9c91d3c2e75589b85427f8fb9d8bcfc69019c3e2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Feb 2014 10:25:10 -0800 Subject: [PATCH 06/48] Use the existing session stuff for metavoxels rather than a separate system. --- .../src/metavoxels/MetavoxelServer.cpp | 91 ++++++------------- .../src/metavoxels/MetavoxelServer.h | 26 ++---- interface/src/DatagramProcessor.cpp | 2 +- interface/src/MetavoxelSystem.cpp | 87 +++++------------- interface/src/MetavoxelSystem.h | 28 ++---- libraries/metavoxels/src/DatagramSequencer.h | 2 +- 6 files changed, 69 insertions(+), 167 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index fa934142d3..3f9f5114dd 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -28,15 +28,16 @@ void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) { edit.apply(_data); } -void MetavoxelServer::removeSession(const QUuid& sessionId) { - _sessions.take(sessionId)->deleteLater(); -} - const char METAVOXEL_SERVER_LOGGING_NAME[] = "metavoxel-server"; void MetavoxelServer::run() { commonInit(METAVOXEL_SERVER_LOGGING_NAME, NodeType::MetavoxelServer); + NodeList* nodeList = NodeList::getInstance(); + nodeList->addNodeTypeToInterestSet(NodeType::Agent); + + connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachSession(const SharedNodePointer&))); + _lastSend = QDateTime::currentMSecsSinceEpoch(); _sendTimer.start(SEND_INTERVAL); } @@ -50,25 +51,31 @@ void MetavoxelServer::readPendingDatagrams() { while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { switch (packetTypeForPacket(receivedPacket)) { - case PacketTypeMetavoxelData: { - SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket); - if (matchingNode) { - processData(receivedPacket, matchingNode); - } + case PacketTypeMetavoxelData: + nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket); break; - } + default: - NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket); + nodeList->processNodeData(senderSockAddr, receivedPacket); break; } } } } +void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) { + if (node->getType() == NodeType::Agent) { + QMutexLocker locker(&node->getMutex()); + node->setLinkedData(new MetavoxelSession(this, NodeList::getInstance()->nodeWithUUID(node->getUUID()))); + } +} + void MetavoxelServer::sendDeltas() { // send deltas for all sessions - foreach (MetavoxelSession* session, _sessions) { - session->sendDelta(); + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getType() == NodeType::Agent) { + static_cast(node->getLinkedData())->sendDelta(); + } } // restart the send timer @@ -79,35 +86,10 @@ void MetavoxelServer::sendDeltas() { _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - elapsed)); } -void MetavoxelServer::processData(const QByteArray& data, const SharedNodePointer& sendingNode) { - // read the session id - int headerPlusIDSize; - QUuid sessionID = readSessionID(data, sendingNode, headerPlusIDSize); - if (sessionID.isNull()) { - return; - } - - // forward to session, creating if necessary - MetavoxelSession*& session = _sessions[sessionID]; - if (!session) { - session = new MetavoxelSession(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize), - sendingNode); - } - session->receivedData(data, sendingNode); -} - -MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, - const QByteArray& datagramHeader, const SharedNodePointer& sendingNode) : - QObject(server), +MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const SharedNodePointer& node) : _server(server), - _sessionId(sessionId), - _sequencer(datagramHeader), - _sendingNode(sendingNode) { - - const int TIMEOUT_INTERVAL = 30 * 1000; - _timeoutTimer.setInterval(TIMEOUT_INTERVAL); - _timeoutTimer.setSingleShot(true); - connect(&_timeoutTimer, SIGNAL(timeout()), SLOT(timedOut())); + _sequencer(byteArrayWithPopluatedHeader(PacketTypeMetavoxelData)), + _node(node) { connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); @@ -117,19 +99,15 @@ MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& session // insert the baseline send record SendRecord record = { 0 }; _sendRecords.append(record); - - qDebug() << "Opened session [sessionId=" << _sessionId << ", sendingNode=" << sendingNode << "]"; } -void MetavoxelSession::receivedData(const QByteArray& data, const SharedNodePointer& sendingNode) { - // reset the timeout timer - _timeoutTimer.start(); +MetavoxelSession::~MetavoxelSession() { +} - // save the most recent sender - _sendingNode = sendingNode; - +int MetavoxelSession::parseData(const QByteArray& packet) { // process through sequencer - _sequencer.receivedDatagram(data); + _sequencer.receivedDatagram(packet); + return packet.size(); } void MetavoxelSession::sendDelta() { @@ -143,13 +121,8 @@ void MetavoxelSession::sendDelta() { _sendRecords.append(record); } -void MetavoxelSession::timedOut() { - qDebug() << "Session timed out [sessionId=" << _sessionId << ", sendingNode=" << _sendingNode << "]"; - _server->removeSession(_sessionId); -} - void MetavoxelSession::sendData(const QByteArray& data) { - NodeList::getInstance()->writeDatagram(data, _sendingNode); + NodeList::getInstance()->writeDatagram(data, _node); } void MetavoxelSession::readPacket(Bitstream& in) { @@ -164,11 +137,7 @@ void MetavoxelSession::clearSendRecordsBefore(int index) { void MetavoxelSession::handleMessage(const QVariant& message) { int userType = message.userType(); - if (userType == CloseSessionMessage::Type) { - qDebug() << "Session closed [sessionId=" << _sessionId << ", sendingNode=" << _sendingNode << "]"; - _server->removeSession(_sessionId); - - } else if (userType == ClientStateMessage::Type) { + if (userType == ClientStateMessage::Type) { ClientStateMessage state = message.value(); _position = state.position; diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 60fdeca5a5..a7939ee115 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -9,12 +9,9 @@ #ifndef __hifi__MetavoxelServer__ #define __hifi__MetavoxelServer__ -#include #include #include -#include -#include #include #include @@ -35,45 +32,38 @@ public: const MetavoxelData& getData() const { return _data; } - void removeSession(const QUuid& sessionId); - virtual void run(); virtual void readPendingDatagrams(); private slots: - + + void maybeAttachSession(const SharedNodePointer& node); void sendDeltas(); private: - void processData(const QByteArray& data, const SharedNodePointer& sendingNode); - QTimer _sendTimer; qint64 _lastSend; - QHash _sessions; - MetavoxelData _data; }; /// Contains the state of a single client session. -class MetavoxelSession : public QObject { +class MetavoxelSession : public NodeData { Q_OBJECT public: - MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, - const QByteArray& datagramHeader, const SharedNodePointer& sendingNode); + MetavoxelSession(MetavoxelServer* server, const SharedNodePointer& node); + virtual ~MetavoxelSession(); - void receivedData(const QByteArray& data, const SharedNodePointer& sendingNode); + virtual int parseData(const QByteArray& packet); void sendDelta(); private slots: - void timedOut(); - void sendData(const QByteArray& data); void readPacket(Bitstream& in); @@ -91,12 +81,10 @@ private: }; MetavoxelServer* _server; - QUuid _sessionId; - QTimer _timeoutTimer; DatagramSequencer _sequencer; - SharedNodePointer _sendingNode; + SharedNodePointer _node; glm::vec3 _position; diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index d8447168cd..18e3742bd7 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -94,7 +94,7 @@ void DatagramProcessor::processDatagrams() { break; } case PacketTypeMetavoxelData: - application->_metavoxels.processData(incomingPacket, senderSockAddr); + nodeList->findNodeAndUpdateWithDataFromPacket(incomingPacket); break; case PacketTypeBulkAvatarData: case PacketTypeKillAvatar: diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 34c3be5308..3c3683f0dc 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -25,12 +25,6 @@ MetavoxelSystem::MetavoxelSystem() : _buffer(QOpenGLBuffer::VertexBuffer) { } -MetavoxelSystem::~MetavoxelSystem() { - for (QHash::const_iterator it = _clients.begin(); it != _clients.end(); it++) { - delete it.value(); - } -} - void MetavoxelSystem::init() { if (!_program.isLinked()) { switchToResourcesParentIfRequired(); @@ -42,33 +36,35 @@ void MetavoxelSystem::init() { // let the script cache know to use our common access manager ScriptCache::getInstance()->setNetworkAccessManager(Application::getInstance()->getNetworkAccessManager()); } - - NodeList* nodeList = NodeList::getInstance(); - - connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); - connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); _buffer.create(); + + connect(NodeList::getInstance(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachClient(const SharedNodePointer&))); } void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit) { - foreach (MetavoxelClient* client, _clients) { - client->applyEdit(edit); + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getType() == NodeType::MetavoxelServer) { + MetavoxelClient* client = static_cast(node->getLinkedData()); + if (client) { + 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)); -} - void MetavoxelSystem::simulate(float deltaTime) { // simulate the clients _points.clear(); - foreach (MetavoxelClient* client, _clients) { - client->simulate(deltaTime, _pointVisitor); + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getType() == NodeType::MetavoxelServer) { + MetavoxelClient* client = static_cast(node->getLinkedData()); + if (client) { + client->simulate(deltaTime, _pointVisitor); + } + } } - + _buffer.bind(); int bytes = _points.size() * sizeof(Point); if (_buffer.size() < bytes) { @@ -120,39 +116,10 @@ void MetavoxelSystem::render() { _program.release(); } -void MetavoxelSystem::nodeAdded(SharedNodePointer node) { +void MetavoxelSystem::maybeAttachClient(const SharedNodePointer& node) { if (node->getType() == NodeType::MetavoxelServer) { - QMetaObject::invokeMethod(this, "addClient", Q_ARG(const SharedNodePointer&, node)); - } -} - -void MetavoxelSystem::nodeKilled(SharedNodePointer node) { - if (node->getType() == NodeType::MetavoxelServer) { - QMetaObject::invokeMethod(this, "removeClient", Q_ARG(const QUuid&, node->getUUID())); - } -} - -void MetavoxelSystem::addClient(const SharedNodePointer& node) { - MetavoxelClient* client = new MetavoxelClient(node); - _clients.insert(node->getUUID(), client); - _clientsBySessionID.insert(client->getSessionID(), client); -} - -void MetavoxelSystem::removeClient(const QUuid& uuid) { - MetavoxelClient* client = _clients.take(uuid); - _clientsBySessionID.remove(client->getSessionID()); - delete client; -} - -void MetavoxelSystem::receivedData(const QByteArray& data, const SharedNodePointer& sendingNode) { - int headerPlusIDSize; - QUuid sessionID = readSessionID(data, sendingNode, headerPlusIDSize); - if (sessionID.isNull()) { - return; - } - MetavoxelClient* client = _clientsBySessionID.value(sessionID); - if (client) { - client->receivedData(data); + QMutexLocker locker(&node->getMutex()); + node->setLinkedData(new MetavoxelClient(NodeList::getInstance()->nodeWithUUID(node->getUUID()))); } } @@ -179,16 +146,9 @@ bool MetavoxelSystem::PointVisitor::visit(MetavoxelInfo& info) { return false; } -static QByteArray createDatagramHeader(const QUuid& sessionID) { - QByteArray header = byteArrayWithPopluatedHeader(PacketTypeMetavoxelData); - header += sessionID.toRfc4122(); - return header; -} - MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : _node(node), - _sessionID(QUuid::createUuid()), - _sequencer(createDatagramHeader(_sessionID)) { + _sequencer(byteArrayWithPopluatedHeader(PacketTypeMetavoxelData)) { connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); @@ -223,9 +183,10 @@ void MetavoxelClient::simulate(float deltaTime, MetavoxelVisitor& visitor) { _data.guide(visitor); } -void MetavoxelClient::receivedData(const QByteArray& data) { +int MetavoxelClient::parseData(const QByteArray& packet) { // process through sequencer - _sequencer.receivedDatagram(data); + QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); + return packet.size(); } void MetavoxelClient::sendData(const QByteArray& data) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 7bb7935e4d..40765723fd 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -23,8 +23,6 @@ #include "renderer/ProgramObject.h" -class MetavoxelClient; - /// Renders a metavoxel tree. class MetavoxelSystem : public QObject { Q_OBJECT @@ -32,28 +30,20 @@ class MetavoxelSystem : public QObject { public: MetavoxelSystem(); - ~MetavoxelSystem(); void init(); void applyEdit(const MetavoxelEditMessage& edit); - void processData(const QByteArray& data, const HifiSockAddr& sender); - void simulate(float deltaTime); void render(); -public slots: +private slots: + + void maybeAttachClient(const SharedNodePointer& node); - void nodeAdded(SharedNodePointer node); - void nodeKilled(SharedNodePointer node); - private: - - Q_INVOKABLE void addClient(const SharedNodePointer& node); - Q_INVOKABLE void removeClient(const QUuid& uuid); - Q_INVOKABLE void receivedData(const QByteArray& data, const SharedNodePointer& sendingNode); - + class Point { public: glm::vec4 vertex; @@ -76,13 +66,10 @@ private: QVector _points; PointVisitor _pointVisitor; QOpenGLBuffer _buffer; - - QHash _clients; - QHash _clientsBySessionID; }; /// A client session associated with a single server. -class MetavoxelClient : public QObject { +class MetavoxelClient : public NodeData { Q_OBJECT public: @@ -90,13 +77,11 @@ public: MetavoxelClient(const SharedNodePointer& node); virtual ~MetavoxelClient(); - const QUuid& getSessionID() const { return _sessionID; } - void applyEdit(const MetavoxelEditMessage& edit); void simulate(float deltaTime, MetavoxelVisitor& visitor); - void receivedData(const QByteArray& data); + virtual int parseData(const QByteArray& packet); private slots: @@ -117,7 +102,6 @@ private: }; SharedNodePointer _node; - QUuid _sessionID; DatagramSequencer _sequencer; diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 7156175f51..13bdf4c7bf 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -70,7 +70,7 @@ public: /// Processes a datagram received from the other party, emitting readyToRead when the entire packet /// has been successfully assembled. - void receivedDatagram(const QByteArray& datagram); + Q_INVOKABLE void receivedDatagram(const QByteArray& datagram); signals: From 8920873d705acbb236d4e70828a22e000694ded0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Feb 2014 15:35:42 -0800 Subject: [PATCH 07/48] Working on support for "spanners" (objects that span multiple octree cells). --- .../metavoxels/src/AttributeRegistry.cpp | 16 +++++++++ libraries/metavoxels/src/AttributeRegistry.h | 25 +++++++++++++- libraries/metavoxels/src/Bitstream.h | 24 ++++++++++++++ libraries/metavoxels/src/MetavoxelData.cpp | 20 +++++++++++ libraries/metavoxels/src/MetavoxelData.h | 27 +++++++++++++++ .../metavoxels/src/MetavoxelMessages.cpp | 15 +++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 33 +++++++++++++++++++ libraries/metavoxels/src/SharedObject.h | 5 +++ 8 files changed, 164 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 8483174dac..3cde1ce961 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -13,6 +13,7 @@ REGISTER_META_OBJECT(QRgbAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) +REGISTER_META_OBJECT(SharedObjectSetAttribute) AttributeRegistry* AttributeRegistry::getInstance() { static AttributeRegistry registry; @@ -22,6 +23,7 @@ AttributeRegistry* AttributeRegistry::getInstance() { AttributeRegistry::AttributeRegistry() : _guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject, SharedObjectPointer(new DefaultMetavoxelGuide())))), + _spannersAttribute(registerAttribute(new SharedObjectSetAttribute("spanners", &Spanner::staticMetaObject))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) { } @@ -227,3 +229,17 @@ QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const { editor->setObject(_defaultValue); return editor; } + +SharedObjectSetAttribute::SharedObjectSetAttribute(const QString& name, const QMetaObject* metaObject) : + InlineAttribute(name), + _metaObject(metaObject) { +} + +bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const { + for (int i = 0; i < MERGE_COUNT; i++) { + if (!decodeInline(children[i]).isEmpty()) { + return false; + } + } + return true; +} diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 1235cefbb5..ebcfb0c8d9 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -57,9 +57,12 @@ public: /// Returns a reference to the attribute hash. const QHash& getAttributes() const { return _attributes; } - /// Returns a reference to the standard PolymorphicDataPointer "guide" attribute. + /// Returns a reference to the standard SharedObjectPointer "guide" attribute. const AttributePointer& getGuideAttribute() const { return _guideAttribute; } + /// Returns a reference to the standard SharedObjectSet "spanners" attribute. + const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; } + /// Returns a reference to the standard QRgb "color" attribute. const AttributePointer& getColorAttribute() const { return _colorAttribute; } @@ -72,6 +75,7 @@ private: QHash _attributes; AttributePointer _guideAttribute; + AttributePointer _spannersAttribute; AttributePointer _colorAttribute; AttributePointer _normalAttribute; }; @@ -281,4 +285,23 @@ private: const QMetaObject* _metaObject; }; +/// An attribute that takes the form of a set of shared objects. +class SharedObjectSetAttribute : public InlineAttribute { + Q_OBJECT + Q_PROPERTY(const QMetaObject* metaObject MEMBER _metaObject) + +public: + + Q_INVOKABLE SharedObjectSetAttribute(const QString& name = QString(), + const QMetaObject* metaObject = &SharedObject::staticMetaObject); + + const QMetaObject* getMetaObject() const { return _metaObject; } + + virtual bool merge(void*& parent, void* children[]) const; + +private: + + const QMetaObject* _metaObject; +}; + #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 87ee66c661..9a3ef95d96 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -266,6 +266,9 @@ public: template Bitstream& operator<<(const QList& list); template Bitstream& operator>>(QList& list); + template Bitstream& operator<<(const QSet& set); + template Bitstream& operator>>(QSet& set); + template Bitstream& operator<<(const QHash& hash); template Bitstream& operator>>(QHash& hash); @@ -348,6 +351,27 @@ template inline Bitstream& Bitstream::operator>>(QList& list) { return *this; } +template inline Bitstream& Bitstream::operator<<(const QSet& set) { + *this << set.size(); + foreach (const T& entry, set) { + *this << entry; + } + return *this; +} + +template inline Bitstream& Bitstream::operator>>(QSet& set) { + int size; + *this >> size; + set.clear(); + set.reserve(size); + for (int i = 0; i < size; i++) { + T entry; + *this >> entry; + set.insert(entry); + } + return *this; +} + template inline Bitstream& Bitstream::operator<<(const QHash& hash) { *this << hash.size(); for (typename QHash::const_iterator it = hash.constBegin(); it != hash.constEnd(); it++) { diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 6296b93693..5f400ef517 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -18,6 +18,7 @@ REGISTER_META_OBJECT(MetavoxelGuide) REGISTER_META_OBJECT(DefaultMetavoxelGuide) REGISTER_META_OBJECT(ScriptedMetavoxelGuide) REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) +REGISTER_META_OBJECT(Spanner) MetavoxelData::MetavoxelData() : _size(1.0f) { } @@ -673,3 +674,22 @@ AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const { return AttributeValue(visitor.getOutputs().at(index)); } +Spanner::Spanner() : _lastVisit(0) { +} + +void Spanner::setBounds(const Box& bounds) { + if (_bounds == bounds) { + return; + } + emit boundsWillChange(); + emit boundsChanged(_bounds = bounds); +} + +bool Spanner::testAndSetVisited(int visit) { + if (_lastVisit == visit) { + return false; + } + _lastVisit = visit; + return true; +} + diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 51cdd6cf64..4b001e2e33 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -242,4 +242,31 @@ public: AttributeValue getInheritedOutputValue(int index) const; }; +/// An object that spans multiple octree cells. +class Spanner : public SharedObject { + Q_OBJECT + Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged) + +public: + + Spanner(); + + void setBounds(const Box& bounds); + const Box& getBounds() const { return _bounds; } + + /// Checks whether we've visited this object on the current traversal. If we have, returns false. + /// If we haven't, sets the last visit identifier and returns true. + bool testAndSetVisited(int visit); + +signals: + + void boundsWillChange(); + void boundsChanged(const Box& bounds); + +private: + + Box _bounds; + int _lastVisit; ///< the identifier of the last visit +}; + #endif /* defined(__interface__MetavoxelData__) */ diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 9f3ccedc0a..36f0fcc10a 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -100,3 +100,18 @@ void GlobalSetEdit::apply(MetavoxelData& data) const { data.guide(visitor); } +InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) : + attribute(attribute), + spanner(spanner) { +} + +void InsertSpannerEdit::apply(MetavoxelData& data) const { +} + +RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) : + attribute(attribute), + id(id) { +} + +void RemoveSpannerEdit::apply(MetavoxelData& data) const { +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 165accbbb1..65bee27d7d 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -116,4 +116,37 @@ public: DECLARE_STREAMABLE_METATYPE(GlobalSetEdit) +/// An edit that inserts a spanner into the tree. +class InsertSpannerEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM AttributePointer attribute; + STREAM SharedObjectPointer spanner; + + InsertSpannerEdit(const AttributePointer& attribute = AttributePointer(), + const SharedObjectPointer& spanner = SharedObjectPointer()); + + virtual void apply(MetavoxelData& data) const; +}; + +DECLARE_STREAMABLE_METATYPE(InsertSpannerEdit) + +/// An edit that removes a a spanner from the tree. +class RemoveSpannerEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM AttributePointer attribute; + STREAM int id; + + RemoveSpannerEdit(const AttributePointer& attribute = AttributePointer(), int id = 0); + + virtual void apply(MetavoxelData& data) const; +}; + +DECLARE_STREAMABLE_METATYPE(RemoveSpannerEdit) + #endif /* defined(__interface__MetavoxelMessages__) */ diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index b62b88d2ee..5a33b64e3d 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -11,6 +11,7 @@ #include #include +#include #include class QComboBox; @@ -144,6 +145,10 @@ typedef SharedObjectPointerTemplate SharedObjectPointer; Q_DECLARE_METATYPE(SharedObjectPointer) +typedef QSet SharedObjectSet; + +Q_DECLARE_METATYPE(SharedObjectSet) + /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { Q_OBJECT From 3e40927d3a1426fc1122f7ebf56d90ecd33da8ef Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Feb 2014 18:01:48 -0800 Subject: [PATCH 08/48] More work on spanning objects. --- interface/src/ui/MetavoxelEditor.cpp | 93 +++++++++++++++---- interface/src/ui/MetavoxelEditor.h | 36 ++++++- .../metavoxels/src/AttributeRegistry.cpp | 4 + libraries/metavoxels/src/AttributeRegistry.h | 2 + libraries/metavoxels/src/MetavoxelData.cpp | 33 +++++++ libraries/metavoxels/src/MetavoxelData.h | 6 +- .../metavoxels/src/MetavoxelMessages.cpp | 3 +- 7 files changed, 156 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 0ba0b08633..54a0c28266 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -94,14 +94,11 @@ MetavoxelEditor::MetavoxelEditor() : valueLayout->addWidget(_valueArea = new QScrollArea()); _valueArea->setWidgetResizable(true); - BoxSetTool* boxSetTool = new BoxSetTool(this); - topLayout->addWidget(boxSetTool); - _toolBox->addItem("Set Value (Box)", QVariant::fromValue(boxSetTool)); - - GlobalSetTool* globalSetTool = new GlobalSetTool(this); - topLayout->addWidget(globalSetTool); - _toolBox->addItem("Set Value (Global)", QVariant::fromValue(globalSetTool)); - + addTool(new BoxSetTool(this)); + addTool(new GlobalSetTool(this)); + addTool(new InsertSpannerTool(this)); + addTool(new RemoveSpannerTool(this)); + updateAttributes(); connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); @@ -153,23 +150,35 @@ QVariant MetavoxelEditor::getValue() const { bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { // pass along to the active tool - return getActiveTool()->eventFilter(watched, event); + MetavoxelTool* tool = getActiveTool(); + return tool && tool->eventFilter(watched, event); } void MetavoxelEditor::selectedAttributeChanged() { + _toolBox->clear(); + QString selected = getSelectedAttribute(); if (selected.isNull()) { _deleteAttribute->setEnabled(false); + _toolBox->setEnabled(false); _value->setVisible(false); return; } _deleteAttribute->setEnabled(true); + _toolBox->setEnabled(true); + + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); + foreach (MetavoxelTool* tool, _tools) { + if (tool->appliesTo(attribute)) { + _toolBox->addItem(tool->objectName(), QVariant::fromValue(tool)); + } + } _value->setVisible(true); if (_valueArea->widget()) { delete _valueArea->widget(); } - QWidget* editor = AttributeRegistry::getInstance()->getAttribute(selected)->createEditor(); + QWidget* editor = attribute->createEditor(); if (editor) { _valueArea->setWidget(editor); } @@ -231,9 +240,11 @@ void MetavoxelEditor::alignGridPosition() { } void MetavoxelEditor::updateTool() { - for (int i = 0; i < _toolBox->count(); i++) { - _toolBox->itemData(i).value()->setVisible(i == _toolBox->currentIndex()); + MetavoxelTool* active = getActiveTool(); + foreach (MetavoxelTool* tool, _tools) { + tool->setVisible(tool == active); } + _value->setVisible(active && active->getUsesValue()); } const float GRID_BRIGHTNESS = 0.5f; @@ -248,7 +259,10 @@ void MetavoxelEditor::render() { glm::vec3 axis = glm::axis(rotation); glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); - getActiveTool()->render(); + MetavoxelTool* tool = getActiveTool(); + if (tool) { + tool->render(); + } glLineWidth(1.0f); @@ -276,6 +290,11 @@ void MetavoxelEditor::render() { glDepthMask(GL_TRUE); } +void MetavoxelEditor::addTool(MetavoxelTool* tool) { + _tools.append(tool); + layout()->addWidget(tool); +} + void MetavoxelEditor::updateAttributes(const QString& select) { // remember the selection in order to preserve it QString selected = select.isNull() ? getSelectedAttribute() : select; @@ -296,26 +315,34 @@ void MetavoxelEditor::updateAttributes(const QString& select) { } MetavoxelTool* MetavoxelEditor::getActiveTool() const { - return static_cast(_toolBox->itemData(_toolBox->currentIndex()).value()); + int index = _toolBox->currentIndex(); + return (index == -1) ? NULL : static_cast(_toolBox->itemData(index).value()); } ProgramObject MetavoxelEditor::_gridProgram; -MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor) : - _editor(editor) { +MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue) : + _editor(editor), + _usesValue(usesValue) { QVBoxLayout* layout = new QVBoxLayout(); setLayout(layout); + setObjectName(name); setVisible(false); } +bool MetavoxelTool::appliesTo(const AttributePointer& attribute) const { + // shared object sets are a special case + return !attribute->inherits("SharedObjectSetAttribute"); +} + void MetavoxelTool::render() { // nothing by default } BoxSetTool::BoxSetTool(MetavoxelEditor* editor) : - MetavoxelTool(editor) { + MetavoxelTool(editor, "Set Value (Box)") { resetState(); } @@ -449,7 +476,7 @@ void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) } GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) : - MetavoxelTool(editor) { + MetavoxelTool(editor, "Set Value (Global)") { QPushButton* button = new QPushButton("Apply"); layout()->addWidget(button); @@ -465,3 +492,33 @@ void GlobalSetTool::apply() { MetavoxelEditMessage message = { QVariant::fromValue(GlobalSetEdit(value)) }; Application::getInstance()->getMetavoxels()->applyEdit(message); } + +InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : + MetavoxelTool(editor, "Insert Spanner") { + + QPushButton* button = new QPushButton("Insert"); + layout()->addWidget(button); + connect(button, SIGNAL(clicked()), SLOT(insert())); +} + +bool InsertSpannerTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("SharedObjectSetAttribute"); +} + +void InsertSpannerTool::insert() { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); + if (!attribute) { + return; + } + SharedObjectPointer spanner = _editor->getValue().value(); + MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message); +} + +RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : + MetavoxelTool(editor, "Remove Spanner", false) { +} + +bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("SharedObjectSetAttribute"); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index be2afd0a36..e3574b9f67 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -10,6 +10,7 @@ #define __interface__MetavoxelEditor__ #include +#include #include "renderer/ProgramObject.h" @@ -53,6 +54,7 @@ private slots: private: + void addTool(MetavoxelTool* tool); void updateAttributes(const QString& select = QString()); MetavoxelTool* getActiveTool() const; @@ -63,6 +65,7 @@ private: QDoubleSpinBox* _gridSpacing; QDoubleSpinBox* _gridPosition; + QList _tools; QComboBox* _toolBox; QGroupBox* _value; @@ -77,7 +80,11 @@ class MetavoxelTool : public QWidget { public: - MetavoxelTool(MetavoxelEditor* editor); + MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true); + + bool getUsesValue() const { return _usesValue; } + + virtual bool appliesTo(const AttributePointer& attribute) const; /// Renders the tool's interface, if any. virtual void render(); @@ -85,6 +92,7 @@ public: protected: MetavoxelEditor* _editor; + bool _usesValue; }; /// Allows setting the value of a region by dragging out a box. @@ -127,4 +135,30 @@ private slots: void apply(); }; +/// Allows inserting a spanner into the scene. +class InsertSpannerTool : public MetavoxelTool { + Q_OBJECT + +public: + + InsertSpannerTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + +private slots: + + void insert(); +}; + +/// Allows removing a spanner from the scene. +class RemoveSpannerTool : public MetavoxelTool { + Q_OBJECT + +public: + + RemoveSpannerTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; +}; + #endif /* defined(__interface__MetavoxelEditor__) */ diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 3cde1ce961..22a629dd1a 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -243,3 +243,7 @@ bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const { } return true; } + +QWidget* SharedObjectSetAttribute::createEditor(QWidget* parent) const { + return new SharedObjectEditor(_metaObject, parent); +} diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index ebcfb0c8d9..1bcadf7f6c 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -299,6 +299,8 @@ public: virtual bool merge(void*& parent, void* children[]) const; + virtual QWidget* createEditor(QWidget* parent = NULL) const; + private: const QMetaObject* _metaObject; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 5f400ef517..a746fda102 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -88,6 +88,39 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { } } +class InsertVisitor : public MetavoxelVisitor { +public: + + InsertVisitor(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object); + + virtual bool visit(MetavoxelInfo& info); + +private: + + const AttributePointer& _attribute; + const Box& _bounds; + const SharedObjectPointer& _object; +}; + +InsertVisitor::InsertVisitor(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object) : + MetavoxelVisitor(QVector() << attribute, QVector() << attribute), + _attribute(attribute), + _bounds(bounds), + _object(object) { +} + +bool InsertVisitor::visit(MetavoxelInfo& info) { + return false; +} + +void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object) { + InsertVisitor visitor(attribute, bounds, object); + guide(visitor); +} + +void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object) { +} + const int X_MAXIMUM_FLAG = 1; const int Y_MAXIMUM_FLAG = 2; const int Z_MAXIMUM_FLAG = 4; diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 4b001e2e33..a0249216f6 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -45,7 +45,11 @@ public: /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); + + void insert(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object); + void remove(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object); + /// Expands the tree, increasing its capacity in all dimensions. void expand(); @@ -249,7 +253,7 @@ class Spanner : public SharedObject { public: - Spanner(); + Q_INVOKABLE Spanner(); void setBounds(const Box& bounds); const Box& getBounds() const { return _bounds; } diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 36f0fcc10a..56336ffa16 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -105,7 +105,8 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh spanner(spanner) { } -void InsertSpannerEdit::apply(MetavoxelData& data) const { +void InsertSpannerEdit::apply(MetavoxelData& data) const { + data.insert(attribute, static_cast(spanner.data())->getBounds(), spanner); } RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) : From 494685140bcead44191513394dc9ce5facab7128 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 17 Feb 2014 21:44:32 -0800 Subject: [PATCH 09/48] Various bits of work on spanners. --- interface/src/Application.cpp | 3 + interface/src/Application.h | 3 + interface/src/MetavoxelSystem.cpp | 47 ++++++++++++ interface/src/MetavoxelSystem.h | 26 +++++++ interface/src/ui/MetavoxelEditor.cpp | 23 ++++++ interface/src/ui/MetavoxelEditor.h | 7 ++ libraries/metavoxels/src/Bitstream.cpp | 4 + libraries/metavoxels/src/Bitstream.h | 3 + libraries/metavoxels/src/MetavoxelData.cpp | 72 +++++++++++++++++- libraries/metavoxels/src/MetavoxelData.h | 85 +++++++++++++++++++++- libraries/metavoxels/src/SharedObject.cpp | 4 + 11 files changed, 274 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b445efeb15..9c81ddca75 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2309,6 +2309,9 @@ void Application::update(float deltaTime) { _particles.update(); // update the particles... _particleCollisionSystem.update(); // collide the particles... + + // let external parties know we're updating + emit simulating(deltaTime); } void Application::updateMyAvatar(float deltaTime) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 574f578bde..4b10873bdf 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -208,6 +208,9 @@ public: signals: + /// Fired when we're simulating; allows external parties to hook in. + void simulating(float deltaTime); + /// Fired when we're rendering in-world interface elements; allows external parties to hook in. void renderingInWorldInterface(); diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 3c3683f0dc..9a5bb4cfd1 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -16,6 +16,9 @@ #include "Application.h" #include "MetavoxelSystem.h" +#include "renderer/Model.h" + +REGISTER_META_OBJECT(StaticModelRenderer) ProgramObject MetavoxelSystem::_program; int MetavoxelSystem::_pointScaleLocation; @@ -226,3 +229,47 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } } } + +StaticModelRenderer::StaticModelRenderer() : + _model(new Model(this)) { +} + +void StaticModelRenderer::init(Spanner* spanner) { + _model->init(); + + StaticModel* staticModel = static_cast(spanner); + applyTranslation(staticModel->getTranslation()); + applyRotation(staticModel->getRotation()); + applyScale(staticModel->getScale()); + applyURL(staticModel->getURL()); + + connect(spanner, SIGNAL(translationChanged(const glm::vec3&)), SLOT(applyTranslation(const glm::vec3&))); + connect(spanner, SIGNAL(rotationChanged(const glm::vec3&)), SLOT(applyRotation(const glm::vec3&))); + connect(spanner, SIGNAL(scaleChanged(float)), SLOT(applyScale(float))); + connect(spanner, SIGNAL(urlChanged(const QUrl&)), SLOT(applyURL(const QUrl&))); +} + +void StaticModelRenderer::simulate(float deltaTime) { + _model->simulate(deltaTime); +} + +void StaticModelRenderer::render(float alpha) { + _model->render(alpha); +} + +void StaticModelRenderer::applyTranslation(const glm::vec3& translation) { + _model->setTranslation(translation); +} + +void StaticModelRenderer::applyRotation(const glm::vec3& rotation) { + _model->setRotation(glm::quat(glm::radians(rotation))); +} + +void StaticModelRenderer::applyScale(float scale) { + const float SCALE_MULTIPLIER = 0.0006f; + _model->setScale(glm::vec3(scale, scale, scale) * SCALE_MULTIPLIER); +} + +void StaticModelRenderer::applyURL(const QUrl& url) { + _model->setURL(url); +} diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 40765723fd..7cc8c114fd 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -23,6 +23,8 @@ #include "renderer/ProgramObject.h" +class Model; + /// Renders a metavoxel tree. class MetavoxelSystem : public QObject { Q_OBJECT @@ -110,4 +112,28 @@ private: QList _receiveRecords; }; +/// Renders static models. +class StaticModelRenderer : public SpannerRenderer { + Q_OBJECT + +public: + + Q_INVOKABLE StaticModelRenderer(); + + virtual void init(Spanner* spanner); + virtual void simulate(float deltaTime); + virtual void render(float alpha); + +private slots: + + void applyTranslation(const glm::vec3& translation); + void applyRotation(const glm::vec3& rotation); + void applyScale(float scale); + void applyURL(const QUrl& url); + +private: + + Model* _model; +}; + #endif /* defined(__interface__MetavoxelSystem__) */ diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 54a0c28266..003d283795 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -101,6 +101,7 @@ MetavoxelEditor::MetavoxelEditor() : updateAttributes(); + connect(Application::getInstance(), SIGNAL(simulating(float)), SLOT(simulate(float))); connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); Application::getInstance()->getGLWidget()->installEventFilter(this); @@ -247,6 +248,13 @@ void MetavoxelEditor::updateTool() { _value->setVisible(active && active->getUsesValue()); } +void MetavoxelEditor::simulate(float deltaTime) { + MetavoxelTool* tool = getActiveTool(); + if (tool) { + tool->simulate(deltaTime); + } +} + const float GRID_BRIGHTNESS = 0.5f; void MetavoxelEditor::render() { @@ -337,6 +345,10 @@ bool MetavoxelTool::appliesTo(const AttributePointer& attribute) const { return !attribute->inherits("SharedObjectSetAttribute"); } +void MetavoxelTool::simulate(float deltaTime) { + // nothing by default +} + void MetavoxelTool::render() { // nothing by default } @@ -501,6 +513,17 @@ InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : connect(button, SIGNAL(clicked()), SLOT(insert())); } +void InsertSpannerTool::simulate(float deltaTime) { + SharedObjectPointer spanner = _editor->getValue().value(); + static_cast(spanner.data())->getRenderer()->simulate(deltaTime); +} + +void InsertSpannerTool::render() { + SharedObjectPointer spanner = _editor->getValue().value(); + const float SPANNER_ALPHA = 1.0f; + static_cast(spanner.data())->getRenderer()->render(SPANNER_ALPHA); +} + bool InsertSpannerTool::appliesTo(const AttributePointer& attribute) const { return attribute->inherits("SharedObjectSetAttribute"); } diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index e3574b9f67..a688a01cec 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -50,6 +50,7 @@ private slots: void alignGridPosition(); void updateTool(); + void simulate(float deltaTime); void render(); private: @@ -86,6 +87,8 @@ public: virtual bool appliesTo(const AttributePointer& attribute) const; + virtual void simulate(float deltaTime); + /// Renders the tool's interface, if any. virtual void render(); @@ -143,6 +146,10 @@ public: InsertSpannerTool(MetavoxelEditor* editor); + virtual void simulate(float deltaTime); + + virtual void render(); + virtual bool appliesTo(const AttributePointer& attribute) const; private slots: diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 61e300eae5..bf2cdd1e96 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -81,6 +81,10 @@ int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { return 0; } +const QMetaObject* Bitstream::getMetaObject(const QByteArray& className) { + return getMetaObjects().value(className); +} + QList Bitstream::getMetaObjectSubClasses(const QMetaObject* metaObject) { return getMetaObjectSubClasses().values(metaObject); } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 9a3ef95d96..74cb7de073 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -196,6 +196,9 @@ public: /// \return zero; the function only returns a value so that it can be used in static initialization static int registerTypeStreamer(int type, TypeStreamer* streamer); + /// Returns the meta-object registered under the supplied class name, if any. + static const QMetaObject* getMetaObject(const QByteArray& className); + /// Returns the list of registered subclasses for the supplied meta-object. static QList getMetaObjectSubClasses(const QMetaObject* metaObject); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index a746fda102..c8c3dbf577 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -19,6 +19,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelGuide) REGISTER_META_OBJECT(ScriptedMetavoxelGuide) REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) REGISTER_META_OBJECT(Spanner) +REGISTER_META_OBJECT(StaticModel) MetavoxelData::MetavoxelData() : _size(1.0f) { } @@ -707,7 +708,9 @@ AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const { return AttributeValue(visitor.getOutputs().at(index)); } -Spanner::Spanner() : _lastVisit(0) { +Spanner::Spanner() : + _lastVisit(0), + _renderer(NULL) { } void Spanner::setBounds(const Box& bounds) { @@ -726,3 +729,70 @@ bool Spanner::testAndSetVisited(int visit) { return true; } +SpannerRenderer* Spanner::getRenderer() { + if (!_renderer) { + QByteArray className = getRendererClassName(); + const QMetaObject* metaObject = Bitstream::getMetaObject(className); + if (!metaObject) { + qDebug() << "Unknown class name:" << className; + metaObject = &SpannerRenderer::staticMetaObject; + } + _renderer = static_cast(metaObject->newInstance()); + _renderer->setParent(this); + _renderer->init(this); + } + return _renderer; +} + +QByteArray Spanner::getRendererClassName() const { + return "SpannerRendererer"; +} + +SpannerRenderer::SpannerRenderer() { +} + +void SpannerRenderer::init(Spanner* spanner) { + // nothing by default +} + +void SpannerRenderer::simulate(float deltaTime) { + // nothing by default +} + +void SpannerRenderer::render(float alpha) { + // nothing by default +} + +Transformable::Transformable() : _scale(1.0f) { +} + +void Transformable::setTranslation(const glm::vec3& translation) { + if (_translation != translation) { + emit translationChanged(_translation = translation); + } +} + +void Transformable::setRotation(const glm::vec3& rotation) { + if (_rotation != rotation) { + emit rotationChanged(_rotation = rotation); + } +} + +void Transformable::setScale(float scale) { + if (_scale != scale) { + emit scaleChanged(_scale = scale); + } +} + +StaticModel::StaticModel() { +} + +void StaticModel::setURL(const QUrl& url) { + if (_url != url) { + emit urlChanged(_url = url); + } +} + +QByteArray StaticModel::getRendererClassName() const { + return "StaticModelRenderer"; +} diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index a0249216f6..b06825ca70 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -28,6 +28,7 @@ class MetavoxelNode; class MetavoxelVisitation; class MetavoxelVisitor; class NetworkValue; +class SpannerRenderer; /// The base metavoxel representation shared between server and client. class MetavoxelData { @@ -249,11 +250,11 @@ public: /// An object that spans multiple octree cells. class Spanner : public SharedObject { Q_OBJECT - Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged) + Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) public: - Q_INVOKABLE Spanner(); + Spanner(); void setBounds(const Box& bounds); const Box& getBounds() const { return _bounds; } @@ -262,15 +263,95 @@ public: /// If we haven't, sets the last visit identifier and returns true. bool testAndSetVisited(int visit); + /// Returns a pointer to the renderer, creating it if necessary. + SpannerRenderer* getRenderer(); + signals: void boundsWillChange(); void boundsChanged(const Box& bounds); +protected: + + /// Returns the name of the class to instantiate in order to render this spanner. + virtual QByteArray getRendererClassName() const; + private: Box _bounds; int _lastVisit; ///< the identifier of the last visit + SpannerRenderer* _renderer; +}; + +/// Base class for objects that can render spanners. +class SpannerRenderer : public QObject { + Q_OBJECT + +public: + + Q_INVOKABLE SpannerRenderer(); + + virtual void init(Spanner* spanner); + virtual void simulate(float deltaTime); + virtual void render(float alpha); +}; + +/// An object with a 3D transform. +class Transformable : public Spanner { + Q_OBJECT + Q_PROPERTY(glm::vec3 translation MEMBER _translation WRITE setTranslation NOTIFY translationChanged) + Q_PROPERTY(glm::vec3 rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged) + Q_PROPERTY(float scale MEMBER _scale WRITE setScale NOTIFY scaleChanged) + +public: + + Transformable(); + + void setTranslation(const glm::vec3& translation); + const glm::vec3& getTranslation() const { return _translation; } + + void setRotation(const glm::vec3& rotation); + const glm::vec3& getRotation() const { return _rotation; } + + void setScale(float scale); + float getScale() const { return _scale; } + +signals: + + void translationChanged(const glm::vec3& translation); + void rotationChanged(const glm::vec3& rotation); + void scaleChanged(float scale); + +private: + + glm::vec3 _translation; + glm::vec3 _rotation; + float _scale; +}; + +/// A static 3D model loaded from the network. +class StaticModel : public Transformable { + Q_OBJECT + Q_PROPERTY(QUrl url MEMBER _url WRITE setURL NOTIFY urlChanged) + +public: + + Q_INVOKABLE StaticModel(); + + void setURL(const QUrl& url); + const QUrl& getURL() const { return _url; } + +signals: + + void urlChanged(const QUrl& url); + +protected: + + virtual QByteArray getRendererClassName() const; + +private: + + QUrl _url; }; #endif /* defined(__interface__MetavoxelData__) */ diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 37f7be54c7..7482d0efb6 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -93,6 +93,7 @@ SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nulla } } connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); + updateType(); } void SharedObjectEditor::setObject(const SharedObjectPointer& object) { @@ -142,6 +143,9 @@ void SharedObjectEditor::updateType() { static_cast(layout())->addLayout(form); for (int i = QObject::staticMetaObject.propertyCount(); i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); + if (!property.isDesignable()) { + continue; + } if (oldMetaObject && i < oldMetaObject->propertyCount() && getOwningAncestor(metaObject, i) == getOwningAncestor(oldMetaObject, i)) { // copy the state of the shared ancestry From 55e2ebd92f60de93a647d2c55026a063c4587f4c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Feb 2014 13:58:44 -0800 Subject: [PATCH 10/48] More spanner bits. --- interface/src/Menu.cpp | 1 + interface/src/renderer/Model.cpp | 4 +- interface/src/ui/MetavoxelEditor.cpp | 19 ++++++-- interface/src/ui/MetavoxelEditor.h | 4 +- libraries/metavoxels/src/MetavoxelUtil.cpp | 51 +++++++++++++++++++--- libraries/metavoxels/src/MetavoxelUtil.h | 31 +++++++++++-- libraries/metavoxels/src/SharedObject.cpp | 25 ++++++++++- libraries/metavoxels/src/SharedObject.h | 1 + 8 files changed, 117 insertions(+), 19 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c61b4cbdaf..a836d462c1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1088,6 +1088,7 @@ void Menu::showMetavoxelEditor() { _MetavoxelEditor = new MetavoxelEditor(); } _MetavoxelEditor->raise(); + _MetavoxelEditor->activateWindow(); } void Menu::audioMuteToggled() { diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 48e1d0f70c..d8188dab63 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -276,7 +276,7 @@ bool Model::render(float alpha) { // render opaque meshes with alpha testing glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.5f); + glAlphaFunc(GL_GREATER, 0.5f * alpha); renderMeshes(alpha, false); @@ -916,7 +916,7 @@ void Model::renderMeshes(float alpha, bool translucent) { if (!mesh.colors.isEmpty()) { glEnableClientState(GL_COLOR_ARRAY); } else { - glColor3f(1.0f, 1.0f, 1.0f); + glColor4f(1.0f, 1.0f, 1.0f, alpha); } if (!mesh.texCoords.isEmpty()) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 003d283795..3b3333e267 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -29,7 +29,7 @@ enum GridPlane { const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); MetavoxelEditor::MetavoxelEditor() : - QDialog(Application::getInstance()->getGLWidget()) { + QWidget(Application::getInstance()->getGLWidget(), Qt::Tool | Qt::WindowStaysOnTopHint) { setWindowTitle("Metavoxel Editor"); setAttribute(Qt::WA_DeleteOnClose); @@ -519,9 +519,20 @@ void InsertSpannerTool::simulate(float deltaTime) { } void InsertSpannerTool::render() { - SharedObjectPointer spanner = _editor->getValue().value(); - const float SPANNER_ALPHA = 1.0f; - static_cast(spanner.data())->getRenderer()->render(SPANNER_ALPHA); + Spanner* spanner = static_cast(_editor->getValue().value().data()); + Transformable* transformable = qobject_cast(spanner); + if (transformable) { + // find the intersection of the mouse ray with the grid and place the transformable there + glm::quat rotation = _editor->getGridRotation(); + glm::quat inverseRotation = glm::inverse(rotation); + glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); + glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); + float position = _editor->getGridPosition(); + float distance = (position - rayOrigin.z) / rayDirection.z; + transformable->setTranslation(rotation * glm::vec3(glm::vec2(rayOrigin + rayDirection * distance), position)); + } + const float SPANNER_ALPHA = 0.25f; + spanner->getRenderer()->render(SPANNER_ALPHA); } bool InsertSpannerTool::appliesTo(const AttributePointer& attribute) const { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index a688a01cec..1709d07497 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -9,8 +9,8 @@ #ifndef __interface__MetavoxelEditor__ #define __interface__MetavoxelEditor__ -#include #include +#include #include "renderer/ProgramObject.h" @@ -24,7 +24,7 @@ class QScrollArea; class MetavoxelTool; /// Allows editing metavoxels. -class MetavoxelEditor : public QDialog { +class MetavoxelEditor : public QWidget { Q_OBJECT public: diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index f7c2a92b75..22c17abf12 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -18,6 +17,7 @@ #include #include #include +#include #include #include @@ -54,6 +54,7 @@ public: DoubleEditor::DoubleEditor(QWidget* parent) : QDoubleSpinBox(parent) { setMinimum(-FLT_MAX); + setMaximum(FLT_MAX); } DelegatingItemEditorFactory::DelegatingItemEditorFactory() : @@ -117,6 +118,12 @@ static QItemEditorCreatorBase* createQColorEditorCreator() { return creator; } +static QItemEditorCreatorBase* createQUrlEditorCreator() { + QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + static QItemEditorCreatorBase* createVec3EditorCreator() { QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); @@ -132,6 +139,7 @@ static QItemEditorCreatorBase* createParameterizedURLEditorCreator() { static QItemEditorCreatorBase* doubleEditorCreator = createDoubleEditorCreator(); static QItemEditorCreatorBase* qMetaObjectEditorCreator = createQMetaObjectEditorCreator(); static QItemEditorCreatorBase* qColorEditorCreator = createQColorEditorCreator(); +static QItemEditorCreatorBase* qUrlEditorCreator = createQUrlEditorCreator(); static QItemEditorCreatorBase* vec3EditorCreator = createVec3EditorCreator(); static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator(); @@ -211,6 +219,36 @@ void QColorEditor::selectColor() { } } +QUrlEditor::QUrlEditor(QWidget* parent) : + QComboBox(parent) { + + setEditable(true); + setInsertPolicy(InsertAtTop); + + // populate initial URL list from settings + addItems(QSettings().value("editorURLs").toStringList()); + + connect(this, SIGNAL(activated(const QString&)), SLOT(updateURL(const QString&))); + connect(model(), SIGNAL(rowsInserted(const QModelIndex&,int,int)), SLOT(updateSettings())); +} + +void QUrlEditor::setURL(const QUrl& url) { + setCurrentText((_url = url).toString()); +} + +void QUrlEditor::updateURL(const QString& text) { + emit urlChanged(_url = text); +} + +void QUrlEditor::updateSettings() { + QStringList urls; + const int MAX_STORED_URLS = 10; + for (int i = 0, size = qMin(MAX_STORED_URLS, count()); i < size; i++) { + urls.append(itemText(i)); + } + QSettings().setValue("editorURLs", urls); +} + Vec3Editor::Vec3Editor(QWidget* parent) : QWidget(parent) { QHBoxLayout* layout = new QHBoxLayout(); layout->setContentsMargins(QMargins()); @@ -235,6 +273,7 @@ void Vec3Editor::updateVector() { QDoubleSpinBox* Vec3Editor::createComponentBox() { QDoubleSpinBox* box = new QDoubleSpinBox(); box->setMinimum(-FLT_MAX); + box->setMaximum(FLT_MAX); box->setMaximumWidth(100); connect(box, SIGNAL(valueChanged(double)), SLOT(updateVector())); return box; @@ -287,8 +326,9 @@ ParameterizedURLEditor::ParameterizedURLEditor(QWidget* parent) : lineContainer->setLayout(lineLayout); lineLayout->setContentsMargins(QMargins()); - lineLayout->addWidget(_line = new QLineEdit(), 1); - connect(_line, SIGNAL(textChanged(const QString&)), SLOT(updateURL())); + lineLayout->addWidget(&_urlEditor, 1); + connect(&_urlEditor, SIGNAL(urlChanged(const QUrl&)), SLOT(updateURL())); + connect(&_urlEditor, SIGNAL(urlChanged(const QUrl&)), SLOT(updateParameters())); QPushButton* refresh = new QPushButton("..."); connect(refresh, SIGNAL(clicked(bool)), SLOT(updateParameters())); @@ -296,8 +336,7 @@ ParameterizedURLEditor::ParameterizedURLEditor(QWidget* parent) : } void ParameterizedURLEditor::setURL(const ParameterizedURL& url) { - _url = url; - _line->setText(url.getURL().toString()); + _urlEditor.setURL((_url = url).getURL()); updateParameters(); } @@ -314,7 +353,7 @@ void ParameterizedURLEditor::updateURL() { widget->property("parameterName").toString()), widgetProperty.read(widget)); } } - emit urlChanged(_url = ParameterizedURL(_line->text(), parameters)); + emit urlChanged(_url = ParameterizedURL(_urlEditor.getURL(), parameters)); if (_program) { _program->disconnect(this); } diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 37a6c8bcda..19fd41e826 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -10,6 +10,7 @@ #define __interface__MetavoxelUtil__ #include +#include #include #include #include @@ -21,9 +22,7 @@ #include "Bitstream.h" class QByteArray; -class QComboBox; class QDoubleSpinBox; -class QLineEdit; class QPushButton; class HifiSockAddr; @@ -107,6 +106,32 @@ private: QColor _color; }; +/// Editor for URL values. +class QUrlEditor : public QComboBox { + Q_OBJECT + Q_PROPERTY(QUrl url READ getURL WRITE setURL NOTIFY urlChanged USER true) + +public: + + QUrlEditor(QWidget* parent = NULL); + + void setURL(const QUrl& url); + const QUrl& getURL() { return _url; } + +signals: + + void urlChanged(const QUrl& url); + +private slots: + + void updateURL(const QString& text); + void updateSettings(); + +private: + + QUrl _url; +}; + /// Editor for vector values. class Vec3Editor : public QWidget { Q_OBJECT @@ -200,7 +225,7 @@ private: ParameterizedURL _url; QSharedPointer _program; - QLineEdit* _line; + QUrlEditor _urlEditor; }; #endif /* defined(__interface__MetavoxelUtil__) */ diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 7482d0efb6..2393785702 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -130,13 +130,17 @@ void SharedObjectEditor::updateType() { } delete form; } + QObject* oldObject = static_cast(_object.data()); + const QMetaObject* oldMetaObject = NULL; + if (oldObject) { + oldMetaObject = oldObject->metaObject(); + oldObject->disconnect(this); + } const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value(); if (metaObject == NULL) { _object.reset(); return; } - QObject* oldObject = static_cast(_object.data()); - const QMetaObject* oldMetaObject = oldObject ? oldObject->metaObject() : NULL; QObject* newObject = metaObject->newInstance(); QFormLayout* form = new QFormLayout(); @@ -162,6 +166,10 @@ void SharedObjectEditor::updateType() { if (widgetProperty.hasNotifySignal()) { connect(widget, signal(widgetProperty.notifySignal().methodSignature()), SLOT(propertyChanged())); } + if (property.hasNotifySignal()) { + widget->setProperty("notifySignalIndex", property.notifySignalIndex()); + connect(newObject, signal(property.notifySignal().methodSignature()), SLOT(updateProperty())); + } } } _object = static_cast(newObject); @@ -181,3 +189,16 @@ void SharedObjectEditor::propertyChanged() { property.write(object, widget->property(valuePropertyName)); } } + +void SharedObjectEditor::updateProperty() { + QFormLayout* form = static_cast(layout()->itemAt(1)); + for (int i = 0; i < form->rowCount(); i++) { + QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); + if (widget->property("notifySignalIndex").toInt() != senderSignalIndex()) { + continue; + } + QMetaProperty property = _object->metaObject()->property(widget->property("propertyIndex").toInt()); + QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); + widget->setProperty(valuePropertyName, property.read(_object.data())); + } +} diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 5a33b64e3d..008ea02e4b 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -168,6 +168,7 @@ private slots: void updateType(); void propertyChanged(); + void updateProperty(); private: From 0138ecb3b2c7538a15142cb26f197bed1764be1e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 19 Feb 2014 11:35:45 -0800 Subject: [PATCH 11/48] Layout tweaks. --- interface/src/ui/MetavoxelEditor.cpp | 10 ++++++++++ interface/src/ui/MetavoxelEditor.h | 2 ++ libraries/metavoxels/src/MetavoxelUtil.cpp | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 3b3333e267..5c099e3e41 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -92,6 +92,7 @@ MetavoxelEditor::MetavoxelEditor() : _value->setLayout(valueLayout); valueLayout->addWidget(_valueArea = new QScrollArea()); + _valueArea->setMinimumHeight(200); _valueArea->setWidgetResizable(true); addTool(new BoxSetTool(this)); @@ -181,6 +182,7 @@ void MetavoxelEditor::selectedAttributeChanged() { } QWidget* editor = attribute->createEditor(); if (editor) { + editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); _valueArea->setWidget(editor); } } @@ -539,6 +541,14 @@ bool InsertSpannerTool::appliesTo(const AttributePointer& attribute) const { return attribute->inherits("SharedObjectSetAttribute"); } +bool InsertSpannerTool::eventFilter(QObject* watched, QEvent* event) { + if (event->type() == QEvent::MouseButtonPress) { + insert(); + return true; + } + return false; +} + void InsertSpannerTool::insert() { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); if (!attribute) { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 1709d07497..e213be8d2c 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -152,6 +152,8 @@ public: virtual bool appliesTo(const AttributePointer& attribute) const; + virtual bool eventFilter(QObject* watched, QEvent* event); + private slots: void insert(); diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 22c17abf12..16f561638b 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -274,7 +274,7 @@ QDoubleSpinBox* Vec3Editor::createComponentBox() { QDoubleSpinBox* box = new QDoubleSpinBox(); box->setMinimum(-FLT_MAX); box->setMaximum(FLT_MAX); - box->setMaximumWidth(100); + box->setMinimumWidth(50); connect(box, SIGNAL(valueChanged(double)), SLOT(updateVector())); return box; } From 25ae81e2deff52e8e18653b3abf94ba5dd37fca1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 19 Feb 2014 15:25:21 -0800 Subject: [PATCH 12/48] More work on spanners. --- interface/src/ui/MetavoxelEditor.cpp | 30 +++++++++++++++++++ interface/src/ui/MetavoxelEditor.h | 17 +++++++++++ libraries/metavoxels/src/MetavoxelData.cpp | 17 +++++++++++ libraries/metavoxels/src/MetavoxelData.h | 4 +++ .../metavoxels/src/MetavoxelMessages.cpp | 8 +++++ libraries/metavoxels/src/MetavoxelMessages.h | 17 ++++++++++- libraries/metavoxels/src/MetavoxelUtil.cpp | 23 ++++---------- libraries/metavoxels/src/MetavoxelUtil.h | 12 +++----- 8 files changed, 102 insertions(+), 26 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 5c099e3e41..87510e9347 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -99,6 +99,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new GlobalSetTool(this)); addTool(new InsertSpannerTool(this)); addTool(new RemoveSpannerTool(this)); + addTool(new ClearSpannersTool(this)); updateAttributes(); @@ -566,3 +567,32 @@ RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const { return attribute->inherits("SharedObjectSetAttribute"); } + +bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) { + if (event->type() == QEvent::MouseButtonPress) { + + return true; + } + return false; +} + +ClearSpannersTool::ClearSpannersTool(MetavoxelEditor* editor) : + MetavoxelTool(editor, "Clear Spanners", false) { + + QPushButton* button = new QPushButton("Clear"); + layout()->addWidget(button); + connect(button, SIGNAL(clicked()), SLOT(clear())); +} + +bool ClearSpannersTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("SharedObjectSetAttribute"); +} + +void ClearSpannersTool::clear() { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); + if (!attribute) { + return; + } + MetavoxelEditMessage message = { QVariant::fromValue(ClearSpannersEdit(attribute)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index e213be8d2c..1bccfae9f5 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -168,6 +168,23 @@ public: RemoveSpannerTool(MetavoxelEditor* editor); virtual bool appliesTo(const AttributePointer& attribute) const; + + virtual bool eventFilter(QObject* watched, QEvent* event); +}; + +/// Allows removing all spanners from the scene. +class ClearSpannersTool : public MetavoxelTool { + Q_OBJECT + +public: + + ClearSpannersTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + +private slots: + + void clear(); }; #endif /* defined(__interface__MetavoxelEditor__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index c8c3dbf577..dbe5b3a21d 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -100,6 +100,7 @@ private: const AttributePointer& _attribute; const Box& _bounds; + float _longestSide; const SharedObjectPointer& _object; }; @@ -107,14 +108,27 @@ InsertVisitor::InsertVisitor(const AttributePointer& attribute, const Box& bound MetavoxelVisitor(QVector() << attribute, QVector() << attribute), _attribute(attribute), _bounds(bounds), + _longestSide(bounds.getLongestSide()), _object(object) { } bool InsertVisitor::visit(MetavoxelInfo& info) { + Box bounds = info.getBounds(); + if (!bounds.intersects(_bounds)) { + return false; + } + if (info.size > _longestSide) { + return true; + } + info.outputValues[0] = AttributeValue(_attribute); return false; } void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object) { + // expand to fit the entire bounds + while (!getBounds().contains(bounds)) { + expand(); + } InsertVisitor visitor(attribute, bounds, object); guide(visitor); } @@ -122,6 +136,9 @@ void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds, void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object) { } +void MetavoxelData::clear(const AttributePointer& attribute) { +} + const int X_MAXIMUM_FLAG = 1; const int Y_MAXIMUM_FLAG = 2; const int Z_MAXIMUM_FLAG = 4; diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index b06825ca70..3b6637d2f9 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -51,6 +51,8 @@ public: void remove(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object); + void clear(const AttributePointer& attribute); + /// Expands the tree, increasing its capacity in all dimensions. void expand(); @@ -128,6 +130,8 @@ public: QVector inputValues; QVector outputValues; bool isLeaf; + + Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); } }; /// Interface for visitors to metavoxels. diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 56336ffa16..3b41a2e891 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -116,3 +116,11 @@ RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) void RemoveSpannerEdit::apply(MetavoxelData& data) const { } + +ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : + attribute(attribute) { +} + +void ClearSpannersEdit::apply(MetavoxelData& data) const { + data.clear(attribute); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 65bee27d7d..d6f07c2966 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -133,7 +133,7 @@ public: DECLARE_STREAMABLE_METATYPE(InsertSpannerEdit) -/// An edit that removes a a spanner from the tree. +/// An edit that removes a spanner from the tree. class RemoveSpannerEdit : public MetavoxelEdit { STREAMABLE @@ -149,4 +149,19 @@ public: DECLARE_STREAMABLE_METATYPE(RemoveSpannerEdit) +/// An edit that clears all spanners from the tree. +class ClearSpannersEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM AttributePointer attribute; + + ClearSpannersEdit(const AttributePointer& attribute = AttributePointer()); + + virtual void apply(MetavoxelData& data) const; +}; + +DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit) + #endif /* defined(__interface__MetavoxelMessages__) */ diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 16f561638b..2e93fb1804 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -21,9 +21,6 @@ #include #include -#include -#include - #include "MetavoxelUtil.h" #include "ScriptCache.h" @@ -143,20 +140,6 @@ static QItemEditorCreatorBase* qUrlEditorCreator = createQUrlEditorCreator(); static QItemEditorCreatorBase* vec3EditorCreator = createVec3EditorCreator(); static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator(); -QUuid readSessionID(const QByteArray& data, const SharedNodePointer& sendingNode, int& headerPlusIDSize) { - // get the header size - int headerSize = numBytesForPacketHeader(data); - - // read the session id - const int UUID_BYTES = 16; - headerPlusIDSize = headerSize + UUID_BYTES; - if (data.size() < headerPlusIDSize) { - qWarning() << "Metavoxel data too short [size=" << data.size() << ", sendingNode=" << sendingNode << "]\n"; - return QUuid(); - } - return QUuid::fromRfc4122(QByteArray::fromRawData(data.constData() + headerSize, UUID_BYTES)); -} - QByteArray signal(const char* signature) { static QByteArray prototype = SIGNAL(dummyMethod()); QByteArray signal = prototype; @@ -173,6 +156,12 @@ bool Box::contains(const Box& other) const { other.minimum.z >= minimum.z && other.maximum.z <= maximum.z; } +bool Box::intersects(const Box& other) const { + return other.maximum.x >= minimum.x && other.minimum.x <= maximum.x && + other.maximum.y >= minimum.y && other.minimum.y <= maximum.y && + other.maximum.z >= minimum.z && other.minimum.z <= maximum.z; +} + QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(QMargins()); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 19fd41e826..4b3fb43523 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -13,10 +13,8 @@ #include #include #include -#include #include -#include #include #include "Bitstream.h" @@ -25,14 +23,8 @@ class QByteArray; class QDoubleSpinBox; class QPushButton; -class HifiSockAddr; class NetworkProgram; -/// Reads and returns the session ID from a datagram. -/// \param[out] headerPlusIDSize the size of the header (including the session ID) within the data -/// \return the session ID, or a null ID if invalid (in which case a warning will be logged) -QUuid readSessionID(const QByteArray& data, const SharedNodePointer& sendingNode, int& headerPlusIDSize); - /// Performs the runtime equivalent of Qt's SIGNAL macro, which is to attach a prefix to the signature. QByteArray signal(const char* signature); @@ -48,6 +40,10 @@ public: Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); bool contains(const Box& other) const; + + bool intersects(const Box& other) const; + + float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); } }; DECLARE_STREAMABLE_METATYPE(Box) From f017d0472fa89528a0c9d7870b89f9375957d448 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 19 Feb 2014 18:17:26 -0800 Subject: [PATCH 13/48] More work on spanners. --- interface/src/MetavoxelSystem.h | 2 + .../metavoxels/src/AttributeRegistry.cpp | 8 ++ libraries/metavoxels/src/AttributeRegistry.h | 3 + libraries/metavoxels/src/MetavoxelData.cpp | 82 +++++++++++++++---- libraries/metavoxels/src/MetavoxelData.h | 11 ++- .../metavoxels/src/MetavoxelMessages.cpp | 3 +- 6 files changed, 89 insertions(+), 20 deletions(-) diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 7cc8c114fd..30ffd308df 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -83,6 +83,8 @@ public: void simulate(float deltaTime, MetavoxelVisitor& visitor); + void render(); + virtual int parseData(const QByteArray& packet); private slots: diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 22a629dd1a..bd83987666 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -235,6 +235,14 @@ SharedObjectSetAttribute::SharedObjectSetAttribute(const QString& name, const QM _metaObject(metaObject) { } +void SharedObjectSetAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + in >> *((SharedObjectSet*)&value); +} + +void SharedObjectSetAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + out << decodeInline(value); +} + bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const { for (int i = 0; i < MERGE_COUNT; i++) { if (!decodeInline(children[i]).isEmpty()) { diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 1bcadf7f6c..2e029f8201 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -297,6 +297,9 @@ public: const QMetaObject* getMetaObject() const { return _metaObject; } + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + virtual bool merge(void*& parent, void* children[]) const; virtual QWidget* createEditor(QWidget* parent = NULL) const; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index dbe5b3a21d..ab7309101b 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -54,7 +54,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { const QVector& outputs = visitor.getOutputs(); MetavoxelVisitation firstVisitation = { NULL, visitor, QVector(inputs.size() + 1), QVector(outputs.size()), { glm::vec3(_size, _size, _size) * -0.5f, _size, - QVector(inputs.size() + 1), QVector(outputs.size()) } }; + QVector(inputs.size() + 1), QVector(outputs.size()) } }; for (int i = 0; i < inputs.size(); i++) { MetavoxelNode* node = _roots.value(inputs.at(i)); firstVisitation.inputNodes[i] = node; @@ -71,7 +71,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { static_cast(firstVisitation.info.inputValues.last().getInlineValue< SharedObjectPointer>().data())->guide(firstVisitation); for (int i = 0; i < outputs.size(); i++) { - AttributeValue& value = firstVisitation.info.outputValues[i]; + OwnedAttributeValue& value = firstVisitation.info.outputValues[i]; if (!value.getAttribute()) { continue; } @@ -92,7 +92,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { class InsertVisitor : public MetavoxelVisitor { public: - InsertVisitor(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object); + InsertVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); virtual bool visit(MetavoxelInfo& info); @@ -104,39 +104,86 @@ private: const SharedObjectPointer& _object; }; -InsertVisitor::InsertVisitor(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object) : +InsertVisitor::InsertVisitor(const AttributePointer& attribute, const Box& bounds, + float granularity, const SharedObjectPointer& object) : MetavoxelVisitor(QVector() << attribute, QVector() << attribute), _attribute(attribute), _bounds(bounds), - _longestSide(bounds.getLongestSide()), + _longestSide(qMax(bounds.getLongestSide(), granularity)), _object(object) { } bool InsertVisitor::visit(MetavoxelInfo& info) { - Box bounds = info.getBounds(); - if (!bounds.intersects(_bounds)) { + if (!info.getBounds().intersects(_bounds)) { return false; } if (info.size > _longestSide) { return true; } - info.outputValues[0] = AttributeValue(_attribute); + SharedObjectSet set = info.inputValues.at(0).getInlineValue(); + set.insert(_object); + info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); return false; } -void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object) { +void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds, + float granularity, const SharedObjectPointer& object) { // expand to fit the entire bounds while (!getBounds().contains(bounds)) { expand(); } - InsertVisitor visitor(attribute, bounds, object); + InsertVisitor visitor(attribute, bounds, granularity, object); guide(visitor); } -void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object) { +class RemoveVisitor : public MetavoxelVisitor { +public: + + RemoveVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + + virtual bool visit(MetavoxelInfo& info); + +private: + + const AttributePointer& _attribute; + const Box& _bounds; + float _longestSide; + const SharedObjectPointer& _object; +}; + +RemoveVisitor::RemoveVisitor(const AttributePointer& attribute, const Box& bounds, + float granularity, const SharedObjectPointer& object) : + MetavoxelVisitor(QVector() << attribute, QVector() << attribute), + _attribute(attribute), + _bounds(bounds), + _longestSide(qMax(bounds.getLongestSide(), granularity)), + _object(object) { +} + +bool RemoveVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return false; + } + if (info.size > _longestSide) { + return true; + } + SharedObjectSet set = info.inputValues.at(0).getInlineValue(); + set.remove(_object); + info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); + return false; +} + +void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds, + float granularity, const SharedObjectPointer& object) { + RemoveVisitor visitor(attribute, bounds, granularity, object); + guide(visitor); } void MetavoxelData::clear(const AttributePointer& attribute) { + MetavoxelNode* node = _roots.take(attribute); + if (node) { + node->decrementReferenceCount(attribute); + } } const int X_MAXIMUM_FLAG = 1; @@ -490,7 +537,7 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { visitation.info.isLeaf = visitation.allInputNodesLeaves(); bool keepGoing = visitation.visitor.visit(visitation.info); for (int i = 0; i < visitation.outputNodes.size(); i++) { - AttributeValue& value = visitation.info.outputValues[i]; + OwnedAttributeValue& value = visitation.info.outputValues[i]; if (!value.getAttribute()) { continue; } @@ -508,7 +555,7 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { MetavoxelVisitation nextVisitation = { &visitation, visitation.visitor, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()), { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), - QVector(visitation.outputNodes.size()) } }; + QVector(visitation.outputNodes.size()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { for (int j = 0; j < visitation.inputNodes.size(); j++) { MetavoxelNode* node = visitation.inputNodes.at(j); @@ -529,12 +576,12 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { static_cast(nextVisitation.info.inputValues.last().getInlineValue< SharedObjectPointer>().data())->guide(nextVisitation); for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { - AttributeValue& value = nextVisitation.info.outputValues[j]; + OwnedAttributeValue& value = nextVisitation.info.outputValues[j]; if (!value.getAttribute()) { continue; } // replace the child - AttributeValue& parentValue = visitation.info.outputValues[j]; + OwnedAttributeValue& parentValue = visitation.info.outputValues[j]; if (!parentValue.getAttribute()) { // shallow-copy the parent node on first change parentValue = value; @@ -562,7 +609,7 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } for (int i = 0; i < visitation.outputNodes.size(); i++) { - AttributeValue& value = visitation.info.outputValues[i]; + OwnedAttributeValue& value = visitation.info.outputValues[i]; if (value.getAttribute()) { MetavoxelNode* node = visitation.outputNodes.at(i); node->mergeChildren(value.getAttribute()); @@ -725,7 +772,10 @@ AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const { return AttributeValue(visitor.getOutputs().at(index)); } +const float DEFAULT_GRANULARITY = 0.01f; + Spanner::Spanner() : + _granularity(DEFAULT_GRANULARITY), _lastVisit(0), _renderer(NULL) { } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 3b6637d2f9..9443e71882 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -47,9 +47,9 @@ public: /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); - void insert(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object); + void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); - void remove(const AttributePointer& attribute, const Box& bounds, const SharedObjectPointer& object); + void remove(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); void clear(const AttributePointer& attribute); @@ -128,7 +128,7 @@ public: glm::vec3 minimum; ///< the minimum extent of the area covered by the voxel float size; ///< the size of the voxel in all dimensions QVector inputValues; - QVector outputValues; + QVector outputValues; bool isLeaf; Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); } @@ -255,6 +255,7 @@ public: class Spanner : public SharedObject { Q_OBJECT Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) + Q_PROPERTY(float granularity MEMBER _granularity DESIGNABLE false) public: @@ -263,6 +264,9 @@ public: void setBounds(const Box& bounds); const Box& getBounds() const { return _bounds; } + void setGranularity(float granularity) { _granularity = granularity; } + float getGranularity() const { return _granularity; } + /// Checks whether we've visited this object on the current traversal. If we have, returns false. /// If we haven't, sets the last visit identifier and returns true. bool testAndSetVisited(int visit); @@ -283,6 +287,7 @@ protected: private: Box _bounds; + float _granularity; int _lastVisit; ///< the identifier of the last visit SpannerRenderer* _renderer; }; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 3b41a2e891..c66ce61ee9 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -106,7 +106,8 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh } void InsertSpannerEdit::apply(MetavoxelData& data) const { - data.insert(attribute, static_cast(spanner.data())->getBounds(), spanner); + Spanner* spanner = static_cast(this->spanner.data()); + data.insert(attribute, spanner->getBounds(), spanner->getGranularity(), this->spanner); } RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) : From 989a76fe6ebbff3d6dca43a16b2bd409282233f9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Feb 2014 16:56:40 -0800 Subject: [PATCH 14/48] Hooked up Clipboard preview --- interface/src/ui/ClipboardOverlay.cpp | 42 +++++++++++++++++++++++++++ interface/src/ui/ClipboardOverlay.h | 25 ++++++++++++++++ interface/src/ui/Overlays.cpp | 7 ++++- 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 interface/src/ui/ClipboardOverlay.cpp create mode 100644 interface/src/ui/ClipboardOverlay.h diff --git a/interface/src/ui/ClipboardOverlay.cpp b/interface/src/ui/ClipboardOverlay.cpp new file mode 100644 index 0000000000..1eaf5e75f0 --- /dev/null +++ b/interface/src/ui/ClipboardOverlay.cpp @@ -0,0 +1,42 @@ +// +// ClipboardOverlay.cpp +// hifi +// +// Created by Clément Brisset on 2/20/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include + +#include "ClipboardOverlay.h" +#include "../Application.h" + +ClipboardOverlay::ClipboardOverlay() { +} + +ClipboardOverlay::~ClipboardOverlay() { +} + +void ClipboardOverlay::render() { + if (!_visible) { + return; // do nothing if we're not visible + } + + VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem(); + VoxelTree* clipboard = Application::getInstance()->getClipboard(); + if (voxelSystem->getTree() != clipboard) { + voxelSystem->changeTree(clipboard); + } + + glPushMatrix(); + glTranslatef(_position.x, _position.y, _position.z); + glScalef(_size, _size, _size); + + voxelSystem->render(); + + glPopMatrix(); +} \ No newline at end of file diff --git a/interface/src/ui/ClipboardOverlay.h b/interface/src/ui/ClipboardOverlay.h new file mode 100644 index 0000000000..49daa73c20 --- /dev/null +++ b/interface/src/ui/ClipboardOverlay.h @@ -0,0 +1,25 @@ +// +// ClipboardOverlay.h +// hifi +// +// Created by Clément Brisset on 2/20/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__ClipboardOverlay__ +#define __interface__ClipboardOverlay__ + +#include "Volume3DOverlay.h" + +class ClipboardOverlay : public Volume3DOverlay { + Q_OBJECT + +public: + ClipboardOverlay(); + ~ClipboardOverlay(); + + virtual void render(); +}; + + +#endif /* defined(__interface__ClipboardOverlay__) */ diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index c35c4fc5ec..e247486e8e 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -12,7 +12,7 @@ #include "Overlays.h" #include "Sphere3DOverlay.h" #include "TextOverlay.h" - +#include "ClipboardOverlay.h" unsigned int Overlays::_nextOverlayID = 1; @@ -73,6 +73,11 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay->setProperties(properties); created = true; is3D = true; + } else if (type == "clipboard") { + thisOverlay = new ClipboardOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + created = true; } if (created) { From b408e31d91e8c72c7488ed78e960e5c7e4bd3ae8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Feb 2014 17:18:15 -0800 Subject: [PATCH 15/48] More work on spanners. --- interface/src/MetavoxelSystem.cpp | 1 - interface/src/ui/MetavoxelEditor.cpp | 9 ++++ interface/src/ui/MetavoxelEditor.h | 1 + libraries/metavoxels/src/Bitstream.cpp | 55 ++++++++++++++++------ libraries/metavoxels/src/Bitstream.h | 5 ++ libraries/metavoxels/src/MetavoxelData.cpp | 11 +---- libraries/metavoxels/src/MetavoxelData.h | 4 -- libraries/metavoxels/src/SharedObject.cpp | 30 +++++++++++- libraries/metavoxels/src/SharedObject.h | 19 +++++++- 9 files changed, 101 insertions(+), 34 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7365c99f0a..0421ca4747 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -218,7 +218,6 @@ int MetavoxelClient::parseData(const QByteArray& packet) { } void MetavoxelClient::sendData(const QByteArray& data) { - QMutexLocker locker(&_node->getMutex()); NodeList::getInstance()->writeDatagram(data, _node); } diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 87510e9347..832c6b5d39 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -151,6 +151,13 @@ QVariant MetavoxelEditor::getValue() const { return editor ? editor->metaObject()->userProperty().read(editor) : QVariant(); } +void MetavoxelEditor::detachValue() { + SharedObjectEditor* editor = qobject_cast(_valueArea->widget()); + if (editor) { + editor->detachObject(); + } +} + bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { // pass along to the active tool MetavoxelTool* tool = getActiveTool(); @@ -522,6 +529,7 @@ void InsertSpannerTool::simulate(float deltaTime) { } void InsertSpannerTool::render() { + _editor->detachValue(); Spanner* spanner = static_cast(_editor->getValue().value().data()); Transformable* transformable = qobject_cast(spanner); if (transformable) { @@ -532,6 +540,7 @@ void InsertSpannerTool::render() { glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); float position = _editor->getGridPosition(); float distance = (position - rayOrigin.z) / rayDirection.z; + transformable->setTranslation(rotation * glm::vec3(glm::vec2(rayOrigin + rayDirection * distance), position)); } const float SPANNER_ALPHA = 0.25f; diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 1bccfae9f5..5b580129a9 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -38,6 +38,7 @@ public: glm::quat getGridRotation() const; QVariant getValue() const; + void detachValue(); virtual bool eventFilter(QObject* watched, QEvent* event); diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index bf2cdd1e96..919bc4cbc8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -355,17 +355,7 @@ Bitstream& Bitstream::operator>>(QObject*& object) { object = NULL; return *this; } - object = metaObject->newInstance(); - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored(object)) { - continue; - } - const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); - if (streamer) { - property.write(object, streamer->read(*this)); - } - } + readProperties(object = metaObject->newInstance()); return *this; } @@ -476,13 +466,34 @@ Bitstream& Bitstream::operator>(QScriptString& string) { } Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { - return *this << object.data(); + if (!object) { + return *this << (int)0; + } + return *this << object->getID() << (QObject*)object.data(); } Bitstream& Bitstream::operator>(SharedObjectPointer& object) { - QObject* rawObject; - *this >> rawObject; - object = static_cast(rawObject); + int id; + *this >> id; + if (id == 0) { + object = SharedObjectPointer(); + return *this; + } + QPointer& pointer = _transientSharedObjects[id]; + if (pointer) { + const QMetaObject* metaObject; + _metaObjectStreamer >> metaObject; + if (metaObject != pointer->metaObject()) { + qWarning() << "Class mismatch: " << pointer->metaObject()->className() << metaObject->className(); + } + readProperties(pointer.data()); + + } else { + QObject* rawObject; + *this >> rawObject; + pointer = static_cast(rawObject); + } + object = static_cast(pointer.data()); return *this; } @@ -492,6 +503,20 @@ void Bitstream::clearSharedObject() { emit sharedObjectCleared(_sharedObjectStreamer.takePersistentID(object)); } +void Bitstream::readProperties(QObject* object) { + const QMetaObject* metaObject = object->metaObject(); + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored(object)) { + continue; + } + const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); + if (streamer) { + property.write(object, streamer->read(*this)); + } + } +} + QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 74cb7de073..c06c4c3b5f 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -317,6 +318,8 @@ private slots: void clearSharedObject(); private: + + void readProperties(QObject* object); QDataStream& _underlying; quint8 _byte; @@ -328,6 +331,8 @@ private: RepeatedValueStreamer _scriptStringStreamer; RepeatedValueStreamer _sharedObjectStreamer; + QHash > _transientSharedObjects; + static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index c9ada9f82e..5665e70f91 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -548,7 +548,7 @@ void SpannerVisitor::prepare() { } bool SpannerVisitor::visit(MetavoxelInfo& info) { - for (int i = info.inputValues.size() - _spannerInputCount; i < info.inputValues.size(); i++) { + for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); if (spanner->testAndSetVisited()) { @@ -862,9 +862,6 @@ void SpannerRenderer::render(float alpha) { } Transformable::Transformable() : _scale(1.0f) { - connect(this, SIGNAL(translationChanged(const glm::vec3&)), SLOT(updateBounds())); - connect(this, SIGNAL(rotationChanged(const glm::vec3&)), SLOT(updateBounds())); - connect(this, SIGNAL(scaleChanged(float)), SLOT(updateBounds())); } void Transformable::setTranslation(const glm::vec3& translation) { @@ -885,12 +882,6 @@ void Transformable::setScale(float scale) { } } -void Transformable::updateBounds() { - // temporary fake bounds - glm::vec3 scaleVector(_scale, _scale, _scale); - setBounds(Box(_translation - scaleVector, _translation + scaleVector)); -} - StaticModel::StaticModel() { } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 9021213ee7..85bdd54938 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -360,10 +360,6 @@ signals: void rotationChanged(const glm::vec3& rotation); void scaleChanged(float scale); -protected slots: - - virtual void updateBounds(); - private: glm::vec3 _translation; diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 2393785702..7e10068afe 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -18,7 +18,7 @@ REGISTER_META_OBJECT(SharedObject) -SharedObject::SharedObject() : _referenceCount(0) { +SharedObject::SharedObject() : _id(++_lastID), _referenceCount(0) { } void SharedObject::incrementReferenceCount() { @@ -74,6 +74,16 @@ bool SharedObject::equals(const SharedObject* other) const { return true; } +void SharedObject::dump(QDebug debug) const { + debug << this; + const QMetaObject* metaObject = this->metaObject(); + for (int i = 0; i < metaObject->propertyCount(); i++) { + debug << metaObject->property(i).name() << metaObject->property(i).read(this); + } +} + +int SharedObject::_lastID = 0; + SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nullable, QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); layout->setAlignment(Qt::AlignTop); @@ -110,6 +120,22 @@ void SharedObjectEditor::setObject(const SharedObjectPointer& object) { } } +void SharedObjectEditor::detachObject() { + SharedObject* oldObject = _object.data(); + if (!_object.detach()) { + return; + } + oldObject->disconnect(this); + const QMetaObject* metaObject = _object->metaObject(); + + QFormLayout* form = static_cast(layout()->itemAt(1)); + for (int i = 0; i < form->rowCount(); i++) { + QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); + QMetaProperty property = metaObject->property(widget->property("propertyIndex").toInt()); + connect(_object.data(), signal(property.notifySignal().methodSignature()), SLOT(updateProperty())); + } +} + const QMetaObject* getOwningAncestor(const QMetaObject* metaObject, int propertyIndex) { while (propertyIndex < metaObject->propertyOffset()) { metaObject = metaObject->superClass(); @@ -182,7 +208,7 @@ void SharedObjectEditor::propertyChanged() { if (widget != sender()) { continue; } - _object.detach(); + detachObject(); QObject* object = _object.data(); QMetaProperty property = object->metaObject()->property(widget->property("propertyIndex").toInt()); QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 008ea02e4b..94f6c192c5 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -13,6 +13,7 @@ #include #include #include +#include class QComboBox; @@ -24,6 +25,8 @@ public: Q_INVOKABLE SharedObject(); + int getID() { return _id; } + int getReferenceCount() const { return _referenceCount; } void incrementReferenceCount(); void decrementReferenceCount(); @@ -34,6 +37,9 @@ public: /// Tests this object for equality with another. virtual bool equals(const SharedObject* other) const; + // Dumps the contents of this object to the debug output. + virtual void dump(QDebug debug = qDebug()) const; + signals: /// Emitted when the reference count drops to one. @@ -41,7 +47,10 @@ signals: private: + int _id; int _referenceCount; + + static int _lastID; }; /// A pointer to a shared object. @@ -54,7 +63,8 @@ public: T* data() const { return _data; } - void detach(); + /// "Detaches" this object, making a new copy if its reference count is greater than one. + bool detach(); void swap(SharedObjectPointerTemplate& other) { qSwap(_data, other._data); } @@ -98,11 +108,13 @@ template inline SharedObjectPointerTemplate::~SharedObjectPointerTem } } -template inline void SharedObjectPointerTemplate::detach() { +template inline bool SharedObjectPointerTemplate::detach() { if (_data && _data->getReferenceCount() > 1) { _data->decrementReferenceCount(); (_data = _data->clone())->incrementReferenceCount(); + return true; } + return false; } template inline void SharedObjectPointerTemplate::reset() { @@ -160,6 +172,9 @@ public: const SharedObjectPointer& getObject() const { return _object; } + /// "Detaches" the object pointer, copying it if anyone else is holding a reference. + void detachObject(); + public slots: void setObject(const SharedObjectPointer& object); From 7026436dee33e332f49ba315746f0c6a7bfc00da Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Feb 2014 17:32:59 -0800 Subject: [PATCH 16/48] first cut at touch support for orbit and pan, skeleton support for pan --- examples/editVoxels.js | 409 +++++++++++++++++++++++++++++++---------- 1 file changed, 310 insertions(+), 99 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 81e3000566..59cb56fab6 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -31,6 +31,11 @@ var key_shift = false; var isAdding = false; var isExtruding = false; var isOrbiting = false; +var isOrbitingFromTouch = false; +var isPanning = false; +var isPanningFromTouch = false; +var touchPointsToOrbit = 2; // you can change these, but be mindful that on some track pads 2 touch points = right click+drag +var touchPointsToPan = 3; var orbitAzimuth = 0.0; var orbitAltitude = 0.0; var orbitCenter = { x: 0, y: 0, z: 0 }; @@ -68,6 +73,7 @@ var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelit var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Switches+and+sliders/toggle+switch+-+medium.raw"); var audioOptions = new AudioInjectionOptions();
 audioOptions.volume = 0.5; +audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0 } ); // start with audio slightly above the avatar var editToolsOn = false; // starts out off @@ -326,7 +332,7 @@ var trackLastMouseY = 0; var trackAsDelete = false; var trackAsRecolor = false; var trackAsEyedropper = false; -var trackAsOrbit = false; +var trackAsOrbitOrPan = false; function calculateVoxelFromIntersection(intersection, operation) { //print("calculateVoxelFromIntersection() operation="+operation); @@ -477,7 +483,7 @@ function showPreviewVoxel() { solid: true, alpha: 0.8 }); - } else if (trackAsOrbit) { + } else if (trackAsOrbitOrPan) { Overlays.editOverlay(voxelPreview, { visible: false }); } else if (!isExtruding) { guidePosition = calculateVoxelFromIntersection(intersection,"add"); @@ -548,16 +554,22 @@ function showPreviewGuides() { } function trackMouseEvent(event) { - trackLastMouseX = event.x; - trackLastMouseY = event.y; - trackAsDelete = event.isControl; - trackAsRecolor = event.isShifted; - trackAsEyedropper = event.isMeta; - trackAsOrbit = event.isAlt; - showPreviewGuides(); + if (!trackAsOrbitOrPan) { + trackLastMouseX = event.x; + trackLastMouseY = event.y; + trackAsDelete = event.isControl; + trackAsRecolor = event.isShifted; + trackAsEyedropper = event.isMeta; + trackAsOrbitOrPan = event.isAlt; // TODO: double check this...?? + showPreviewGuides(); + } } function trackKeyPressEvent(event) { + if (!editToolsOn) { + return; + } + if (event.text == "CONTROL") { trackAsDelete = true; moveTools(); @@ -571,41 +583,13 @@ function trackKeyPressEvent(event) { moveTools(); } if (event.text == "ALT") { - trackAsOrbit = true; + trackAsOrbitOrPan = true; moveTools(); } showPreviewGuides(); } function trackKeyReleaseEvent(event) { - if (event.text == "ESC") { - pointerVoxelScaleSet = false; - } - if (event.text == "-") { - thumbX -= thumbDeltaPerStep; - calcScaleFromThumb(thumbX); - } - if (event.text == "+") { - thumbX += thumbDeltaPerStep; - calcScaleFromThumb(thumbX); - } - if (event.text == "CONTROL") { - trackAsDelete = false; - moveTools(); - } - if (event.text == "SHIFT") { - trackAsRecolor = false; - moveTools(); - } - if (event.text == "META") { - trackAsEyedropper = false; - moveTools(); - } - if (event.text == "ALT") { - trackAsOrbit = false; - moveTools(); - } - // on TAB release, toggle our tool state if (event.text == "TAB") { editToolsOn = !editToolsOn; @@ -613,14 +597,108 @@ function trackKeyReleaseEvent(event) { Audio.playSound(clickSound, audioOptions); } - // on F1 toggle the preview mode between cubes and lines - if (event.text == "F1") { - previewAsVoxel = !previewAsVoxel; - } + if (editToolsOn) { + if (event.text == "ESC") { + pointerVoxelScaleSet = false; + } + if (event.text == "-") { + thumbX -= thumbDeltaPerStep; + calcScaleFromThumb(thumbX); + } + if (event.text == "+") { + thumbX += thumbDeltaPerStep; + calcScaleFromThumb(thumbX); + } + if (event.text == "CONTROL") { + trackAsDelete = false; + moveTools(); + } + if (event.text == "SHIFT") { + trackAsRecolor = false; + moveTools(); + } + if (event.text == "META") { + trackAsEyedropper = false; + moveTools(); + } + if (event.text == "ALT") { + trackAsOrbitOrPan = false; + moveTools(); + } + + // on F1 toggle the preview mode between cubes and lines + if (event.text == "F1") { + previewAsVoxel = !previewAsVoxel; + } - showPreviewGuides(); + showPreviewGuides(); + } } +function startOrbitMode(event) { + mouseX = event.x; + mouseY = event.y; + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Voxels.findRayIntersection(pickRay); + + // start orbit camera! + var cameraPosition = Camera.getPosition(); + oldMode = Camera.getMode(); + Camera.setMode("independent"); + Camera.keepLookingAt(intersection.intersection); + // get position for initial azimuth, elevation + orbitCenter = intersection.intersection; + var orbitVector = Vec3.subtract(cameraPosition, orbitCenter); + orbitRadius = Vec3.length(orbitVector); + orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x); + orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector)); + + print("startOrbitMode..."); +} + +function handleOrbitingMove(event) { + var cameraOrientation = Camera.getOrientation(); + var origEulers = Quat.safeEulerAngles(cameraOrientation); + var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation)); + var dx = event.x - mouseX; + var dy = event.y - mouseY; + orbitAzimuth += dx / ORBIT_RATE_AZIMUTH; + orbitAltitude += dy / ORBIT_RATE_ALTITUDE; + var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius, + y:Math.sin(orbitAltitude) * orbitRadius, + z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius }; + orbitPosition = Vec3.sum(orbitCenter, orbitVector); + Camera.setPosition(orbitPosition); + mouseX = event.x; + mouseY = event.y; + print("handleOrbitingMove..."); +} + +function endOrbitMode(event) { + var cameraOrientation = Camera.getOrientation(); + var eulers = Quat.safeEulerAngles(cameraOrientation); + MyAvatar.position = Camera.getPosition(); + MyAvatar.orientation = cameraOrientation; + Camera.stopLooking(); + Camera.setMode(oldMode); + Camera.setOrientation(cameraOrientation); + print("endOrbitMode..."); +} + +function startPanMode(event, intersection) { + // start pan camera! + print("handle PAN mode!!!"); +} + +function handlePanMove(event) { + print("PANNING mode!!! isPanning="+isPanning + " inPanningFromTouch="+isPanningFromTouch + " trackAsOrbitOrPan="+trackAsOrbitOrPan); +} + +function endPanMode(event) { + print("ending PAN mode!!!"); +} + + function mousePressEvent(event) { // if our tools are off, then don't do anything @@ -628,29 +706,58 @@ function mousePressEvent(event) { return; } - var clickedOnSwatch = false; - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - - // If the user clicked on the thumb, handle the slider logic - if (clickedOverlay == thumb) { - isMovingSlider = true; - thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb - return; // no further processing - } else { - // if the user clicked on one of the color swatches, update the selectedSwatch - for (s = 0; s < numColors; s++) { - if (clickedOverlay == swatches[s]) { - whichColor = s; - moveTools(); - clickedOnSwatch = true; + // Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence. + // but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to + // cancel any touch mode, and then let the right-click through + if (isOrbitingFromTouch || isPanningFromTouch) { + + // if the user is holding the ALT key AND they are clicking the RIGHT button (or on multi-touch doing a two + // finger touch, then we want to let the new panning behavior take over. + // if it's any other case we still want to bail + if (event.button == "RIGHT" && trackAsOrbitOrPan) { + // cancel our current multitouch operation... + if (isOrbitingFromTouch) { + endOrbitMode(event); + isOrbitingFromTouch = false; } + if (isPanningFromTouch) { + //print("mousePressEvent... calling endPanMode()"); + endPanMode(event); + isPanningFromTouch = false; + } + // let things fall through + } else { + return; } - if (clickedOnSwatch) { + } + + // no clicking on overlays while in panning mode + if (!trackAsOrbitOrPan) { + var clickedOnSwatch = false; + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + // If the user clicked on the thumb, handle the slider logic + if (clickedOverlay == thumb) { + isMovingSlider = true; + thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb return; // no further processing + } else { + // if the user clicked on one of the color swatches, update the selectedSwatch + for (s = 0; s < numColors; s++) { + if (clickedOverlay == swatches[s]) { + whichColor = s; + moveTools(); + clickedOnSwatch = true; + } + } + if (clickedOnSwatch) { + return; // no further processing + } } } + // TODO: does any of this stuff need to execute if we're panning or orbiting? trackMouseEvent(event); // used by preview support mouseX = event.x; mouseY = event.y; @@ -663,20 +770,16 @@ function mousePressEvent(event) { calcThumbFromScale(intersection.voxel.s); } - if (event.isAlt) { - // start orbit camera! - var cameraPosition = Camera.getPosition(); - oldMode = Camera.getMode(); - Camera.setMode("independent"); - isOrbiting = true; - Camera.keepLookingAt(intersection.intersection); - // get position for initial azimuth, elevation - orbitCenter = intersection.intersection; - var orbitVector = Vec3.subtract(cameraPosition, orbitCenter); - orbitRadius = Vec3.length(orbitVector); - orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x); - orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector)); - + // Note: touch and mouse events can cross paths, so we want to ignore any mouse events that would + // start a pan or orbit if we're already doing a pan or orbit via touch... + if ((event.isAlt || trackAsOrbitOrPan) && !(isOrbitingFromTouch || isPanningFromTouch)) { + if (event.isLeftButton && !event.isRightButton) { + startOrbitMode(event); + isOrbiting = true; + } else if (event.isRightButton && !event.isLeftButton) { + startPanMode(event); + isPanning = true; + } } else if (trackAsDelete || (event.isRightButton && !trackAsEyedropper)) { // Delete voxel voxelDetails = calculateVoxelFromIntersection(intersection,"delete"); @@ -784,7 +887,28 @@ function keyReleaseEvent(event) { function mouseMoveEvent(event) { - if (isMovingSlider) { + if (!editToolsOn) { + return; + } + + // if we're panning or orbiting from touch, ignore these... because our touch takes precedence. + if (isOrbitingFromTouch || isPanningFromTouch) { + return; + } + + // double check that we didn't accidentally miss a pan or orbit click request + if (trackAsOrbitOrPan && !isPanning && !isOrbiting) { + if (event.isLeftButton && !event.isRightButton) { + startOrbitMode(event); + isOrbiting = true; + } + if (!event.isLeftButton && event.isRightButton) { + startPanMode(event); + isPanning = true; + } + } + + if (!trackAsOrbitOrPan && isMovingSlider) { thumbX = (event.x - thumbClickOffsetX) - sliderX; if (thumbX < minThumbX) { thumbX = minThumbX; @@ -795,21 +919,10 @@ function mouseMoveEvent(event) { calcScaleFromThumb(thumbX); } else if (isOrbiting) { - var cameraOrientation = Camera.getOrientation(); - var origEulers = Quat.safeEulerAngles(cameraOrientation); - var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation)); - var dx = event.x - mouseX; - var dy = event.y - mouseY; - orbitAzimuth += dx / ORBIT_RATE_AZIMUTH; - orbitAltitude += dy / ORBIT_RATE_ALTITUDE; - var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius, - y:Math.sin(orbitAltitude) * orbitRadius, - z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius }; - orbitPosition = Vec3.sum(orbitCenter, orbitVector); - Camera.setPosition(orbitPosition); - mouseX = event.x; - mouseY = event.y; - } else if (isAdding) { + handleOrbitingMove(event); + } else if (isPanning) { + handlePanMove(event); + } else if (!trackAsOrbitOrPan && isAdding) { // Watch the drag direction to tell which way to 'extrude' this voxel if (!isExtruding) { var pickRay = Camera.computePickRay(event.x, event.y); @@ -861,18 +974,17 @@ function mouseReleaseEvent(event) { if (isMovingSlider) { isMovingSlider = false; } - + if (isOrbiting) { - var cameraOrientation = Camera.getOrientation(); - var eulers = Quat.safeEulerAngles(cameraOrientation); - MyAvatar.position = Camera.getPosition(); - MyAvatar.orientation = cameraOrientation; - Camera.stopLooking(); - Camera.setMode(oldMode); - Camera.setOrientation(cameraOrientation); + endOrbitMode(event); + isOrbiting = false; + } + if (isPanning) { + print("mouseReleaseEvent... calling endPanMode()"); + endPanMode(event); + isPanning = false; } isAdding = false; - isOrbiting = false; isExtruding = false; } @@ -915,7 +1027,7 @@ function moveTools() { recolorToolColor = toolSelectedColor; } else if (trackAsEyedropper) { eyedropperToolColor = toolSelectedColor; - } else if (trackAsOrbit) { + } else if (trackAsOrbitOrPan) { // nothing gets selected in this case... } else { addToolColor = toolSelectedColor; @@ -962,6 +1074,101 @@ function moveTools() { } +function touchBeginEvent(event) { + if (!editToolsOn) { + return; + } + + // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... + if (isOrbiting || isPanning) { + return; + } + + if (event.isAlt || trackAsOrbitOrPan) { + if (event.touchPoints == touchPointsToOrbit) { + // we need to double check that we didn't start an orbit, because the touch events will sometimes + // come in as 2 then 3 touches... + if (isPanningFromTouch) { + print("touchBeginEvent... calling endPanMode()"); + endPanMode(event); + isPanningFromTouch = false; + } + startOrbitMode(event); + isOrbitingFromTouch = true; + } else if (event.touchPoints == touchPointsToPan) { + // we need to double check that we didn't start an orbit, because the touch events will sometimes + // come in as 2 then 3 touches... + if (isOrbitingFromTouch) { + endOrbitMode(event); + isOrbitingFromTouch = false; + } + startPanMode(event); + isPanningFromTouch = true; + } + } +} + +function touchUpdateEvent(event) { + if (!editToolsOn) { + return; + } + + // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... + if (isOrbiting || isPanning) { + return; + } + + if (isOrbitingFromTouch) { + // we need to double check that we didn't start an orbit, because the touch events will sometimes + // come in as 2 then 3 touches... + if (event.touchPoints == touchPointsToPan) { + //print("we now have touchPointsToPan touches... switch to pan..."); + endOrbitMode(event); + isOrbitingFromTouch = false; + startPanMode(event); + isPanningFromTouch = true; + } else { + handleOrbitingMove(event); + } + } + if (isPanningFromTouch) { + //print("touchUpdateEvent... isPanningFromTouch... event.touchPoints=" + event.touchPoints); + // we need to double check that we didn't start an orbit, because the touch events will sometimes + // come in as 2 then 3 touches... + if (event.touchPoints == touchPointsToOrbit) { + //print("we now have touchPointsToOrbit touches... switch to orbit..."); + //print("touchUpdateEvent... calling endPanMode()"); + endPanMode(event); + isPanningFromTouch = false; + startOrbitMode(event); + isOrbitingFromTouch = true; + handleOrbitingMove(event); + } else { + handlePanMove(event); + } + } +} + +function touchEndEvent(event) { + if (!editToolsOn) { + return; + } + + // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... + if (isOrbiting || isPanning) { + return; + } + + if (isOrbitingFromTouch) { + endOrbitMode(event); + isOrbitingFromTouch = false; + } + if (isPanningFromTouch) { + print("touchEndEvent... calling endPanMode()"); + endPanMode(event); + isPanningFromTouch = false; + } +} function update() { var newWindowDimensions = Controller.getViewportDimensions(); @@ -976,6 +1183,10 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); +Controller.touchBeginEvent.connect(touchBeginEvent); +Controller.touchUpdateEvent.connect(touchUpdateEvent); +Controller.touchEndEvent.connect(touchEndEvent); + function scriptEnding() { Overlays.deleteOverlay(voxelPreview); From 6b90a3994d439d2d0e61e253f58685949bcb8c6f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Feb 2014 19:22:59 -0800 Subject: [PATCH 17/48] Working on avatar billboards. --- interface/src/Application.cpp | 148 +++++++++++++----------- interface/src/Application.h | 7 +- interface/src/avatar/MyAvatar.cpp | 21 +++- interface/src/avatar/MyAvatar.h | 5 + interface/src/renderer/Model.cpp | 15 +++ interface/src/renderer/Model.h | 2 + interface/src/renderer/TextureCache.cpp | 7 +- interface/src/renderer/TextureCache.h | 3 + 8 files changed, 137 insertions(+), 71 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dfd98863a7..30a6fb4e74 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -527,73 +527,8 @@ void Application::paintGL() { _glowEffect.render(); if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - - bool eyeRelativeCamera = false; - if (_rearMirrorTools->getZoomLevel() == BODY) { - _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); - _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); - } else { // HEAD zoom level - _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); - if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) { - // as a hack until we have a better way of dealing with coordinate precision issues, reposition the - // face/body so that the average eye position lies at the origin - eyeRelativeCamera = true; - _mirrorCamera.setTargetPosition(glm::vec3()); - - } else { - _mirrorCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); - } - } - - _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); - _mirrorCamera.update(1.0f/_fps); - - // set the bounds of rear mirror view - glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), - _mirrorViewRect.width(), _mirrorViewRect.height()); - glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), - _mirrorViewRect.width(), _mirrorViewRect.height()); - bool updateViewFrustum = false; - updateProjectionMatrix(_mirrorCamera, updateViewFrustum); - glEnable(GL_SCISSOR_TEST); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // render rear mirror view - glPushMatrix(); - if (eyeRelativeCamera) { - // save absolute translations - glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation(); - glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation(); - - // get the eye positions relative to the neck and use them to set the face translation - glm::vec3 leftEyePosition, rightEyePosition; - _myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3()); - _myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); - _myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); - - // get the neck position relative to the body and use it to set the skeleton translation - glm::vec3 neckPosition; - _myAvatar->getSkeletonModel().setTranslation(glm::vec3()); - _myAvatar->getSkeletonModel().getNeckPosition(neckPosition); - _myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() - - neckPosition); - - displaySide(_mirrorCamera, true); - - // restore absolute translations - _myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation); - _myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation); - } else { - displaySide(_mirrorCamera, true); - } - glPopMatrix(); - - _rearMirrorTools->render(false); - - // reset Viewport and projection matrix - glViewport(0, 0, _glWidget->width(), _glWidget->height()); - glDisable(GL_SCISSOR_TEST); - updateProjectionMatrix(_myCamera, updateViewFrustum); + renderRearViewMirror(); + } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(true); } @@ -2743,6 +2678,14 @@ void Application::setupWorldLight() { glMateriali(GL_FRONT, GL_SHININESS, 96); } +QImage Application::renderAvatarBillboard() { + renderRearViewMirror(true); + + QImage image(_glWidget->width(), _glWidget->height(), QImage::Format_ARGB32); + glReadPixels(0, 0, _glWidget->width(), _glWidget->height(), GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); + return image; +} + void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); // transform by eye offset @@ -3660,6 +3603,77 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) { } } +void Application::renderRearViewMirror(bool billboard) { + bool eyeRelativeCamera = false; + if (_rearMirrorTools->getZoomLevel() == BODY && !billboard) { + _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); + _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); + } else { // HEAD zoom level + _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); + if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) { + // as a hack until we have a better way of dealing with coordinate precision issues, reposition the + // face/body so that the average eye position lies at the origin + eyeRelativeCamera = true; + _mirrorCamera.setTargetPosition(glm::vec3()); + + } else { + _mirrorCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); + } + } + + _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); + _mirrorCamera.update(1.0f/_fps); + + // set the bounds of rear mirror view + glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), + _mirrorViewRect.width(), _mirrorViewRect.height()); + glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), + _mirrorViewRect.width(), _mirrorViewRect.height()); + bool updateViewFrustum = false; + updateProjectionMatrix(_mirrorCamera, updateViewFrustum); + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // render rear mirror view + glPushMatrix(); + if (eyeRelativeCamera) { + // save absolute translations + glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation(); + glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation(); + + // get the eye positions relative to the neck and use them to set the face translation + glm::vec3 leftEyePosition, rightEyePosition; + _myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3()); + _myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); + _myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); + + // get the neck position relative to the body and use it to set the skeleton translation + glm::vec3 neckPosition; + _myAvatar->getSkeletonModel().setTranslation(glm::vec3()); + _myAvatar->getSkeletonModel().getNeckPosition(neckPosition); + _myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() - + neckPosition); + + displaySide(_mirrorCamera, true); + + // restore absolute translations + _myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation); + _myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation); + } else { + displaySide(_mirrorCamera, true); + } + glPopMatrix(); + + if (!billboard) { + _rearMirrorTools->render(false); + } + + // reset Viewport and projection matrix + glViewport(0, 0, _glWidget->width(), _glWidget->height()); + glDisable(GL_SCISSOR_TEST); + updateProjectionMatrix(_myCamera, updateViewFrustum); +} + // renderViewFrustum() // // Description: this will render the view frustum bounds for EITHER the head diff --git a/interface/src/Application.h b/interface/src/Application.h index abe51b8bb1..b35e4b4e08 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -185,6 +186,8 @@ public: void setupWorldLight(); + QImage renderAvatarBillboard(); + void displaySide(Camera& whichCamera, bool selfAvatarOnly = false); /// Loads a view matrix that incorporates the specified model translation without the precision issues that can @@ -200,6 +203,8 @@ public: void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const; + + VoxelShader& getVoxelShader() { return _voxelShader; } PointShader& getPointShader() { return _pointShader; } FileLogger* getLogger() { return _logger; } @@ -328,7 +333,7 @@ private: void displayStats(); void checkStatsClick(); void toggleStatsExpanded(); - void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); + void renderRearViewMirror(bool billboard = false); void renderViewFrustum(ViewFrustum& viewFrustum); void checkBandwidthMeterClick(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 016159f415..7358a34fda 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -57,7 +57,8 @@ MyAvatar::MyAvatar() : _thrustMultiplier(1.0f), _moveTarget(0,0,0), _moveTargetStepCounter(0), - _lookAtTargetAvatar() + _lookAtTargetAvatar(), + _billboardValid(false) { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; @@ -332,7 +333,13 @@ void MyAvatar::simulate(float deltaTime) { // Zero thrust out now that we've added it to velocity in this frame _thrust = glm::vec3(0, 0, 0); - + + // consider updating our billboard + if (!_billboardValid && _skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures()) { + QImage image = Application::getInstance()->renderAvatarBillboard(); + image.save("test.png"); + _billboardValid = true; + } } const float MAX_PITCH = 90.0f; @@ -712,6 +719,16 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const { return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); } +void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) { + Avatar::setFaceModelURL(faceModelURL); + _billboardValid = false; +} + +void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { + Avatar::setSkeletonModelURL(skeletonModelURL); + _billboardValid = false; +} + void MyAvatar::renderBody(bool forceRenderHead) { // Render the body's voxels and head _skeletonModel.render(1.0f); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1bc5de204b..29f8f6d3bf 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -84,6 +84,9 @@ public: void updateLookAtTargetAvatar(glm::vec3& eyePosition); void clearLookAtTargetAvatar(); + virtual void setFaceModelURL(const QUrl& faceModelURL); + virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + public slots: void goHome(); void increaseSize(); @@ -119,6 +122,8 @@ private: glm::vec3 _transmitterPickStart; glm::vec3 _transmitterPickEnd; + bool _billboardValid; + // private methods void renderBody(bool forceRenderHead); void updateThrust(float deltaTime); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f1916db4d1..ecb741ceb8 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -46,6 +46,21 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati program.release(); } +bool Model::isLoadedWithTextures() const { + if (!isActive()) { + return false; + } + foreach (const NetworkMesh& mesh, _geometry->getMeshes()) { + foreach (const NetworkMeshPart& part, mesh.parts) { + if (part.diffuseTexture && !part.diffuseTexture->isLoaded() || + part.normalTexture && !part.normalTexture->isLoaded()) { + return false; + } + } + } + return true; +} + void Model::init() { if (!_program.isLinked()) { switchToResourcesParentIfRequired(); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 28189d0379..5da2891cfe 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -46,6 +46,8 @@ public: bool isActive() const { return _geometry && _geometry->isLoaded(); } + bool isLoadedWithTextures() const; + void init(); void reset(); void simulate(float deltaTime); diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index dc6883a5d0..8bfef5a742 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -258,9 +258,11 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : _reply(NULL), _attempts(0), _averageColor(1.0f, 1.0f, 1.0f, 1.0f), - _translucent(false) { + _translucent(false), + _loaded(false) { if (!url.isValid()) { + _loaded = true; return; } _request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); @@ -298,6 +300,7 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo _reply->disconnect(this); _reply->deleteLater(); _reply = NULL; + _loaded = true; QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32); @@ -345,6 +348,8 @@ void NetworkTexture::handleReplyError() { QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest())); debug << " -- retrying..."; + } else { + _loaded = true; } } diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index e560acf6f7..ca7bf67a32 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -118,6 +118,8 @@ public: NetworkTexture(const QUrl& url, bool normalMap); ~NetworkTexture(); + bool isLoaded() const { return _loaded; } + /// Returns the average color over the entire texture. const glm::vec4& getAverageColor() const { return _averageColor; } @@ -142,6 +144,7 @@ private: int _attempts; glm::vec4 _averageColor; bool _translucent; + bool _loaded; }; /// Caches derived, dilated textures. From fb06512e67d88abbebb3fc7d27ff433378ce1efc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Feb 2014 23:47:46 -0800 Subject: [PATCH 18/48] improve z fighting adjust for preview voxels and highlights --- examples/editVoxels.js | 77 ++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 59cb56fab6..4e1dff008b 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -24,6 +24,9 @@ var ORBIT_RATE_ALTITUDE = 200.0; var ORBIT_RATE_AZIMUTH = 90.0; var PIXELS_PER_EXTRUDE_VOXEL = 16; +var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting +var previewLineWidth = 1.5; + var oldMode = Camera.getMode(); var key_alt = false; @@ -98,7 +101,7 @@ var linePreviewTop = Overlays.addOverlay("line3d", { color: { red: 255, green: 255, blue: 255}, alpha: 1, visible: false, - lineWidth: 1 + lineWidth: previewLineWidth }); var linePreviewBottom = Overlays.addOverlay("line3d", { @@ -107,7 +110,7 @@ var linePreviewBottom = Overlays.addOverlay("line3d", { color: { red: 255, green: 255, blue: 255}, alpha: 1, visible: false, - lineWidth: 1 + lineWidth: previewLineWidth }); var linePreviewLeft = Overlays.addOverlay("line3d", { @@ -116,7 +119,7 @@ var linePreviewLeft = Overlays.addOverlay("line3d", { color: { red: 255, green: 255, blue: 255}, alpha: 1, visible: false, - lineWidth: 1 + lineWidth: previewLineWidth }); var linePreviewRight = Overlays.addOverlay("line3d", { @@ -125,7 +128,7 @@ var linePreviewRight = Overlays.addOverlay("line3d", { color: { red: 255, green: 255, blue: 255}, alpha: 1, visible: false, - lineWidth: 1 + lineWidth: previewLineWidth }); @@ -356,82 +359,82 @@ function calculateVoxelFromIntersection(intersection, operation) { // now we also want to calculate the "edge square" for the face for this voxel if (intersection.face == "MIN_X_FACE") { - highlightAt.x = intersection.voxel.x; + highlightAt.x = intersection.voxel.x - zFightingSizeAdjust; resultVoxel.x = intersection.voxel.x; if (operation == "add") { resultVoxel.x -= voxelSize; } - resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; - resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize }; - resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z }; - resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize }; + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; } else if (intersection.face == "MAX_X_FACE") { - highlightAt.x = intersection.voxel.x + intersection.voxel.s; + highlightAt.x = intersection.voxel.x + intersection.voxel.s + zFightingSizeAdjust; resultVoxel.x = intersection.voxel.x + intersection.voxel.s; if (operation != "add") { resultVoxel.x -= voxelSize; } - resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; - resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize }; - resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z }; - resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize }; + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; } else if (intersection.face == "MIN_Y_FACE") { - highlightAt.y = intersection.voxel.y; + highlightAt.y = intersection.voxel.y - zFightingSizeAdjust; resultVoxel.y = intersection.voxel.y; if (operation == "add") { resultVoxel.y -= voxelSize; } - resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; - resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z}; - resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize }; - resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z + voxelSize}; + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; } else if (intersection.face == "MAX_Y_FACE") { - highlightAt.y = intersection.voxel.y + intersection.voxel.s; + highlightAt.y = intersection.voxel.y + intersection.voxel.s + zFightingSizeAdjust; resultVoxel.y = intersection.voxel.y + intersection.voxel.s; if (operation != "add") { resultVoxel.y -= voxelSize; } - resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; - resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z}; - resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize }; - resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z + voxelSize}; + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust}; + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; } else if (intersection.face == "MIN_Z_FACE") { - highlightAt.z = intersection.voxel.z; + highlightAt.z = intersection.voxel.z - zFightingSizeAdjust; resultVoxel.z = intersection.voxel.z; if (operation == "add") { resultVoxel.z -= voxelSize; } - resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; - resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z}; - resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z }; - resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y + voxelSize, z: highlightAt.z}; + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; } else if (intersection.face == "MAX_Z_FACE") { - highlightAt.z = intersection.voxel.z + intersection.voxel.s; + highlightAt.z = intersection.voxel.z + intersection.voxel.s + zFightingSizeAdjust; resultVoxel.z = intersection.voxel.z + intersection.voxel.s; if (operation != "add") { resultVoxel.z -= voxelSize; } - resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; - resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z}; - resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z }; - resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y + voxelSize, z: highlightAt.z}; + resultVoxel.bottomLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.bottomRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; + resultVoxel.topLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.topRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; } @@ -466,7 +469,7 @@ function showPreviewVoxel() { guidePosition = calculateVoxelFromIntersection(intersection,"delete"); Overlays.editOverlay(voxelPreview, { position: guidePosition, - size: guidePosition.s, + size: guidePosition.s + zFightingSizeAdjust, visible: true, color: { red: 255, green: 0, blue: 0 }, solid: false, @@ -477,7 +480,7 @@ function showPreviewVoxel() { Overlays.editOverlay(voxelPreview, { position: guidePosition, - size: guidePosition.s + 0.002, + size: guidePosition.s + zFightingSizeAdjust, visible: true, color: voxelColor, solid: true, @@ -490,7 +493,7 @@ function showPreviewVoxel() { Overlays.editOverlay(voxelPreview, { position: guidePosition, - size: guidePosition.s, + size: (guidePosition.s - zFightingSizeAdjust), visible: true, color: voxelColor, solid: true, From 8dac269c3cd0437f010be41944abde6a799b02e6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Feb 2014 00:27:40 -0800 Subject: [PATCH 19/48] added selectable tools to the tool palette --- examples/editVoxels.js | 140 +++++++++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 39 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 4e1dff008b..0ab94918c0 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -29,8 +29,6 @@ var previewLineWidth = 1.5; var oldMode = Camera.getMode(); -var key_alt = false; -var key_shift = false; var isAdding = false; var isExtruding = false; var isOrbiting = false; @@ -54,8 +52,6 @@ var dragStart = { x: 0, y: 0 }; var mouseX = 0; var mouseY = 0; - - // Create a table of the different colors you can choose var colors = new Array(); colors[0] = { red: 237, green: 175, blue: 0 }; @@ -337,6 +333,12 @@ var trackAsRecolor = false; var trackAsEyedropper = false; var trackAsOrbitOrPan = false; +var addToolSelected = true; +var deleteToolSelected = false; +var recolorToolSelected = false; +var eyedropperToolSelected = false; +var selectToolSelected = false; + function calculateVoxelFromIntersection(intersection, operation) { //print("calculateVoxelFromIntersection() operation="+operation); var resultVoxel; @@ -465,7 +467,7 @@ function showPreviewVoxel() { var guidePosition; - if (trackAsDelete) { + if (trackAsDelete || deleteToolSelected) { guidePosition = calculateVoxelFromIntersection(intersection,"delete"); Overlays.editOverlay(voxelPreview, { position: guidePosition, @@ -475,7 +477,17 @@ function showPreviewVoxel() { solid: false, alpha: 1 }); - } else if (trackAsRecolor || trackAsEyedropper) { + } else if (selectToolSelected) { + guidePosition = calculateVoxelFromIntersection(intersection,"select"); + Overlays.editOverlay(voxelPreview, { + position: guidePosition, + size: guidePosition.s + zFightingSizeAdjust, + visible: true, + color: { red: 255, green: 255, blue: 0 }, + solid: false, + alpha: 1 + }); + } else if (trackAsRecolor || recolorToolSelected || trackAsEyedropper|| eyedropperToolSelected) { guidePosition = calculateVoxelFromIntersection(intersection,"recolor"); Overlays.editOverlay(voxelPreview, { @@ -488,7 +500,7 @@ function showPreviewVoxel() { }); } else if (trackAsOrbitOrPan) { Overlays.editOverlay(voxelPreview, { visible: false }); - } else if (!isExtruding) { + } else if (addToolSelected && !isExtruding) { guidePosition = calculateVoxelFromIntersection(intersection,"add"); Overlays.editOverlay(voxelPreview, { @@ -517,12 +529,25 @@ function showPreviewLines() { } resultVoxel = calculateVoxelFromIntersection(intersection,""); - Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); - Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); - Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); - Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); - + if (selectToolSelected) { + Overlays.editOverlay(voxelPreview, { + position: resultVoxel, + size: resultVoxel.s + zFightingSizeAdjust, + visible: true, + color: { red: 255, green: 255, blue: 255 }, + lineWidth: previewLineWidth, + solid: false, + alpha: 1 + }); + } else { + Overlays.editOverlay(voxelPreview, { visible: false }); + Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); + Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); + Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); + Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); + } } else { + Overlays.editOverlay(voxelPreview, { visible: false }); Overlays.editOverlay(linePreviewTop, { visible: false }); Overlays.editOverlay(linePreviewBottom, { visible: false }); Overlays.editOverlay(linePreviewLeft, { visible: false }); @@ -542,9 +567,6 @@ function showPreviewGuides() { Overlays.editOverlay(linePreviewRight, { visible: false }); } else { showPreviewLines(); - - // make sure alternative is hidden - Overlays.editOverlay(voxelPreview, { visible: false }); } } else { // make sure all previews are off @@ -656,7 +678,7 @@ function startOrbitMode(event) { orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x); orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector)); - print("startOrbitMode..."); + //print("startOrbitMode..."); } function handleOrbitingMove(event) { @@ -674,7 +696,7 @@ function handleOrbitingMove(event) { Camera.setPosition(orbitPosition); mouseX = event.x; mouseY = event.y; - print("handleOrbitingMove..."); + //print("handleOrbitingMove..."); } function endOrbitMode(event) { @@ -685,7 +707,7 @@ function endOrbitMode(event) { Camera.stopLooking(); Camera.setMode(oldMode); Camera.setOrientation(cameraOrientation); - print("endOrbitMode..."); + //print("endOrbitMode..."); } function startPanMode(event, intersection) { @@ -694,7 +716,8 @@ function startPanMode(event, intersection) { } function handlePanMove(event) { - print("PANNING mode!!! isPanning="+isPanning + " inPanningFromTouch="+isPanningFromTouch + " trackAsOrbitOrPan="+trackAsOrbitOrPan); + print("PANNING mode!!! "); + //print("isPanning="+isPanning + " inPanningFromTouch="+isPanningFromTouch + " trackAsOrbitOrPan="+trackAsOrbitOrPan); } function endPanMode(event) { @@ -736,30 +759,72 @@ function mousePressEvent(event) { // no clicking on overlays while in panning mode if (!trackAsOrbitOrPan) { - var clickedOnSwatch = false; + var clickedOnSomething = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + +print("clickedOverlay="+clickedOverlay); // If the user clicked on the thumb, handle the slider logic if (clickedOverlay == thumb) { isMovingSlider = true; thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb - return; // no further processing + clickedOnSomething = true; + } else if (clickedOverlay == addTool) { + addToolSelected = true; + deleteToolSelected = false; + recolorToolSelected = false; + eyedropperToolSelected = false; + selectToolSelected = false; + moveTools(); + clickedOnSomething = true; + } else if (clickedOverlay == deleteTool) { + addToolSelected = false; + deleteToolSelected = true; + recolorToolSelected = false; + eyedropperToolSelected = false; + selectToolSelected = false; + moveTools(); + clickedOnSomething = true; + } else if (clickedOverlay == recolorTool) { + addToolSelected = false; + deleteToolSelected = false; + recolorToolSelected = true; + eyedropperToolSelected = false; + selectToolSelected = false; + moveTools(); + clickedOnSomething = true; + } else if (clickedOverlay == eyedropperTool) { + addToolSelected = false; + deleteToolSelected = false; + recolorToolSelected = false; + eyedropperToolSelected = true; + selectToolSelected = false; + moveTools(); + clickedOnSomething = true; + } else if (clickedOverlay == selectTool) { + addToolSelected = false; + deleteToolSelected = false; + recolorToolSelected = false; + eyedropperToolSelected = false; + selectToolSelected = true; + moveTools(); + clickedOnSomething = true; } else { // if the user clicked on one of the color swatches, update the selectedSwatch for (s = 0; s < numColors; s++) { if (clickedOverlay == swatches[s]) { whichColor = s; moveTools(); - clickedOnSwatch = true; + clickedOnSomething = true; + break; } } - if (clickedOnSwatch) { - return; // no further processing - } + } + if (clickedOnSomething) { + return; // no further processing } } - // TODO: does any of this stuff need to execute if we're panning or orbiting? trackMouseEvent(event); // used by preview support mouseX = event.x; @@ -783,13 +848,13 @@ function mousePressEvent(event) { startPanMode(event); isPanning = true; } - } else if (trackAsDelete || (event.isRightButton && !trackAsEyedropper)) { + } else if (deleteToolSelected || trackAsDelete || (event.isRightButton && !trackAsEyedropper)) { // Delete voxel voxelDetails = calculateVoxelFromIntersection(intersection,"delete"); Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Audio.playSound(deleteSound, audioOptions); Overlays.editOverlay(voxelPreview, { visible: false }); - } else if (trackAsEyedropper) { + } else if (eyedropperToolSelected || trackAsEyedropper) { if (whichColor != -1) { colors[whichColor].red = intersection.voxel.red; colors[whichColor].green = intersection.voxel.green; @@ -797,7 +862,7 @@ function mousePressEvent(event) { moveTools(); } - } else if (trackAsRecolor) { + } else if (recolorToolSelected || trackAsRecolor) { // Recolor Voxel voxelDetails = calculateVoxelFromIntersection(intersection,"recolor"); @@ -807,7 +872,7 @@ function mousePressEvent(event) { colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue); Audio.playSound(changeColorSound, audioOptions); Overlays.editOverlay(voxelPreview, { visible: false }); - } else { + } else if (addToolSelected) { // Add voxel on face if (whichColor == -1) { // Copy mode - use clicked voxel color @@ -833,15 +898,13 @@ function mousePressEvent(event) { Overlays.editOverlay(voxelPreview, { visible: false }); dragStart = { x: event.x, y: event.y }; isAdding = true; - } + } } } function keyPressEvent(event) { // if our tools are off, then don't do anything if (editToolsOn) { - key_alt = event.isAlt; - key_shift = event.isShifted; var nVal = parseInt(event.text); if (event.text == "0") { print("Color = Copy"); @@ -884,8 +947,6 @@ function keyPressEvent(event) { function keyReleaseEvent(event) { trackKeyReleaseEvent(event); // used by preview support - key_alt = false; - key_shift = false; } @@ -1024,12 +1085,14 @@ function moveTools() { eyedropperToolColor = notSelectedColor; selectToolColor = notSelectedColor; - if (trackAsDelete) { + if (trackAsDelete || deleteToolSelected) { deleteToolColor = toolSelectedColor; - } else if (trackAsRecolor) { + } else if (trackAsRecolor || recolorToolSelected) { recolorToolColor = toolSelectedColor; - } else if (trackAsEyedropper) { + } else if (trackAsEyedropper || eyedropperToolSelected) { eyedropperToolColor = toolSelectedColor; + } else if (selectToolSelected) { + selectToolColor = toolSelectedColor; } else if (trackAsOrbitOrPan) { // nothing gets selected in this case... } else { @@ -1066,7 +1129,6 @@ function moveTools() { visible: editToolsOn }); - sliderX = swatchesX + swatchesWidth; sliderY = windowDimensions.y - sliderHeight; Overlays.editOverlay(slider, { x: sliderX, y: sliderY, visible: editToolsOn }); From 18ea1d6a855a1e57989c4fd43781b768b41c6d9d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Feb 2014 10:16:06 -0800 Subject: [PATCH 20/48] More work on billboards. --- interface/src/Application.cpp | 32 +++++++++++++++++++--------- interface/src/Application.h | 2 +- libraries/shared/src/PacketHeaders.h | 3 ++- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 30a6fb4e74..9ef3fdc76b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -527,7 +527,7 @@ void Application::paintGL() { _glowEffect.render(); if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - renderRearViewMirror(); + renderRearViewMirror(_mirrorViewRect); } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(true); @@ -2679,10 +2679,20 @@ void Application::setupWorldLight() { } QImage Application::renderAvatarBillboard() { - renderRearViewMirror(true); + _textureCache.getPrimaryFramebufferObject()->bind(); + + glDisable(GL_BLEND); + + const int BILLBOARD_SIZE = 128; + renderRearViewMirror(QRect(0, _glWidget->height() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); + + QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); + glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); + + glEnable(GL_BLEND); + + _textureCache.getPrimaryFramebufferObject()->release(); - QImage image(_glWidget->width(), _glWidget->height(), QImage::Format_ARGB32); - glReadPixels(0, 0, _glWidget->width(), _glWidget->height(), GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); return image; } @@ -3603,9 +3613,13 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) { } } -void Application::renderRearViewMirror(bool billboard) { +void Application::renderRearViewMirror(const QRect& region, bool billboard) { bool eyeRelativeCamera = false; - if (_rearMirrorTools->getZoomLevel() == BODY && !billboard) { + if (billboard) { + const float BILLBOARD_DISTANCE = 5.0f; + _mirrorCamera.setDistance(BILLBOARD_DISTANCE * _myAvatar->getScale()); + _mirrorCamera.setTargetPosition(_myAvatar->getPosition()); + } else if (_rearMirrorTools->getZoomLevel() == BODY) { _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); } else { // HEAD zoom level @@ -3625,10 +3639,8 @@ void Application::renderRearViewMirror(bool billboard) { _mirrorCamera.update(1.0f/_fps); // set the bounds of rear mirror view - glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), - _mirrorViewRect.width(), _mirrorViewRect.height()); - glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), - _mirrorViewRect.width(), _mirrorViewRect.height()); + glViewport(region.x(), _glWidget->height() - region.y() - region.height(), region.width(), region.height()); + glScissor(region.x(), _glWidget->height() - region.y() - region.height(), region.width(), region.height()); bool updateViewFrustum = false; updateProjectionMatrix(_mirrorCamera, updateViewFrustum); glEnable(GL_SCISSOR_TEST); diff --git a/interface/src/Application.h b/interface/src/Application.h index b35e4b4e08..d7bafc72b1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -333,7 +333,7 @@ private: void displayStats(); void checkStatsClick(); void toggleStatsExpanded(); - void renderRearViewMirror(bool billboard = false); + void renderRearViewMirror(const QRect& region, bool billboard = false); void renderViewFrustum(ViewFrustum& viewFrustum); void checkBandwidthMeterClick(); diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h index c1a5a34114..bb5821dd67 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -53,7 +53,8 @@ enum PacketType { PacketTypeParticleErase, PacketTypeParticleAddResponse, PacketTypeMetavoxelData, - PacketTypeAvatarIdentity + PacketTypeAvatarIdentity, + PacketTypeAvatarBillboard }; typedef char PacketVersion; From 10ce2cb3f0e0e61872ce11a0f1dd03620cd55899 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Feb 2014 11:49:29 -0800 Subject: [PATCH 21/48] Working on sending the billboards over the network. --- assignment-client/src/avatars/AvatarMixer.cpp | 43 +++++++++++++++++++ .../src/avatars/AvatarMixerClientData.cpp | 3 +- .../src/avatars/AvatarMixerClientData.h | 6 +++ interface/src/Application.cpp | 5 +++ interface/src/avatar/MyAvatar.cpp | 22 +++++++--- interface/src/avatar/MyAvatar.h | 1 + libraries/avatars/src/AvatarData.cpp | 16 +++++++ libraries/avatars/src/AvatarData.h | 10 +++++ 8 files changed, 100 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 63bdccbab7..d7d11b78b0 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -123,6 +123,23 @@ void broadcastIdentityPacket() { } } +void broadcastBillboardPacket(const SharedNodePointer& node) { + +} + +void broadcastBillboardPackets() { + + NodeList* nodeList = NodeList::getInstance(); + + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { + if (node->getLinkedData() && node->getType() == NodeType::Agent) { + AvatarMixerClientData* nodeData = static_cast(node->getLinkedData()); + broadcastBillboardPacket(node); + nodeData->setHasSentBillboardBetweenKeyFrames(false); + } + } +} + void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { if (killedNode->getType() == NodeType::Agent && killedNode->getLinkedData()) { @@ -170,6 +187,23 @@ void AvatarMixer::readPendingDatagrams() { nodeList->broadcastToNodes(identityPacket, NodeSet() << NodeType::Agent); } } + break; + } + case PacketTypeAvatarBillboard: { + + // check if we have a matching node in our list + SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket); + + if (avatarNode && avatarNode->getLinkedData()) { + AvatarMixerClientData* nodeData = static_cast(avatarNode->getLinkedData()); + if (nodeData->hasBillboardChangedAfterParsing(receivedPacket) + && !nodeData->hasSentBillboardBetweenKeyFrames()) { + // this avatar changed their billboard and we haven't sent a packet in this keyframe + broadcastBillboardPacket(avatarNode); + nodeData->setHasSentBillboardBetweenKeyFrames(true); + } + } + break; } case PacketTypeKillAvatar: { nodeList->processKillNode(receivedPacket); @@ -185,6 +219,7 @@ void AvatarMixer::readPendingDatagrams() { } const qint64 AVATAR_IDENTITY_KEYFRAME_MSECS = 5000; +const qint64 AVATAR_BILLBOARD_KEYFRAME_MSECS = 5000; void AvatarMixer::run() { commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer); @@ -202,6 +237,9 @@ void AvatarMixer::run() { QElapsedTimer identityTimer; identityTimer.start(); + QElapsedTimer billboardTimer; + billboardTimer.start(); + while (!_isFinished) { QCoreApplication::processEvents(); @@ -219,6 +257,11 @@ void AvatarMixer::run() { // restart the timer so we do it again in AVATAR_IDENTITY_KEYFRAME_MSECS identityTimer.restart(); } + + if (billboardTimer.elapsed() >= AVATAR_BILLBOARD_KEYFRAME_MSECS) { + broadcastBillboardPackets(); + billboardTimer.restart(); + } int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow(); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 0261613532..388d6f6488 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -9,7 +9,8 @@ #include "AvatarMixerClientData.h" AvatarMixerClientData::AvatarMixerClientData() : - _hasSentIdentityBetweenKeyFrames(false) + _hasSentIdentityBetweenKeyFrames(false), + _hasSentBillboardBetweenKeyFrames(false) { } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 8e046d9212..7240288306 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -21,9 +21,15 @@ public: bool hasSentIdentityBetweenKeyFrames() const { return _hasSentIdentityBetweenKeyFrames; } void setHasSentIdentityBetweenKeyFrames(bool hasSentIdentityBetweenKeyFrames) { _hasSentIdentityBetweenKeyFrames = hasSentIdentityBetweenKeyFrames; } + + bool hasSentBillboardBetweenKeyFrames() const { return _hasSentBillboardBetweenKeyFrames; } + void setHasSentBillboardBetweenKeyFrames(bool hasSentBillboardBetweenKeyFrames) + { _hasSentBillboardBetweenKeyFrames = hasSentBillboardBetweenKeyFrames; } + private: bool _hasSentIdentityBetweenKeyFrames; + bool _hasSentBillboardBetweenKeyFrames; }; #endif /* defined(__hifi__AvatarMixerClientData__) */ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a71cedec70..35793cb730 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -255,6 +255,11 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); identityPacketTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); + // send the billboard packet for our avatar every few seconds + QTimer* billboardPacketTimer = new QTimer(); + connect(billboardPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendBillboardPacket); + billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); _networkAccessManager = new QNetworkAccessManager(this); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f388f89701..61d1a8b8a8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include @@ -335,11 +337,7 @@ void MyAvatar::simulate(float deltaTime) { _thrust = glm::vec3(0, 0, 0); // consider updating our billboard - if (!_billboardValid && _skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures()) { - QImage image = Application::getInstance()->renderAvatarBillboard(); - image.save("test.png"); - _billboardValid = true; - } + maybeUpdateBillboard(); } const float MAX_PITCH = 90.0f; @@ -1141,6 +1139,20 @@ void MyAvatar::updateChatCircle(float deltaTime) { _position = glm::mix(_position, targetPosition, APPROACH_RATE); } +void MyAvatar::maybeUpdateBillboard() { + if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) { + return; + } + QImage image = Application::getInstance()->renderAvatarBillboard(); + _billboard.clear(); + QBuffer buffer(&_billboard); + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "JPG"); + _billboardValid = true; + + sendBillboardPacket(); +} + void MyAvatar::setGravity(glm::vec3 gravity) { _gravity = gravity; getHead()->setGravity(_gravity); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cbefd6ba0a..2b5be47419 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -133,6 +133,7 @@ private: void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void updateChatCircle(float deltaTime); + void maybeUpdateBillboard(); }; #endif diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3c50f2622c..26c8abefea 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -305,6 +305,15 @@ QByteArray AvatarData::identityByteArray() { return identityData; } +bool AvatarData::hasBillboardChangedAfterParsing(const QByteArray& packet) { + QByteArray newBillboard = packet.mid(numBytesForPacketHeader(packet)); + if (newBillboard == _billboard) { + return false; + } + _billboard = newBillboard; + return true; +} + void AvatarData::setFaceModelURL(const QUrl& faceModelURL) { _faceModelURL = faceModelURL.isEmpty() ? DEFAULT_HEAD_MODEL_URL : faceModelURL; @@ -344,3 +353,10 @@ void AvatarData::sendIdentityPacket() { NodeList::getInstance()->broadcastToNodes(identityPacket, NodeSet() << NodeType::AvatarMixer); } + +void AvatarData::sendBillboardPacket() { + QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); + billboardPacket.append(_billboard); + + NodeList::getInstance()->broadcastToNodes(billboardPacket, NodeSet() << NodeType::AvatarMixer); +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 07c774d5e6..f597edcc3a 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -29,6 +29,7 @@ typedef unsigned long long quint64; #include #include +#include #include #include #include @@ -54,6 +55,7 @@ static const float MIN_AVATAR_SCALE = .005f; const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000; +const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000; const QUrl DEFAULT_HEAD_MODEL_URL = QUrl("http://public.highfidelity.io/meshes/defaultAvatar_head.fst"); const QUrl DEFAULT_BODY_MODEL_URL = QUrl("http://public.highfidelity.io/meshes/defaultAvatar_body.fst"); @@ -151,6 +153,8 @@ public: bool hasIdentityChangedAfterParsing(const QByteArray& packet); QByteArray identityByteArray(); + bool hasBillboardChangedAfterParsing(const QByteArray& packet); + const QUrl& getFaceModelURL() const { return _faceModelURL; } QString getFaceModelURLString() const { return _faceModelURL.toString(); } const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } @@ -159,6 +163,9 @@ public: virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setDisplayName(const QString& displayName); + void setBillboard(const QByteArray& billboard) { _billboard = billboard; } + const QByteArray& getBillboard() const { return _billboard; } + QString getFaceModelURLFromScript() const { return _faceModelURL.toString(); } void setFaceModelURLFromScript(const QString& faceModelString) { setFaceModelURL(faceModelString); } @@ -169,6 +176,7 @@ public: public slots: void sendIdentityPacket(); + void sendBillboardPacket(); protected: glm::vec3 _position; @@ -204,6 +212,8 @@ protected: float _displayNameTargetAlpha; float _displayNameAlpha; + QByteArray _billboard; + private: // privatize the copy constructor and assignment operator so they cannot be called AvatarData(const AvatarData&); From 060e5f8c67eaa6461da977067ed3eaa1debb5f27 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Feb 2014 13:52:54 -0800 Subject: [PATCH 22/48] updates to bot script for more bot styles --- examples/bot.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/examples/bot.js b/examples/bot.js index 6d0abd740e..d2db3d3b48 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -25,10 +25,31 @@ positionZ = getRandomFloat(0, 50); Avatar.position = {x: positionX, y: 0, z: positionZ}; // pick an integer between 1 and 20 for the face model for this bot -botNumber = getRandomInt(1, 20); +botNumber = getRandomInt(1, 100); + +newBodyFilePrefix = "defaultAvatar"; + +if (botNumber <= 20) { + newFaceFilePrefix = "bot" + botNumber; +} else { + + if (botNumber <= 40) { + newFaceFilePrefix = "superhero"; + } else if (botNumber <= 60) { + newFaceFilePrefix = "amber"; + } else if (botNumber <= 80) { + newFaceFilePrefix = "ron"; + } else { + newFaceFilePrefix = "angie"; + } + + newBodyFilePrefix = "bot" + botNumber; +} + // set the face model fst using the bot number // there is no need to change the body model - we're using the default -Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/bot" + botNumber + ".fst"; +Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix ".fst"; +Avatar.bodyModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix ".fst"; Agent.isAvatar = true; \ No newline at end of file From fc7692417f6baf709cec2a78aaa83368dc13d727 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 21 Feb 2014 15:15:16 -0800 Subject: [PATCH 23/48] Fix is3D not set to true --- interface/src/ui/Overlays.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index e247486e8e..84944332f1 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -78,6 +78,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay->init(_parent); thisOverlay->setProperties(properties); created = true; + is3D = true; } if (created) { From e8c9dc50d0871eb774f4f669069e2f2ec8ea23f4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 21 Feb 2014 15:23:28 -0800 Subject: [PATCH 24/48] Hack to get preview working --- interface/src/ui/ClipboardOverlay.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/interface/src/ui/ClipboardOverlay.cpp b/interface/src/ui/ClipboardOverlay.cpp index 1eaf5e75f0..12c72c7690 100644 --- a/interface/src/ui/ClipboardOverlay.cpp +++ b/interface/src/ui/ClipboardOverlay.cpp @@ -15,6 +15,8 @@ #include "ClipboardOverlay.h" #include "../Application.h" +static int lastVoxelCount = 0; + ClipboardOverlay::ClipboardOverlay() { } @@ -36,6 +38,15 @@ void ClipboardOverlay::render() { glTranslatef(_position.x, _position.y, _position.z); glScalef(_size, _size, _size); + qDebug() << "[DEBUG] " << voxelSystem->getVoxelsRendered() << " " + << clipboard->getOctreeElementsCount(); + + // TODO : replace that hack to get the preview working corectly + if (lastVoxelCount != clipboard->getOctreeElementsCount()) { + voxelSystem->forceRedrawEntireTree(); + lastVoxelCount = clipboard->getOctreeElementsCount(); + } + voxelSystem->render(); glPopMatrix(); From ac02609bc2a239491b2bc0bcdf9f174bacc05777 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Feb 2014 15:38:49 -0800 Subject: [PATCH 25/48] The basic billboard behavior, closes #1864. --- assignment-client/src/avatars/AvatarMixer.cpp | 17 ++-- interface/src/Application.cpp | 12 ++- interface/src/Application.h | 3 + interface/src/DatagramProcessor.cpp | 3 +- interface/src/avatar/Avatar.cpp | 77 ++++++++++++++++++- interface/src/avatar/Avatar.h | 9 +++ interface/src/avatar/AvatarManager.cpp | 17 ++++ interface/src/avatar/AvatarManager.h | 1 + interface/src/avatar/Head.cpp | 3 + interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/renderer/Model.cpp | 7 +- interface/src/renderer/Model.h | 6 +- libraries/avatars/src/AvatarData.cpp | 6 ++ libraries/avatars/src/AvatarData.h | 2 +- 14 files changed, 146 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index db534c7f53..75265bdf10 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -123,15 +123,22 @@ void broadcastIdentityPacket() { } } -void broadcastBillboardPacket(const SharedNodePointer& node) { +void broadcastBillboardPacket(const SharedNodePointer& sendingNode) { + AvatarMixerClientData* nodeData = static_cast(sendingNode->getLinkedData()); + QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); + packet.append(sendingNode->getUUID().toRfc4122()); + packet.append(nodeData->getBillboard()); + NodeList* nodeList = NodeList::getInstance(); + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { + if (node->getType() == NodeType::Agent && node != sendingNode) { + nodeList->writeDatagram(packet, node); + } + } } void broadcastBillboardPackets() { - - NodeList* nodeList = NodeList::getInstance(); - - foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getLinkedData() && node->getType() == NodeType::Agent) { AvatarMixerClientData* nodeData = static_cast(node->getLinkedData()); broadcastBillboardPacket(node); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 35793cb730..7738be1d3b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -98,6 +98,7 @@ const int MIRROR_VIEW_HEIGHT = 215; const float MIRROR_FULLSCREEN_DISTANCE = 0.35f; const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; +const float MIRROR_FIELD_OF_VIEW = 30.0f; const QString CHECK_VERSION_URL = "http://highfidelity.io/latestVersion.xml"; const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; @@ -2688,7 +2689,7 @@ QImage Application::renderAvatarBillboard() { glDisable(GL_BLEND); - const int BILLBOARD_SIZE = 128; + const int BILLBOARD_SIZE = 64; renderRearViewMirror(QRect(0, _glWidget->height() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); @@ -3621,13 +3622,17 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) { void Application::renderRearViewMirror(const QRect& region, bool billboard) { bool eyeRelativeCamera = false; if (billboard) { - const float BILLBOARD_DISTANCE = 5.0f; + _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); _mirrorCamera.setDistance(BILLBOARD_DISTANCE * _myAvatar->getScale()); _mirrorCamera.setTargetPosition(_myAvatar->getPosition()); + } else if (_rearMirrorTools->getZoomLevel() == BODY) { + _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); + } else { // HEAD zoom level + _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) { // as a hack until we have a better way of dealing with coordinate precision issues, reposition the @@ -3639,7 +3644,8 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { _mirrorCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); } } - + _mirrorCamera.setAspectRatio((float)region.width() / region.height()); + _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); _mirrorCamera.update(1.0f/_fps); diff --git a/interface/src/Application.h b/interface/src/Application.h index d7bafc72b1..cb1ba2f949 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -97,6 +97,9 @@ static const float NODE_KILLED_BLUE = 0.0f; static const QString SNAPSHOT_EXTENSION = ".jpg"; +static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; +static const float BILLBOARD_DISTANCE = 5.0f; + class Application : public QApplication { Q_OBJECT diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index d8447168cd..e5fd37af4f 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -98,7 +98,8 @@ void DatagramProcessor::processDatagrams() { break; case PacketTypeBulkAvatarData: case PacketTypeKillAvatar: - case PacketTypeAvatarIdentity: { + case PacketTypeAvatarIdentity: + case PacketTypeAvatarBillboard: { // update having heard from the avatar-mixer and record the bytes received SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index dcecd0258d..e41d1207fb 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -26,6 +26,7 @@ #include "Physics.h" #include "world.h" #include "devices/OculusManager.h" +#include "renderer/TextureCache.h" #include "ui/TextRenderer.h" using namespace std; @@ -107,6 +108,10 @@ glm::quat Avatar::getWorldAlignedOrientation () const { return computeRotationFromBodyToWorldUp() * getOrientation(); } +float Avatar::getLODDistance() const { + return glm::distance(Application::getInstance()->getCamera()->getPosition(), _position) / _scale; +} + void Avatar::simulate(float deltaTime) { if (_scale != _targetScale) { setScale(_targetScale); @@ -116,6 +121,7 @@ void Avatar::simulate(float deltaTime) { glm::vec3 oldVelocity = getVelocity(); getHand()->simulate(deltaTime, false); + _skeletonModel.setLODDistance(getLODDistance()); _skeletonModel.simulate(deltaTime); Head* head = getHead(); head->setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); @@ -282,9 +288,11 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::renderBody(bool forceRenderHead) { - // Render the body's voxels and head - glm::vec3 pos = getPosition(); - //printf("Render other at %.3f, %.2f, %.2f\n", pos.x, pos.y, pos.z); + const float BILLBOARD_DISTANCE = 40.0f; + if (!_billboard.isEmpty() && getLODDistance() >= BILLBOARD_DISTANCE) { + renderBillboard(); + return; + } _skeletonModel.render(1.0f); if (forceRenderHead) { getHead()->render(1.0f); @@ -292,6 +300,62 @@ void Avatar::renderBody(bool forceRenderHead) { getHand()->render(false); } +void Avatar::renderBillboard() { + if (!_billboardTexture) { + QImage image = QImage::fromData(_billboard).convertToFormat(QImage::Format_ARGB32); + + _billboardTexture.reset(new Texture()); + glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, + GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + } else { + glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); + } + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); + + glEnable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + + glPushMatrix(); + glTranslatef(_position.x, _position.y, _position.z); + + // rotate about vertical to face the camera + glm::quat rotation = getOrientation(); + glm::vec3 cameraVector = glm::inverse(rotation) * (Application::getInstance()->getCamera()->getPosition() - _position); + rotation = rotation * glm::angleAxis(glm::degrees(atan2f(-cameraVector.x, -cameraVector.z)), 0.0f, 1.0f, 0.0f); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + + // compute the size from the billboard camera parameters and scale + float size = _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f)); + glScalef(size, size, size); + + glColor3f(1.0f, 1.0f, 1.0f); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex2f(1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex2f(1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex2f(-1.0f, 1.0f); + glEnd(); + + glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + + glBindTexture(GL_TEXTURE_2D, 0); +} + void Avatar::renderDisplayName() { if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) { @@ -502,6 +566,13 @@ void Avatar::setDisplayName(const QString& displayName) { _displayNameBoundingRect = textRenderer(DISPLAYNAME)->metrics().tightBoundingRect(displayName); } +void Avatar::setBillboard(const QByteArray& billboard) { + AvatarData::setBillboard(billboard); + + // clear out any existing billboard texture + _billboardTexture.reset(); +} + int Avatar::parseData(const QByteArray& packet) { // change in position implies movement glm::vec3 oldPosition = _position; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 6fa0f203e9..b9433f15dc 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -62,6 +63,8 @@ enum ScreenTintLayer { // Grayson as he's building a street around here for demo dinner 2 const glm::vec3 START_LOCATION(0.485f * TREE_SCALE, 0.f, 0.5f * TREE_SCALE); +class Texture; + class Avatar : public AvatarData { Q_OBJECT @@ -87,6 +90,9 @@ public: Head* getHead() { return static_cast(_headData); } Hand* getHand() { return static_cast(_handData); } glm::quat getWorldAlignedOrientation() const; + + /// Returns the distance to use as a LOD parameter. + float getLODDistance() const; Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); } void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } @@ -114,6 +120,7 @@ public: virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setDisplayName(const QString& displayName); + virtual void setBillboard(const QByteArray& billboard); void setShowDisplayName(bool showDisplayName); @@ -167,8 +174,10 @@ protected: private: bool _initialized; + QScopedPointer _billboardTexture; void renderBody(bool forceRenderHead); + void renderBillboard(); void renderDisplayName(); }; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b873b10017..f65566fe14 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -133,6 +133,9 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const case PacketTypeAvatarIdentity: processAvatarIdentityPacket(datagram); break; + case PacketTypeAvatarBillboard: + processAvatarBillboardPacket(datagram); + break; case PacketTypeKillAvatar: processKillAvatar(datagram); break; @@ -212,6 +215,20 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) { } } +void AvatarManager::processAvatarBillboardPacket(const QByteArray& packet) { + int headerSize = numBytesForPacketHeader(packet); + QUuid nodeUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID)); + + AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); + if (matchingAvatar) { + Avatar* avatar = static_cast(matchingAvatar.data()); + QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID); + if (avatar->getBillboard() != billboard) { + avatar->setBillboard(billboard); + } + } +} + void AvatarManager::processKillAvatar(const QByteArray& datagram) { // read the node id QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID)); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 35dbf8dbb0..115423e618 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -42,6 +42,7 @@ private: void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer& mixerWeakPointer); void processAvatarIdentityPacket(const QByteArray& packet); + void processAvatarBillboardPacket(const QByteArray& packet); void processKillAvatar(const QByteArray& datagram); void simulateAvatarFades(float deltaTime); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 5c6100764a..1653597d3e 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -158,6 +158,9 @@ void Head::simulate(float deltaTime, bool isMine) { glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) - JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients); } + if (!isMine) { + _faceModel.setLODDistance(static_cast(_owningAvatar)->getLODDistance()); + } _faceModel.simulate(deltaTime); // the blend face may have custom eye meshes diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 61d1a8b8a8..9d457cedd7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1147,7 +1147,7 @@ void MyAvatar::maybeUpdateBillboard() { _billboard.clear(); QBuffer buffer(&_billboard); buffer.open(QIODevice::WriteOnly); - image.save(&buffer, "JPG"); + image.save(&buffer, "PNG"); _billboardValid = true; sendBillboardPacket(); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index ecb741ceb8..c3aae7146a 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -17,8 +17,8 @@ using namespace std; Model::Model(QObject* parent) : QObject(parent), - _pupilDilation(0.0f) -{ + _lodDistance(0.0f), + _pupilDilation(0.0f) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); } @@ -107,8 +107,7 @@ void Model::reset() { void Model::simulate(float deltaTime) { // update our LOD if (_geometry) { - QSharedPointer geometry = _geometry->getLODOrFallback(glm::distance(_translation, - Application::getInstance()->getCamera()->getPosition()), _lodHysteresis); + QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); if (_geometry != geometry) { deleteGeometry(); _dilatedTextures.clear(); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 5da2891cfe..cf6e1fea6d 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -56,6 +56,9 @@ public: Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl()); const QUrl& getURL() const { return _url; } + /// Sets the distance parameter used for LOD computations. + void setLODDistance(float distance) { _lodDistance = distance; } + /// Returns the extents of the model in its bind pose. Extents getBindExtents() const; @@ -230,13 +233,14 @@ private: void renderMeshes(float alpha, bool translucent); QSharedPointer _baseGeometry; ///< reference required to prevent collection of base + float _lodDistance; float _lodHysteresis; float _pupilDilation; std::vector _blendshapeCoefficients; QUrl _url; - + QVector _blendedVertexBufferIDs; QVector > > _dilatedTextures; bool _resetStates; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 26c8abefea..2cb719c446 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -332,6 +332,12 @@ void AvatarData::setDisplayName(const QString& displayName) { qDebug() << "Changing display name for avatar to" << displayName; } +void AvatarData::setBillboard(const QByteArray& billboard) { + _billboard = billboard; + + qDebug() << "Changing billboard for avatar."; +} + void AvatarData::setClampedTargetScale(float targetScale) { targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index f597edcc3a..f6bde135b4 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -163,7 +163,7 @@ public: virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setDisplayName(const QString& displayName); - void setBillboard(const QByteArray& billboard) { _billboard = billboard; } + virtual void setBillboard(const QByteArray& billboard); const QByteArray& getBillboard() const { return _billboard; } QString getFaceModelURLFromScript() const { return _faceModelURL.toString(); } From e66ec6b702e603b34a1326859c90046cba8b8117 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 21 Feb 2014 15:47:40 -0800 Subject: [PATCH 26/48] Final fix for clipboard preview --- interface/src/ui/ClipboardOverlay.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/src/ui/ClipboardOverlay.cpp b/interface/src/ui/ClipboardOverlay.cpp index 12c72c7690..7eee318ec9 100644 --- a/interface/src/ui/ClipboardOverlay.cpp +++ b/interface/src/ui/ClipboardOverlay.cpp @@ -38,10 +38,7 @@ void ClipboardOverlay::render() { glTranslatef(_position.x, _position.y, _position.z); glScalef(_size, _size, _size); - qDebug() << "[DEBUG] " << voxelSystem->getVoxelsRendered() << " " - << clipboard->getOctreeElementsCount(); - - // TODO : replace that hack to get the preview working corectly + // We only force the redraw when the clipboard content has changed if (lastVoxelCount != clipboard->getOctreeElementsCount()) { voxelSystem->forceRedrawEntireTree(); lastVoxelCount = clipboard->getOctreeElementsCount(); From 6fb2d57e403ebcc1b29d076feb4771cce5282ad4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 21 Feb 2014 16:04:15 -0800 Subject: [PATCH 27/48] Add clipboardOverlay example to OverlaysExample --- examples/overlaysExample.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index b57f82e7e9..d97ec9e8fd 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -156,6 +156,13 @@ var line3d = Overlays.addOverlay("line3d", { lineWidth: 5 }); +// this will display the content of your clipboard at the origin of the domain +var clipboardPreview = Overlays.addOverlay("clipboard", { + position: { x: 0, y: 0, z: 0}, + size: 1 / 32, + visible: true + }); + // When our script shuts down, we should clean up all of our overlays function scriptEnding() { @@ -170,6 +177,7 @@ function scriptEnding() { Overlays.deleteOverlay(solidCube); Overlays.deleteOverlay(sphere); Overlays.deleteOverlay(line3d); + Overlays.deleteOverlay(clipboardPreview); } Script.scriptEnding.connect(scriptEnding); From 6f8f15018c26db4a78a54521fccb034ba05cb753 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Feb 2014 17:52:20 -0800 Subject: [PATCH 28/48] Allow setting the combined head orientation, which changes the body yaw and applies the rest to the head. Closes #2050. --- examples/editVoxels.js | 2 +- interface/src/avatar/Avatar.cpp | 1 - interface/src/avatar/Head.cpp | 7 +------ interface/src/avatar/Head.h | 3 --- interface/src/avatar/MyAvatar.cpp | 1 - libraries/avatars/src/AvatarData.h | 4 ++++ libraries/avatars/src/HeadData.cpp | 23 +++++++++++++++++++++++ libraries/avatars/src/HeadData.h | 4 ++++ 8 files changed, 33 insertions(+), 12 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 81e3000566..393dd52fef 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -866,7 +866,7 @@ function mouseReleaseEvent(event) { var cameraOrientation = Camera.getOrientation(); var eulers = Quat.safeEulerAngles(cameraOrientation); MyAvatar.position = Camera.getPosition(); - MyAvatar.orientation = cameraOrientation; + MyAvatar.headOrientation = cameraOrientation; Camera.stopLooking(); Camera.setMode(oldMode); Camera.setOrientation(cameraOrientation); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index dcecd0258d..0f820f871a 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -118,7 +118,6 @@ void Avatar::simulate(float deltaTime) { getHand()->simulate(deltaTime, false); _skeletonModel.simulate(deltaTime); Head* head = getHead(); - head->setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); glm::vec3 headPosition; if (!_skeletonModel.getHeadPosition(headPosition)) { headPosition = _position; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 5c6100764a..f6c8fabc0d 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -28,7 +28,6 @@ Head::Head(Avatar* owningAvatar) : _gravity(0.0f, -1.0f, 0.0f), _lastLoudness(0.0f), _audioAttack(0.0f), - _bodyRotation(0.0f, 0.0f, 0.0f), _angularVelocity(0,0,0), _renderLookatVectors(false), _saccade(0.0f, 0.0f, 0.0f), @@ -180,12 +179,8 @@ void Head::setScale (float scale) { _scale = scale; } -glm::quat Head::getOrientation() const { - return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll))); -} - glm::quat Head::getTweakedOrientation() const { - return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() ))); + return _owningAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() ))); } glm::quat Head::getCameraOrientation () const { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index c88e654d95..39a2f4eeb6 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -40,13 +40,11 @@ public: void render(float alpha); void setScale(float scale); void setPosition(glm::vec3 position) { _position = position; } - void setBodyRotation(glm::vec3 bodyRotation) { _bodyRotation = bodyRotation; } void setGravity(glm::vec3 gravity) { _gravity = gravity; } void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; } void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } - glm::quat getOrientation() const; glm::quat getTweakedOrientation() const; glm::quat getCameraOrientation () const; const glm::vec3& getAngularVelocity() const { return _angularVelocity; } @@ -97,7 +95,6 @@ private: glm::vec3 _gravity; float _lastLoudness; float _audioAttack; - glm::vec3 _bodyRotation; glm::vec3 _angularVelocity; bool _renderLookatVectors; glm::vec3 _saccade; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 90ef18848b..60af1560ce 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -321,7 +321,6 @@ void MyAvatar::simulate(float deltaTime) { _skeletonModel.simulate(deltaTime); Head* head = getHead(); - head->setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); glm::vec3 headPosition; if (!_skeletonModel.getHeadPosition(headPosition)) { headPosition = _position; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 07c774d5e6..8f6ce850a6 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -78,6 +78,7 @@ class AvatarData : public NodeData { Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) + Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) Q_PROPERTY(float audioLoudness READ getAudioLoudness WRITE setAudioLoudness) @@ -109,6 +110,9 @@ public: glm::quat getOrientation() const { return glm::quat(glm::radians(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll))); } void setOrientation(const glm::quat& orientation); + glm::quat getHeadOrientation() const { return _headData->getOrientation(); } + void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); } + // access to Head().set/getMousePitch float getHeadPitch() const { return _headData->getPitch(); } void setHeadPitch(float value) { _headData->setPitch(value); }; diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index 62e8276bd3..68a5c2c826 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -6,6 +6,11 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include + +#include + +#include "AvatarData.h" #include "HeadData.h" HeadData::HeadData(AvatarData* owningAvatar) : @@ -26,6 +31,24 @@ HeadData::HeadData(AvatarData* owningAvatar) : } +glm::quat HeadData::getOrientation() const { + return _owningAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll))); +} + +void HeadData::setOrientation(const glm::quat& orientation) { + // rotate body about vertical axis + glm::quat bodyOrientation = _owningAvatar->getOrientation(); + glm::vec3 newFront = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FRONT); + bodyOrientation = bodyOrientation * glm::angleAxis(glm::degrees(atan2f(-newFront.x, -newFront.z)), 0.0f, 1.0f, 0.0f); + _owningAvatar->setOrientation(bodyOrientation); + + // the rest goes to the head + glm::vec3 eulers = safeEulerAngles(glm::inverse(bodyOrientation) * orientation); + _pitch = eulers.x; + _yaw = eulers.y; + _roll = eulers.z; +} + void HeadData::addYaw(float yaw) { setYaw(_yaw + yaw); } diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index 04d5fe5b46..618de89b31 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -13,6 +13,7 @@ #include #include +#include const float MIN_HEAD_YAW = -110; const float MAX_HEAD_YAW = 110; @@ -42,6 +43,9 @@ public: float getRoll() const { return _roll; } void setRoll(float roll) { _roll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } + glm::quat getOrientation() const; + void setOrientation(const glm::quat& orientation); + float getAudioLoudness() const { return _audioLoudness; } void setAudioLoudness(float audioLoudness) { _audioLoudness = audioLoudness; } From 68fea8de1e49191f8b14eecfd629a9ac36558a0c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Feb 2014 18:32:05 -0800 Subject: [PATCH 29/48] More merge fixes. --- assignment-client/src/metavoxels/MetavoxelServer.cpp | 2 +- interface/src/MetavoxelSystem.cpp | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index d50ca963eb..fa60d3a88c 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -88,7 +88,7 @@ void MetavoxelServer::sendDeltas() { MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const SharedNodePointer& node) : _server(server), - _sequencer(byteArrayWithPopluatedHeader(PacketTypeMetavoxelData)), + _sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData)), _node(node) { connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a14e133bc1..0421ca4747 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -168,19 +168,12 @@ bool MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { return false; } -<<<<<<< HEAD MetavoxelSystem::RenderVisitor::RenderVisitor() : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()) { } void MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) { spanner->getRenderer()->render(1.0f); -======= -static QByteArray createDatagramHeader(const QUuid& sessionID) { - QByteArray header = byteArrayWithPopulatedHeader(PacketTypeMetavoxelData); - header += sessionID.toRfc4122(); - return header; ->>>>>>> 224f42c366bedfe91546888a9039c917ff6d416c } MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : From 7bb3ea66acea8e6299595abafa87c84e8b244a48 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Feb 2014 18:32:29 -0800 Subject: [PATCH 30/48] And another one. --- interface/src/MetavoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 0421ca4747..59a714ece5 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -178,7 +178,7 @@ void MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) { MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : _node(node), - _sequencer(byteArrayWithPopluatedHeader(PacketTypeMetavoxelData)) { + _sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData)) { connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); From 03d6173c90d7c80b5dc1287be32811f1cd70a8bd Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 11:38:23 -0800 Subject: [PATCH 31/48] add Voxels.getFaceVector() to JS --- .../voxels/src/VoxelsScriptingInterface.cpp | 18 ++++++++++++++++++ .../voxels/src/VoxelsScriptingInterface.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index c0468ccca6..bc9639f6a9 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -64,3 +64,21 @@ RayToVoxelIntersectionResult VoxelsScriptingInterface::findRayIntersection(const } return result; } + +glm::vec3 VoxelsScriptingInterface::getFaceVector(const QString& face) { + if (face == "MIN_X_FACE") { + return glm::vec3(-1, 0, 0); + } else if (face == "MAX_X_FACE") { + return glm::vec3(1, 0, 0); + } else if (face == "MIN_Y_FACE") { + return glm::vec3(0, -1, 0); + } else if (face == "MAX_Y_FACE") { + return glm::vec3(0, 1, 0); + } else if (face == "MIN_Z_FACE") { + return glm::vec3(0, 0, -1); + } else if (face == "MAX_Z_FACE") { + return glm::vec3(0, 0, 1); + } + return glm::vec3(0, 0, 0); //error case +} + diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index f87e8d0a4c..88363bef39 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -60,6 +60,9 @@ public slots: /// If the scripting context has visible voxels, this will determine a ray intersection RayToVoxelIntersectionResult findRayIntersection(const PickRay& ray); + /// returns a voxel space axis aligned vector for the face, useful in doing voxel math + glm::vec3 getFaceVector(const QString& face); + private: void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails); VoxelTree* _tree; From 9831fb57971c446d35a0acba4cb48a493f397268 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 12:09:53 -0800 Subject: [PATCH 32/48] fix calculateVoxelFromIntersection() in editVoxels.js to work same as C++ code for voxel sizes larger than intersecting voxel --- examples/editVoxels.js | 100 ++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 31 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 0ab94918c0..219589f78d 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -25,7 +25,7 @@ var ORBIT_RATE_AZIMUTH = 90.0; var PIXELS_PER_EXTRUDE_VOXEL = 16; var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting -var previewLineWidth = 1.5; +var previewLineWidth = 3; // 1.5; var oldMode = Camera.getMode(); @@ -339,9 +339,22 @@ var recolorToolSelected = false; var eyedropperToolSelected = false; var selectToolSelected = false; + function calculateVoxelFromIntersection(intersection, operation) { //print("calculateVoxelFromIntersection() operation="+operation); var resultVoxel; + + var wantDebug = false; + if (wantDebug) { + print(">>>>> calculateVoxelFromIntersection().... intersection voxel.red/green/blue=" + intersection.voxel.red + ", " + + intersection.voxel.green + ", " + intersection.voxel.blue); + print(" intersection voxel.x/y/z/s=" + intersection.voxel.x + ", " + + intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s); + print(" intersection face=" + intersection.face); + print(" intersection distance=" + intersection.distance); + print(" intersection intersection.x/y/z=" + intersection.intersection.x + ", " + + intersection.intersection.y + ", " + intersection.intersection.z); + } var voxelSize; if (pointerVoxelScaleSet) { @@ -350,20 +363,48 @@ function calculateVoxelFromIntersection(intersection, operation) { voxelSize = intersection.voxel.s; } - // first, calculate the enclosed voxel of size voxelSize that the intersection point falls inside of. - // if you have a voxelSize that's smaller than the voxel you're intersecting, this calculation will result - // in the subvoxel that the intersection point falls in - var x = Math.floor(intersection.intersection.x / voxelSize) * voxelSize; - var y = Math.floor(intersection.intersection.y / voxelSize) * voxelSize; - var z = Math.floor(intersection.intersection.z / voxelSize) * voxelSize; + var x; + var y; + var z; + + // if our "target voxel size" is larger than the voxel we intersected with, then we need to find the closest + // ancestor voxel of our target size that contains our intersected voxel. + if (voxelSize > intersection.voxel.s) { + if (wantDebug) { + print("voxelSize > intersection.voxel.s.... choose the larger voxel that encompasses the one selected"); + } + x = Math.floor(intersection.voxel.x / voxelSize) * voxelSize; + y = Math.floor(intersection.voxel.y / voxelSize) * voxelSize; + z = Math.floor(intersection.voxel.z / voxelSize) * voxelSize; + } else { + // otherwise, calculate the enclosed voxel of size voxelSize that the intersection point falls inside of. + // if you have a voxelSize that's smaller than the voxel you're intersecting, this calculation will result + // in the subvoxel that the intersection point falls in, if the target voxelSize matches the intersecting + // voxel this still works and results in returning the intersecting voxel which is what we want + var adjustToCenter = Vec3.multiply(Voxels.getFaceVector(intersection.face), (voxelSize * -0.5)); + if (wantDebug) { + print("adjustToCenter=" + adjustToCenter.x + "," + adjustToCenter.y + "," + adjustToCenter.z); + } + var centerOfIntersectingVoxel = Vec3.sum(intersection.intersection, adjustToCenter); + x = Math.floor(centerOfIntersectingVoxel.x / voxelSize) * voxelSize; + y = Math.floor(centerOfIntersectingVoxel.y / voxelSize) * voxelSize; + z = Math.floor(centerOfIntersectingVoxel.z / voxelSize) * voxelSize; + } resultVoxel = { x: x, y: y, z: z, s: voxelSize }; highlightAt = { x: x, y: y, z: z, s: voxelSize }; + // we only do the "add to the face we're pointing at" adjustment, if the operation is an add + // operation, and the target voxel size is equal to or smaller than the intersecting voxel. + var wantAddAdjust = (operation == "add" && (voxelSize <= intersection.voxel.s)); + if (wantDebug) { + print("wantAddAdjust="+wantAddAdjust); + } + // now we also want to calculate the "edge square" for the face for this voxel if (intersection.face == "MIN_X_FACE") { - highlightAt.x = intersection.voxel.x - zFightingSizeAdjust; - resultVoxel.x = intersection.voxel.x; - if (operation == "add") { + + highlightAt.x = x - zFightingSizeAdjust; + if (wantAddAdjust) { resultVoxel.x -= voxelSize; } @@ -373,10 +414,10 @@ function calculateVoxelFromIntersection(intersection, operation) { resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; } else if (intersection.face == "MAX_X_FACE") { - highlightAt.x = intersection.voxel.x + intersection.voxel.s + zFightingSizeAdjust; - resultVoxel.x = intersection.voxel.x + intersection.voxel.s; - if (operation != "add") { - resultVoxel.x -= voxelSize; + + highlightAt.x = x + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.x += resultVoxel.s; } resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; @@ -386,10 +427,8 @@ function calculateVoxelFromIntersection(intersection, operation) { } else if (intersection.face == "MIN_Y_FACE") { - highlightAt.y = intersection.voxel.y - zFightingSizeAdjust; - resultVoxel.y = intersection.voxel.y; - - if (operation == "add") { + highlightAt.y = y - zFightingSizeAdjust; + if (wantAddAdjust) { resultVoxel.y -= voxelSize; } @@ -400,10 +439,9 @@ function calculateVoxelFromIntersection(intersection, operation) { } else if (intersection.face == "MAX_Y_FACE") { - highlightAt.y = intersection.voxel.y + intersection.voxel.s + zFightingSizeAdjust; - resultVoxel.y = intersection.voxel.y + intersection.voxel.s; - if (operation != "add") { - resultVoxel.y -= voxelSize; + highlightAt.y = y + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.y += voxelSize; } resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; @@ -413,10 +451,8 @@ function calculateVoxelFromIntersection(intersection, operation) { } else if (intersection.face == "MIN_Z_FACE") { - highlightAt.z = intersection.voxel.z - zFightingSizeAdjust; - resultVoxel.z = intersection.voxel.z; - - if (operation == "add") { + highlightAt.z = z - zFightingSizeAdjust; + if (wantAddAdjust) { resultVoxel.z -= voxelSize; } @@ -427,10 +463,9 @@ function calculateVoxelFromIntersection(intersection, operation) { } else if (intersection.face == "MAX_Z_FACE") { - highlightAt.z = intersection.voxel.z + intersection.voxel.s + zFightingSizeAdjust; - resultVoxel.z = intersection.voxel.z + intersection.voxel.s; - if (operation != "add") { - resultVoxel.z -= voxelSize; + highlightAt.z = z + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.z += voxelSize; } resultVoxel.bottomLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; @@ -534,7 +569,7 @@ function showPreviewLines() { position: resultVoxel, size: resultVoxel.s + zFightingSizeAdjust, visible: true, - color: { red: 255, green: 255, blue: 255 }, + color: { red: 0, green: 255, blue: 0 }, lineWidth: previewLineWidth, solid: false, alpha: 1 @@ -888,6 +923,7 @@ print("clickedOverlay="+clickedOverlay); } voxelDetails = calculateVoxelFromIntersection(intersection,"add"); + Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, newColor.red, newColor.green, newColor.blue); lastVoxelPosition = { x: voxelDetails.x, y: voxelDetails.y, z: voxelDetails.z }; @@ -929,6 +965,7 @@ function keyPressEvent(event) { red: colors[color].red, green: colors[color].green, blue: colors[color].blue }; + Voxels.eraseVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s); Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue); setAudioPosition(); Audio.playSound(addSound, audioOptions); @@ -1017,6 +1054,7 @@ function mouseMoveEvent(event) { var dy = event.y - mouseY; if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) { lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection); + Voxels.eraseVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,extrudeScale); Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue); mouseX = event.x; From f1577d12a018d7ee31f314aa5236f79f9e9811f0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 12:36:38 -0800 Subject: [PATCH 33/48] make changes to local tree in Voxels JS interface --- examples/editVoxels.js | 3 --- .../voxels/src/VoxelsScriptingInterface.cpp | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 219589f78d..31a8e7a787 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -923,7 +923,6 @@ print("clickedOverlay="+clickedOverlay); } voxelDetails = calculateVoxelFromIntersection(intersection,"add"); - Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, newColor.red, newColor.green, newColor.blue); lastVoxelPosition = { x: voxelDetails.x, y: voxelDetails.y, z: voxelDetails.z }; @@ -965,7 +964,6 @@ function keyPressEvent(event) { red: colors[color].red, green: colors[color].green, blue: colors[color].blue }; - Voxels.eraseVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s); Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue); setAudioPosition(); Audio.playSound(addSound, audioOptions); @@ -1054,7 +1052,6 @@ function mouseMoveEvent(event) { var dy = event.y - mouseY; if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) { lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection); - Voxels.eraseVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,extrudeScale); Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue); mouseX = event.x; diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index bc9639f6a9..719df8dccf 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -20,6 +20,13 @@ void VoxelsScriptingInterface::setVoxelNonDestructive(float x, float y, float z, // queue the packet queueVoxelAdd(PacketTypeVoxelSet, addVoxelDetail); + + // handle the local tree also... + if (_tree) { + _tree->lockForWrite(); + _tree->createVoxel(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s, red, green, blue, false); + _tree->unlock(); + } } void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale, @@ -30,6 +37,13 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale, // queue the destructive add queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail); + + // handle the local tree also... + if (_tree) { + _tree->lockForWrite(); + _tree->createVoxel(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s, red, green, blue, true); + _tree->unlock(); + } } void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale) { @@ -39,6 +53,13 @@ void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale scale / (float)TREE_SCALE, 0, 0, 0}; getVoxelPacketSender()->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &deleteVoxelDetail); + + // handle the local tree also... + if (_tree) { + _tree->lockForWrite(); + _tree->deleteVoxelAt(deleteVoxelDetail.x, deleteVoxelDetail.y, deleteVoxelDetail.z, deleteVoxelDetail.s); + _tree->unlock(); + } } From 78de562f4164ad47c9b3619df461182632c07ea4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 12:52:41 -0800 Subject: [PATCH 34/48] added clipboard support to editVoxels.js --- examples/editVoxels.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 31a8e7a787..fa0f130fd7 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -982,6 +982,44 @@ function keyPressEvent(event) { function keyReleaseEvent(event) { trackKeyReleaseEvent(event); // used by preview support + + // handle clipboard items + if (selectToolSelected) { + var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); + var intersection = Voxels.findRayIntersection(pickRay); + selectedVoxel = calculateVoxelFromIntersection(intersection,"select"); + + // Note: this sample uses Alt+ as the key codes for these clipboard items + if ((event.key == 199 || event.key == 67 || event.text == "C" || event.text == "c") && event.isAlt) { + print("the Alt+C key was pressed... copy"); + Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + if ((event.key == 8776 || event.key == 88 || event.text == "X" || event.text == "x") && event.isAlt) { + print("the Alt+X key was pressed... cut"); + Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + if ((event.key == 8730 || event.key == 86 || event.text == "V" || event.text == "v") && event.isAlt) { + print("the Alt+V key was pressed... paste"); + Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + if (event.text == "DELETE" || event.text == "BACKSPACE") { + print("the DELETE/BACKSPACE key was pressed... delete"); + Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + + if ((event.text == "E" || event.text == "e") && event.isMeta) { + print("the Ctl+E key was pressed... export"); + Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + if ((event.text == "I" || event.text == "i") && event.isMeta) { + print("the Ctl+I key was pressed... import"); + Clipboard.importVoxels(); + } + if ((event.key == 78 || event.text == "N" || event.text == "n") && event.isMeta) { + print("the Ctl+N key was pressed, nudging to left 1 meter... nudge"); + Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 }); + } + } } @@ -1286,6 +1324,8 @@ Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.touchBeginEvent.connect(touchBeginEvent); Controller.touchUpdateEvent.connect(touchUpdateEvent); Controller.touchEndEvent.connect(touchEndEvent); +Controller.captureKeyEvents({ text: "+" }); +Controller.captureKeyEvents({ text: "-" }); function scriptEnding() { @@ -1302,6 +1342,8 @@ function scriptEnding() { Overlays.deleteOverlay(recolorTool); Overlays.deleteOverlay(eyedropperTool); Overlays.deleteOverlay(selectTool); + Controller.releaseKeyEvents({ text: "+" }); + Controller.releaseKeyEvents({ text: "-" }); } Script.scriptEnding.connect(scriptEnding); From a8b34ae756d21337876a8c9444bac42cb0c5155f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 12:58:20 -0800 Subject: [PATCH 35/48] removed some debug settings --- examples/editVoxels.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index fa0f130fd7..ce444885af 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -25,7 +25,7 @@ var ORBIT_RATE_AZIMUTH = 90.0; var PIXELS_PER_EXTRUDE_VOXEL = 16; var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting -var previewLineWidth = 3; // 1.5; +var previewLineWidth = 1.5; var oldMode = Camera.getMode(); @@ -569,7 +569,7 @@ function showPreviewLines() { position: resultVoxel, size: resultVoxel.s + zFightingSizeAdjust, visible: true, - color: { red: 0, green: 255, blue: 0 }, + color: { red: 255, green: 255, blue: 0 }, lineWidth: previewLineWidth, solid: false, alpha: 1 From 892cc01dd95652b014d64c40815367639f2cb342 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 13:18:36 -0800 Subject: [PATCH 36/48] added Voxels.getVoxelAt() to inspect local tree --- examples/rayPickExample.js | 6 +++++ .../voxels/src/VoxelsScriptingInterface.cpp | 24 +++++++++++++++++++ .../voxels/src/VoxelsScriptingInterface.h | 8 +++++++ 3 files changed, 38 insertions(+) diff --git a/examples/rayPickExample.js b/examples/rayPickExample.js index 39dc910e78..9c34cca1de 100644 --- a/examples/rayPickExample.js +++ b/examples/rayPickExample.js @@ -26,6 +26,12 @@ function mouseMoveEvent(event) { print("intersection distance=" + intersection.distance); print("intersection intersection.x/y/z=" + intersection.intersection.x + ", " + intersection.intersection.y + ", " + intersection.intersection.z); + + // also test the getVoxelAt() api which should find and return same voxel + + var voxelAt = Voxels.getVoxelAt(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s); + print("voxelAt.x/y/z/s=" + voxelAt.x + ", " + voxelAt.y + ", " + voxelAt.z + ": " + voxelAt.s); + print("voxelAt.red/green/blue=" + voxelAt.red + ", " + voxelAt.green + ", " + voxelAt.blue); } } diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index 719df8dccf..1a0a234e0a 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -12,6 +12,30 @@ void VoxelsScriptingInterface::queueVoxelAdd(PacketType addPacketType, VoxelDeta getVoxelPacketSender()->queueVoxelEditMessages(addPacketType, 1, &addVoxelDetails); } +VoxelDetail VoxelsScriptingInterface::getVoxelAt(float x, float y, float z, float scale) { + // setup a VoxelDetail struct with the data + VoxelDetail result = {0,0,0,0,0,0,0}; + + if (_tree) { + _tree->lockForRead(); + + VoxelTreeElement* voxel = static_cast(_tree->getOctreeElementAt(x / (float)TREE_SCALE, y / (float)TREE_SCALE, + z / (float)TREE_SCALE, scale / (float)TREE_SCALE)); + _tree->unlock(); + if (voxel) { + // Note: these need to be in voxel space because the VoxelDetail -> js converter will upscale + result.x = voxel->getCorner().x; + result.y = voxel->getCorner().y; + result.z = voxel->getCorner().z; + result.s = voxel->getScale(); + result.red = voxel->getColor()[RED_INDEX]; + result.green = voxel->getColor()[GREEN_INDEX]; + result.blue = voxel->getColor()[BLUE_INDEX]; + } + } + return result; +} + void VoxelsScriptingInterface::setVoxelNonDestructive(float x, float y, float z, float scale, uchar red, uchar green, uchar blue) { // setup a VoxelDetail struct with the data diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index 88363bef39..fb0bd6ab03 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -30,6 +30,14 @@ public: void setVoxelTree(VoxelTree* tree) { _tree = tree; } public slots: + + /// checks the local voxel tree for a voxel at the specified location and scale + /// \param x the x-coordinate of the voxel (in meter units) + /// \param y the y-coordinate of the voxel (in meter units) + /// \param z the z-coordinate of the voxel (in meter units) + /// \param scale the scale of the voxel (in meter units) + VoxelDetail getVoxelAt(float x, float y, float z, float scale); + /// queues the creation of a voxel which will be sent by calling process on the PacketSender /// \param x the x-coordinate of the voxel (in meter units) /// \param y the y-coordinate of the voxel (in meter units) From 43f63781fcb08c0e76b52c14cdff5b30a7c7b64b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 18:35:39 -0800 Subject: [PATCH 37/48] removed files we don't need --- examples/alternate-gameoflife.js | 137 ----------------- examples/cubes.svo | Bin 145 -> 0 bytes examples/invader.svo | Bin 130 -> 0 bytes examples/invader2.svo | Bin 130 -> 0 bytes examples/slider.jpg | Bin 8797 -> 0 bytes examples/slider.png | Bin 10738 -> 0 bytes examples/swatches.svg | 20 --- examples/testing-hifi-interface-tools.svg | 177 ---------------------- examples/testing-swatches | 20 --- examples/testing-swatches.svg | 20 --- examples/thumb.jpg | Bin 5221 -> 0 bytes examples/thumb.png | Bin 5198 -> 0 bytes 12 files changed, 374 deletions(-) delete mode 100644 examples/alternate-gameoflife.js delete mode 100644 examples/cubes.svo delete mode 100644 examples/invader.svo delete mode 100644 examples/invader2.svo delete mode 100644 examples/slider.jpg delete mode 100644 examples/slider.png delete mode 100644 examples/swatches.svg delete mode 100644 examples/testing-hifi-interface-tools.svg delete mode 100644 examples/testing-swatches delete mode 100644 examples/testing-swatches.svg delete mode 100644 examples/thumb.jpg delete mode 100644 examples/thumb.png diff --git a/examples/alternate-gameoflife.js b/examples/alternate-gameoflife.js deleted file mode 100644 index 8ab992138a..0000000000 --- a/examples/alternate-gameoflife.js +++ /dev/null @@ -1,137 +0,0 @@ -// Add your JavaScript for assignment below this line - -// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) - -var gameZ = 100; -var NUMBER_OF_CELLS_EACH_DIMENSION = 64; -var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; - -var currentCells = []; -var nextCells = []; - -var METER_LENGTH = 1; -var cellScale = (NUMBER_OF_CELLS_EACH_DIMENSION * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION; - -// randomly populate the cell start values -for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - // create the array to hold this row - currentCells[i] = []; - - // create the array to hold this row in the nextCells array - nextCells[i] = []; - - for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - currentCells[i][j] = Math.floor(Math.random() * 2); - - // put the same value in the nextCells array for first board draw - nextCells[i][j] = currentCells[i][j]; - } -} - -function isNeighbourAlive(i, j) { - if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION - || i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) { - return 0; - } else { - return currentCells[i][j]; - } -} - -function updateCells() { - var i = 0; - var j = 0; - - for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - // figure out the number of live neighbours for the i-j cell - var liveNeighbours = - isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) + - isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) + - isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1); - - if (currentCells[i][j]) { - // live cell - - if (liveNeighbours < 2) { - // rule #1 - under-population - this cell will die - // mark it zero to mark the change - nextCells[i][j] = 0; - } else if (liveNeighbours < 4) { - // rule #2 - this cell lives - // mark it -1 to mark no change - nextCells[i][j] = -1; - } else { - // rule #3 - overcrowding - this cell dies - // mark it zero to mark the change - nextCells[i][j] = 0; - } - } else { - // dead cell - if (liveNeighbours == 3) { - // rule #4 - reproduction - this cell revives - // mark it one to mark the change - nextCells[i][j] = 1; - } else { - // this cell stays dead - // mark it -1 for no change - nextCells[i][j] = -1; - } - } - - if (Math.random() < 0.001) { - // Random mutation to keep things interesting in there. - nextCells[i][j] = 1; - } - } - } - - for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - if (nextCells[i][j] != -1) { - // there has been a change to this cell, change the value in the currentCells array - currentCells[i][j] = nextCells[i][j]; - } - } - } -} - -function sendNextCells() { - for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - if (nextCells[i][j] != -1) { - // there has been a change to the state of this cell, send it - - // find the x and y position for this voxel, z = 0 - var x = j * cellScale; - var y = i * cellScale; - - // queue a packet to add a voxel for the new cell - var color = (nextCells[i][j] == 1) ? 255 : 1; - if (color == 255) { - Voxels.setVoxel(x, y, gameZ, cellScale, color, color, color); - } else { - Voxels.setVoxel(x, y, gameZ, cellScale, 255, 0, 0); - } - } - } - } -} - -var sentFirstBoard = false; - -function step() { - if (sentFirstBoard) { - // we've already sent the first full board, perform a step in time - updateCells(); - } else { - // this will be our first board send - sentFirstBoard = true; - } - - sendNextCells(); -} - -print("here"); -//Script.willSendVisualDataCallback.connect(step); -Voxels.setPacketsPerSecond(200); -print("now here"); diff --git a/examples/cubes.svo b/examples/cubes.svo deleted file mode 100644 index dee8f428d5f8ce33d9aacca999b1bd4c92b414a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmZQr#ef7>1u`%&yj>4M4(A~>GRfeIkp7G;0bw%)K%^c1V3BiRU|?{t$0EV7%J#Cy P+w}~_XU_aLb7cSk!{1-@O+06`At;Y>J#!4=MQ_zY*l84Ljsro$h&5S+o_@OC|egFT!NXD|Q& DBSAZq diff --git a/examples/invader2.svo b/examples/invader2.svo deleted file mode 100644 index bacabfe662ec646e704b899a0a5c0cf609664d27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130 zcmZQr#ef7>1u`%&yj>4M4(A~>GRfeIkp7G;0bw%)K%^c1V3BiRU|?{t$07j$Zi6)> diff --git a/examples/slider.jpg b/examples/slider.jpg deleted file mode 100644 index 0d9652d212edf69e3bb95b05b8da3335e85738e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8797 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 diff --git a/examples/slider.png b/examples/slider.png deleted file mode 100644 index b4ba7d90b827130d4c78576b337ebc951de7d158..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10738 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 R`uE;d;jOAn`5V*E{|6%+f)M}! diff --git a/examples/swatches.svg b/examples/swatches.svg deleted file mode 100644 index ee144170c5..0000000000 --- a/examples/swatches.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/examples/testing-hifi-interface-tools.svg b/examples/testing-hifi-interface-tools.svg deleted file mode 100644 index b7bdbbcb58..0000000000 --- a/examples/testing-hifi-interface-tools.svg +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/testing-swatches b/examples/testing-swatches deleted file mode 100644 index ee144170c5..0000000000 --- a/examples/testing-swatches +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/examples/testing-swatches.svg b/examples/testing-swatches.svg deleted file mode 100644 index ee144170c5..0000000000 --- a/examples/testing-swatches.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/examples/thumb.jpg b/examples/thumb.jpg deleted file mode 100644 index ab33cec76416883592c39c1034b075f7528873dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5221 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 G{P3Ur<$c5e diff --git a/examples/thumb.png b/examples/thumb.png deleted file mode 100644 index fd5f87cacabe1e11989d4115aab0f73eb5e9d8e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5198 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 Date: Sun, 23 Feb 2014 18:52:15 -0800 Subject: [PATCH 38/48] removed unneeded code --- examples/editVoxels.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 4e24f6021e..26090d2f49 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -736,7 +736,6 @@ function handleOrbitingMove(event) { function endOrbitMode(event) { var cameraOrientation = Camera.getOrientation(); - var eulers = Quat.safeEulerAngles(cameraOrientation); MyAvatar.position = Camera.getPosition(); MyAvatar.headOrientation = cameraOrientation; Camera.stopLooking(); From dad97cd52ea324f9d42b6df3de6028737962575d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 18:54:15 -0800 Subject: [PATCH 39/48] removed accidental code --- examples/hifi-interface-tools.svg | 177 ------------------------------ examples/touchVsMouse.js | 36 ------ 2 files changed, 213 deletions(-) delete mode 100644 examples/hifi-interface-tools.svg delete mode 100644 examples/touchVsMouse.js diff --git a/examples/hifi-interface-tools.svg b/examples/hifi-interface-tools.svg deleted file mode 100644 index 311514581f..0000000000 --- a/examples/hifi-interface-tools.svg +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/touchVsMouse.js b/examples/touchVsMouse.js deleted file mode 100644 index c5007e076b..0000000000 --- a/examples/touchVsMouse.js +++ /dev/null @@ -1,36 +0,0 @@ -// -// toucheVsMouse.js -// hifi -// -// Created by Brad Hefta-Gaub on 1/28/14. -// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. -// -// This is an example script that demonstrates use of the Controller class -// -// - -function touchBeginEvent(event) { - print("touchBeginEvent event.x,y=" + event.x + ", " + event.y); -} - -function touchEndEvent(event) { - print("touchEndEvent event.x,y=" + event.x + ", " + event.y); -} - -function touchUpdateEvent(event) { - print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y); -} -function mouseMoveEvent(event) { - print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y); -} - -// Map the mouse events to our functions -Controller.touchBeginEvent.connect(touchBeginEvent); -Controller.touchUpdateEvent.connect(touchUpdateEvent); -Controller.touchEndEvent.connect(touchEndEvent); - -Controller.mouseMoveEvent.connect(mouseMoveEvent); - -function scriptEnding() { -} -Script.scriptEnding.connect(scriptEnding); From 766a922fb3d8ac253399535fdaa031cbbd458abe Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Feb 2014 19:02:10 -0800 Subject: [PATCH 40/48] add work around for voxel add of larger voxel on top of smaller voxels --- examples/editVoxels.js | 3 +++ libraries/voxels/src/VoxelsScriptingInterface.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 26090d2f49..d676e71087 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -922,6 +922,7 @@ print("clickedOverlay="+clickedOverlay); } voxelDetails = calculateVoxelFromIntersection(intersection,"add"); + Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, newColor.red, newColor.green, newColor.blue); lastVoxelPosition = { x: voxelDetails.x, y: voxelDetails.y, z: voxelDetails.z }; @@ -963,6 +964,7 @@ function keyPressEvent(event) { red: colors[color].red, green: colors[color].green, blue: colors[color].blue }; + Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue); setAudioPosition(); Audio.playSound(addSound, audioOptions); @@ -1089,6 +1091,7 @@ function mouseMoveEvent(event) { var dy = event.y - mouseY; if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) { lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection); + Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue); mouseX = event.x; diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index 1a0a234e0a..db61460d69 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -42,7 +42,7 @@ void VoxelsScriptingInterface::setVoxelNonDestructive(float x, float y, float z, VoxelDetail addVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE, scale / (float)TREE_SCALE, red, green, blue}; - // queue the packet + // queue the add packet queueVoxelAdd(PacketTypeVoxelSet, addVoxelDetail); // handle the local tree also... From f5fece5eb51adf07ec71393fc3f4828707677eef Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 23 Feb 2014 20:11:46 -0800 Subject: [PATCH 41/48] Fix for Xcode warnings. --- interface/src/renderer/Model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c3aae7146a..9513e45d60 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -52,8 +52,8 @@ bool Model::isLoadedWithTextures() const { } foreach (const NetworkMesh& mesh, _geometry->getMeshes()) { foreach (const NetworkMeshPart& part, mesh.parts) { - if (part.diffuseTexture && !part.diffuseTexture->isLoaded() || - part.normalTexture && !part.normalTexture->isLoaded()) { + if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) || + (part.normalTexture && !part.normalTexture->isLoaded())) { return false; } } From 5cf6c434143cf1c37a991d380d8dfb884f762241 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 23 Feb 2014 20:29:54 -0800 Subject: [PATCH 42/48] Fix for Windows build error. --- libraries/metavoxels/src/SharedObject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 94f6c192c5..9895073e44 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -38,7 +38,7 @@ public: virtual bool equals(const SharedObject* other) const; // Dumps the contents of this object to the debug output. - virtual void dump(QDebug debug = qDebug()) const; + virtual void dump(QDebug debug = QDebug(QtDebugMsg)) const; signals: From ba327232d5bc09190d03ede8837ff75db35bab8e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 23 Feb 2014 21:07:00 -0800 Subject: [PATCH 43/48] Fixed another Window signed/unsigned mismatch. --- libraries/metavoxels/src/DatagramSequencer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index a60d4e9df3..0c7b8ce8ab 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -736,7 +736,7 @@ void ReliableChannel::readData(QDataStream& in) { // if we're expecting a message, peek into the buffer to see if we have the whole thing. // if so, read it in, handle it, and loop back around in case there are more if (_messagesEnabled) { - int available = _buffer.bytesAvailable(); + quint32 available = _buffer.bytesAvailable(); if (available >= sizeof(quint32)) { quint32 length; _buffer.readBytes(_buffer.pos(), sizeof(quint32), (char*)&length); From 6591a4b7faecc4d08cfc994b03e80a566ad92d30 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 24 Feb 2014 09:36:18 -0800 Subject: [PATCH 44/48] correctly shutdown slider --- examples/editVoxels.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index d676e71087..80f37a76a3 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -1344,6 +1344,8 @@ function scriptEnding() { Overlays.deleteOverlay(recolorTool); Overlays.deleteOverlay(eyedropperTool); Overlays.deleteOverlay(selectTool); + Overlays.deleteOverlay(slider); + Overlays.deleteOverlay(thumb); Controller.releaseKeyEvents({ text: "+" }); Controller.releaseKeyEvents({ text: "-" }); } From 6cef72473def95bb7a846094d54473ce99f0cc84 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 24 Feb 2014 10:04:08 -0800 Subject: [PATCH 45/48] changes to the bot script --- examples/bot.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/bot.js b/examples/bot.js index d2db3d3b48..8ac718e34d 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -2,7 +2,7 @@ // bot.js // hifi // -// Created by Brad Hefta-Gaub on 2/20/14. +// Created by Stephen Birarda on 2/20/14. // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // // This is an example script that demonstrates an NPC avatar. @@ -46,10 +46,9 @@ if (botNumber <= 20) { newBodyFilePrefix = "bot" + botNumber; } - // set the face model fst using the bot number // there is no need to change the body model - we're using the default -Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix ".fst"; -Avatar.bodyModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix ".fst"; +Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst"; +Avatar.bodyModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + ".fst"; Agent.isAvatar = true; \ No newline at end of file From 6ea2c81e68149e74fd16fc837b79978a9f030939 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 24 Feb 2014 10:10:00 -0800 Subject: [PATCH 46/48] fix reload of scripts --- interface/src/Application.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 38fee4baba..4dd7b66d1c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4201,7 +4201,9 @@ void Application::reloadAllScripts() { scriptAction->activate(QAction::Trigger); qDebug() << "stopping script..." << scriptAction->text(); } - _activeScripts.clear(); + + // NOTE: we don't need to clear the _activeScripts list because that is handled on script shutdown. + foreach (QString scriptName, reloadList){ qDebug() << "reloading script..." << scriptName; loadScript(scriptName); @@ -4209,7 +4211,7 @@ void Application::reloadAllScripts() { } void Application::removeScriptName(const QString& fileNameString) { - _activeScripts.removeOne(fileNameString); + _activeScripts.removeOne(fileNameString); } void Application::loadScript(const QString& fileNameString) { From d80b52dc9f55b2fb1af5586a40fbd234de12317f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 24 Feb 2014 11:33:52 -0800 Subject: [PATCH 47/48] Fixes for camera mode transitions, seeing inside head. --- interface/src/Application.cpp | 29 +++++++++++++++----------- interface/src/Application.h | 4 ++-- interface/src/Camera.cpp | 5 +++++ interface/src/Camera.h | 1 + interface/src/Menu.cpp | 6 ++---- interface/src/avatar/Avatar.cpp | 10 ++++----- interface/src/avatar/Avatar.h | 4 ++-- interface/src/avatar/AvatarManager.cpp | 10 ++++----- interface/src/avatar/AvatarManager.h | 2 +- interface/src/avatar/MyAvatar.cpp | 12 ++++------- interface/src/avatar/MyAvatar.h | 2 +- 11 files changed, 44 insertions(+), 41 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 38fee4baba..844ddaaee0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1983,6 +1983,9 @@ void Application::updateMouseRay() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseRay()"); + // make sure the frustum is up-to-date + loadViewFrustum(_myCamera, _viewFrustum); + // if the mouse pointer isn't visible, act like it's at the center of the screen float x = 0.5f, y = 0.5f; if (!_mouseHidden) { @@ -2030,11 +2033,12 @@ void Application::updateVisage() { _visage.update(); } -void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) { +void Application::updateMyAvatarLookAtPosition() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); + glm::vec3 lookAtSpot; if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { lookAtSpot = _myCamera.getPosition(); @@ -2223,21 +2227,22 @@ void Application::updateMetavoxels(float deltaTime) { } } -void Application::cameraMenuChanged() { +void Application::updateCameraMode() { + float modeShiftPeriod = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? 0.0f : 1.0f; if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); - _myCamera.setModeShiftPeriod(0.00f); + _myCamera.setModeShiftPeriod(0.0f); } } else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); - _myCamera.setModeShiftPeriod(1.0f); + _myCamera.setModeShiftPeriod(modeShiftPeriod); } } else { if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); - _myCamera.setModeShiftPeriod(1.0f); + _myCamera.setModeShiftPeriod(modeShiftPeriod); } } } @@ -2312,16 +2317,16 @@ void Application::update(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); + // lots of things depend on the camera mode, so get that first + updateCameraMode(); + // check what's under the mouse and update the mouse voxel updateMouseRay(); - // Set where I am looking based on my mouse ray (so that other people can see) - glm::vec3 lookAtSpot; - updateFaceshift(); updateVisage(); - _myAvatar->updateLookAtTargetAvatar(lookAtSpot); - updateMyAvatarLookAtPosition(lookAtSpot); + _myAvatar->updateLookAtTargetAvatar(); + updateMyAvatarLookAtPosition(); // Find the voxel we are hovering over, and respond if clicked float distance; @@ -2898,8 +2903,8 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } } - bool renderMyHead = (whichCamera.getInterpolatedMode() != CAMERA_MODE_FIRST_PERSON); - _avatarManager.renderAvatars(renderMyHead, selfAvatarOnly); + bool forceRenderMyHead = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR); + _avatarManager.renderAvatars(forceRenderMyHead, selfAvatarOnly); if (!selfAvatarOnly) { // Render the world box diff --git a/interface/src/Application.h b/interface/src/Application.h index ddd8d16c56..c1a96283a9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -269,7 +269,6 @@ private slots: void setFullscreen(bool fullscreen); void setEnable3DTVMode(bool enable3DTVMode); - void cameraMenuChanged(); void renderThrustAtVoxel(const glm::vec3& thrust); @@ -307,7 +306,7 @@ private: void updateMouseRay(); void updateFaceshift(); void updateVisage(); - void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot); + void updateMyAvatarLookAtPosition(); void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face); void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face); void updateHandAndTouch(float deltaTime); @@ -316,6 +315,7 @@ private: void updateSerialDevices(float deltaTime); void updateThreads(float deltaTime); void updateMetavoxels(float deltaTime); + void updateCameraMode(); void updateCamera(float deltaTime); void updateDialogs(float deltaTime); void updateAudio(float deltaTime); diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 8729ef58b6..be7e7bac50 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -123,6 +123,11 @@ void Camera::setModeShiftPeriod (float period) { const float MIN_PERIOD = 0.001f; const float MAX_PERIOD = 3.0f; _modeShiftPeriod = glm::clamp(period, MIN_PERIOD, MAX_PERIOD); + + // if a zero period was requested, we clearly want to snap immediately to the target + if (period == 0.0f) { + update(MIN_PERIOD); + } } void Camera::setMode(CameraMode m) { diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 7b95ce97f1..b5bf6ea0b7 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -55,6 +55,7 @@ public: const glm::vec3& getPosition() const { return _position; } const glm::quat& getRotation() const { return _rotation; } CameraMode getMode() const { return _mode; } + float getModeShiftPeriod() const { return _modeShiftPeriod; } const glm::vec3& getTargetPosition() const { return _targetPosition; } const glm::quat& getTargetRotation() const { return _targetRotation; } float getFieldOfView() const { return _fieldOfView; } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7e5fe63524..1a7cbe5e6f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -232,11 +232,9 @@ Menu::Menu() : false, appInstance, SLOT(setFullscreen(bool))); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true, - appInstance,SLOT(cameraMenuChanged())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, - appInstance, SLOT(cameraMenuChanged())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false, diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f0f950adc1..e2e364c616 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -187,7 +187,7 @@ static TextRenderer* textRenderer(TextRendererType type) { return displayNameRenderer; } -void Avatar::render(bool forceRenderHead) { +void Avatar::render() { glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition(); float lengthToTarget = glm::length(toTarget); @@ -205,7 +205,7 @@ void Avatar::render(bool forceRenderHead) { getHead()->getFaceModel().renderCollisionProxies(0.7f); } if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { - renderBody(forceRenderHead); + renderBody(); } // render sphere when far away @@ -286,16 +286,14 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { return glm::angleAxis(angle * proportion, axis); } -void Avatar::renderBody(bool forceRenderHead) { +void Avatar::renderBody() { const float BILLBOARD_DISTANCE = 40.0f; if (!_billboard.isEmpty() && getLODDistance() >= BILLBOARD_DISTANCE) { renderBillboard(); return; } _skeletonModel.render(1.0f); - if (forceRenderHead) { - getHead()->render(1.0f); - } + getHead()->render(1.0f); getHand()->render(false); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b9433f15dc..be98254696 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -74,7 +74,7 @@ public: void init(); void simulate(float deltaTime); - void render(bool forceRenderHead); + void render(); //setters void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); } @@ -176,7 +176,7 @@ private: bool _initialized; QScopedPointer _billboardTexture; - void renderBody(bool forceRenderHead); + void renderBody(); void renderBillboard(); void renderDisplayName(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index f65566fe14..fed6716ffa 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -69,7 +69,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { simulateAvatarFades(deltaTime); } -void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { +void AvatarManager::renderAvatars(bool forceRenderMyHead, bool selfAvatarOnly) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::renderAvatars()"); bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors); @@ -83,16 +83,16 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { avatar->init(); } if (avatar == static_cast(_myAvatar.data())) { - avatar->render(forceRenderHead); + _myAvatar->render(forceRenderMyHead); } else { - avatar->render(true); + avatar->render(); } avatar->setDisplayingLookatVectors(renderLookAtVectors); } renderAvatarFades(); } else { // just render myAvatar - _myAvatar->render(forceRenderHead); + _myAvatar->render(forceRenderMyHead); _myAvatar->setDisplayingLookatVectors(renderLookAtVectors); } } @@ -121,7 +121,7 @@ void AvatarManager::renderAvatarFades() { foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) { Avatar* avatar = static_cast(fadingAvatar.data()); - avatar->render(false); + avatar->render(); } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 115423e618..24921f45dd 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -30,7 +30,7 @@ public: MyAvatar* getMyAvatar() { return _myAvatar.data(); } void updateOtherAvatars(float deltaTime); - void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); + void renderAvatars(bool forceRenderMyHead, bool selfAvatarOnly = false); void clearOtherAvatars(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3bb156edcf..50b182c20c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -669,7 +669,7 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { setPosition(position + rotation * (getPosition() - position)); } -void MyAvatar::updateLookAtTargetAvatar(glm::vec3 &eyePosition) { +void MyAvatar::updateLookAtTargetAvatar() { Application* applicationInstance = Application::getInstance(); if (!applicationInstance->isMousePressed()) { @@ -683,14 +683,9 @@ void MyAvatar::updateLookAtTargetAvatar(glm::vec3 &eyePosition) { } float distance; if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) { - // rescale to compensate for head embiggening - eyePosition = (avatar->getHead()->calculateAverageEyePosition() - avatar->getHead()->getScalePivot()) * - (avatar->getScale() / avatar->getHead()->getScale()) + avatar->getHead()->getScalePivot(); _lookAtTargetAvatar = avatarPointer; return; - } else { } - } _lookAtTargetAvatar.clear(); } @@ -724,9 +719,10 @@ void MyAvatar::renderBody(bool forceRenderHead) { _skeletonModel.render(1.0f); // Render head so long as the camera isn't inside it - const float RENDER_HEAD_CUTOFF_DISTANCE = 0.10f; + const float RENDER_HEAD_CUTOFF_DISTANCE = 0.40f; Camera* myCamera = Application::getInstance()->getCamera(); - if (forceRenderHead || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE)) { + if (forceRenderHead || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) > + RENDER_HEAD_CUTOFF_DISTANCE * _scale)) { getHead()->render(1.0f); } getHand()->render(true); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2b5be47419..2e80a9408d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -81,7 +81,7 @@ public: void orbit(const glm::vec3& position, int deltaX, int deltaY); AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } - void updateLookAtTargetAvatar(glm::vec3& eyePosition); + void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); virtual void setFaceModelURL(const QUrl& faceModelURL); From e40fe85c3651e5f93430cd0386360c3aec9e6715 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 24 Feb 2014 11:42:47 -0800 Subject: [PATCH 48/48] Updating the camera mode every frame screws with the scripts, so revert to updating it when the menu changes. --- interface/src/Application.cpp | 5 +---- interface/src/Application.h | 2 +- interface/src/Menu.cpp | 6 ++++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a08800d625..99e3f5be12 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2227,7 +2227,7 @@ void Application::updateMetavoxels(float deltaTime) { } } -void Application::updateCameraMode() { +void Application::cameraMenuChanged() { float modeShiftPeriod = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? 0.0f : 1.0f; if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { @@ -2317,9 +2317,6 @@ void Application::update(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); - // lots of things depend on the camera mode, so get that first - updateCameraMode(); - // check what's under the mouse and update the mouse voxel updateMouseRay(); diff --git a/interface/src/Application.h b/interface/src/Application.h index c1a96283a9..f3f4f3dbb2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -269,6 +269,7 @@ private slots: void setFullscreen(bool fullscreen); void setEnable3DTVMode(bool enable3DTVMode); + void cameraMenuChanged(); void renderThrustAtVoxel(const glm::vec3& thrust); @@ -315,7 +316,6 @@ private: void updateSerialDevices(float deltaTime); void updateThreads(float deltaTime); void updateMetavoxels(float deltaTime); - void updateCameraMode(); void updateCamera(float deltaTime); void updateDialogs(float deltaTime); void updateAudio(float deltaTime); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1a7cbe5e6f..7e5fe63524 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -232,9 +232,11 @@ Menu::Menu() : false, appInstance, SLOT(setFullscreen(bool))); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true, + appInstance,SLOT(cameraMenuChanged())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, + appInstance, SLOT(cameraMenuChanged())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false,