From 5c3c94a6188d2da27e49587b6627f898ab0bd44c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 2 Mar 2015 17:21:43 -0800 Subject: [PATCH] remove metavoxels --- assignment-client/CMakeLists.txt | 2 +- assignment-client/src/AssignmentFactory.cpp | 3 - .../src/metavoxels/MetavoxelServer.cpp | 367 -- .../src/metavoxels/MetavoxelServer.h | 157 - domain-server/src/DomainServer.cpp | 3 +- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 38 +- interface/src/Application.h | 5 - interface/src/DatagramProcessor.cpp | 3 - interface/src/Menu.cpp | 10 - interface/src/Menu.h | 6 - interface/src/MetavoxelSystem.cpp | 2433 ---------- interface/src/MetavoxelSystem.h | 445 -- interface/src/ui/BandwidthDialog.cpp | 8 +- interface/src/ui/BandwidthDialog.h | 3 +- interface/src/ui/CachesSizeDialog.cpp | 4 - interface/src/ui/DialogsManager.cpp | 12 - interface/src/ui/DialogsManager.h | 6 - interface/src/ui/MetavoxelEditor.cpp | 1101 ----- interface/src/ui/MetavoxelEditor.h | 479 -- .../src/ui/MetavoxelNetworkSimulator.cpp | 87 - interface/src/ui/MetavoxelNetworkSimulator.h | 41 - interface/src/ui/Stats.cpp | 42 +- interface/src/ui/Stats.h | 9 - libraries/metavoxels/CMakeLists.txt | 13 - .../metavoxels/src/AttributeRegistry.cpp | 499 -- libraries/metavoxels/src/AttributeRegistry.h | 409 -- libraries/metavoxels/src/Bitstream.cpp | 3552 --------------- libraries/metavoxels/src/Bitstream.h | 1781 -------- .../metavoxels/src/DatagramSequencer.cpp | 952 ---- libraries/metavoxels/src/DatagramSequencer.h | 445 -- libraries/metavoxels/src/Endpoint.cpp | 117 - libraries/metavoxels/src/Endpoint.h | 89 - .../metavoxels/src/MetavoxelClientManager.cpp | 446 -- .../metavoxels/src/MetavoxelClientManager.h | 157 - libraries/metavoxels/src/MetavoxelData.cpp | 1818 -------- libraries/metavoxels/src/MetavoxelData.h | 539 --- .../metavoxels/src/MetavoxelMessages.cpp | 282 -- libraries/metavoxels/src/MetavoxelMessages.h | 269 -- libraries/metavoxels/src/MetavoxelUtil.cpp | 723 --- libraries/metavoxels/src/MetavoxelUtil.h | 362 -- libraries/metavoxels/src/ScriptCache.cpp | 209 - libraries/metavoxels/src/ScriptCache.h | 152 - libraries/metavoxels/src/SharedObject.cpp | 319 -- libraries/metavoxels/src/SharedObject.h | 268 -- libraries/metavoxels/src/Spanner.cpp | 4058 ----------------- libraries/metavoxels/src/Spanner.h | 866 ---- libraries/networking/src/Assignment.cpp | 4 - libraries/networking/src/Assignment.h | 2 +- libraries/networking/src/Node.cpp | 1 - libraries/networking/src/Node.h | 1 - libraries/networking/src/PacketHeaders.cpp | 3 - libraries/networking/src/PacketHeaders.h | 20 +- .../render-utils/src/DeferredLightingEffect.h | 2 +- libraries/script-engine/CMakeLists.txt | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 2 - tests/metavoxels/CMakeLists.txt | 10 - tests/metavoxels/src/MetavoxelTests.cpp | 1230 ----- tests/metavoxels/src/MetavoxelTests.h | 251 - tests/metavoxels/src/main.cpp | 18 - tests/octree/CMakeLists.txt | 2 +- tools/CMakeLists.txt | 2 - tools/bitstream2json/CMakeLists.txt | 6 - tools/bitstream2json/src/main.cpp | 70 - tools/json2bitstream/CMakeLists.txt | 6 - tools/json2bitstream/src/main.cpp | 76 - 66 files changed, 30 insertions(+), 25269 deletions(-) delete mode 100644 assignment-client/src/metavoxels/MetavoxelServer.cpp delete mode 100644 assignment-client/src/metavoxels/MetavoxelServer.h delete mode 100644 interface/src/MetavoxelSystem.cpp delete mode 100644 interface/src/MetavoxelSystem.h delete mode 100644 interface/src/ui/MetavoxelEditor.cpp delete mode 100644 interface/src/ui/MetavoxelEditor.h delete mode 100644 interface/src/ui/MetavoxelNetworkSimulator.cpp delete mode 100644 interface/src/ui/MetavoxelNetworkSimulator.h delete mode 100644 libraries/metavoxels/CMakeLists.txt delete mode 100644 libraries/metavoxels/src/AttributeRegistry.cpp delete mode 100644 libraries/metavoxels/src/AttributeRegistry.h delete mode 100644 libraries/metavoxels/src/Bitstream.cpp delete mode 100644 libraries/metavoxels/src/Bitstream.h delete mode 100644 libraries/metavoxels/src/DatagramSequencer.cpp delete mode 100644 libraries/metavoxels/src/DatagramSequencer.h delete mode 100644 libraries/metavoxels/src/Endpoint.cpp delete mode 100644 libraries/metavoxels/src/Endpoint.h delete mode 100644 libraries/metavoxels/src/MetavoxelClientManager.cpp delete mode 100644 libraries/metavoxels/src/MetavoxelClientManager.h delete mode 100644 libraries/metavoxels/src/MetavoxelData.cpp delete mode 100644 libraries/metavoxels/src/MetavoxelData.h delete mode 100644 libraries/metavoxels/src/MetavoxelMessages.cpp delete mode 100644 libraries/metavoxels/src/MetavoxelMessages.h delete mode 100644 libraries/metavoxels/src/MetavoxelUtil.cpp delete mode 100644 libraries/metavoxels/src/MetavoxelUtil.h delete mode 100644 libraries/metavoxels/src/ScriptCache.cpp delete mode 100644 libraries/metavoxels/src/ScriptCache.h delete mode 100644 libraries/metavoxels/src/SharedObject.cpp delete mode 100644 libraries/metavoxels/src/SharedObject.h delete mode 100644 libraries/metavoxels/src/Spanner.cpp delete mode 100644 libraries/metavoxels/src/Spanner.h delete mode 100644 tests/metavoxels/CMakeLists.txt delete mode 100644 tests/metavoxels/src/MetavoxelTests.cpp delete mode 100644 tests/metavoxels/src/MetavoxelTests.h delete mode 100644 tests/metavoxels/src/main.cpp delete mode 100644 tools/bitstream2json/CMakeLists.txt delete mode 100644 tools/bitstream2json/src/main.cpp delete mode 100644 tools/json2bitstream/CMakeLists.txt delete mode 100644 tools/json2bitstream/src/main.cpp diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index ada534431a..2561a1502d 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -8,7 +8,7 @@ target_include_directories(${TARGET_NAME} PRIVATE ${GLM_INCLUDE_DIRS}) # link in the shared libraries link_hifi_libraries( - audio avatars octree environment gpu model fbx entities metavoxels + audio avatars octree environment gpu model fbx entities networking animation shared script-engine embedded-webserver physics ) diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index 78e905edfa..4dafd64a92 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -15,7 +15,6 @@ #include "AssignmentFactory.h" #include "audio/AudioMixer.h" #include "avatars/AvatarMixer.h" -#include "metavoxels/MetavoxelServer.h" #include "entities/EntityServer.h" ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) { @@ -34,8 +33,6 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet return new AvatarMixer(packet); case Assignment::AgentType: return new Agent(packet); - case Assignment::MetavoxelServerType: - return new MetavoxelServer(packet); case Assignment::EntityServerType: return new EntityServer(packet); default: diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp deleted file mode 100644 index 669972cbee..0000000000 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ /dev/null @@ -1,367 +0,0 @@ -// -// MetavoxelServer.cpp -// assignment-client/src/metavoxels -// -// Created by Andrzej Kapolka on 12/18/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "MetavoxelServer.h" - -MetavoxelServer::MetavoxelServer(const QByteArray& packet) : - ThreadedAssignment(packet), - _nextSender(0), - _savedDataInitialized(false) { -} - -void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) { - MetavoxelData data = _data; - edit.apply(data, SharedObject::getWeakHash()); - setData(data); -} - -void MetavoxelServer::setData(const MetavoxelData& data, bool loaded) { - if (_data == data) { - return; - } - emit dataChanged(_data = data); - - if (loaded) { - _savedData = data; - - } else if (!_savedDataInitialized) { - _savedDataInitialized = true; - - // start the save timer - QTimer* saveTimer = new QTimer(this); - connect(saveTimer, &QTimer::timeout, this, &MetavoxelServer::maybeSaveData); - const int SAVE_INTERVAL = 1000 * 30; - saveTimer->start(SAVE_INTERVAL); - } -} - -const QString METAVOXEL_SERVER_LOGGING_NAME = "metavoxel-server"; - -void MetavoxelServer::run() { - commonInit(METAVOXEL_SERVER_LOGGING_NAME, NodeType::MetavoxelServer); - - auto nodeList = DependencyManager::get(); - nodeList->addNodeTypeToInterestSet(NodeType::Agent); - - connect(nodeList.data(), &NodeList::nodeAdded, this, &MetavoxelServer::maybeAttachSession); - connect(nodeList.data(), &NodeList::nodeKilled, this, &MetavoxelServer::maybeDeleteSession); - - // initialize Bitstream before using it in multiple threads - Bitstream::preThreadingInit(); - - // create the senders, each with its own thread - int threadCount = QThread::idealThreadCount(); - if (threadCount == -1) { - const int DEFAULT_THREAD_COUNT = 4; - threadCount = DEFAULT_THREAD_COUNT; - } - qDebug() << "Creating" << threadCount << "sender threads"; - for (int i = 0; i < threadCount; i++) { - QThread* thread = new QThread(this); - MetavoxelSender* sender = new MetavoxelSender(this); - sender->moveToThread(thread); - connect(thread, &QThread::finished, sender, &QObject::deleteLater); - thread->start(); - QMetaObject::invokeMethod(sender, "start"); - _senders.append(sender); - } - - // create the persister and start it in its own thread - _persister = new MetavoxelPersister(this); - QThread* persistenceThread = new QThread(this); - _persister->moveToThread(persistenceThread); - connect(persistenceThread, &QThread::finished, _persister, &QObject::deleteLater); - persistenceThread->start(); - - // queue up the load - QMetaObject::invokeMethod(_persister, "load"); -} - -void MetavoxelServer::readPendingDatagrams() { - QByteArray receivedPacket; - HifiSockAddr senderSockAddr; - - auto nodeList = DependencyManager::get(); - - while (readAvailableDatagram(receivedPacket, senderSockAddr)) { - if (nodeList->packetVersionAndHashMatch(receivedPacket)) { - switch (packetTypeForPacket(receivedPacket)) { - case PacketTypeMetavoxelData: - nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket); - break; - - default: - nodeList->processNodeData(senderSockAddr, receivedPacket); - break; - } - } - } -} - -void MetavoxelServer::aboutToFinish() { - QMetaObject::invokeMethod(_persister, "save", Q_ARG(const MetavoxelData&, _data)); - - foreach (MetavoxelSender* sender, _senders) { - sender->thread()->quit(); - sender->thread()->wait(); - } - _persister->thread()->quit(); - _persister->thread()->wait(); -} - -void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) { - if (node->getType() == NodeType::Agent) { - QMutexLocker locker(&node->getMutex()); - MetavoxelSender* sender = _senders.at(_nextSender); - _nextSender = (_nextSender + 1) % _senders.size(); - MetavoxelSession* session = new MetavoxelSession(node, sender); - session->moveToThread(sender->thread()); - QMetaObject::invokeMethod(sender, "addSession", Q_ARG(QObject*, session)); - node->setLinkedData(session); - } -} - -void MetavoxelServer::maybeDeleteSession(const SharedNodePointer& node) { - if (node->getType() == NodeType::Agent) { - // we assume the node is already locked - MetavoxelSession* session = static_cast(node->getLinkedData()); - if (session) { - node->setLinkedData(NULL); - session->deleteLater(); - } - } -} - -void MetavoxelServer::maybeSaveData() { - if (_savedData != _data) { - QMetaObject::invokeMethod(_persister, "save", Q_ARG(const MetavoxelData&, _savedData = _data)); - } -} - -MetavoxelSender::MetavoxelSender(MetavoxelServer* server) : - _server(server), - _sendTimer(this) { - - _sendTimer.setSingleShot(true); - connect(&_sendTimer, &QTimer::timeout, this, &MetavoxelSender::sendDeltas); - - connect(_server, &MetavoxelServer::dataChanged, this, &MetavoxelSender::setData); -} - -const int SEND_INTERVAL = 50; - -void MetavoxelSender::start() { - _lastSend = QDateTime::currentMSecsSinceEpoch(); - _sendTimer.start(SEND_INTERVAL); -} - -void MetavoxelSender::addSession(QObject* session) { - _sessions.insert(static_cast(session)); - connect(session, &QObject::destroyed, this, &MetavoxelSender::removeSession); -} - -void MetavoxelSender::sendDeltas() { - // send deltas for all sessions associated with our thread - foreach (MetavoxelSession* session, _sessions) { - session->update(); - } - - // restart the send timer - qint64 now = QDateTime::currentMSecsSinceEpoch(); - int elapsed = now - _lastSend; - _lastSend = now; - - _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL))); -} - -void MetavoxelSender::removeSession(QObject* session) { - _sessions.remove(static_cast(session)); -} - -MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender) : - Endpoint(node, new PacketRecord(), NULL), - _sender(sender), - _reliableDeltaChannel(NULL), - _reliableDeltaID(0) { - - connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleMessage(const QVariant&))); - connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(checkReliableDeltaReceived())); - connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&, Bitstream&)), - SLOT(handleMessage(const QVariant&, Bitstream&))); -} - -void MetavoxelSession::update() { - // wait until we have a valid lod before sending - if (!_lod.isValid()) { - return; - } - // if we're sending a reliable delta, wait until it's acknowledged - if (_reliableDeltaChannel) { - sendPacketGroup(); - return; - } - Bitstream& out = _sequencer.startPacket(); - int start = _sequencer.getOutputStream().getUnderlying().device()->pos(); - out << QVariant::fromValue(MetavoxelDeltaMessage()); - PacketRecord* sendRecord = getLastAcknowledgedSendRecord(); - _sender->getData().writeDelta(sendRecord->getData(), sendRecord->getLOD(), out, _lod); - out.flush(); - int end = _sequencer.getOutputStream().getUnderlying().device()->pos(); - if (end > _sequencer.getMaxPacketSize()) { - // we need to send the delta on the reliable channel - _reliableDeltaChannel = _sequencer.getReliableOutputChannel(RELIABLE_DELTA_CHANNEL_INDEX); - _reliableDeltaChannel->startMessage(); - _reliableDeltaChannel->getBuffer().write(_sequencer.getOutgoingPacketData().constData() + start, end - start); - _reliableDeltaChannel->endMessage(); - - _reliableDeltaWriteMappings = out.getAndResetWriteMappings(); - _reliableDeltaReceivedOffset = _reliableDeltaChannel->getBytesWritten(); - _reliableDeltaData = _sender->getData(); - _reliableDeltaLOD = _lod; - - // go back to the beginning with the current packet and note that there's a delta pending - _sequencer.getOutputStream().getUnderlying().device()->seek(start); - MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID, sendRecord->getPacketNumber(), _lodPacketNumber }; - out << (_reliableDeltaMessage = QVariant::fromValue(msg)); - _sequencer.endPacket(); - - } else { - _sequencer.endPacket(); - } - - // perhaps send additional packets to fill out the group - sendPacketGroup(1); -} - -void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { - handleMessage(message); -} - -PacketRecord* MetavoxelSession::maybeCreateSendRecord() const { - return _reliableDeltaChannel ? new PacketRecord(_sequencer.getOutgoingPacketNumber(), - _reliableDeltaLOD, _reliableDeltaData) : new PacketRecord(_sequencer.getOutgoingPacketNumber(), - _lod, _sender->getData()); -} - -void MetavoxelSession::handleMessage(const QVariant& message) { - int userType = message.userType(); - if (userType == ClientStateMessage::Type) { - ClientStateMessage state = message.value(); - _lod = state.lod; - _lodPacketNumber = _sequencer.getIncomingPacketNumber(); - - } else if (userType == MetavoxelEditMessage::Type) { - if (_node->getCanAdjustLocks()) { - QMetaObject::invokeMethod(_sender->getServer(), "applyEdit", - Q_ARG(const MetavoxelEditMessage&, message.value())); - } - } else if (userType == QMetaType::QVariantList) { - foreach (const QVariant& element, message.toList()) { - handleMessage(element); - } - } -} - -void MetavoxelSession::checkReliableDeltaReceived() { - if (!_reliableDeltaChannel || _reliableDeltaChannel->getOffset() < _reliableDeltaReceivedOffset) { - return; - } - _sequencer.getOutputStream().persistWriteMappings(_reliableDeltaWriteMappings); - _reliableDeltaWriteMappings = Bitstream::WriteMappings(); - _reliableDeltaData = MetavoxelData(); - _reliableDeltaChannel = NULL; -} - -void MetavoxelSession::sendPacketGroup(int alreadySent) { - int additionalPackets = _sequencer.notePacketGroup() - alreadySent; - for (int i = 0; i < additionalPackets; i++) { - Bitstream& out = _sequencer.startPacket(); - if (_reliableDeltaChannel) { - out << _reliableDeltaMessage; - } else { - out << QVariant(); - } - _sequencer.endPacket(); - } -} - -MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) : - _server(server) { -} - -const char* SAVE_FILE = "/resources/metavoxels.dat"; - -const int FILE_MAGIC = 0xDADAFACE; -const int FILE_VERSION = 4; - -void MetavoxelPersister::load() { - QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; - QFile file(path); - if (!file.exists()) { - return; - } - MetavoxelData data; - { - QDebug debug = qDebug() << "Reading from" << path << "..."; - file.open(QIODevice::ReadOnly); - QDataStream inStream(&file); - Bitstream in(inStream); - int magic, version; - in >> magic; - if (magic != FILE_MAGIC) { - debug << "wrong file magic: " << magic; - return; - } - in >> version; - if (version != FILE_VERSION) { - debug << "wrong file version: " << version; - return; - } - try { - in >> data; - } catch (const BitstreamException& e) { - debug << "failed, " << e.getDescription(); - return; - } - QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data), Q_ARG(bool, true)); - debug << "done."; - } - data.dumpStats(); -} - -void MetavoxelPersister::save(const MetavoxelData& data) { - QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; - QDir directory = QFileInfo(path).dir(); - if (!directory.exists()) { - directory.mkpath("."); - } - QDebug debug = qDebug() << "Writing to" << path << "..."; - QSaveFile file(path); - file.open(QIODevice::WriteOnly); - QDataStream outStream(&file); - Bitstream out(outStream); - out << FILE_MAGIC << FILE_VERSION << data; - out.flush(); - file.commit(); - debug << "done."; -} diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h deleted file mode 100644 index 2d1529b1b1..0000000000 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ /dev/null @@ -1,157 +0,0 @@ -// -// MetavoxelServer.h -// assignment-client/src/metavoxels -// -// Created by Andrzej Kapolka on 12/18/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelServer_h -#define hifi_MetavoxelServer_h - -#include -#include - -#include - -#include - -class MetavoxelEditMessage; -class MetavoxelPersister; -class MetavoxelSender; -class MetavoxelSession; - -/// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. -class MetavoxelServer : public ThreadedAssignment { - Q_OBJECT - -public: - - MetavoxelServer(const QByteArray& packet); - - Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit); - - const MetavoxelData& getData() const { return _data; } - - Q_INVOKABLE void setData(const MetavoxelData& data, bool loaded = false); - - virtual void run(); - - virtual void readPendingDatagrams(); - - virtual void aboutToFinish(); - -signals: - - void dataChanged(const MetavoxelData& data); - -private slots: - - void maybeAttachSession(const SharedNodePointer& node); - void maybeDeleteSession(const SharedNodePointer& node); - void maybeSaveData(); - -private: - - QVector _senders; - int _nextSender; - - MetavoxelPersister* _persister; - - MetavoxelData _data; - MetavoxelData _savedData; - bool _savedDataInitialized; -}; - -/// Handles update sending for one thread. -class MetavoxelSender : public QObject { - Q_OBJECT - -public: - - MetavoxelSender(MetavoxelServer* server); - - MetavoxelServer* getServer() const { return _server; } - - const MetavoxelData& getData() const { return _data; } - - Q_INVOKABLE void start(); - - Q_INVOKABLE void addSession(QObject* session); - -private slots: - - void setData(const MetavoxelData& data) { _data = data; } - void sendDeltas(); - void removeSession(QObject* session); - -private: - - MetavoxelServer* _server; - QSet _sessions; - - QTimer _sendTimer; - qint64 _lastSend; - - MetavoxelData _data; -}; - -/// Contains the state of a single client session. -class MetavoxelSession : public Endpoint { - Q_OBJECT - -public: - - MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender); - - virtual void update(); - -protected: - - virtual void handleMessage(const QVariant& message, Bitstream& in); - - virtual PacketRecord* maybeCreateSendRecord() const; - -private slots: - - void handleMessage(const QVariant& message); - void checkReliableDeltaReceived(); - -private: - - void sendPacketGroup(int alreadySent = 0); - - MetavoxelSender* _sender; - - MetavoxelLOD _lod; - int _lodPacketNumber; - - ReliableChannel* _reliableDeltaChannel; - int _reliableDeltaReceivedOffset; - MetavoxelData _reliableDeltaData; - MetavoxelLOD _reliableDeltaLOD; - Bitstream::WriteMappings _reliableDeltaWriteMappings; - int _reliableDeltaID; - QVariant _reliableDeltaMessage; -}; - -/// Handles persistence in a separate thread. -class MetavoxelPersister : public QObject { - Q_OBJECT - -public: - - MetavoxelPersister(MetavoxelServer* server); - - Q_INVOKABLE void load(); - Q_INVOKABLE void save(const MetavoxelData& data); - -private: - - MetavoxelServer* _server; -}; - -#endif // hifi_MetavoxelServer_h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 9aec456587..adb32448ab 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -559,8 +559,7 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet #include #include +#include #include #include @@ -75,7 +76,7 @@ #include #include #include -#include +//#include #include #include #include @@ -220,7 +221,7 @@ bool setupEssentials(int& argc, char** argv) { auto addressManager = DependencyManager::set(); auto nodeList = DependencyManager::set(NodeType::Agent, listenPort); auto geometryCache = DependencyManager::set(); - auto scriptCache = DependencyManager::set(); + //auto scriptCache = DependencyManager::set(); auto soundCache = DependencyManager::set(); auto glowEffect = DependencyManager::set(); auto faceshift = DependencyManager::set(); @@ -416,8 +417,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // tell the NodeList instance who to tell the domain server we care about nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer - << NodeType::EntityServer - << NodeType::MetavoxelServer); + << NodeType::EntityServer); // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); @@ -600,7 +600,7 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); - DependencyManager::destroy(); + //DependencyManager::destroy(); DependencyManager::destroy(); qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages @@ -773,8 +773,9 @@ void Application::paintGL() { { PerformanceTimer perfTimer("renderOverlay"); + // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() + _applicationOverlay.renderOverlay(true); if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { - _applicationOverlay.renderOverlay(true); _applicationOverlay.displayOverlayTexture(); } } @@ -1443,8 +1444,7 @@ void Application::sendPingPackets() { QByteArray pingPacket = DependencyManager::get()->constructPingPacket(); controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::EntityServer - << NodeType::AudioMixer << NodeType::AvatarMixer - << NodeType::MetavoxelServer); + << NodeType::AudioMixer << NodeType::AvatarMixer); } // Every second, check the frame rates and other stuff @@ -1781,8 +1781,6 @@ void Application::init() { _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); - _metavoxels.init(); - _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect); connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); @@ -1945,16 +1943,6 @@ void Application::updateThreads(float deltaTime) { } } -void Application::updateMetavoxels(float deltaTime) { - PerformanceTimer perfTimer("updateMetavoxels"); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()"); - - if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) { - _metavoxels.simulate(deltaTime); - } -} - void Application::cameraMenuChanged() { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { @@ -2055,7 +2043,6 @@ void Application::update(float deltaTime) { DependencyManager::get()->updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them... - updateMetavoxels(deltaTime); // update metavoxels updateCamera(deltaTime); // handle various camera tweaks like off axis projection updateDialogs(deltaTime); // update various stats dialogs if present updateCursor(deltaTime); // Handle cursor updates @@ -2801,14 +2788,6 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs float originSphereRadius = 0.05f; DependencyManager::get()->renderSphere(originSphereRadius, 15, 15, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - // also, metavoxels - if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) { - PerformanceTimer perfTimer("metavoxels"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... metavoxels..."); - _metavoxels.render(); - } - // render models... if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) { PerformanceTimer perfTimer("entities"); @@ -3485,7 +3464,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); - scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels); scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); diff --git a/interface/src/Application.h b/interface/src/Application.h index 4a8e712d30..980dc27be6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -45,7 +45,6 @@ #include "FileLogger.h" #include "GLCanvas.h" #include "Menu.h" -#include "MetavoxelSystem.h" #include "PacketHeaders.h" #include "Physics.h" #include "Stars.h" @@ -179,7 +178,6 @@ public: ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; } ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } - MetavoxelSystem* getMetavoxels() { return &_metavoxels; } EntityTreeRenderer* getEntities() { return &_entities; } Environment* getEnvironment() { return &_environment; } PrioVR* getPrioVR() { return &_prioVR; } @@ -423,7 +421,6 @@ private: void updateMouseRay(); void updateMyAvatarLookAtPosition(); void updateThreads(float deltaTime); - void updateMetavoxels(float deltaTime); void updateCamera(float deltaTime); void updateDialogs(float deltaTime); void updateCursor(float deltaTime); @@ -476,8 +473,6 @@ private: EntityTreeRenderer _entityClipboardRenderer; EntityTree _entityClipboard; - MetavoxelSystem _metavoxels; - ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels) ViewFrustum _displayViewFrustum; diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index a491f9444d..1e63ce6655 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -100,9 +100,6 @@ void DatagramProcessor::processDatagrams() { } break; } - case PacketTypeMetavoxelData: - nodeList->findNodeAndUpdateWithDataFromPacket(incomingPacket); - break; case PacketTypeBulkAvatarData: case PacketTypeKillAvatar: case PacketTypeAvatarIdentity: diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index be5b495fe9..2ebd589d69 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -148,8 +148,6 @@ Menu::Menu() { dialogsManager.data(), SLOT(editAnimations())); QMenu* toolsMenu = addMenu("Tools"); - addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, - dialogsManager.data(), SLOT(showMetavoxelEditor())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, dialogsManager.data(), SLOT(showScriptEditor())); @@ -295,7 +293,6 @@ Menu::Menu() { QMenu* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Avatars, 0, true); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Entities, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges); @@ -394,13 +391,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); - QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); - addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false); - addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderHeightfields, 0, true); - addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true); - addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, - dialogsManager.data(), SLOT(showMetavoxelNetworkSimulator())); - QMenu* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 584e4d5254..fe1035d54b 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -145,7 +145,6 @@ namespace MenuOption { const QString DisableNackPackets = "Disable NACK Packets"; const QString DisplayHands = "Show Hand Info"; const QString DisplayHandTargets = "Show Hand Targets"; - const QString DisplayHermiteData = "Display Hermite Data"; const QString DisplayModelBounds = "Display Model Bounds"; const QString DisplayModelTriangles = "Display Model Triangles"; const QString DisplayModelElementChildProxies = "Display Model Element Children"; @@ -186,12 +185,9 @@ namespace MenuOption { const QString Login = "Login"; const QString Log = "Log"; const QString LowVelocityFilter = "Low Velocity Filter"; - const QString MetavoxelEditor = "Metavoxel Editor..."; - const QString Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; - const QString NetworkSimulator = "Network Simulator..."; const QString NewVoxelCullingMode = "New Voxel Culling Mode"; const QString NoFaceTracking = "None"; const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity"; @@ -205,10 +201,8 @@ namespace MenuOption { const QString Quit = "Quit"; const QString ReloadAllScripts = "Reload All Scripts"; const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes"; - const QString RenderDualContourSurfaces = "Render Dual Contour Surfaces"; const QString RenderFocusIndicator = "Show Eye Focus"; const QString RenderHeadCollisionShapes = "Show Head Collision Shapes"; - const QString RenderHeightfields = "Render Heightfields"; const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; const QString RenderTargetFramerate = "Framerate"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp deleted file mode 100644 index df8632d875..0000000000 --- a/interface/src/MetavoxelSystem.cpp +++ /dev/null @@ -1,2433 +0,0 @@ -// -// MetavoxelSystem.cpp -// interface/src -// -// Created by Andrzej Kapolka on 12/10/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "Application.h" -#include "MetavoxelSystem.h" - -using namespace std; - -REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) -REGISTER_META_OBJECT(SphereRenderer) -REGISTER_META_OBJECT(CuboidRenderer) -REGISTER_META_OBJECT(StaticModelRenderer) -REGISTER_META_OBJECT(HeightfieldRenderer) - -MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, - int minimumDelay, int maximumDelay, int bandwidthLimit) : - dropRate(dropRate), - repeatRate(repeatRate), - minimumDelay(minimumDelay), - maximumDelay(maximumDelay), - bandwidthLimit(bandwidthLimit) { -} - -MetavoxelSystem::~MetavoxelSystem() { - // kill the updater before we delete our network simulation objects - _updater->thread()->quit(); - _updater->thread()->wait(); - _updater = NULL; -} - -void MetavoxelSystem::init() { - MetavoxelClientManager::init(); - - _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + - "shaders/metavoxel_heightfield_base.vert"); - _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + - "shaders/metavoxel_heightfield_base.frag"); - _baseHeightfieldProgram.link(); - - _baseHeightfieldProgram.bind(); - _baseHeightfieldProgram.setUniformValue("heightMap", 0); - _baseHeightfieldProgram.setUniformValue("diffuseMap", 1); - _baseHeightScaleLocation = _baseHeightfieldProgram.uniformLocation("heightScale"); - _baseColorScaleLocation = _baseHeightfieldProgram.uniformLocation("colorScale"); - _baseHeightfieldProgram.release(); - - loadSplatProgram("heightfield", _splatHeightfieldProgram, _splatHeightfieldLocations); - - _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + - "shaders/metavoxel_heightfield_cursor.vert"); - _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + - "shaders/metavoxel_cursor.frag"); - _heightfieldCursorProgram.link(); - - _heightfieldCursorProgram.bind(); - _heightfieldCursorProgram.setUniformValue("heightMap", 0); - _heightfieldCursorProgram.release(); - - _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + - "shaders/metavoxel_voxel_base.vert"); - _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + - "shaders/metavoxel_voxel_base.frag"); - _baseVoxelProgram.link(); - - loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations); - - _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + - "shaders/metavoxel_voxel_cursor.vert"); - _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + - "shaders/metavoxel_cursor.frag"); - _voxelCursorProgram.link(); -} - -MetavoxelLOD MetavoxelSystem::getLOD() { - QReadLocker locker(&_lodLock); - return _lod; -} - -void MetavoxelSystem::setNetworkSimulation(const NetworkSimulation& simulation) { - QWriteLocker locker(&_networkSimulationLock); - _networkSimulation = simulation; -} - -MetavoxelSystem::NetworkSimulation MetavoxelSystem::getNetworkSimulation() { - QReadLocker locker(&_networkSimulationLock); - return _networkSimulation; -} - -class SimulateVisitor : public MetavoxelVisitor { -public: - - SimulateVisitor(float deltaTime, const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info); - -private: - - float _deltaTime; -}; - -SimulateVisitor::SimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getRendererAttribute(), - QVector(), lod), - _deltaTime(deltaTime) { -} - -int SimulateVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - static_cast(info.inputValues.at(0).getInlineValue< - SharedObjectPointer>().data())->getImplementation()->simulate(*_data, _deltaTime, info, _lod); - return STOP_RECURSION; -} - -void MetavoxelSystem::simulate(float deltaTime) { - // update the lod - { - QWriteLocker locker(&_lodLock); - const float DEFAULT_LOD_THRESHOLD = 0.01f; - _lod = MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), DEFAULT_LOD_THRESHOLD); - } - - SimulateVisitor simulateVisitor(deltaTime, getLOD()); - guideToAugmented(simulateVisitor); -} - -class RenderVisitor : public MetavoxelVisitor { -public: - - RenderVisitor(const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info); -}; - -RenderVisitor::RenderVisitor(const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getRendererAttribute(), - QVector(), lod) { -} - -int RenderVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - static_cast(info.inputValues.at(0).getInlineValue< - SharedObjectPointer>().data())->getImplementation()->render(*_data, info, _lod); - return STOP_RECURSION; -} - -class HeightfieldPoint { -public: - glm::vec3 vertex; - glm::vec2 textureCoord; -}; - -const int SPLAT_COUNT = 4; -const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 }; - -static const int EIGHT_BIT_MAXIMUM = 255; -static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; - -void MetavoxelSystem::render() { - // update the frustum - ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum(); - _frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(), - viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(), - viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); - - RenderVisitor renderVisitor(getLOD()); - guideToAugmented(renderVisitor, true); - - if (!_heightfieldBaseBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - DependencyManager::get()->setPrimaryDrawBuffers(true, true); - - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_EQUAL, 0.0f); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - _baseHeightfieldProgram.bind(); - - foreach (const HeightfieldBaseLayerBatch& batch, _heightfieldBaseBatches) { - glPushMatrix(); - glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); - glm::vec3 axis = glm::axis(batch.rotation); - glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); - glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); - - HeightfieldPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); - glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); - - glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); - - _baseHeightfieldProgram.setUniform(_baseHeightScaleLocation, batch.heightScale); - _baseHeightfieldProgram.setUniform(_baseColorScaleLocation, batch.colorScale); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, batch.colorTextureID); - - glDrawRangeElements(GL_TRIANGLES, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - glPopMatrix(); - } - - DependencyManager::get()->setPrimaryDrawBuffers(true, false); - - _baseHeightfieldProgram.release(); - - glDisable(GL_ALPHA_TEST); - glEnable(GL_BLEND); - - if (!_heightfieldSplatBatches.isEmpty()) { - glDepthFunc(GL_LEQUAL); - glDepthMask(false); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - _splatHeightfieldProgram.bind(); - - foreach (const HeightfieldSplatBatch& batch, _heightfieldSplatBatches) { - glPushMatrix(); - glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); - glm::vec3 axis = glm::axis(batch.rotation); - glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); - glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); - - HeightfieldPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); - glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); - - glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); - - _splatHeightfieldProgram.setUniformValue(_splatHeightfieldLocations.heightScale, - batch.heightScale.x, batch.heightScale.y); - _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.textureScale, batch.textureScale); - _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureOffset, batch.splatTextureOffset); - - const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureScalesS, batch.splatTextureScalesS); - _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureScalesT, batch.splatTextureScalesT); - _splatHeightfieldProgram.setUniformValue( - _splatHeightfieldLocations.textureValueMinima, - (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - _splatHeightfieldProgram.setUniformValue( - _splatHeightfieldLocations.textureValueMaxima, - (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, batch.materialTextureID); - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, batch.splatTextureIDs[i]); - } - - glDrawRangeElements(GL_TRIANGLES, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - glPopMatrix(); - } - - _splatHeightfieldProgram.release(); - - glDisable(GL_POLYGON_OFFSET_FILL); - glDepthMask(true); - glDepthFunc(GL_LESS); - } - - glDisable(GL_CULL_FACE); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - } - _heightfieldBaseBatches.clear(); - _heightfieldSplatBatches.clear(); - - if (!_voxelBaseBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { - DependencyManager::get()->setPrimaryDrawBuffers(true, true); - - glEnableClientState(GL_VERTEX_ARRAY); - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_EQUAL, 0.0f); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - _baseVoxelProgram.bind(); - - foreach (const MetavoxelBatch& batch, _voxelBaseBatches) { - glPushMatrix(); - glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); - glm::vec3 axis = glm::axis(batch.rotation); - glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); - glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); - - VoxelPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); - glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); - - glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - glPopMatrix(); - } - - _baseVoxelProgram.release(); - - glDisable(GL_ALPHA_TEST); - glEnable(GL_BLEND); - - DependencyManager::get()->setPrimaryDrawBuffers(true, false); - - if (!_voxelSplatBatches.isEmpty()) { - glDepthFunc(GL_LEQUAL); - glDepthMask(false); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - _splatVoxelProgram.bind(); - - _splatVoxelProgram.enableAttributeArray(_splatVoxelLocations.materials); - _splatVoxelProgram.enableAttributeArray(_splatVoxelLocations.materialWeights); - - foreach (const VoxelSplatBatch& batch, _voxelSplatBatches) { - glPushMatrix(); - glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); - glm::vec3 axis = glm::axis(batch.rotation); - glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); - glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); - - VoxelPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); - glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); - - _splatVoxelProgram.setAttributeBuffer(_splatVoxelLocations.materials, - GL_UNSIGNED_BYTE, (qint64)&point->materials, SPLAT_COUNT, sizeof(VoxelPoint)); - _splatVoxelProgram.setAttributeBuffer(_splatVoxelLocations.materialWeights, - GL_UNSIGNED_BYTE, (qint64)&point->materialWeights, SPLAT_COUNT, sizeof(VoxelPoint)); - - const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureOffset, batch.splatTextureOffset); - _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureScalesS, batch.splatTextureScalesS); - _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureScalesT, batch.splatTextureScalesT); - _splatVoxelProgram.setUniformValue( - _splatVoxelLocations.textureValueMinima, - (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - _splatVoxelProgram.setUniformValue( - _splatVoxelLocations.textureValueMaxima, - (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, batch.splatTextureIDs[i]); - } - - glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - glPopMatrix(); - } - - glDisable(GL_POLYGON_OFFSET_FILL); - glDepthMask(true); - glDepthFunc(GL_LESS); - - _splatVoxelProgram.disableAttributeArray(_splatVoxelLocations.materials); - _splatVoxelProgram.disableAttributeArray(_splatVoxelLocations.materialWeights); - } - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisable(GL_CULL_FACE); - } - _voxelBaseBatches.clear(); - _voxelSplatBatches.clear(); - - if (!_hermiteBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) { - DependencyManager::get()->setPrimaryDrawBuffers(true, true); - - glEnableClientState(GL_VERTEX_ARRAY); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glNormal3f(0.0f, 1.0f, 0.0f); - - DependencyManager::get()->bindSimpleProgram(); - - foreach (const HermiteBatch& batch, _hermiteBatches) { - glPushMatrix(); - glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); - glm::vec3 axis = glm::axis(batch.rotation); - glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); - glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); - - glVertexPointer(3, GL_FLOAT, 0, 0); - - glDrawArrays(GL_LINES, 0, batch.vertexCount); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glPopMatrix(); - } - - DependencyManager::get()->releaseSimpleProgram(); - - glDisableClientState(GL_VERTEX_ARRAY); - - DependencyManager::get()->setPrimaryDrawBuffers(true, false); - } - _hermiteBatches.clear(); - - // give external parties a chance to join in - emit rendering(); -} - -void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) { - Sphere* sphere = new Sphere(); - sphere->setTranslation(position); - sphere->setScale(radius); - setHeightfieldColor(SharedObjectPointer(sphere), color, true); -} - -void MetavoxelSystem::paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) { - Sphere* sphere = new Sphere(); - sphere->setTranslation(position); - sphere->setScale(radius); - setHeightfieldMaterial(SharedObjectPointer(sphere), material, true); -} - -void MetavoxelSystem::setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color, bool paint) { - MetavoxelEditMessage edit = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, - SharedObjectPointer(), color, paint)) }; - applyEdit(edit, true); -} - -void MetavoxelSystem::setHeightfieldMaterial(const SharedObjectPointer& spanner, - const SharedObjectPointer& material, bool paint) { - MetavoxelEditMessage edit = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, material, QColor(), paint)) }; - applyMaterialEdit(edit, true); -} - -void MetavoxelSystem::deleteTextures(int heightTextureID, int colorTextureID, int materialTextureID) const { - glDeleteTextures(1, (const GLuint*)&heightTextureID); - glDeleteTextures(1, (const GLuint*)&colorTextureID); - glDeleteTextures(1, (const GLuint*)&materialTextureID); -} - -void MetavoxelSystem::deleteBuffers(int vertexBufferID, int indexBufferID, int hermiteBufferID) const { - glDeleteBuffers(1, (const GLuint*)&vertexBufferID); - glDeleteBuffers(1, (const GLuint*)&indexBufferID); - glDeleteBuffers(1, (const GLuint*)&hermiteBufferID); -} - -class SpannerRenderVisitor : public SpannerVisitor { -public: - - SpannerRenderVisitor(const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info); - virtual bool visit(Spanner* spanner); - -protected: - - int _containmentDepth; -}; - -SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), lod, - encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())), - _containmentDepth(INT_MAX) { -} - -int SpannerRenderVisitor::visit(MetavoxelInfo& info) { - if (_containmentDepth >= _depth) { - Frustum::IntersectionType intersection = Application::getInstance()->getMetavoxels()->getFrustum().getIntersectionType( - info.getBounds()); - if (intersection == Frustum::NO_INTERSECTION) { - return STOP_RECURSION; - } - _containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX; - } - return SpannerVisitor::visit(info); -} - -bool SpannerRenderVisitor::visit(Spanner* spanner) { - spanner->getRenderer()->render(_lod, _containmentDepth <= _depth); - return true; -} - -class SpannerCursorRenderVisitor : public SpannerRenderVisitor { -public: - - SpannerCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds); - - virtual bool visit(Spanner* spanner); - - virtual int visit(MetavoxelInfo& info); - -private: - - Box _bounds; -}; - -SpannerCursorRenderVisitor::SpannerCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds) : - SpannerRenderVisitor(lod), - _bounds(bounds) { -} - -bool SpannerCursorRenderVisitor::visit(Spanner* spanner) { - if (spanner->isHeightfield()) { - spanner->getRenderer()->render(_lod, _containmentDepth <= _depth, true); - } - return true; -} - -int SpannerCursorRenderVisitor::visit(MetavoxelInfo& info) { - return info.getBounds().intersects(_bounds) ? SpannerRenderVisitor::visit(info) : STOP_RECURSION; -} - -void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float radius) { - glDepthFunc(GL_LEQUAL); - glEnable(GL_CULL_FACE); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - glActiveTexture(GL_TEXTURE4); - float scale = 1.0f / radius; - glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x); - glm::vec4 tCoefficients(0.0f, scale, 0.0f, -scale * position.y); - glm::vec4 rCoefficients(0.0f, 0.0f, scale, -scale * position.z); - glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients); - glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients); - glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&rCoefficients); - glActiveTexture(GL_TEXTURE0); - - glm::vec3 extents(radius, radius, radius); - SpannerCursorRenderVisitor visitor(getLOD(), Box(position - extents, position + extents)); - guide(visitor); - - if (!_heightfieldBaseBatches.isEmpty()) { - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - _heightfieldCursorProgram.bind(); - - foreach (const HeightfieldBaseLayerBatch& batch, _heightfieldBaseBatches) { - glPushMatrix(); - glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); - glm::vec3 axis = glm::axis(batch.rotation); - glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); - glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); - - HeightfieldPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); - glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); - - glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); - - glDrawRangeElements(GL_TRIANGLES, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - glPopMatrix(); - } - - _heightfieldCursorProgram.release(); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - } - _heightfieldBaseBatches.clear(); - - if (!_voxelBaseBatches.isEmpty()) { - glEnableClientState(GL_VERTEX_ARRAY); - - _voxelCursorProgram.bind(); - - foreach (const MetavoxelBatch& batch, _voxelBaseBatches) { - glPushMatrix(); - glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); - glm::vec3 axis = glm::axis(batch.rotation); - glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); - glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); - - VoxelPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); - - glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - glPopMatrix(); - } - - _voxelCursorProgram.release(); - - glDisableClientState(GL_VERTEX_ARRAY); - } - _voxelBaseBatches.clear(); - - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_CULL_FACE); - glDepthFunc(GL_LESS); -} - -class MaterialEditApplier : public SignalHandler { -public: - - MaterialEditApplier(const MetavoxelEditMessage& message, const QSharedPointer texture); - - virtual void handle(); - -protected: - - MetavoxelEditMessage _message; - QSharedPointer _texture; -}; - -MaterialEditApplier::MaterialEditApplier(const MetavoxelEditMessage& message, const QSharedPointer texture) : - _message(message), - _texture(texture) { -} - -void MaterialEditApplier::handle() { - static_cast(_message.edit.data())->averageColor = _texture->getAverageColor(); - Application::getInstance()->getMetavoxels()->applyEdit(_message, true); - deleteLater(); -} - -void MetavoxelSystem::applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable) { - const MaterialEdit* edit = static_cast(message.edit.constData()); - MaterialObject* material = static_cast(edit->material.data()); - if (material && material->getDiffuse().isValid()) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "applyMaterialEdit", Q_ARG(const MetavoxelEditMessage&, message), - Q_ARG(bool, reliable)); - return; - } - auto texture = DependencyManager::get()->getTexture( - material->getDiffuse(), SPLAT_TEXTURE); - if (texture->isLoaded()) { - MetavoxelEditMessage newMessage = message; - static_cast(newMessage.edit.data())->averageColor = texture->getAverageColor(); - applyEdit(newMessage, true); - - } else { - MaterialEditApplier* applier = new MaterialEditApplier(message, texture); - connect(texture.data(), &Resource::loaded, applier, &SignalHandler::handle); - } - } else { - applyEdit(message, true); - } -} - -MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { - return new MetavoxelSystemClient(node, _updater); -} - -void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { - DependencyManager::get()->eachNode([&visitor, &render](const SharedNodePointer& node){ - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelSystemClient* client = static_cast(node->getLinkedData()); - if (client) { - MetavoxelData data = client->getAugmentedData(); - data.guide(visitor); - if (render) { - // save the rendered augmented data so that its cached texture references, etc., don't - // get collected when we replace it with more recent versions - client->setRenderedAugmentedData(data); - } - } - } - }); -} - -void MetavoxelSystem::loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations) { - program.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + - "shaders/metavoxel_" + type + "_splat.vert"); - program.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + - "shaders/metavoxel_" + type + "_splat.frag"); - program.link(); - - program.bind(); - program.setUniformValue("heightMap", 0); - program.setUniformValue("textureMap", 1); - program.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); - locations.heightScale = program.uniformLocation("heightScale"); - locations.textureScale = program.uniformLocation("textureScale"); - locations.splatTextureOffset = program.uniformLocation("splatTextureOffset"); - locations.splatTextureScalesS = program.uniformLocation("splatTextureScalesS"); - locations.splatTextureScalesT = program.uniformLocation("splatTextureScalesT"); - locations.textureValueMinima = program.uniformLocation("textureValueMinima"); - locations.textureValueMaxima = program.uniformLocation("textureValueMaxima"); - locations.materials = program.attributeLocation("materials"); - locations.materialWeights = program.attributeLocation("materialWeights"); - program.release(); -} - -Throttle::Throttle() : - _limit(INT_MAX), - _total(0) { -} - -bool Throttle::shouldThrottle(int bytes) { - // clear expired buckets - qint64 now = QDateTime::currentMSecsSinceEpoch(); - while (!_buckets.isEmpty() && now >= _buckets.first().first) { - _total -= _buckets.takeFirst().second; - } - - // if possible, add the new bucket - if (_total + bytes > _limit) { - return true; - } - const int BUCKET_DURATION = 1000; - _buckets.append(Bucket(now + BUCKET_DURATION, bytes)); - _total += bytes; - return false; -} - -MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : - MetavoxelClient(node, updater) { -} - -void MetavoxelSystemClient::setAugmentedData(const MetavoxelData& data) { - QWriteLocker locker(&_augmentedDataLock); - _augmentedData = data; -} - -MetavoxelData MetavoxelSystemClient::getAugmentedData() { - QReadLocker locker(&_augmentedDataLock); - return _augmentedData; -} - -class ReceiveDelayer : public QObject { -public: - - ReceiveDelayer(const SharedNodePointer& node, const QByteArray& packet); - -protected: - - virtual void timerEvent(QTimerEvent* event); - -private: - - SharedNodePointer _node; - QByteArray _packet; -}; - -ReceiveDelayer::ReceiveDelayer(const SharedNodePointer& node, const QByteArray& packet) : - _node(node), - _packet(packet) { -} - -void ReceiveDelayer::timerEvent(QTimerEvent* event) { - QMutexLocker locker(&_node->getMutex()); - MetavoxelClient* client = static_cast(_node->getLinkedData()); - if (client) { - QMetaObject::invokeMethod(&client->getSequencer(), "receivedDatagram", Q_ARG(const QByteArray&, _packet)); - } - deleteLater(); -} - -int MetavoxelSystemClient::parseData(const QByteArray& packet) { - // process through sequencer - MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); - if (randFloat() < simulation.dropRate) { - return packet.size(); - } - int count = (randFloat() < simulation.repeatRate) ? 2 : 1; - for (int i = 0; i < count; i++) { - if (simulation.bandwidthLimit > 0) { - _receiveThrottle.setLimit(simulation.bandwidthLimit); - if (_receiveThrottle.shouldThrottle(packet.size())) { - continue; - } - } - int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); - if (delay > 0) { - ReceiveDelayer* delayer = new ReceiveDelayer(_node, packet); - delayer->startTimer(delay); - - } else { - QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); - } - } - return packet.size(); -} - -class AugmentVisitor : public MetavoxelVisitor { -public: - - AugmentVisitor(const MetavoxelLOD& lod, const MetavoxelData& previousData); - - virtual int visit(MetavoxelInfo& info); - -private: - - const MetavoxelData& _previousData; -}; - -AugmentVisitor::AugmentVisitor(const MetavoxelLOD& lod, const MetavoxelData& previousData) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getRendererAttribute(), - QVector(), lod), - _previousData(previousData) { -} - -int AugmentVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - static_cast(info.inputValues.at(0).getInlineValue< - SharedObjectPointer>().data())->getImplementation()->augment(*_data, _previousData, info, _lod); - return STOP_RECURSION; -} - -class Augmenter : public QRunnable { -public: - - Augmenter(const SharedNodePointer& node, const MetavoxelData& data, - const MetavoxelData& previousData, const MetavoxelLOD& lod); - - virtual void run(); - -private: - - QWeakPointer _node; - MetavoxelData _data; - MetavoxelData _previousData; - MetavoxelLOD _lod; -}; - -Augmenter::Augmenter(const SharedNodePointer& node, const MetavoxelData& data, - const MetavoxelData& previousData, const MetavoxelLOD& lod) : - _node(node), - _data(data), - _previousData(previousData), - _lod(lod) { -} - -void Augmenter::run() { - SharedNodePointer node = _node; - if (!node) { - return; - } - AugmentVisitor visitor(_lod, _previousData); - _data.guide(visitor); - QMutexLocker locker(&node->getMutex()); - QMetaObject::invokeMethod(node->getLinkedData(), "setAugmentedData", Q_ARG(const MetavoxelData&, _data)); -} - -void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { - MetavoxelClient::dataChanged(oldData); - QThreadPool::globalInstance()->start(new Augmenter(_node, _data, getAugmentedData(), _remoteDataLOD)); -} - -class SendDelayer : public QObject { -public: - - SendDelayer(const SharedNodePointer& node, const QByteArray& data); - - virtual void timerEvent(QTimerEvent* event); - -private: - - SharedNodePointer _node; - QByteArray _data; -}; - -SendDelayer::SendDelayer(const SharedNodePointer& node, const QByteArray& data) : - _node(node), - _data(data.constData(), data.size()) { -} - -void SendDelayer::timerEvent(QTimerEvent* event) { - DependencyManager::get()->writeDatagram(_data, _node); - deleteLater(); -} - -void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { - MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); - if (randFloat() < simulation.dropRate) { - return; - } - int count = (randFloat() < simulation.repeatRate) ? 2 : 1; - for (int i = 0; i < count; i++) { - if (simulation.bandwidthLimit > 0) { - _sendThrottle.setLimit(simulation.bandwidthLimit); - if (_sendThrottle.shouldThrottle(data.size())) { - continue; - } - } - int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); - if (delay > 0) { - SendDelayer* delayer = new SendDelayer(_node, data); - delayer->startTimer(delay); - - } else { - DependencyManager::get()->writeDatagram(data, _node); - } - } -} - -BufferData::~BufferData() { -} - -void VoxelPoint::setNormal(const glm::vec3& normal) { - this->normal[0] = (char)(normal.x * 127.0f); - this->normal[1] = (char)(normal.y * 127.0f); - this->normal[2] = (char)(normal.z * 127.0f); -} - -VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QMultiHash& quadIndices, int size, const QVector& materials) : - _vertices(vertices), - _indices(indices), - _hermite(hermite), - _hermiteEnabled(Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)), - _quadIndices(quadIndices), - _size(size), - _vertexCount(vertices.size()), - _indexCount(indices.size()), - _hermiteCount(hermite.size()), - _vertexBufferID(0), - _indexBufferID(0), - _hermiteBufferID(0), - _materials(materials) { -} - -VoxelBuffer::~VoxelBuffer() { - QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "deleteBuffers", Q_ARG(int, _vertexBufferID), - Q_ARG(int, _indexBufferID), Q_ARG(int, _hermiteBufferID)); -} - -bool VoxelBuffer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float boundsDistance, float& distance) const { - float highest = _size - 1.0f; - glm::vec3 position = (origin + direction * boundsDistance) * highest; - glm::vec3 floors = glm::floor(position); - int max = _size - 2; - int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max); - forever { - VoxelCoord key(qRgb(x, y, z)); - for (QMultiHash::const_iterator it = _quadIndices.constFind(key); - it != _quadIndices.constEnd() && it.key() == key; it++) { - const int* indices = _indices.constData() + *it; - if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, - _vertices.at(indices[1]).vertex, _vertices.at(indices[2]).vertex, distance) || - findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, - _vertices.at(indices[2]).vertex, _vertices.at(indices[3]).vertex, distance)) { - return true; - } - } - float xDistance = FLT_MAX, yDistance = FLT_MAX, zDistance = FLT_MAX; - if (direction.x > 0.0f) { - xDistance = (x + 1.0f - position.x) / direction.x; - } else if (direction.x < 0.0f) { - xDistance = (x - position.x) / direction.x; - } - if (direction.y > 0.0f) { - yDistance = (y + 1.0f - position.y) / direction.y; - } else if (direction.y < 0.0f) { - yDistance = (y - position.y) / direction.y; - } - if (direction.z > 0.0f) { - zDistance = (z + 1.0f - position.z) / direction.z; - } else if (direction.z < 0.0f) { - zDistance = (z - position.z) / direction.z; - } - float minimumDistance = qMin(xDistance, qMin(yDistance, zDistance)); - if (minimumDistance == xDistance) { - if (direction.x > 0.0f) { - if (x++ == max) { - return false; - } - } else if (x-- == 0) { - return false; - } - } - if (minimumDistance == yDistance) { - if (direction.y > 0.0f) { - if (y++ == max) { - return false; - } - } else if (y-- == 0) { - return false; - } - } - if (minimumDistance == zDistance) { - if (direction.z > 0.0f) { - if (z++ == max) { - return false; - } - } else if (z-- == 0) { - return false; - } - } - position += direction * minimumDistance; - } - return false; -} - -void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { - if (_vertexBufferID == 0) { - glGenBuffers(1, &_vertexBufferID); - glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID); - glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(VoxelPoint), _vertices.constData(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glGenBuffers(1, &_indexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferID); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(int), _indices.constData(), GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - if (!_materials.isEmpty()) { - _networkTextures.resize(_materials.size()); - auto textureCache = DependencyManager::get(); - for (int i = 0; i < _materials.size(); i++) { - const SharedObjectPointer material = _materials.at(i); - if (material) { - _networkTextures[i] = textureCache->getTexture( - static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); - } - } - } - } - - MetavoxelBatch baseBatch; - baseBatch.translation = translation; - baseBatch.rotation = rotation; - baseBatch.scale = scale; - baseBatch.vertexBufferID = _vertexBufferID; - baseBatch.indexBufferID = _indexBufferID; - baseBatch.vertexCount = _vertexCount; - baseBatch.indexCount = _indexCount; - Application::getInstance()->getMetavoxels()->addVoxelBaseBatch(baseBatch); - - if (!(cursor || _materials.isEmpty())) { - VoxelSplatBatch splatBatch; - splatBatch.translation = translation; - splatBatch.rotation = rotation; - splatBatch.scale = scale; - splatBatch.vertexBufferID = _vertexBufferID; - splatBatch.indexBufferID = _indexBufferID; - splatBatch.vertexCount = _vertexCount; - splatBatch.indexCount = _indexCount; - splatBatch.splatTextureOffset = glm::vec3( - glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x, - glm::dot(translation, rotation * glm::vec3(0.0f, 1.0f, 0.0f)) / scale.y, - glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z); - - for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { - for (int j = 0; j < SPLAT_COUNT; j++) { - int index = i + j; - if (index < _networkTextures.size()) { - const NetworkTexturePointer& texture = _networkTextures.at(index); - if (texture) { - MaterialObject* material = static_cast(_materials.at(index).data()); - splatBatch.splatTextureScalesS[j] = scale.x / material->getScaleS(); - splatBatch.splatTextureScalesT[j] = scale.z / material->getScaleT(); - splatBatch.splatTextureIDs[j] = texture->getID(); - - } else { - splatBatch.splatTextureIDs[j] = 0; - } - } else { - splatBatch.splatTextureIDs[j] = 0; - } - } - splatBatch.materialIndex = i; - Application::getInstance()->getMetavoxels()->addVoxelSplatBatch(splatBatch); - } - } - - if (_hermiteCount > 0) { - if (_hermiteBufferID == 0) { - glGenBuffers(1, &_hermiteBufferID); - glBindBuffer(GL_ARRAY_BUFFER, _hermiteBufferID); - glBufferData(GL_ARRAY_BUFFER, _hermite.size() * sizeof(glm::vec3), _hermite.constData(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - _hermite.clear(); - } - HermiteBatch hermiteBatch; - hermiteBatch.translation = translation; - hermiteBatch.rotation = rotation; - hermiteBatch.scale = scale; - hermiteBatch.vertexBufferID = _hermiteBufferID; - hermiteBatch.vertexCount = _hermiteCount; - Application::getInstance()->getMetavoxels()->addHermiteBatch(hermiteBatch); - } -} - -DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() { -} - -class SpannerSimulateVisitor : public SpannerVisitor { -public: - - SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod); - - virtual bool visit(Spanner* spanner); - -private: - - float _deltaTime; -}; - -SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), lod), - _deltaTime(deltaTime) { -} - -bool SpannerSimulateVisitor::visit(Spanner* spanner) { - spanner->getRenderer()->simulate(_deltaTime); - return true; -} - -void DefaultMetavoxelRendererImplementation::simulate(MetavoxelData& data, float deltaTime, - MetavoxelInfo& info, const MetavoxelLOD& lod) { - SpannerSimulateVisitor spannerSimulateVisitor(deltaTime, lod); - data.guide(spannerSimulateVisitor); -} - -void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { - SpannerRenderVisitor spannerRenderVisitor(lod); - data.guide(spannerRenderVisitor); -} - -SphereRenderer::SphereRenderer() { -} - -void SphereRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { - Sphere* sphere = static_cast(_spanner); - const QColor& color = sphere->getColor(); - - glPushMatrix(); - const glm::vec3& translation = sphere->getTranslation(); - glTranslatef(translation.x, translation.y, translation.z); - glm::quat rotation = sphere->getRotation(); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - DependencyManager::get()->renderSolidSphere(sphere->getScale(), 32, 32, - glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF())); - - glPopMatrix(); -} - -CuboidRenderer::CuboidRenderer() { -} - -void CuboidRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { - Cuboid* cuboid = static_cast(_spanner); - const QColor& color = cuboid->getColor(); - - glPushMatrix(); - const glm::vec3& translation = cuboid->getTranslation(); - glTranslatef(translation.x, translation.y, translation.z); - glm::quat rotation = cuboid->getRotation(); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(1.0f, cuboid->getAspectY(), cuboid->getAspectZ()); - - DependencyManager::get()->renderSolidCube(cuboid->getScale() * 2.0f, - glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF())); - - glPopMatrix(); -} - -StaticModelRenderer::StaticModelRenderer() : - _model(new Model(this)) { -} - -void StaticModelRenderer::init(Spanner* spanner) { - SpannerRenderer::init(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::quat&)), SLOT(applyRotation(const glm::quat&))); - connect(spanner, SIGNAL(scaleChanged(float)), SLOT(applyScale(float))); - connect(spanner, SIGNAL(urlChanged(const QUrl&)), SLOT(applyURL(const QUrl&))); -} - -void StaticModelRenderer::simulate(float deltaTime) { - // update the bounds - Box bounds; - if (_model->isActive()) { - const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents; - bounds = Box(extents.minimum, extents.maximum); - } - static_cast(_spanner)->setBounds(glm::translate(_model->getTranslation()) * - glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds); - _model->simulate(deltaTime); -} - -void StaticModelRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { - _model->render(); -} - -bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - RayIntersectionInfo info; - info._rayStart = origin; - info._rayDirection = direction; - if (!_model->findRayIntersection(info)) { - return false; - } - distance = info._hitDistance; - return true; -} - -void StaticModelRenderer::applyTranslation(const glm::vec3& translation) { - _model->setTranslation(translation); -} - -void StaticModelRenderer::applyRotation(const glm::quat& rotation) { - _model->setRotation(rotation); -} - -void StaticModelRenderer::applyScale(float scale) { - _model->setScale(glm::vec3(scale, scale, scale)); -} - -void StaticModelRenderer::applyURL(const QUrl& url) { - _model->setURL(url); -} - -HeightfieldRenderer::HeightfieldRenderer() { -} - -const int X_MAXIMUM_FLAG = 1; -const int Y_MAXIMUM_FLAG = 2; - -static void renderNode(const HeightfieldNodePointer& node, Heightfield* heightfield, const MetavoxelLOD& lod, - const glm::vec2& minimum, float size, bool contained, bool cursor) { - const glm::quat& rotation = heightfield->getRotation(); - glm::vec3 scale(heightfield->getScale() * size, heightfield->getScale() * heightfield->getAspectY(), - heightfield->getScale() * heightfield->getAspectZ() * size); - glm::vec3 translation = heightfield->getTranslation() + rotation * glm::vec3(minimum.x * heightfield->getScale(), - 0.0f, minimum.y * heightfield->getScale() * heightfield->getAspectZ()); - if (!contained) { - Frustum::IntersectionType type = Application::getInstance()->getMetavoxels()->getFrustum().getIntersectionType( - glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale)); - if (type == Frustum::NO_INTERSECTION) { - return; - } - if (type == Frustum::CONTAINS_INTERSECTION) { - contained = true; - } - } - if (!node->isLeaf() && lod.shouldSubdivide(minimum, size)) { - float nextSize = size * 0.5f; - for (int i = 0; i < HeightfieldNode::CHILD_COUNT; i++) { - renderNode(node->getChild(i), heightfield, lod, minimum + glm::vec2(i & X_MAXIMUM_FLAG ? nextSize : 0.0f, - i & Y_MAXIMUM_FLAG ? nextSize : 0.0f), nextSize, contained, cursor); - } - return; - } - HeightfieldNodeRenderer* renderer = static_cast(node->getRenderer()); - if (!renderer) { - node->setRenderer(renderer = new HeightfieldNodeRenderer()); - } - renderer->render(node, translation, rotation, scale, cursor); -} - -void HeightfieldRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { - Heightfield* heightfield = static_cast(_spanner); - renderNode(heightfield->getRoot(), heightfield, heightfield->transformLOD(lod), glm::vec2(), 1.0f, contained, cursor); -} - -HeightfieldNodeRenderer::HeightfieldNodeRenderer() : - _heightTextureID(0), - _colorTextureID(0), - _materialTextureID(0) { -} - -HeightfieldNodeRenderer::~HeightfieldNodeRenderer() { - QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "deleteTextures", Q_ARG(int, _heightTextureID), - Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID)); -} - -bool HeightfieldNodeRenderer::findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, - const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction, - float boundsDistance, float& distance) const { - if (!_voxels) { - return false; - } - glm::quat inverseRotation = glm::inverse(rotation); - float inverseScale = 1.0f / scale.x; - return static_cast(_voxels.data())->findRayIntersection( - inverseRotation * (origin - translation) * inverseScale, inverseRotation * direction * inverseScale, - boundsDistance, distance); -} - -class EdgeCrossing { -public: - glm::vec3 point; - glm::vec3 normal; - QRgb color; - char material; - - void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; } - - void mix(const EdgeCrossing& first, const EdgeCrossing& second, float t); - - VoxelPoint createPoint(int clampedX, int clampedZ, float step) const; -}; - -void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, float t) { - point = glm::mix(first.point, second.point, t); - normal = glm::normalize(glm::mix(first.normal, second.normal, t)); - color = qRgb(glm::mix(qRed(first.color), qRed(second.color), t), glm::mix(qGreen(first.color), qGreen(second.color), t), - glm::mix(qBlue(first.color), qBlue(second.color), t)); - material = (t < 0.5f) ? first.material : second.material; -} - -VoxelPoint EdgeCrossing::createPoint(int clampedX, int clampedZ, float step) const { - VoxelPoint voxelPoint = { glm::vec3(clampedX + point.x, point.y, clampedZ + point.z) * step, - { (quint8)qRed(color), (quint8)qGreen(color), (quint8)qBlue(color) }, - { (char)(normal.x * numeric_limits::max()), (char)(normal.y * numeric_limits::max()), - (char)(normal.z * numeric_limits::max()) }, - { (quint8)material, 0, 0, 0 }, - { numeric_limits::max(), 0, 0, 0 } }; - return voxelPoint; -} - -const int MAX_NORMALS_PER_VERTEX = 4; - -class NormalIndex { -public: - int indices[MAX_NORMALS_PER_VERTEX]; - - bool isValid() const; - - int getClosestIndex(const glm::vec3& normal, QVector& vertices) const; -}; - -bool NormalIndex::isValid() const { - for (int i = 0; i < MAX_NORMALS_PER_VERTEX; i++) { - if (indices[i] >= 0) { - return true; - } - } - return false; -} - -int NormalIndex::getClosestIndex(const glm::vec3& normal, QVector& vertices) const { - int firstIndex = indices[0]; - int closestIndex = firstIndex; - const VoxelPoint& firstVertex = vertices.at(firstIndex); - float closest = normal.x * firstVertex.normal[0] + normal.y * firstVertex.normal[1] + normal.z * firstVertex.normal[2]; - for (int i = 1; i < MAX_NORMALS_PER_VERTEX; i++) { - int index = indices[i]; - if (index == firstIndex) { - break; - } - const VoxelPoint& vertex = vertices.at(index); - float product = normal.x * vertex.normal[0] + normal.y * vertex.normal[1] + normal.z * vertex.normal[2]; - if (product > closest) { - closest = product; - closestIndex = index; - } - } - return closestIndex; -} - -static glm::vec3 safeNormalize(const glm::vec3& vector) { - float length = glm::length(vector); - return (length > 0.0f) ? (vector / length) : vector; -} - -class IndexVector : public QVector { -public: - - int position; - - void swap(IndexVector& other) { QVector::swap(other); qSwap(position, other.position); } - - const NormalIndex& get(int y) const; -}; - -const NormalIndex& IndexVector::get(int y) const { - static NormalIndex invalidIndex = { { -1, -1, -1, -1 } }; - int relative = y - position; - return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex; -} - -static inline glm::vec3 getNormal(const QVector& vertices, const NormalIndex& i0, - const NormalIndex& i1, const NormalIndex& i2, const NormalIndex& i3) { - // check both triangles in case one is degenerate - const glm::vec3& v0 = vertices.at(i0.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(i1.indices[0]).vertex - v0, vertices.at(i2.indices[0]).vertex - v0); - if (glm::length(normal) > EPSILON) { - return normal; - } - return glm::cross(vertices.at(i2.indices[0]).vertex - v0, vertices.at(i3.indices[0]).vertex - v0); -} - -static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1, const EdgeCrossing& e2, - int clampedX, int clampedZ, float step, QVector& vertices, QVector& indices, - QMultiHash& quadIndices) { - int firstIndex = vertices.size(); - vertices.append(e0.createPoint(clampedX, clampedZ, step)); - vertices.append(e1.createPoint(clampedX, clampedZ, step)); - vertices.append(e2.createPoint(clampedX, clampedZ, step)); - indices.append(firstIndex); - indices.append(firstIndex + 1); - indices.append(firstIndex + 2); - indices.append(firstIndex + 2); - - int minimumY = qMin((int)e0.point.y, qMin((int)e1.point.y, (int)e2.point.y)); - int maximumY = qMax((int)e0.point.y, qMax((int)e1.point.y, (int)e2.point.y)); - for (int y = minimumY; y <= maximumY; y++) { - quadIndices.insert(qRgb(clampedX, y, clampedZ), firstIndex); - } -} - -const int CORNER_COUNT = 4; - -static StackArray::Entry getEntry(const StackArray* lineSrc, int stackWidth, int y, float heightfieldHeight, - EdgeCrossing cornerCrossings[CORNER_COUNT], int cornerIndex) { - int offsetX = (cornerIndex & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (cornerIndex & Y_MAXIMUM_FLAG) ? 1 : 0; - const StackArray& src = lineSrc[offsetZ * stackWidth + offsetX]; - int count = src.getEntryCount(); - if (count > 0) { - int relative = y - src.getPosition(); - if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f)) { - return src.getEntry(y, heightfieldHeight); - } - } - const EdgeCrossing& cornerCrossing = cornerCrossings[cornerIndex]; - if (cornerCrossing.point.y == 0.0f) { - return src.getEntry(y, heightfieldHeight); - } - StackArray::Entry entry; - bool set = false; - if (cornerCrossing.point.y >= y) { - entry.color = cornerCrossing.color; - entry.material = cornerCrossing.material; - set = true; - entry.setHermiteY(cornerCrossing.normal, glm::clamp(cornerCrossing.point.y - y, 0.0f, 1.0f)); - - } else { - entry.material = entry.color = 0; - } - if (!(cornerIndex & X_MAXIMUM_FLAG)) { - const EdgeCrossing& nextCornerCrossingX = cornerCrossings[cornerIndex | X_MAXIMUM_FLAG]; - if (nextCornerCrossingX.point.y != 0.0f && (nextCornerCrossingX.point.y >= y) != set) { - float t = glm::clamp((y - cornerCrossing.point.y) / - (nextCornerCrossingX.point.y - cornerCrossing.point.y), 0.0f, 1.0f); - entry.setHermiteX(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingX.normal, t)), t); - } - } - if (!(cornerIndex & Y_MAXIMUM_FLAG)) { - const EdgeCrossing& nextCornerCrossingZ = cornerCrossings[cornerIndex | Y_MAXIMUM_FLAG]; - if (nextCornerCrossingZ.point.y != 0.0f && (nextCornerCrossingZ.point.y >= y) != set) { - float t = glm::clamp((y - cornerCrossing.point.y) / - (nextCornerCrossingZ.point.y - cornerCrossing.point.y), 0.0f, 1.0f); - entry.setHermiteZ(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingZ.normal, t)), t); - } - } - return entry; -} - -void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, - const glm::quat& rotation, const glm::vec3& scale, bool cursor) { - if (!node->getHeight()) { - return; - } - int width = node->getHeight()->getWidth(); - int height = node->getHeight()->getContents().size() / width; - int innerWidth = width - 2 * HeightfieldHeight::HEIGHT_BORDER; - int innerHeight = height - 2 * HeightfieldHeight::HEIGHT_BORDER; - int vertexCount = width * height; - int rows = height - 1; - int columns = width - 1; - int indexCount = rows * columns * 3 * 2; - BufferPair& bufferPair = _bufferPairs[IntPair(width, height)]; - if (!bufferPair.first.isCreated()) { - QVector vertices(vertexCount); - HeightfieldPoint* point = vertices.data(); - - float xStep = 1.0f / (innerWidth - 1); - float zStep = 1.0f / (innerHeight - 1); - float z = -zStep; - float sStep = 1.0f / width; - float tStep = 1.0f / height; - float t = tStep / 2.0f; - for (int i = 0; i < height; i++, z += zStep, t += tStep) { - float x = -xStep; - float s = sStep / 2.0f; - const float SKIRT_LENGTH = 0.25f; - float baseY = (i == 0 || i == height - 1) ? -SKIRT_LENGTH : 0.0f; - for (int j = 0; j < width; j++, point++, x += xStep, s += sStep) { - point->vertex = glm::vec3(x, (j == 0 || j == width - 1) ? -SKIRT_LENGTH : baseY, z); - point->textureCoord = glm::vec2(s, t); - } - } - - bufferPair.first.setUsagePattern(QOpenGLBuffer::StaticDraw); - bufferPair.first.create(); - bufferPair.first.bind(); - bufferPair.first.allocate(vertices.constData(), vertexCount * sizeof(HeightfieldPoint)); - bufferPair.first.release(); - - QVector indices(indexCount); - int* index = indices.data(); - for (int i = 0; i < rows; i++) { - int lineIndex = i * width; - int nextLineIndex = (i + 1) * width; - for (int j = 0; j < columns; j++) { - *index++ = lineIndex + j; - *index++ = nextLineIndex + j; - *index++ = nextLineIndex + j + 1; - - *index++ = nextLineIndex + j + 1; - *index++ = lineIndex + j + 1; - *index++ = lineIndex + j; - } - } - - bufferPair.second = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); - bufferPair.second.create(); - bufferPair.second.bind(); - bufferPair.second.allocate(indices.constData(), indexCount * sizeof(int)); - bufferPair.second.release(); - } - if (_heightTextureID == 0) { - // we use non-aligned data for the various layers - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glGenTextures(1, &_heightTextureID); - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, width, height, 0, - GL_RED, GL_UNSIGNED_SHORT, node->getHeight()->getContents().constData()); - - glGenTextures(1, &_colorTextureID); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (node->getColor()) { - const QByteArray& contents = node->getColor()->getContents(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, node->getColor()->getWidth(), - contents.size() / (node->getColor()->getWidth() * DataBlock::COLOR_BYTES), - 0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData()); - glGenerateMipmap(GL_TEXTURE_2D); - - } else { - const quint8 WHITE_COLOR[] = { 255, 255, 255 }; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); - } - - glGenTextures(1, &_materialTextureID); - glBindTexture(GL_TEXTURE_2D, _materialTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (node->getMaterial()) { - const QByteArray& contents = node->getMaterial()->getContents(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, node->getMaterial()->getWidth(), - contents.size() / node->getMaterial()->getWidth(), - 0, GL_RED, GL_UNSIGNED_BYTE, contents.constData()); - - const QVector& materials = node->getMaterial()->getMaterials(); - _networkTextures.resize(materials.size()); - auto textureCache = DependencyManager::get(); - for (int i = 0; i < materials.size(); i++) { - const SharedObjectPointer& material = materials.at(i); - if (material) { - _networkTextures[i] = textureCache->getTexture( - static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); - } - } - } else { - const quint8 ZERO_VALUE = 0; - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &ZERO_VALUE); - } - glBindTexture(GL_TEXTURE_2D, 0); - - // restore the default alignment; it's what Qt uses for image storage - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - } - bool displayHermite = Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData); - if ((!_voxels || (displayHermite && !static_cast(_voxels.data())->isHermiteEnabled())) && node->getStack()) { - // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the - // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges - - QVector vertices; - QVector indices; - QVector hermiteSegments; - QMultiHash quadIndices; - - int stackWidth = node->getStack()->getWidth(); - int stackHeight = node->getStack()->getContents().size() / stackWidth; - int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; - int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; - const StackArray* src = node->getStack()->getContents().constData(); - const quint16* heightSrc = node->getHeight()->getContents().constData() + - (width + 1) * HeightfieldHeight::HEIGHT_BORDER; - QVector stackMaterials = node->getStack()->getMaterials(); - QHash materialMap; - - int colorWidth; - const uchar* colorSrc = NULL; - float colorStepX, colorStepZ; - if (node->getColor()) { - colorWidth = node->getColor()->getWidth(); - int colorHeight = node->getColor()->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - colorSrc = (const uchar*)node->getColor()->getContents().constData(); - colorStepX = (colorWidth - HeightfieldData::SHARED_EDGE) / (float)innerStackWidth; - colorStepZ = (colorHeight - HeightfieldData::SHARED_EDGE) / (float)innerStackHeight; - } - - int materialWidth; - const uchar* materialSrc = NULL; - float materialStepX, materialStepZ; - if (node->getMaterial()) { - materialWidth = node->getMaterial()->getWidth(); - int materialHeight = node->getMaterial()->getContents().size() / materialWidth; - materialSrc = (const uchar*)node->getMaterial()->getContents().constData(); - materialStepX = (materialWidth - HeightfieldData::SHARED_EDGE) / (float)innerStackWidth; - materialStepZ = (materialHeight - HeightfieldData::SHARED_EDGE) / (float)innerStackHeight; - } - - const int EDGES_PER_CUBE = 12; - EdgeCrossing crossings[EDGES_PER_CUBE * 2]; - - // as we scan down the cube generating vertices between grid points, we remember the indices of the last - // (element, line, section--x, y, z) so that we can connect generated vertices as quads - IndexVector indicesX; - IndexVector lastIndicesX; - QVector indicesZ(stackWidth + 1); - QVector lastIndicesZ(stackWidth + 1); - float step = 1.0f / innerStackWidth; - float voxelScale = scale.y / (numeric_limits::max() * scale.x * step); - - for (int z = 0; z <= stackHeight; z++) { - bool middleZ = (z != 0 && z != stackHeight); - const StackArray* lineSrc = src; - const quint16* heightLineSrc = heightSrc; - for (int x = 0; x <= stackWidth; x++) { - bool middleX = (x != 0 && x != stackWidth); - - // find the y extents of this and the neighboring columns - int minimumY = INT_MAX, maximumY = -1; - lineSrc->getExtents(minimumY, maximumY); - if (middleX) { - lineSrc[1].getExtents(minimumY, maximumY); - if (middleZ) { - lineSrc[stackWidth + 1].getExtents(minimumY, maximumY); - } - } - if (middleZ) { - lineSrc[stackWidth].getExtents(minimumY, maximumY); - } - if (maximumY >= minimumY) { - float heightfieldHeight = *heightLineSrc * voxelScale; - float nextHeightfieldHeightX = heightLineSrc[1] * voxelScale; - float nextHeightfieldHeightZ = heightLineSrc[width] * voxelScale; - float nextHeightfieldHeightXZ = heightLineSrc[width + 1] * voxelScale; - const int UPPER_LEFT_CORNER = 1; - const int UPPER_RIGHT_CORNER = 2; - const int LOWER_LEFT_CORNER = 4; - const int LOWER_RIGHT_CORNER = 8; - const int NO_CORNERS = 0; - const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER; - const int NEXT_CORNERS[] = { 1, 3, 0, 2 }; - int corners = NO_CORNERS; - if (heightfieldHeight != 0.0f) { - corners |= UPPER_LEFT_CORNER; - } - if (nextHeightfieldHeightX != 0.0f && x != stackWidth) { - corners |= UPPER_RIGHT_CORNER; - } - if (nextHeightfieldHeightZ != 0.0f && z != stackHeight) { - corners |= LOWER_LEFT_CORNER; - } - if (nextHeightfieldHeightXZ != 0.0f && x != stackWidth && z != stackHeight) { - corners |= LOWER_RIGHT_CORNER; - } - bool stitchable = x != 0 && z != 0 && !(corners == NO_CORNERS || corners == ALL_CORNERS); - EdgeCrossing cornerCrossings[CORNER_COUNT]; - int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); - int cornerMinimumY = INT_MAX, cornerMaximumY = -1; - if (stitchable) { - for (int i = 0; i < CORNER_COUNT; i++) { - if (!(corners & (1 << i))) { - continue; - } - int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; - const quint16* height = heightLineSrc + offsetZ * width + offsetX; - float heightValue = *height * voxelScale; - int y = (int)heightValue; - cornerMinimumY = qMin(cornerMinimumY, y); - cornerMaximumY = qMax(cornerMaximumY, y); - EdgeCrossing& crossing = cornerCrossings[i]; - crossing.point = glm::vec3(offsetX, heightValue, offsetZ); - int left = height[-1]; - int right = height[1]; - int down = height[-width]; - int up = height[width]; - crossing.normal = glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : left - right, - 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : down - up)); - int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ; - if (colorSrc) { - const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth + - (int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES; - crossing.color = qRgb(color[0], color[1], color[2]); - - } else { - crossing.color = qRgb(numeric_limits::max(), numeric_limits::max(), - numeric_limits::max()); - } - int material = 0; - if (materialSrc) { - material = materialSrc[(int)(clampedOffsetZ * materialStepZ) * materialWidth + - (int)(clampedOffsetX * materialStepX)]; - if (material != 0) { - int& mapping = materialMap[material]; - if (mapping == 0) { - mapping = getMaterialIndex(node->getMaterial()->getMaterials().at(material - 1), - stackMaterials); - } - material = mapping; - } - } - crossing.material = material; - } - minimumY = qMin(minimumY, cornerMinimumY); - maximumY = qMax(maximumY, cornerMaximumY); - - if (corners == (LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER)) { - appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], - clampedX, clampedZ, step, vertices, indices, quadIndices); - - } else if (corners == (UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER)) { - appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], - clampedX, clampedZ, step, vertices, indices, quadIndices); - } - } - int position = minimumY; - int count = maximumY - minimumY + 1; - NormalIndex lastIndexY = { { -1, -1, -1, -1 } }; - indicesX.position = position; - indicesX.resize(count); - indicesZ[x].position = position; - indicesZ[x].resize(count); - for (int y = position, end = position + count; y < end; y++) { - StackArray::Entry entry = getEntry(lineSrc, stackWidth, y, heightfieldHeight, cornerCrossings, 0); - if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) { - glm::vec3 normal; - if (entry.hermiteX != 0) { - glm::vec3 start = glm::vec3(clampedX + entry.getHermiteX(normal), y, clampedZ) * step; - hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); - } - if (entry.hermiteY != 0) { - glm::vec3 start = glm::vec3(clampedX, y + entry.getHermiteY(normal), clampedZ) * step; - hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); - } - if (entry.hermiteZ != 0) { - glm::vec3 start = glm::vec3(clampedX, y, clampedZ + entry.getHermiteZ(normal)) * step; - hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); - } - } - // number variables correspond to cube corners, where the x, y, and z components are represented as - // bits in the 0, 1, and 2 position, respectively; hence, alpha0 is the value at the minimum x, y, and - // z corner and alpha7 is the value at the maximum x, y, and z - int alpha0 = lineSrc->getEntryAlpha(y, heightfieldHeight); - int alpha2 = lineSrc->getEntryAlpha(y + 1, heightfieldHeight); - int alpha1 = alpha0, alpha3 = alpha2, alpha4 = alpha0, alpha6 = alpha2; - int alphaTotal = alpha0 + alpha2; - int possibleTotal = 2 * numeric_limits::max(); - - // cubes on the edge are two-dimensional: this ensures that their vertices will be shared between - // neighboring blocks, which share only one layer of points - if (middleZ) { - alphaTotal += (alpha4 = lineSrc[stackWidth].getEntryAlpha(y, nextHeightfieldHeightZ)); - possibleTotal += numeric_limits::max(); - - alphaTotal += (alpha6 = lineSrc[stackWidth].getEntryAlpha(y + 1, nextHeightfieldHeightZ)); - possibleTotal += numeric_limits::max(); - } - int alpha5 = alpha4, alpha7 = alpha6; - if (middleX) { - alphaTotal += (alpha1 = lineSrc[1].getEntryAlpha(y, nextHeightfieldHeightX)); - possibleTotal += numeric_limits::max(); - - alphaTotal += (alpha3 = lineSrc[1].getEntryAlpha(y + 1, nextHeightfieldHeightX)); - possibleTotal += numeric_limits::max(); - - if (middleZ) { - alphaTotal += (alpha5 = lineSrc[stackWidth + 1].getEntryAlpha(y, nextHeightfieldHeightXZ)); - possibleTotal += numeric_limits::max(); - - alphaTotal += (alpha7 = lineSrc[stackWidth + 1].getEntryAlpha(y + 1, nextHeightfieldHeightXZ)); - possibleTotal += numeric_limits::max(); - } - } - if (alphaTotal == 0 || alphaTotal == possibleTotal) { - continue; // no corners set/all corners set - } - // we first look for crossings with the heightfield corner vertices; these take priority - int crossingCount = 0; - if (y >= cornerMinimumY && y <= cornerMaximumY) { - // first look for set corners, which override any interpolated values - int crossedCorners = NO_CORNERS; - for (int i = 0; i < CORNER_COUNT; i++) { - if (!(corners & (1 << i))) { - continue; - } - const EdgeCrossing& cornerCrossing = cornerCrossings[i]; - if (cornerCrossing.point.y >= y && cornerCrossing.point.y < y + 1) { - crossedCorners |= (1 << i); - } - } - switch (crossedCorners) { - case UPPER_LEFT_CORNER: - case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: - case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: - case UPPER_LEFT_CORNER | LOWER_RIGHT_CORNER: - crossings[crossingCount++] = cornerCrossings[0]; - crossings[crossingCount - 1].point.y -= y; - break; - - case UPPER_RIGHT_CORNER: - case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: - case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: - case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: - crossings[crossingCount++] = cornerCrossings[1]; - crossings[crossingCount - 1].point.y -= y; - break; - - case LOWER_LEFT_CORNER: - case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: - case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: - crossings[crossingCount++] = cornerCrossings[2]; - crossings[crossingCount - 1].point.y -= y; - break; - - case LOWER_RIGHT_CORNER: - case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: - case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: - crossings[crossingCount++] = cornerCrossings[3]; - crossings[crossingCount - 1].point.y -= y; - break; - - case NO_CORNERS: - for (int i = 0; i < CORNER_COUNT; i++) { - if (!(corners & (1 << i))) { - continue; - } - int nextIndex = NEXT_CORNERS[i]; - if (!(corners & (1 << nextIndex))) { - continue; - } - const EdgeCrossing& cornerCrossing = cornerCrossings[i]; - const EdgeCrossing& nextCornerCrossing = cornerCrossings[nextIndex]; - float divisor = (nextCornerCrossing.point.y - cornerCrossing.point.y); - if (divisor == 0.0f) { - continue; - } - float t1 = (y - cornerCrossing.point.y) / divisor; - float t2 = (y + 1 - cornerCrossing.point.y) / divisor; - if (t1 >= 0.0f && t1 <= 1.0f) { - crossings[crossingCount++].mix(cornerCrossing, nextCornerCrossing, t1); - crossings[crossingCount - 1].point.y -= y; - } - if (t2 >= 0.0f && t2 <= 1.0f) { - crossings[crossingCount++].mix(cornerCrossing, nextCornerCrossing, t2); - crossings[crossingCount - 1].point.y -= y; - } - } - break; - } - } - - // the terrifying conditional code that follows checks each cube edge for a crossing, gathering - // its properties (color, material, normal) if one is present; as before, boundary edges are excluded - if (crossingCount == 0) { - StackArray::Entry nextEntryY = getEntry(lineSrc, stackWidth, y + 1, - heightfieldHeight, cornerCrossings, 0); - if (middleX) { - StackArray::Entry nextEntryX = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightX, - cornerCrossings, 1); - StackArray::Entry nextEntryXY = getEntry(lineSrc, stackWidth, y + 1, nextHeightfieldHeightX, - cornerCrossings, 1); - if (alpha0 != alpha1) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(entry.getHermiteX(crossing.normal), 0.0f, 0.0f); - crossing.setColorMaterial(alpha0 == 0 ? nextEntryX : entry); - } - if (alpha1 != alpha3) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(1.0f, nextEntryX.getHermiteY(crossing.normal), 0.0f); - crossing.setColorMaterial(alpha1 == 0 ? nextEntryXY : nextEntryX); - } - if (alpha2 != alpha3) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(nextEntryY.getHermiteX(crossing.normal), 1.0f, 0.0f); - crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY); - } - if (middleZ) { - StackArray::Entry nextEntryZ = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightZ, - cornerCrossings, 2); - StackArray::Entry nextEntryXZ = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightXZ, - cornerCrossings, 3); - StackArray::Entry nextEntryXYZ = getEntry(lineSrc, stackWidth, y + 1, - nextHeightfieldHeightXZ, cornerCrossings, 3); - if (alpha1 != alpha5) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(1.0f, 0.0f, nextEntryX.getHermiteZ(crossing.normal)); - crossing.setColorMaterial(alpha1 == 0 ? nextEntryXZ : nextEntryX); - } - if (alpha3 != alpha7) { - EdgeCrossing& crossing = crossings[crossingCount++]; - StackArray::Entry nextEntryXY = getEntry(lineSrc, stackWidth, y + 1, - nextHeightfieldHeightX, cornerCrossings, 1); - crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal)); - crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY); - } - if (alpha4 != alpha5) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(nextEntryZ.getHermiteX(crossing.normal), 0.0f, 1.0f); - crossing.setColorMaterial(alpha4 == 0 ? nextEntryXZ : nextEntryZ); - } - if (alpha5 != alpha7) { - EdgeCrossing& crossing = crossings[crossingCount++]; - StackArray::Entry nextEntryXZ = getEntry(lineSrc, stackWidth, y, - nextHeightfieldHeightXZ, cornerCrossings, 3); - crossing.point = glm::vec3(1.0f, nextEntryXZ.getHermiteY(crossing.normal), 1.0f); - crossing.setColorMaterial(alpha5 == 0 ? nextEntryXYZ : nextEntryXZ); - } - if (alpha6 != alpha7) { - EdgeCrossing& crossing = crossings[crossingCount++]; - StackArray::Entry nextEntryYZ = getEntry(lineSrc, stackWidth, y + 1, - nextHeightfieldHeightZ, cornerCrossings, 2); - crossing.point = glm::vec3(nextEntryYZ.getHermiteX(crossing.normal), 1.0f, 1.0f); - crossing.setColorMaterial(alpha6 == 0 ? nextEntryXYZ : nextEntryYZ); - } - } - } - if (alpha0 != alpha2) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(0.0f, entry.getHermiteY(crossing.normal), 0.0f); - crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : entry); - } - if (middleZ) { - StackArray::Entry nextEntryZ = getEntry(lineSrc, stackWidth, y, - nextHeightfieldHeightZ, cornerCrossings, 2); - StackArray::Entry nextEntryYZ = getEntry(lineSrc, stackWidth, y + 1, - nextHeightfieldHeightZ, cornerCrossings, 2); - if (alpha0 != alpha4) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal)); - crossing.setColorMaterial(alpha0 == 0 ? nextEntryZ : entry); - } - if (alpha2 != alpha6) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(0.0f, 1.0f, nextEntryY.getHermiteZ(crossing.normal)); - crossing.setColorMaterial(alpha2 == 0 ? nextEntryYZ : nextEntryY); - } - if (alpha4 != alpha6) { - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(0.0f, nextEntryZ.getHermiteY(crossing.normal), 1.0f); - crossing.setColorMaterial(alpha4 == 0 ? nextEntryYZ : nextEntryZ); - } - } - } - // make sure we have valid crossings to include - int validCrossings = 0; - for (int i = 0; i < crossingCount; i++) { - if (qAlpha(crossings[i].color) != 0) { - validCrossings++; - } - } - NormalIndex index = { { -1, -1, -1, -1 } }; - if (validCrossings != 0) { - index.indices[0] = index.indices[1] = index.indices[2] = index.indices[3] = vertices.size(); - glm::vec3 center; - glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; - int normalCount = 0; - const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); - const int MAX_MATERIALS_PER_VERTEX = 4; - quint8 materials[] = { 0, 0, 0, 0 }; - glm::vec4 materialWeights; - float totalWeight = 0.0f; - int red = 0, green = 0, blue = 0; - for (int i = 0; i < crossingCount; i++) { - const EdgeCrossing& crossing = crossings[i]; - if (qAlpha(crossing.color) == 0) { - continue; - } - center += crossing.point; - - int j = 0; - for (; j < normalCount; j++) { - if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { - normals[j] = safeNormalize(normals[j] + crossing.normal); - break; - } - } - if (j == normalCount) { - normals[normalCount++] = crossing.normal; - } - - red += qRed(crossing.color); - green += qGreen(crossing.color); - blue += qBlue(crossing.color); - - // when assigning a material, search for its presence and, if not found, - // place it in the first empty slot - if (crossing.material != 0) { - for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { - if (materials[j] == crossing.material) { - materialWeights[j] += 1.0f; - totalWeight += 1.0f; - break; - - } else if (materials[j] == 0) { - materials[j] = crossing.material; - materialWeights[j] = 1.0f; - totalWeight += 1.0f; - break; - } - } - } - } - center /= validCrossings; - - // use a sequence of Givens rotations to perform a QR decomposition - // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf - glm::mat4 r(0.0f); - glm::vec4 bottom; - for (int i = 0; i < crossingCount; i++) { - const EdgeCrossing& crossing = crossings[i]; - if (qAlpha(crossing.color) == 0) { - continue; - } - bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); - - for (int j = 0; j < 4; j++) { - float angle = glm::atan(-bottom[j], r[j][j]); - float sina = glm::sin(angle); - float cosa = glm::cos(angle); - - for (int k = 0; k < 4; k++) { - float tmp = bottom[k]; - bottom[k] = sina * r[k][j] + cosa * tmp; - r[k][j] = cosa * r[k][j] - sina * tmp; - } - } - } - - // extract the submatrices, form ata - glm::mat3 a(r); - glm::vec3 b(r[3]); - glm::mat3 atrans = glm::transpose(a); - glm::mat3 ata = atrans * a; - - // find the eigenvalues and eigenvectors of ata - // (see http://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm) - glm::mat3 d = ata; - glm::quat combinedRotation; - const int MAX_ITERATIONS = 20; - for (int i = 0; i < MAX_ITERATIONS; i++) { - glm::vec3 offDiagonals = glm::abs(glm::vec3(d[1][0], d[2][0], d[2][1])); - int largestIndex = (offDiagonals[0] > offDiagonals[1]) ? - (offDiagonals[0] > offDiagonals[2] ? 0 : 2) : (offDiagonals[1] > offDiagonals[2] ? 1 : 2); - const float DESIRED_PRECISION = 0.00001f; - if (offDiagonals[largestIndex] < DESIRED_PRECISION) { - break; - } - int largestJ = (largestIndex == 2) ? 1 : 0; - int largestI = (largestIndex == 0) ? 1 : 2; - float sjj = d[largestJ][largestJ]; - float sii = d[largestI][largestI]; - float angle = glm::atan(2.0f * d[largestJ][largestI], sjj - sii) / 2.0f; - glm::quat rotation = glm::angleAxis(angle, largestIndex == 0 ? glm::vec3(0.0f, 0.0f, -1.0f) : - (largestIndex == 1 ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(-1.0f, 0.0f, 0.0f))); - combinedRotation = glm::normalize(rotation * combinedRotation); - glm::mat3 matrix = glm::mat3_cast(combinedRotation); - d = matrix * ata * glm::transpose(matrix); - } - - // form the singular matrix from the eigenvalues - const float MIN_SINGULAR_THRESHOLD = 0.1f; - d[0][0] = (d[0][0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[0][0]; - d[1][1] = (d[1][1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[1][1]; - d[2][2] = (d[2][2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[2][2]; - - // compute the pseudo-inverse, ataplus, and use to find the minimizing solution - glm::mat3 u = glm::mat3_cast(combinedRotation); - glm::mat3 ataplus = glm::transpose(u) * d * u; - glm::vec3 solution = (ataplus * atrans * b) + center; - - // make sure it doesn't fall beyond the cell boundaries - center = glm::clamp(solution, 0.0f, 1.0f); - - if (totalWeight > 0.0f) { - materialWeights *= (numeric_limits::max() / totalWeight); - } - VoxelPoint point = { (glm::vec3(clampedX, y, clampedZ) + center) * step, - { (quint8)(red / validCrossings), (quint8)(green / validCrossings), - (quint8)(blue / validCrossings) }, - { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), - (char)(normals[0].z * 127.0f) }, - { materials[0], materials[1], materials[2], materials[3] }, - { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], - (quint8)materialWeights[3] } }; - - vertices.append(point); - for (int i = 1; i < normalCount; i++) { - index.indices[i] = vertices.size(); - point.setNormal(normals[i]); - vertices.append(point); - } - } - - // the first x, y, and z are repeated for the boundary edge; past that, we consider generating - // quads for each edge that includes a transition, using indices of previously generated vertices - int reclampedX = qMin(clampedX, stackWidth - 1); - int reclampedZ = qMin(clampedZ, stackHeight - 1); - if (alpha0 != alpha1 && y > position && z > 0) { - const NormalIndex& index1 = lastIndexY; - const NormalIndex& index2 = lastIndicesZ[x].get(y - 1); - const NormalIndex& index3 = lastIndicesZ[x].get(y); - if (index.isValid() && index1.isValid() && index2.isValid() && index3.isValid()) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); - if (reclampedZ > 0) { - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); - quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); - } - glm::vec3 normal = getNormal(vertices, index, index1, index2, index3); - if (alpha0 == 0) { // quad faces negative x - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive x - indices.append(index1.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); - } - } - - if (alpha0 != alpha2 && x > 0 && z > 0) { - const NormalIndex& index1 = lastIndicesZ[x].get(y); - const NormalIndex& index2 = lastIndicesZ[x - 1].get(y); - const NormalIndex& index3 = lastIndicesX.get(y); - if (index.isValid() && index1.isValid() && index2.isValid() && index3.isValid()) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); - if (reclampedX > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); - if (reclampedZ > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ - 1), indices.size()); - } - } - if (reclampedZ > 0) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); - } - glm::vec3 normal = getNormal(vertices, index, index3, index2, index1); - if (alpha0 == 0) { // quad faces negative y - indices.append(index3.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive y - indices.append(index1.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); - } - } - - if (alpha0 != alpha4 && x > 0 && y > position) { - const NormalIndex& index1 = lastIndexY; - const NormalIndex& index2 = lastIndicesX.get(y - 1); - const NormalIndex& index3 = lastIndicesX.get(y); - if (index.isValid() && index1.isValid() && index2.isValid() && index3.isValid()) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); - if (reclampedX > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); - quadIndices.insert(qRgb(reclampedX - 1, y - 1, reclampedZ), indices.size()); - } - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); - - glm::vec3 normal = getNormal(vertices, index, index1, index2, index3); - if (alpha0 == 0) { // quad faces negative z - indices.append(index1.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } else { // quad faces positive z - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); - } - } - lastIndexY = index; - indicesX[y - position] = index; - indicesZ[x][y - position] = index; - } - } else { - indicesX.clear(); - indicesZ[x].clear(); - } - if (x != 0) { - lineSrc++; - heightLineSrc++; - } - indicesX.swap(lastIndicesX); - } - if (z != 0) { - src += stackWidth; - heightSrc += width; - } - indicesZ.swap(lastIndicesZ); - lastIndicesX.clear(); - } - _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, stackWidth, stackMaterials); - } - - if (_voxels) { - _voxels->render(translation, rotation, glm::vec3(scale.x, scale.x, scale.x), cursor); - } - - HeightfieldBaseLayerBatch baseBatch; - baseBatch.vertexBufferID = bufferPair.first.bufferId(); - baseBatch.indexBufferID = bufferPair.second.bufferId(); - baseBatch.translation = translation; - baseBatch.rotation = rotation; - baseBatch.scale = scale; - baseBatch.vertexCount = vertexCount; - baseBatch.indexCount = indexCount; - baseBatch.heightTextureID = _heightTextureID; - baseBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f); - baseBatch.colorTextureID = _colorTextureID; - float widthMultiplier = 1.0f / (0.5f - 1.5f / width); - float heightMultiplier = 1.0f / (0.5f - 1.5f / height); - if (node->getColor()) { - int colorWidth = node->getColor()->getWidth(); - int colorHeight = node->getColor()->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - baseBatch.colorScale = glm::vec2((0.5f - 0.5f / colorWidth) * widthMultiplier, - (0.5f - 0.5f / colorHeight) * heightMultiplier); - } - Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch); - - if (!(cursor || _networkTextures.isEmpty())) { - HeightfieldSplatBatch splatBatch; - splatBatch.vertexBufferID = bufferPair.first.bufferId(); - splatBatch.indexBufferID = bufferPair.second.bufferId(); - splatBatch.translation = translation; - splatBatch.rotation = rotation; - splatBatch.scale = scale; - splatBatch.vertexCount = vertexCount; - splatBatch.indexCount = indexCount; - splatBatch.heightTextureID = _heightTextureID; - splatBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, 0.0f, 0.0f); - splatBatch.materialTextureID = _materialTextureID; - if (node->getMaterial()) { - int materialWidth = node->getMaterial()->getWidth(); - int materialHeight = node->getMaterial()->getContents().size() / materialWidth; - splatBatch.textureScale = glm::vec2((0.5f - 0.5f / materialWidth) * widthMultiplier, - (0.5f - 0.5f / materialHeight) * heightMultiplier); - } - splatBatch.splatTextureOffset = glm::vec2( - glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x, - glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z); - - const QVector& materials = node->getMaterial()->getMaterials(); - for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { - for (int j = 0; j < SPLAT_COUNT; j++) { - int index = i + j; - if (index < _networkTextures.size()) { - const NetworkTexturePointer& texture = _networkTextures.at(index); - if (texture) { - MaterialObject* material = static_cast(materials.at(index).data()); - splatBatch.splatTextureScalesS[j] = scale.x / material->getScaleS(); - splatBatch.splatTextureScalesT[j] = scale.z / material->getScaleT(); - splatBatch.splatTextureIDs[j] = texture->getID(); - - } else { - splatBatch.splatTextureIDs[j] = 0; - } - } else { - splatBatch.splatTextureIDs[j] = 0; - } - } - splatBatch.materialIndex = i; - Application::getInstance()->getMetavoxels()->addHeightfieldSplatBatch(splatBatch); - } - } -} - -QHash HeightfieldNodeRenderer::_bufferPairs; - diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h deleted file mode 100644 index 28e5758840..0000000000 --- a/interface/src/MetavoxelSystem.h +++ /dev/null @@ -1,445 +0,0 @@ -// -// MetavoxelSystem.h -// interface/src -// -// Created by Andrzej Kapolka on 12/10/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelSystem_h -#define hifi_MetavoxelSystem_h - -#include -#include -#include -#include - -#include - -#include -#include -#include - -class HeightfieldBaseLayerBatch; -class HeightfieldSplatBatch; -class HermiteBatch; -class MetavoxelBatch; -class Model; -class VoxelSplatBatch; - -/// Renders a metavoxel tree. -class MetavoxelSystem : public MetavoxelClientManager { - Q_OBJECT - -public: - - class NetworkSimulation { - public: - float dropRate; - float repeatRate; - int minimumDelay; - int maximumDelay; - int bandwidthLimit; - - NetworkSimulation(float dropRate = 0.0f, float repeatRate = 0.0f, int minimumDelay = 0, - int maximumDelay = 0, int bandwidthLimit = 0); - }; - - virtual ~MetavoxelSystem(); - - virtual void init(); - - virtual MetavoxelLOD getLOD(); - - const Frustum& getFrustum() const { return _frustum; } - - void setNetworkSimulation(const NetworkSimulation& simulation); - NetworkSimulation getNetworkSimulation(); - - void simulate(float deltaTime); - void render(); - - void renderHeightfieldCursor(const glm::vec3& position, float radius); - - Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color); - - Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); - - Q_INVOKABLE void setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color, bool paint = false); - - Q_INVOKABLE void setHeightfieldMaterial(const SharedObjectPointer& spanner, - const SharedObjectPointer& material, bool paint = false); - - void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); } - void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); } - - void addVoxelBaseBatch(const MetavoxelBatch& batch) { _voxelBaseBatches.append(batch); } - void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); } - - void addHermiteBatch(const HermiteBatch& batch) { _hermiteBatches.append(batch); } - - Q_INVOKABLE void deleteTextures(int heightTextureID, int colorTextureID, int materialTextureID) const; - Q_INVOKABLE void deleteBuffers(int vertexBufferID, int indexBufferID, int hermiteBufferID) const; - -signals: - - void rendering(); - -protected: - - Q_INVOKABLE void applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable = false); - - virtual MetavoxelClient* createClient(const SharedNodePointer& node); - -private: - - void guideToAugmented(MetavoxelVisitor& visitor, bool render = false); - - MetavoxelLOD _lod; - QReadWriteLock _lodLock; - Frustum _frustum; - - NetworkSimulation _networkSimulation; - QReadWriteLock _networkSimulationLock; - - QVector _heightfieldBaseBatches; - QVector _heightfieldSplatBatches; - QVector _voxelBaseBatches; - QVector _voxelSplatBatches; - QVector _hermiteBatches; - - ProgramObject _baseHeightfieldProgram; - int _baseHeightScaleLocation; - int _baseColorScaleLocation; - - class SplatLocations { - public: - int heightScale; - int textureScale; - int splatTextureOffset; - int splatTextureScalesS; - int splatTextureScalesT; - int textureValueMinima; - int textureValueMaxima; - int materials; - int materialWeights; - }; - - ProgramObject _splatHeightfieldProgram; - SplatLocations _splatHeightfieldLocations; - - int _splatHeightScaleLocation; - int _splatTextureScaleLocation; - int _splatTextureOffsetLocation; - int _splatTextureScalesSLocation; - int _splatTextureScalesTLocation; - int _splatTextureValueMinimaLocation; - int _splatTextureValueMaximaLocation; - - ProgramObject _heightfieldCursorProgram; - - ProgramObject _baseVoxelProgram; - ProgramObject _splatVoxelProgram; - SplatLocations _splatVoxelLocations; - - ProgramObject _voxelCursorProgram; - - static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations); -}; - -/// Base class for all batches. -class MetavoxelBatch { -public: - GLuint vertexBufferID; - GLuint indexBufferID; - glm::vec3 translation; - glm::quat rotation; - glm::vec3 scale; - int vertexCount; - int indexCount; -}; - -/// Base class for heightfield batches. -class HeightfieldBatch : public MetavoxelBatch { -public: - GLuint heightTextureID; - glm::vec4 heightScale; -}; - -/// A batch containing a heightfield base layer. -class HeightfieldBaseLayerBatch : public HeightfieldBatch { -public: - GLuint colorTextureID; - glm::vec2 colorScale; -}; - -/// A batch containing a heightfield splat. -class HeightfieldSplatBatch : public HeightfieldBatch { -public: - GLuint materialTextureID; - glm::vec2 textureScale; - glm::vec2 splatTextureOffset; - int splatTextureIDs[4]; - glm::vec4 splatTextureScalesS; - glm::vec4 splatTextureScalesT; - int materialIndex; -}; - -/// A batch containing a voxel splat. -class VoxelSplatBatch : public MetavoxelBatch { -public: - glm::vec3 splatTextureOffset; - int splatTextureIDs[4]; - glm::vec4 splatTextureScalesS; - glm::vec4 splatTextureScalesT; - int materialIndex; -}; - -/// A batch containing Hermite data for debugging. -class HermiteBatch { -public: - GLuint vertexBufferID; - glm::vec3 translation; - glm::quat rotation; - glm::vec3 scale; - int vertexCount; -}; - -/// Generic abstract base class for objects that handle a signal. -class SignalHandler : public QObject { - Q_OBJECT - -public slots: - - virtual void handle() = 0; -}; - -/// Simple throttle for limiting bandwidth on a per-second basis. -class Throttle { -public: - - Throttle(); - - /// Sets the per-second limit. - void setLimit(int limit) { _limit = limit; } - - /// Determines whether the message with the given size should be throttled (discarded). If not, registers the message - /// as having been processed (i.e., contributing to later throttling). - bool shouldThrottle(int bytes); - -private: - - int _limit; - int _total; - - typedef QPair Bucket; - QList _buckets; -}; - -/// A client session associated with a single server. -class MetavoxelSystemClient : public MetavoxelClient { - Q_OBJECT - -public: - - MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater); - - Q_INVOKABLE void setAugmentedData(const MetavoxelData& data); - - /// Returns a copy of the augmented data. This function is thread-safe. - MetavoxelData getAugmentedData(); - - void setRenderedAugmentedData(const MetavoxelData& data) { _renderedAugmentedData = data; } - - virtual int parseData(const QByteArray& packet); - -protected: - - virtual void dataChanged(const MetavoxelData& oldData); - virtual void sendDatagram(const QByteArray& data); - -private: - - MetavoxelData _augmentedData; - MetavoxelData _renderedAugmentedData; - QReadWriteLock _augmentedDataLock; - - Throttle _sendThrottle; - Throttle _receiveThrottle; -}; - -/// Base class for cached static buffers. -class BufferData : public QSharedData { -public: - - virtual ~BufferData(); - - virtual void render(const glm::vec3& translation, const glm::quat& rotation, - const glm::vec3& scale, bool cursor = false) = 0; -}; - -typedef QExplicitlySharedDataPointer BufferDataPointer; - -/// Describes contents of a vertex in a voxel buffer. -class VoxelPoint { -public: - glm::vec3 vertex; - quint8 color[3]; - char normal[3]; - quint8 materials[4]; - quint8 materialWeights[4]; - - void setNormal(const glm::vec3& normal); -}; - -/// A container for a coordinate within a voxel block. -class VoxelCoord { -public: - QRgb encoded; - - VoxelCoord(QRgb encoded) : encoded(encoded) { } - - bool operator==(const VoxelCoord& other) const { return encoded == other.encoded; } -}; - -inline uint qHash(const VoxelCoord& coord, uint seed) { - // multiply by prime numbers greater than the possible size - return qHash(qRed(coord.encoded) + 257 * (qGreen(coord.encoded) + 263 * qBlue(coord.encoded)), seed); -} - -/// Contains the information necessary to render a voxel block. -class VoxelBuffer : public BufferData { -public: - - VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QMultiHash& quadIndices, int size, const QVector& materials = - QVector()); - virtual ~VoxelBuffer(); - - bool isHermiteEnabled() const { return _hermiteEnabled; } - - /// Finds the first intersection between the described ray and the voxel data. - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const; - - virtual void render(const glm::vec3& translation, const glm::quat& rotation, - const glm::vec3& scale, bool cursor = false); - -private: - - QVector _vertices; - QVector _indices; - QVector _hermite; - bool _hermiteEnabled; - QMultiHash _quadIndices; - int _size; - int _vertexCount; - int _indexCount; - int _hermiteCount; - GLuint _vertexBufferID; - GLuint _indexBufferID; - GLuint _hermiteBufferID; - QVector _materials; - QVector _networkTextures; -}; - -/// Renders metavoxels as points. -class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplementation { - Q_OBJECT - -public: - - Q_INVOKABLE DefaultMetavoxelRendererImplementation(); - - virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod); - virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod); -}; - -/// Renders spheres. -class SphereRenderer : public SpannerRenderer { - Q_OBJECT - -public: - - Q_INVOKABLE SphereRenderer(); - - virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); -}; - -/// Renders cuboids. -class CuboidRenderer : public SpannerRenderer { - Q_OBJECT - -public: - - Q_INVOKABLE CuboidRenderer(); - - virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); -}; - -/// 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(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - -private slots: - - void applyTranslation(const glm::vec3& translation); - void applyRotation(const glm::quat& rotation); - void applyScale(float scale); - void applyURL(const QUrl& url); - -private: - - Model* _model; -}; - -/// Renders heightfields. -class HeightfieldRenderer : public SpannerRenderer { - Q_OBJECT - -public: - - Q_INVOKABLE HeightfieldRenderer(); - - virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); -}; - -/// Renders a single quadtree node. -class HeightfieldNodeRenderer : public AbstractHeightfieldNodeRenderer { -public: - - HeightfieldNodeRenderer(); - virtual ~HeightfieldNodeRenderer(); - - virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const; - - void render(const HeightfieldNodePointer& node, const glm::vec3& translation, - const glm::quat& rotation, const glm::vec3& scale, bool cursor); - -private: - - GLuint _heightTextureID; - GLuint _colorTextureID; - GLuint _materialTextureID; - QVector _networkTextures; - - BufferDataPointer _voxels; - - typedef QPair IntPair; - typedef QPair BufferPair; - static QHash _bufferPairs; -}; - -#endif // hifi_MetavoxelSystem_h diff --git a/interface/src/ui/BandwidthDialog.cpp b/interface/src/ui/BandwidthDialog.cpp index 044774fad6..b3bc934006 100644 --- a/interface/src/ui/BandwidthDialog.cpp +++ b/interface/src/ui/BandwidthDialog.cpp @@ -86,12 +86,10 @@ BandwidthDialog::BandwidthDialog(QWidget* parent) : new BandwidthChannelDisplay({NodeType::EntityServer}, form, "Octree", "Kbps", 1.0, COLOR2); _allChannelDisplays[3] = _octreeChannelDisplay = new BandwidthChannelDisplay({NodeType::DomainServer}, form, "Domain", "Kbps", 1.0, COLOR2); - _allChannelDisplays[4] = _metavoxelsChannelDisplay = - new BandwidthChannelDisplay({NodeType::MetavoxelServer, NodeType::EnvironmentServer}, form, "Metavoxels", "Kbps", 1.0, COLOR2); - _allChannelDisplays[5] = _otherChannelDisplay = + _allChannelDisplays[4] = _otherChannelDisplay = new BandwidthChannelDisplay({NodeType::Unassigned}, form, "Other", "Kbps", 1.0, COLOR2); - _allChannelDisplays[6] = _totalChannelDisplay = - new BandwidthChannelDisplay({NodeType::DomainServer, NodeType::EntityServer, NodeType::MetavoxelServer, + _allChannelDisplays[5] = _totalChannelDisplay = + new BandwidthChannelDisplay({NodeType::DomainServer, NodeType::EntityServer, NodeType::EnvironmentServer, NodeType::AudioMixer, NodeType::Agent, NodeType::AvatarMixer, NodeType::Unassigned}, form, "Total", "Kbps", 1.0, COLOR2); diff --git a/interface/src/ui/BandwidthDialog.h b/interface/src/ui/BandwidthDialog.h index cf4c34b0a9..a504a5964f 100644 --- a/interface/src/ui/BandwidthDialog.h +++ b/interface/src/ui/BandwidthDialog.h @@ -63,11 +63,10 @@ private: BandwidthChannelDisplay* _avatarsChannelDisplay; BandwidthChannelDisplay* _octreeChannelDisplay; BandwidthChannelDisplay* _domainChannelDisplay; - BandwidthChannelDisplay* _metavoxelsChannelDisplay; BandwidthChannelDisplay* _otherChannelDisplay; BandwidthChannelDisplay* _totalChannelDisplay; // sums of all the other channels - static const unsigned int _CHANNELCOUNT = 7; + static const unsigned int _CHANNELCOUNT = 6; BandwidthChannelDisplay* _allChannelDisplays[_CHANNELCOUNT]; diff --git a/interface/src/ui/CachesSizeDialog.cpp b/interface/src/ui/CachesSizeDialog.cpp index d21d1c2db8..a29793349f 100644 --- a/interface/src/ui/CachesSizeDialog.cpp +++ b/interface/src/ui/CachesSizeDialog.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -42,7 +41,6 @@ CachesSizeDialog::CachesSizeDialog(QWidget* parent) : form->addRow("Animations cache size (MB):", _animations = createDoubleSpinBox(this)); form->addRow("Geometries cache size (MB):", _geometries = createDoubleSpinBox(this)); - form->addRow("Scripts cache size (MB):", _scripts = createDoubleSpinBox(this)); form->addRow("Sounds cache size (MB):", _sounds = createDoubleSpinBox(this)); form->addRow("Textures cache size (MB):", _textures = createDoubleSpinBox(this)); @@ -59,7 +57,6 @@ CachesSizeDialog::CachesSizeDialog(QWidget* parent) : void CachesSizeDialog::confirmClicked(bool checked) { DependencyManager::get()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES); - DependencyManager::get()->setUnusedResourceCacheSize(_scripts->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES); @@ -69,7 +66,6 @@ void CachesSizeDialog::confirmClicked(bool checked) { void CachesSizeDialog::resetClicked(bool checked) { _animations->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _geometries->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _scripts->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _sounds->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _textures->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); } diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 752e205a6a..7bbf92462b 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -23,8 +23,6 @@ #include "HMDToolsDialog.h" #include "LodToolsDialog.h" #include "LoginDialog.h" -#include "MetavoxelEditor.h" -#include "MetavoxelNetworkSimulator.h" #include "OctreeStatsDialog.h" #include "PreferencesDialog.h" #include "ScriptEditorWindow.h" @@ -148,16 +146,6 @@ void DialogsManager::hmdToolsClosed() { _hmdToolsDialog->hide(); } -void DialogsManager::showMetavoxelEditor() { - maybeCreateDialog(_metavoxelEditor); - _metavoxelEditor->raise(); -} - -void DialogsManager::showMetavoxelNetworkSimulator() { - maybeCreateDialog(_metavoxelNetworkSimulator); - _metavoxelNetworkSimulator->raise(); -} - void DialogsManager::showScriptEditor() { maybeCreateDialog(_scriptEditor); _scriptEditor->raise(); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 465c6829ad..8aafda1da8 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -29,8 +29,6 @@ class ChatWindow; class BandwidthDialog; class LodToolsDialog; class LoginDialog; -class MetavoxelEditor; -class MetavoxelNetworkSimulator; class OctreeStatsDialog; class PreferencesDialog; class ScriptEditorWindow; @@ -59,8 +57,6 @@ public slots: void bandwidthDetails(); void lodTools(); void hmdTools(bool showTools); - void showMetavoxelEditor(); - void showMetavoxelNetworkSimulator(); void showScriptEditor(); void showChat(); @@ -95,8 +91,6 @@ private: QPointer _hmdToolsDialog; QPointer _lodToolsDialog; QPointer _loginDialog; - QPointer _metavoxelEditor; - QPointer _metavoxelNetworkSimulator; QPointer _octreeStatsDialog; QPointer _preferencesDialog; QPointer _scriptEditor; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp deleted file mode 100644 index 34c6ec9a30..0000000000 --- a/interface/src/ui/MetavoxelEditor.cpp +++ /dev/null @@ -1,1101 +0,0 @@ -// -// MetavoxelEditor.cpp -// interface/src/ui -// -// Created by Andrzej Kapolka on 1/21/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "Application.h" -#include "MetavoxelEditor.h" - -using namespace std; - -enum GridPlane { - GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ -}; - -const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); - -MetavoxelEditor::MetavoxelEditor(QWidget* parent) : - QWidget(parent, Qt::Tool) { - - setWindowTitle("Metavoxel Editor"); - setAttribute(Qt::WA_DeleteOnClose); - - QVBoxLayout* topLayout = new QVBoxLayout(); - setLayout(topLayout); - - QGroupBox* attributeGroup = new QGroupBox(); - attributeGroup->setTitle("Attributes"); - topLayout->addWidget(attributeGroup); - - QVBoxLayout* attributeLayout = new QVBoxLayout(); - attributeGroup->setLayout(attributeLayout); - - attributeLayout->addWidget(_attributes = new QListWidget()); - connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(selectedAttributeChanged())); - - QHBoxLayout* attributeButtonLayout = new QHBoxLayout(); - attributeLayout->addLayout(attributeButtonLayout); - - QPushButton* newAttribute = new QPushButton("New..."); - attributeButtonLayout->addWidget(newAttribute, 1); - connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute())); - - attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"), 1); - _deleteAttribute->setEnabled(false); - connect(_deleteAttribute, SIGNAL(clicked()), SLOT(deleteSelectedAttribute())); - - attributeButtonLayout->addWidget(_showAll = new QCheckBox("Show All")); - connect(_showAll, SIGNAL(clicked()), SLOT(updateAttributes())); - - QFormLayout* formLayout = new QFormLayout(); - topLayout->addLayout(formLayout); - - formLayout->addRow("Grid Plane:", _gridPlane = new QComboBox()); - _gridPlane->addItem("X/Y"); - _gridPlane->addItem("X/Z"); - _gridPlane->addItem("Y/Z"); - _gridPlane->setCurrentIndex(GRID_PLANE_XZ); - connect(_gridPlane, SIGNAL(currentIndexChanged(int)), SLOT(centerGridPosition())); - - formLayout->addRow("Grid Spacing:", _gridSpacing = new QDoubleSpinBox()); - _gridSpacing->setMinimum(-FLT_MAX); - _gridSpacing->setMaximum(FLT_MAX); - _gridSpacing->setPrefix("2^"); - _gridSpacing->setValue(0.0); - connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(alignGridPosition())); - - formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox()); - _gridPosition->setMinimum(-FLT_MAX); - _gridPosition->setMaximum(FLT_MAX); - alignGridPosition(); - centerGridPosition(); - - formLayout->addRow("Tool:", _toolBox = new QComboBox()); - connect(_toolBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTool())); - - _value = new QGroupBox(); - _value->setTitle("Value"); - topLayout->addWidget(_value); - - QVBoxLayout* valueLayout = new QVBoxLayout(); - _value->setLayout(valueLayout); - - valueLayout->addWidget(_valueArea = new QScrollArea()); - _valueArea->setMinimumHeight(200); - _valueArea->setWidgetResizable(true); - - addTool(new BoxSetTool(this)); - addTool(new GlobalSetTool(this)); - addTool(new InsertSpannerTool(this)); - addTool(new RemoveSpannerTool(this)); - addTool(new ClearSpannersTool(this)); - addTool(new ImportHeightfieldTool(this)); - addTool(new HeightfieldHeightBrushTool(this)); - addTool(new HeightfieldMaterialBrushTool(this)); - addTool(new HeightfieldSculptBrushTool(this)); - addTool(new HeightfieldFillBrushTool(this)); - addTool(new HeightfieldMaterialBoxTool(this)); - addTool(new HeightfieldMaterialSpannerTool(this)); - - updateAttributes(); - - connect(Application::getInstance(), SIGNAL(simulating(float)), SLOT(simulate(float))); - connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); - connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering, - this, &MetavoxelEditor::renderPreview); - - Application::getInstance()->getGLWidget()->installEventFilter(this); - - show(); - - if (_gridProgram.isLinked()) { - return; - } - - _gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert"); - _gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag"); - _gridProgram.link(); -} - -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 GRID_PLANE_XZ: - return glm::angleAxis(-PI_OVER_TWO, glm::vec3(1.0f, 0.0f, 0.0f)); - - case GRID_PLANE_YZ: - default: - return glm::angleAxis(PI_OVER_TWO, glm::vec3(0.0f, 1.0f, 0.0f)); - } -} - -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 - 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) && (tool->isUserFacing() || _showAll->isChecked())) { - _toolBox->addItem(tool->objectName(), QVariant::fromValue(tool)); - } - } - _value->setVisible(true); - - if (_valueArea->widget()) { - delete _valueArea->widget(); - } - QWidget* editor = attribute->createEditor(); - if (editor) { - editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); - _valueArea->setWidget(editor); - } - updateTool(); -} - -void MetavoxelEditor::createNewAttribute() { - QDialog dialog(this); - dialog.setWindowTitle("New Attribute"); - - QVBoxLayout layout; - dialog.setLayout(&layout); - - QFormLayout form; - layout.addLayout(&form); - - QLineEdit name; - form.addRow("Name:", &name); - - SharedObjectEditor editor(&Attribute::staticMetaObject, false); - editor.setObject(new FloatAttribute()); - layout.addWidget(&editor); - - QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - dialog.connect(&buttons, SIGNAL(accepted()), SLOT(accept())); - dialog.connect(&buttons, SIGNAL(rejected()), SLOT(reject())); - - layout.addWidget(&buttons); - - if (!dialog.exec()) { - return; - } - QString nameText = name.text().trimmed(); - 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() { - const float CENTER_OFFSET = 0.625f; - float eyePosition = (glm::inverse(getGridRotation()) * Application::getInstance()->getCamera()->getPosition()).z - - DependencyManager::get()->getMyAvatar()->getScale() * CENTER_OFFSET; - double step = getGridSpacing(); - _gridPosition->setValue(step * floor(eyePosition / step)); -} - -void MetavoxelEditor::alignGridPosition() { - // make sure our grid position matches our grid spacing - double step = getGridSpacing(); - _gridPosition->setSingleStep(step); - _gridPosition->setValue(step * floor(_gridPosition->value() / step)); -} - -void MetavoxelEditor::updateAttributes(const QString& select) { - // remember the selection in order to preserve it - QString selected = select.isNull() ? getSelectedAttribute() : select; - _attributes->clear(); - - // sort the names for consistent ordering - QList names; - if (_showAll->isChecked()) { - names = AttributeRegistry::getInstance()->getAttributes().keys(); - - } else { - foreach (const AttributePointer& attribute, AttributeRegistry::getInstance()->getAttributes()) { - if (attribute->isUserFacing()) { - names.append(attribute->getName()); - } - } - } - qSort(names); - - foreach (const QString& name, names) { - QListWidgetItem* item = new QListWidgetItem(name); - _attributes->addItem(item); - if (name == selected || selected.isNull()) { - item->setSelected(true); - selected = name; - } - } -} - -void MetavoxelEditor::updateTool() { - MetavoxelTool* active = getActiveTool(); - foreach (MetavoxelTool* tool, _tools) { - tool->setVisible(tool == active); - } - _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() { - glDisable(GL_LIGHTING); - - MetavoxelTool* tool = getActiveTool(); - if (tool) { - tool->render(); - if (!tool->getUsesGrid()) { - return; - } - } - - glDepthMask(GL_FALSE); - - glPushMatrix(); - - glm::quat rotation = getGridRotation(); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - glLineWidth(1.0f); - - // center the grid around the camera position on the plane - 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), _gridPosition->value()); - - float scale = GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - - _gridProgram.bind(); - - DependencyManager::get()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS, glm::vec4(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, 1.0f)); - - _gridProgram.release(); - - glPopMatrix(); - - glEnable(GL_LIGHTING); - glDepthMask(GL_TRUE); -} - -void MetavoxelEditor::renderPreview() { - MetavoxelTool* tool = getActiveTool(); - if (tool) { - tool->renderPreview(); - } -} - -void MetavoxelEditor::addTool(MetavoxelTool* tool) { - _tools.append(tool); - layout()->addWidget(tool); -} - -MetavoxelTool* MetavoxelEditor::getActiveTool() const { - int index = _toolBox->currentIndex(); - return (index == -1) ? NULL : static_cast(_toolBox->itemData(index).value()); -} - -ProgramObject MetavoxelEditor::_gridProgram; - -MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing, bool usesGrid) : - _editor(editor), - _usesValue(usesValue), - _userFacing(userFacing), - _usesGrid(usesGrid) { - - 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::simulate(float deltaTime) { - // nothing by default -} - -void MetavoxelTool::render() { - // nothing by default -} - -void MetavoxelTool::renderPreview() { - // nothing by default -} - -BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) : - MetavoxelTool(editor, name, usesValue, userFacing) { - - resetState(); -} - -void BoxTool::render() { - if (Application::getInstance()->isMouseHidden()) { - resetState(); - return; - } - QString selected = _editor->getSelectedAttribute(); - if (selected.isNull()) { - resetState(); - return; - } - glDepthMask(GL_FALSE); - - glPushMatrix(); - - glm::quat rotation = _editor->getGridRotation(); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(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 = shouldSnapToGrid() ? _editor->getGridSpacing() : 0.0f; - 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 == 0.0f ? projection : 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 == 0.0f) ? _mousePosition : 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(); - } - - 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 = getColor(); - glm::vec4 cubeColor; - if (color.isValid()) { - cubeColor = glm::vec4(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA); - } else { - cubeColor = glm::vec4(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA); - } - glEnable(GL_CULL_FACE); - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); - glDisable(GL_CULL_FACE); - } - DependencyManager::get()->renderWireCube(1.0f, glm::vec4(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, 1.0f)); - glPopMatrix(); - } - - glPopMatrix(); -} - -bool BoxTool::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 = shouldSnapToGrid() ? _editor->getGridSpacing() : 0.0f; - 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; -} - -bool BoxTool::shouldSnapToGrid() { - return true; -} - -void BoxTool::resetState() { - _state = HOVERING_STATE; - _startPosition = INVALID_VECTOR; - _height = 0.0f; -} - -BoxSetTool::BoxSetTool(MetavoxelEditor* editor) : - BoxTool(editor, "Set Value (Box)", true, false) { -} - -QColor BoxSetTool::getColor() { - return _editor->getValue().value(); -} - -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(_editor->getValue())); - MetavoxelEditMessage message = { QVariant::fromValue(BoxSetEdit(Box(minimum, maximum), - _editor->getGridSpacing(), value)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message); -} - -GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Set Value (Global)", true, false) { - - QPushButton* button = new QPushButton("Apply"); - layout()->addWidget(button); - connect(button, SIGNAL(clicked()), SLOT(apply())); -} - -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); -} - -PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText, bool usesValue) : - MetavoxelTool(editor, name, usesValue) { - - QWidget* widget = new QWidget(this); - layout()->addWidget(widget); - QHBoxLayout* box = new QHBoxLayout(); - widget->setLayout(box); - box->setContentsMargins(QMargins()); - box->addStretch(1); - box->addWidget(_followMouse = new QCheckBox("Follow Mouse")); - _followMouse->setChecked(true); - box->addStretch(1); - - if (!placeText.isEmpty()) { - QPushButton* button = new QPushButton(placeText); - layout()->addWidget(button); - connect(button, SIGNAL(clicked()), SLOT(place())); - } -} - -void PlaceSpannerTool::simulate(float deltaTime) { - Spanner* spanner = static_cast(getSpanner().data()); - Transformable* transformable = qobject_cast(spanner); - if (transformable && _followMouse->isChecked() && !Application::getInstance()->isMouseHidden()) { - // 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)); - } - spanner->getRenderer()->simulate(deltaTime); -} - -void PlaceSpannerTool::renderPreview() { - Spanner* spanner = static_cast(getSpanner().data()); - spanner->getRenderer()->render(Application::getInstance()->getMetavoxels()->getLOD()); -} - -bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SpannerSetAttribute"); -} - -bool PlaceSpannerTool::eventFilter(QObject* watched, QEvent* event) { - if (event->type() == QEvent::MouseButtonPress) { - place(); - return true; - } - return false; -} - -SharedObjectPointer PlaceSpannerTool::getSpanner() { - return _editor->getValue().value(); -} - -QColor PlaceSpannerTool::getColor() { - return Qt::white; -} - -void PlaceSpannerTool::place() { - AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); - if (attribute) { - applyEdit(attribute, getSpanner()->clone()); - } -} - -InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : - PlaceSpannerTool(editor, "Insert Spanner", "Insert") { -} - -void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { - MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); -} - -RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Remove Spanner", false, true, false) { -} - -bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SpannerSetAttribute"); -} - -bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) { - AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); - if (!attribute) { - return false; - } - if (event->type() == QEvent::MouseButtonPress) { - float distance; - SharedObjectPointer spanner = Application::getInstance()->getMetavoxels()->findFirstRaySpannerIntersection( - Application::getInstance()->getMouseRayOrigin(), Application::getInstance()->getMouseRayDirection(), - attribute, distance); - if (spanner) { - MetavoxelEditMessage message = { QVariant::fromValue(RemoveSpannerEdit(attribute, spanner->getRemoteID())) }; - Application::getInstance()->getMetavoxels()->applyEdit(message); - } - return true; - } - return false; -} - -ClearSpannersTool::ClearSpannersTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Clear Spanners", false, true, 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("SpannerSetAttribute"); -} - -void ClearSpannersTool::clear() { - AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); - if (!attribute) { - return; - } - MetavoxelEditMessage message = { QVariant::fromValue(ClearSpannersEdit(attribute)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message); -} - -HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : - MetavoxelTool(editor, name, false, true, false) { - - QWidget* widget = new QWidget(); - widget->setLayout(_form = new QFormLayout()); - layout()->addWidget(widget); - - _form->addRow("Translation:", _translation = new Vec3Editor(widget)); - _form->addRow("Spacing:", _spacing = new QDoubleSpinBox()); - _spacing->setMaximum(FLT_MAX); - _spacing->setDecimals(3); - _spacing->setSingleStep(0.001); - _spacing->setValue(1.0); - - QPushButton* applyButton = new QPushButton("Apply"); - layout()->addWidget(applyButton); - connect(applyButton, &QAbstractButton::clicked, this, &HeightfieldTool::apply); -} - -bool HeightfieldTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SpannerSetAttribute"); -} - -ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : - HeightfieldTool(editor, "Import Heightfield"), - _spanner(new Heightfield()) { - - _form->addRow("Height Scale:", _heightScale = new QDoubleSpinBox()); - _heightScale->setMaximum(FLT_MAX); - _heightScale->setSingleStep(0.01); - _heightScale->setValue(16.0); - connect(_heightScale, static_cast(&QDoubleSpinBox::valueChanged), this, - &ImportHeightfieldTool::updateSpanner); - - _form->addRow("Height Offset:", _heightOffset = new QDoubleSpinBox()); - _heightOffset->setMinimum(-FLT_MAX); - _heightOffset->setMaximum(FLT_MAX); - _heightOffset->setSingleStep(0.01); - connect(_heightOffset, static_cast(&QDoubleSpinBox::valueChanged), this, - &ImportHeightfieldTool::updateSpanner); - - _form->addRow("Height:", _height = new HeightfieldHeightEditor(this)); - connect(_height, &HeightfieldHeightEditor::heightChanged, this, &ImportHeightfieldTool::updateSpanner); - - _form->addRow("Color:", _color = new HeightfieldColorEditor(this)); - connect(_color, &HeightfieldColorEditor::colorChanged, this, &ImportHeightfieldTool::updateSpanner); - - connect(_translation, &Vec3Editor::valueChanged, this, &ImportHeightfieldTool::updateSpanner); - connect(_spacing, static_cast(&QDoubleSpinBox::valueChanged), this, - &ImportHeightfieldTool::updateSpanner); -} - -void ImportHeightfieldTool::simulate(float deltaTime) { - static_cast(_spanner.data())->getRenderer()->simulate(deltaTime); -} - -void ImportHeightfieldTool::renderPreview() { - static_cast(_spanner.data())->getRenderer()->render(Application::getInstance()->getMetavoxels()->getLOD()); -} - -void ImportHeightfieldTool::apply() { - AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); - if (!(_height->getHeight() && attribute)) { - return; - } - MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, _spanner->clone())) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); -} - -void ImportHeightfieldTool::updateSpanner() { - Heightfield* heightfield = static_cast(_spanner.data()); - heightfield->setHeight(_height->getHeight()); - heightfield->setColor(_color->getColor()); - - float scale = 1.0f; - float aspectZ = 1.0f; - if (_height->getHeight()) { - int width = _height->getHeight()->getWidth(); - int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeight = _height->getHeight()->getContents().size() / width - HeightfieldHeight::HEIGHT_EXTENSION; - scale = innerWidth * _spacing->value(); - aspectZ = (float)innerHeight / innerWidth; - } - heightfield->setScale(scale); - heightfield->setAspectY(_heightScale->value() / scale); - heightfield->setAspectZ(aspectZ); - heightfield->setTranslation(_translation->getValue() + glm::vec3(0.0f, _heightOffset->value(), 0.0f)); -} - -HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) : - MetavoxelTool(editor, name, false, true, false), - _positionValid(false) { - - QWidget* widget = new QWidget(); - widget->setLayout(_form = new QFormLayout()); - layout()->addWidget(widget); - - _form->addRow("Radius:", _radius = new QDoubleSpinBox()); - _radius->setSingleStep(0.01); - _radius->setMaximum(FLT_MAX); - _radius->setValue(5.0); - - _form->addRow("Granularity:", _granularity = new QDoubleSpinBox()); - _granularity->setMinimum(-FLT_MAX); - _granularity->setMaximum(FLT_MAX); - _granularity->setPrefix("2^"); - _granularity->setValue(8.0); -} - -bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SpannerSetAttribute"); -} - -void HeightfieldBrushTool::render() { - if (Application::getInstance()->isMouseHidden()) { - return; - } - - // find the intersection with the heightfield - glm::vec3 origin = Application::getInstance()->getMouseRayOrigin(); - glm::vec3 direction = Application::getInstance()->getMouseRayDirection(); - - float distance; - if (!Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection(origin, direction, distance)) { - _positionValid = false; - return; - } - _positionValid = true; - Application::getInstance()->getMetavoxels()->renderHeightfieldCursor( - _position = origin + distance * direction, _radius->value()); -} - -bool HeightfieldBrushTool::eventFilter(QObject* watched, QEvent* event) { - if (event->type() == QEvent::Wheel) { - float angle = static_cast(event)->angleDelta().y(); - const float ANGLE_SCALE = 1.0f / 1000.0f; - _radius->setValue(_radius->value() * pow(2.0f, angle * ANGLE_SCALE)); - return true; - - } else if (event->type() == QEvent::MouseButtonPress && _positionValid) { - MetavoxelEditMessage message = { createEdit(static_cast(event)->button() == Qt::RightButton) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - return true; - } - return false; -} - -HeightfieldHeightBrushTool::HeightfieldHeightBrushTool(MetavoxelEditor* editor) : - HeightfieldBrushTool(editor, "Height Brush") { - - _form->addRow("Height:", _height = new QDoubleSpinBox()); - _height->setMinimum(-FLT_MAX); - _height->setMaximum(FLT_MAX); - _height->setValue(1.0); - - _form->addRow("Mode:", _mode = new QComboBox()); - _mode->addItem("Raise/Lower"); - _mode->addItem("Set"); - _mode->addItem("Erase"); -} - -QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) { - const int SET_MODE_INDEX = 1; - const int ERASE_MODE_INDEX = 2; - return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(), - alternate ? -_height->value() : _height->value(), _mode->currentIndex() == SET_MODE_INDEX, - _mode->currentIndex() == ERASE_MODE_INDEX, pow(2.0f, _granularity->value()))); -} - -MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool clearable) : - QObject(widget) { - - QHBoxLayout* colorLayout = new QHBoxLayout(); - form->addRow(colorLayout); - colorLayout->addWidget(new QLabel("Color:")); - colorLayout->addWidget(_color = new QColorEditor(widget), 1); - connect(_color, &QColorEditor::colorChanged, this, &MaterialControl::clearTexture); - if (clearable) { - QPushButton* eraseButton = new QPushButton("Erase"); - colorLayout->addWidget(eraseButton); - connect(eraseButton, &QPushButton::clicked, this, &MaterialControl::clearColor); - } - - form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false)); - connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &MaterialControl::updateTexture); -} - -SharedObjectPointer MaterialControl::getMaterial() { - SharedObjectPointer material = _materialEditor->getObject(); - if (static_cast(material.data())->getDiffuse().isValid()) { - material = material->clone(); - - } else { - material = SharedObjectPointer(); - } - return material; -} - -void MaterialControl::clearColor() { - _color->setColor(QColor(0, 0, 0, 0)); - clearTexture(); -} - -void MaterialControl::clearTexture() { - _materialEditor->setObject(new MaterialObject()); -} - -void MaterialControl::updateTexture() { - if (_texture) { - _texture->disconnect(this); - } - MaterialObject* material = static_cast(_materialEditor->getObject().data()); - if (!material->getDiffuse().isValid()) { - _texture.clear(); - return; - } - _texture = DependencyManager::get()->getTexture(material->getDiffuse(), SPLAT_TEXTURE); - if (_texture) { - if (_texture->isLoaded()) { - textureLoaded(); - } else { - connect(_texture.data(), &Resource::loaded, this, &MaterialControl::textureLoaded); - } - } -} - -void MaterialControl::textureLoaded() { - _color->setColor(_texture->getAverageColor()); -} - -HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* editor) : - HeightfieldBrushTool(editor, "Material Brush"), - _materialControl(new MaterialControl(this, _form)) { -} - -QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) { - Sphere* sphere = new Sphere(); - sphere->setTranslation(_position); - sphere->setScale(_radius->value()); - if (alternate) { - return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - SharedObjectPointer(), QColor(0, 0, 0, 0), true, false, pow(2.0f, _granularity->value()))); - } else { - return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - _materialControl->getMaterial(), _materialControl->getColor(), - true, false, pow(2.0f, _granularity->value()))); - } -} - -HeightfieldSculptBrushTool::HeightfieldSculptBrushTool(MetavoxelEditor* editor) : - HeightfieldBrushTool(editor, "Sculpt Brush"), - _materialControl(new MaterialControl(this, _form, true)) { -} - -QVariant HeightfieldSculptBrushTool::createEdit(bool alternate) { - Sphere* sphere = new Sphere(); - sphere->setTranslation(_position); - sphere->setScale(_radius->value()); - if (alternate) { - return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - SharedObjectPointer(), QColor(0, 0, 0, 0), false, false, pow(2.0f, _granularity->value()))); - } else { - return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - _materialControl->getMaterial(), _materialControl->getColor(), - false, false, pow(2.0f, _granularity->value()))); - } -} - -HeightfieldFillBrushTool::HeightfieldFillBrushTool(MetavoxelEditor* editor) : - HeightfieldBrushTool(editor, "Fill Brush") { - - _form->addRow("Mode:", _mode = new QComboBox()); - _mode->addItem("Fill"); - _mode->addItem("Voxelize"); -} - -QVariant HeightfieldFillBrushTool::createEdit(bool alternate) { - const int FILL_MODE_INDEX = 0; - if (_mode->currentIndex() == FILL_MODE_INDEX) { - return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value(), - pow(2.0f, _granularity->value()))); - } - Sphere* sphere = new Sphere(); - sphere->setTranslation(_position); - sphere->setScale(_radius->value()); - return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - SharedObjectPointer(), QColor(), false, true, pow(2.0f, _granularity->value()))); -} - -HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) : - BoxTool(editor, "Set Material (Box)", false) { - - QWidget* widget = new QWidget(); - QFormLayout* form = new QFormLayout(); - widget->setLayout(form); - layout()->addWidget(widget); - - QHBoxLayout* gridLayout = new QHBoxLayout(); - gridLayout->addStretch(1); - gridLayout->addWidget(_snapToGrid = new QCheckBox("Snap to Grid")); - gridLayout->addStretch(1); - form->addRow(gridLayout); - _snapToGrid->setChecked(true); - - _materialControl = new MaterialControl(this, form, true); - - form->addRow("Granularity:", _granularity = new QDoubleSpinBox()); - _granularity->setMinimum(-FLT_MAX); - _granularity->setMaximum(FLT_MAX); - _granularity->setPrefix("2^"); - _granularity->setValue(8.0); -} - -bool HeightfieldMaterialBoxTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SpannerSetAttribute"); -} - -bool HeightfieldMaterialBoxTool::shouldSnapToGrid() { - return _snapToGrid->isChecked(); -} - -QColor HeightfieldMaterialBoxTool::getColor() { - return _materialControl->getColor(); -} - -void HeightfieldMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { - Cuboid* cuboid = new Cuboid(); - cuboid->setTranslation((maximum + minimum) * 0.5f); - glm::vec3 vector = (maximum - minimum) * 0.5f; - cuboid->setScale(vector.x); - cuboid->setAspectY(vector.y / vector.x); - cuboid->setAspectZ(vector.z / vector.x); - MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(cuboid), - _materialControl->getMaterial(), _materialControl->getColor(), false, false, pow(2.0f, _granularity->value()))) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); -} - -HeightfieldMaterialSpannerTool::HeightfieldMaterialSpannerTool(MetavoxelEditor* editor) : - PlaceSpannerTool(editor, "Set Material (Spanner)", QString(), false) { - - QWidget* widget = new QWidget(); - QFormLayout* form = new QFormLayout(); - widget->setLayout(form); - layout()->addWidget(widget); - - form->addRow(_spannerEditor = new SharedObjectEditor(&Spanner::staticMetaObject, false, this)); - _spannerEditor->setObject(new Sphere()); - - _materialControl = new MaterialControl(this, form, true); - - form->addRow("Granularity:", _granularity = new QDoubleSpinBox()); - _granularity->setMinimum(-FLT_MAX); - _granularity->setMaximum(FLT_MAX); - _granularity->setPrefix("2^"); - _granularity->setValue(8.0); - - QPushButton* place = new QPushButton("Set"); - layout()->addWidget(place); - connect(place, &QPushButton::clicked, this, &HeightfieldMaterialSpannerTool::place); -} - -bool HeightfieldMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SpannerSetAttribute"); -} - -SharedObjectPointer HeightfieldMaterialSpannerTool::getSpanner() { - return _spannerEditor->getObject(); -} - -QColor HeightfieldMaterialSpannerTool::getColor() { - return _materialControl->getColor(); -} - -void HeightfieldMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { - static_cast(spanner.data())->setWillBeVoxelized(true); - MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, - _materialControl->getMaterial(), _materialControl->getColor(), false, false, pow(2.0f, _granularity->value()))) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); -} - diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h deleted file mode 100644 index 2a44a2d7d7..0000000000 --- a/interface/src/ui/MetavoxelEditor.h +++ /dev/null @@ -1,479 +0,0 @@ -// -// MetavoxelEditor.h -// interface/src/ui -// -// Created by Andrzej Kapolka on 1/21/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelEditor_h -#define hifi_MetavoxelEditor_h - -#include -#include -#include - -#include - -#include "MetavoxelSystem.h" - -class QColorEditor; -class QComboBox; -class QDoubleSpinBox; -class QGroupBox; -class QListWidget; -class QPushButton; -class QScrollArea; -class QSpinBox; - -class MetavoxelTool; -class SharedObjectEditor; -class Vec3Editor; - -/// Allows editing metavoxels. -class MetavoxelEditor : public QWidget { - Q_OBJECT - -public: - - MetavoxelEditor(QWidget* parent = nullptr); - - 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: - - void selectedAttributeChanged(); - void createNewAttribute(); - void deleteSelectedAttribute(); - void centerGridPosition(); - void alignGridPosition(); - void updateAttributes(const QString& select = QString()); - void updateTool(); - - void simulate(float deltaTime); - void render(); - void renderPreview(); - -private: - - void addTool(MetavoxelTool* tool); - MetavoxelTool* getActiveTool() const; - - QListWidget* _attributes; - QPushButton* _deleteAttribute; - QCheckBox* _showAll; - - QComboBox* _gridPlane; - QDoubleSpinBox* _gridSpacing; - QDoubleSpinBox* _gridPosition; - - QList _tools; - QComboBox* _toolBox; - - QGroupBox* _value; - QScrollArea* _valueArea; - - static ProgramObject _gridProgram; -}; - -/// Base class for editor tools. -class MetavoxelTool : public QWidget { - Q_OBJECT - -public: - - MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, - bool userFacing = true, bool usesGrid = true); - - bool getUsesValue() const { return _usesValue; } - - bool isUserFacing() const { return _userFacing; } - - bool getUsesGrid() const { return _usesGrid; } - - virtual bool appliesTo(const AttributePointer& attribute) const; - - virtual void simulate(float deltaTime); - - /// Renders the tool's interface, if any. - virtual void render(); - - /// Renders the tool's metavoxel preview, if any. - virtual void renderPreview(); - -protected: - - MetavoxelEditor* _editor; - bool _usesValue; - bool _userFacing; - bool _usesGrid; -}; - -/// Base class for tools that allow dragging out a 3D box. -class BoxTool : public MetavoxelTool { - Q_OBJECT - -public: - - BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true); - - virtual void render(); - - virtual bool eventFilter(QObject* watched, QEvent* event); - -protected: - - virtual bool shouldSnapToGrid(); - - virtual QColor getColor() = 0; - - virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum) = 0; - -private: - - void resetState(); - - 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 of a region by dragging out a box. -class BoxSetTool : public BoxTool { - Q_OBJECT - -public: - - BoxSetTool(MetavoxelEditor* editor); - -protected: - - virtual QColor getColor(); - - virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); -}; - -/// Allows setting the value across the entire space. -class GlobalSetTool : public MetavoxelTool { - Q_OBJECT - -public: - - GlobalSetTool(MetavoxelEditor* editor); - -private slots: - - void apply(); -}; - -/// Base class for insert/set spanner tools. -class PlaceSpannerTool : public MetavoxelTool { - Q_OBJECT - -public: - - PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, - const QString& placeText = QString(), bool usesValue = true); - - virtual void simulate(float deltaTime); - - virtual void renderPreview(); - - virtual bool appliesTo(const AttributePointer& attribute) const; - - virtual bool eventFilter(QObject* watched, QEvent* event); - -protected: - - virtual QColor getColor(); - virtual SharedObjectPointer getSpanner(); - virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) = 0; - -protected slots: - - void place(); - -private: - - QCheckBox* _followMouse; -}; - -/// Allows inserting a spanner into the scene. -class InsertSpannerTool : public PlaceSpannerTool { - Q_OBJECT - -public: - - InsertSpannerTool(MetavoxelEditor* editor); - -protected: - - virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); -}; - -/// Allows removing a spanner from the scene. -class RemoveSpannerTool : public MetavoxelTool { - Q_OBJECT - -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(); -}; - -/// Base class for heightfield tools. -class HeightfieldTool : public MetavoxelTool { - Q_OBJECT - -public: - - HeightfieldTool(MetavoxelEditor* editor, const QString& name); - - virtual bool appliesTo(const AttributePointer& attribute) const; - -protected slots: - - virtual void apply() = 0; - -protected: - - QFormLayout* _form; - Vec3Editor* _translation; - QDoubleSpinBox* _spacing; -}; - -/// Allows importing a heightfield. -class ImportHeightfieldTool : public HeightfieldTool { - Q_OBJECT - -public: - - ImportHeightfieldTool(MetavoxelEditor* editor); - - virtual void simulate(float deltaTime); - - virtual void renderPreview(); - -protected: - - virtual void apply(); - -private slots: - - void updateSpanner(); - -private: - - QDoubleSpinBox* _heightScale; - QDoubleSpinBox* _heightOffset; - - HeightfieldHeightEditor* _height; - HeightfieldColorEditor* _color; - - SharedObjectPointer _spanner; -}; - -/// Base class for tools that allow painting on heightfields. -class HeightfieldBrushTool : public MetavoxelTool { - Q_OBJECT - -public: - - HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name); - - virtual bool appliesTo(const AttributePointer& attribute) const; - - virtual void render(); - - virtual bool eventFilter(QObject* watched, QEvent* event); - -protected: - - virtual QVariant createEdit(bool alternate) = 0; - - QFormLayout* _form; - QDoubleSpinBox* _radius; - QDoubleSpinBox* _granularity; - - glm::vec3 _position; - bool _positionValid; -}; - -/// Allows raising or lowering parts of the heightfield. -class HeightfieldHeightBrushTool : public HeightfieldBrushTool { - Q_OBJECT - -public: - - HeightfieldHeightBrushTool(MetavoxelEditor* editor); - -protected: - - virtual QVariant createEdit(bool alternate); - -private: - - QDoubleSpinBox* _height; - QComboBox* _mode; -}; - -/// Contains widgets for editing materials. -class MaterialControl : public QObject { - Q_OBJECT - -public: - - MaterialControl(QWidget* widget, QFormLayout* form, bool clearable = false); - - SharedObjectPointer getMaterial(); - - const QColor& getColor() const { return _color->getColor(); } - -private slots: - - void clearColor(); - void clearTexture(); - void updateTexture(); - void textureLoaded(); - -private: - - QColorEditor* _color; - SharedObjectEditor* _materialEditor; - QSharedPointer _texture; -}; - -/// Allows texturing parts of the heightfield. -class HeightfieldMaterialBrushTool : public HeightfieldBrushTool { - Q_OBJECT - -public: - - HeightfieldMaterialBrushTool(MetavoxelEditor* editor); - -protected: - - virtual QVariant createEdit(bool alternate); - -private: - - MaterialControl* _materialControl; -}; - -/// Allows sculpting parts of the heightfield. -class HeightfieldSculptBrushTool : public HeightfieldBrushTool { - Q_OBJECT - -public: - - HeightfieldSculptBrushTool(MetavoxelEditor* editor); - -protected: - - virtual QVariant createEdit(bool alternate); - -private: - - MaterialControl* _materialControl; -}; - -/// Allows "filling" (removing dual contour stack data) parts of the heightfield. -class HeightfieldFillBrushTool : public HeightfieldBrushTool { - Q_OBJECT - -public: - - HeightfieldFillBrushTool(MetavoxelEditor* editor); - -protected: - - virtual QVariant createEdit(bool alternate); - -private: - - QComboBox* _mode; -}; - -/// Allows setting heightfield materials by dragging out a box. -class HeightfieldMaterialBoxTool : public BoxTool { - Q_OBJECT - -public: - - HeightfieldMaterialBoxTool(MetavoxelEditor* editor); - - virtual bool appliesTo(const AttributePointer& attribute) const; - -protected: - - virtual bool shouldSnapToGrid(); - - virtual QColor getColor(); - - virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); - -private: - - QCheckBox* _snapToGrid; - MaterialControl* _materialControl; - QDoubleSpinBox* _granularity; -}; - -/// Allows setting heightfield materials by placing a spanner. -class HeightfieldMaterialSpannerTool : public PlaceSpannerTool { - Q_OBJECT - -public: - - HeightfieldMaterialSpannerTool(MetavoxelEditor* editor); - - virtual bool appliesTo(const AttributePointer& attribute) const; - -protected: - - virtual SharedObjectPointer getSpanner(); - virtual QColor getColor(); - virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); - -private: - - SharedObjectEditor* _spannerEditor; - MaterialControl* _materialControl; - QDoubleSpinBox* _granularity; -}; - -#endif // hifi_MetavoxelEditor_h diff --git a/interface/src/ui/MetavoxelNetworkSimulator.cpp b/interface/src/ui/MetavoxelNetworkSimulator.cpp deleted file mode 100644 index dfc9f4be25..0000000000 --- a/interface/src/ui/MetavoxelNetworkSimulator.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// -// MetavoxelNetworkSimulator.cpp -// interface/src/ui -// -// Created by Andrzej Kapolka on 10/20/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include - -#include "Application.h" -#include "MetavoxelNetworkSimulator.h" - -const int BYTES_PER_KILOBYTE = 1024; - -MetavoxelNetworkSimulator::MetavoxelNetworkSimulator(QWidget* parent) : - QWidget(parent, Qt::Dialog) { - - setWindowTitle("Metavoxel Network Simulator"); - setAttribute(Qt::WA_DeleteOnClose); - - QVBoxLayout* topLayout = new QVBoxLayout(); - setLayout(topLayout); - - QFormLayout* form = new QFormLayout(); - topLayout->addLayout(form); - - MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); - - form->addRow("Drop Rate:", _dropRate = new QDoubleSpinBox()); - _dropRate->setSuffix("%"); - _dropRate->setValue(simulation.dropRate * 100.0); - connect(_dropRate, static_cast(&QDoubleSpinBox::valueChanged), this, - &MetavoxelNetworkSimulator::updateMetavoxelSystem); - - form->addRow("Repeat Rate:", _repeatRate = new QDoubleSpinBox()); - _repeatRate->setSuffix("%"); - _repeatRate->setValue(simulation.repeatRate * 100.0); - connect(_repeatRate, static_cast(&QDoubleSpinBox::valueChanged), this, - &MetavoxelNetworkSimulator::updateMetavoxelSystem); - - form->addRow("Minimum Delay:", _minimumDelay = new QSpinBox()); - _minimumDelay->setMaximum(1000); - _minimumDelay->setSuffix("ms"); - _minimumDelay->setValue(simulation.minimumDelay); - connect(_minimumDelay, static_cast(&QSpinBox::valueChanged), this, - &MetavoxelNetworkSimulator::updateMetavoxelSystem); - - form->addRow("Maximum Delay:", _maximumDelay = new QSpinBox()); - _maximumDelay->setMaximum(1000); - _maximumDelay->setSuffix("ms"); - _maximumDelay->setValue(simulation.maximumDelay); - connect(_maximumDelay, static_cast(&QSpinBox::valueChanged), this, - &MetavoxelNetworkSimulator::updateMetavoxelSystem); - - form->addRow("Bandwidth Limit:", _bandwidthLimit = new QSpinBox()); - _bandwidthLimit->setMaximum(1024 * 1024); - _bandwidthLimit->setSuffix("KB/s"); - _bandwidthLimit->setValue(simulation.bandwidthLimit / BYTES_PER_KILOBYTE); - connect(_bandwidthLimit, static_cast(&QSpinBox::valueChanged), this, - &MetavoxelNetworkSimulator::updateMetavoxelSystem); - - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); - topLayout->addWidget(buttons); - connect(buttons, &QDialogButtonBox::accepted, this, &QWidget::close); - - show(); -} - -void MetavoxelNetworkSimulator::updateMetavoxelSystem() { - int bandwidthLimit = _bandwidthLimit->value() * BYTES_PER_KILOBYTE; - if (bandwidthLimit > 0) { - // make sure the limit is enough to let at least one packet through - const int MINIMUM_BANDWIDTH_LIMIT = 2048; - bandwidthLimit = qMax(bandwidthLimit, MINIMUM_BANDWIDTH_LIMIT); - } - Application::getInstance()->getMetavoxels()->setNetworkSimulation(MetavoxelSystem::NetworkSimulation( - _dropRate->value() / 100.0, _repeatRate->value() / 100.0, qMin(_minimumDelay->value(), _maximumDelay->value()), - qMax(_minimumDelay->value(), _maximumDelay->value()), bandwidthLimit)); -} diff --git a/interface/src/ui/MetavoxelNetworkSimulator.h b/interface/src/ui/MetavoxelNetworkSimulator.h deleted file mode 100644 index 211df48c00..0000000000 --- a/interface/src/ui/MetavoxelNetworkSimulator.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// MetavoxelNetworkSimulator.h -// interface/src/ui -// -// Created by Andrzej Kapolka on 10/20/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelNetworkSimulator_h -#define hifi_MetavoxelNetworkSimulator_h - -#include - -class QDoubleSpinBox; -class QSpinBox; - -/// Allows tweaking network simulation (packet drop percentage, etc.) settings for metavoxels. -class MetavoxelNetworkSimulator : public QWidget { - Q_OBJECT - -public: - - MetavoxelNetworkSimulator(QWidget* parent = nullptr); - -private slots: - - void updateMetavoxelSystem(); - -private: - - QDoubleSpinBox* _dropRate; - QDoubleSpinBox* _repeatRate; - QSpinBox* _minimumDelay; - QSpinBox* _maximumDelay; - QSpinBox* _bandwidthLimit; -}; - -#endif // hifi_MetavoxelNetworkSimulator_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 62adcb20e8..ec225dde72 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -55,13 +55,7 @@ Stats::Stats(): _pingStatsWidth(STATS_PING_MIN_WIDTH), _geoStatsWidth(STATS_GEO_MIN_WIDTH), _octreeStatsWidth(STATS_OCTREE_MIN_WIDTH), - _lastHorizontalOffset(0), - _metavoxelInternal(0), - _metavoxelLeaves(0), - _metavoxelSendProgress(0), - _metavoxelSendTotal(0), - _metavoxelReceiveProgress(0), - _metavoxelReceiveTotal(0) + _lastHorizontalOffset(0) { auto glCanvas = Application::getInstance()->getGLWidget(); resetWidth(glCanvas->width(), 0); @@ -460,31 +454,6 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); - - QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels()->getUpdater(), "getStats", - Q_ARG(QObject*, this), Q_ARG(const QByteArray&, "setMetavoxelStats")); - - stringstream nodes; - nodes << "Metavoxels: " << (_metavoxelInternal + _metavoxelLeaves); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodes.str().c_str(), color); - - stringstream nodeTypes; - nodeTypes << "Internal: " << _metavoxelInternal << " Leaves: " << _metavoxelLeaves; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color); - - if (_metavoxelSendTotal > 0 || _metavoxelReceiveTotal > 0) { - stringstream reliableStats; - if (_metavoxelSendTotal > 0) { - reliableStats << "Upload: " << (_metavoxelSendProgress * 100LL / _metavoxelSendTotal) << "% "; - } - if (_metavoxelReceiveTotal > 0) { - reliableStats << "Download: " << (_metavoxelReceiveProgress * 100LL / _metavoxelReceiveTotal) << "%"; - } - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableStats.str().c_str(), color); - } } verticalOffset = STATS_PELS_INITIALOFFSET; @@ -648,12 +617,3 @@ void Stats::display( } } -void Stats::setMetavoxelStats(int internal, int leaves, int sendProgress, - int sendTotal, int receiveProgress, int receiveTotal) { - _metavoxelInternal = internal; - _metavoxelLeaves = leaves; - _metavoxelSendProgress = sendProgress; - _metavoxelSendTotal = sendTotal; - _metavoxelReceiveProgress = receiveProgress; - _metavoxelReceiveTotal = receiveTotal; -} diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index d6da8d8cc6..406496d858 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -33,9 +33,6 @@ public: int inKbitsPerSecond, int outKbitsPerSecond, int voxelPacketsToProcess); bool includeTimingRecord(const QString& name); - Q_INVOKABLE void setMetavoxelStats(int internal, int leaves, int sendProgress, - int sendTotal, int receiveProgress, int receiveTotal); - private: static Stats* _sharedInstance; @@ -52,12 +49,6 @@ private: int _lastHorizontalOffset; - int _metavoxelInternal; - int _metavoxelLeaves; - int _metavoxelSendProgress; - int _metavoxelSendTotal; - int _metavoxelReceiveProgress; - int _metavoxelReceiveTotal; }; #endif // hifi_Stats_h diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt deleted file mode 100644 index 240a2d8853..0000000000 --- a/libraries/metavoxels/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -set(TARGET_NAME metavoxels) - -auto_mtc() - -# use setup_hifi_library macro to setup our project and link appropriate Qt modules -setup_hifi_library(Network Script Widgets) - -# link in the networking library -link_hifi_libraries(shared networking) - -add_dependency_external_projects(glm) -find_package(GLM REQUIRED) -target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) \ No newline at end of file diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp deleted file mode 100644 index b23d83de55..0000000000 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ /dev/null @@ -1,499 +0,0 @@ -// -// AttributeRegistry.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/6/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include - -#include "AttributeRegistry.h" -#include "MetavoxelData.h" -#include "Spanner.h" - -REGISTER_META_OBJECT(FloatAttribute) -REGISTER_META_OBJECT(SharedObjectAttribute) -REGISTER_META_OBJECT(SharedObjectSetAttribute) -REGISTER_META_OBJECT(SpannerSetAttribute) - -static int attributePointerMetaTypeId = qRegisterMetaType(); -static int ownedAttributeValueMetaTypeId = qRegisterMetaType(); - -AttributeRegistry* AttributeRegistry::getInstance() { - static AttributeRegistry registry; - return ®istry; -} - -AttributeRegistry::AttributeRegistry() : - _guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject, - new DefaultMetavoxelGuide()))), - _rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject, - new DefaultMetavoxelRenderer()))), - _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))) { - - // our baseline LOD threshold is for voxels; spanners are a different story - const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 16.0f; - _spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER); - _spannersAttribute->setUserFacing(true); -} - -static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { - QDebug debug = qDebug(); - - for (int i = 0; i < context->argumentCount(); i++) { - debug << context->argument(i).toString(); - } - - return QScriptValue(); -} - -void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) { - QScriptValue registry = engine->newObject(); - registry.setProperty("getAttribute", engine->newFunction(getAttribute, 1)); - engine->globalObject().setProperty("AttributeRegistry", registry); - engine->globalObject().setProperty("qDebug", engine->newFunction(qDebugFunction, 1)); -} - -AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute) { - if (!attribute) { - return attribute; - } - QWriteLocker locker(&_attributesLock); - AttributePointer& pointer = _attributes[attribute->getName()]; - if (!pointer) { - pointer = attribute; - } - return pointer; -} - -void AttributeRegistry::deregisterAttribute(const QString& name) { - QWriteLocker locker(&_attributesLock); - _attributes.remove(name); -} - -AttributePointer AttributeRegistry::getAttribute(const QString& name) { - QReadLocker locker(&_attributesLock); - return _attributes.value(name); -} - -QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) { - return engine->newQObject(getInstance()->getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership, - QScriptEngine::PreferExistingWrapperObject); -} - -AttributeValue::AttributeValue(const AttributePointer& attribute) : - _attribute(attribute), _value(attribute ? attribute->getDefaultValue() : NULL) { -} - -AttributeValue::AttributeValue(const AttributePointer& attribute, void* value) : - _attribute(attribute), _value(value) { -} - -void* AttributeValue::copy() const { - return _attribute->create(_value); -} - -bool AttributeValue::isDefault() const { - return !_attribute || _attribute->equal(_value, _attribute->getDefaultValue()); -} - -bool AttributeValue::operator==(const AttributeValue& other) const { - return _attribute == other._attribute && (!_attribute || _attribute->equal(_value, other._value)); -} - -bool AttributeValue::operator==(void* other) const { - return _attribute && _attribute->equal(_value, other); -} - -bool AttributeValue::operator!=(const AttributeValue& other) const { - return _attribute != other._attribute || (_attribute && !_attribute->equal(_value, other._value)); -} - -bool AttributeValue::operator!=(void* other) const { - return !_attribute || !_attribute->equal(_value, other); -} - -OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) : - AttributeValue(attribute, value) { -} - -OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) : - AttributeValue(attribute, attribute ? attribute->create() : NULL) { -} - -OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) : - AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) { -} - -OwnedAttributeValue::OwnedAttributeValue(const OwnedAttributeValue& other) : - AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) { -} - -OwnedAttributeValue::~OwnedAttributeValue() { - if (_attribute) { - _attribute->destroy(_value); - } -} - -void OwnedAttributeValue::mix(const AttributeValue& first, const AttributeValue& second, float alpha) { - if (_attribute) { - _attribute->destroy(_value); - } - _attribute = first.getAttribute(); - _value = _attribute->mix(first.getValue(), second.getValue(), alpha); -} - -void OwnedAttributeValue::blend(const AttributeValue& source, const AttributeValue& dest) { - if (_attribute) { - _attribute->destroy(_value); - } - _attribute = source.getAttribute(); - _value = _attribute->blend(source.getValue(), dest.getValue()); -} - -OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) { - if (_attribute) { - _attribute->destroy(_value); - } - if ((_attribute = other.getAttribute())) { - _value = other.copy(); - } - return *this; -} - -OwnedAttributeValue& OwnedAttributeValue::operator=(const OwnedAttributeValue& other) { - if (_attribute) { - _attribute->destroy(_value); - } - if ((_attribute = other.getAttribute())) { - _value = other.copy(); - } - return *this; -} - -Attribute::Attribute(const QString& name) : - _lodThresholdMultiplier(1.0f), - _userFacing(false) { - setObjectName(name); -} - -Attribute::~Attribute() { -} - -void Attribute::readSubdivided(MetavoxelStreamState& state, void*& value, - const MetavoxelStreamState& ancestorState, void* ancestorValue, bool isLeaf) const { - read(state.base.stream, value, isLeaf); -} - -void Attribute::writeSubdivided(MetavoxelStreamState& state, void* value, - const MetavoxelStreamState& ancestorState, void* ancestorValue, bool isLeaf) const { - write(state.base.stream, value, isLeaf); -} - -MetavoxelNode* Attribute::createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const { - return new MetavoxelNode(value); -} - -void Attribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { - data.createRoot(state.base.attribute)->read(state); -} - -void Attribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { - root.write(state); -} - -void Attribute::readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { - data.createRoot(state.base.attribute)->readDelta(reference, state); -} - -void Attribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { - root.writeDelta(reference, state); -} - -void Attribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { - // copy if changed - MetavoxelNode* oldRoot = data.getRoot(state.base.attribute); - MetavoxelNode* newRoot = oldRoot->readSubdivision(state); - if (newRoot != oldRoot) { - data.setRoot(state.base.attribute, newRoot); - } -} - -void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { - root.writeSubdivision(state); -} - -bool Attribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, - const glm::vec3& minimum, float size, const MetavoxelLOD& lod) { - return firstRoot.deepEquals(this, secondRoot, minimum, size, lod); -} - -MetavoxelNode* Attribute::expandMetavoxelRoot(const MetavoxelNode& root) { - AttributePointer attribute(this); - MetavoxelNode* newParent = new MetavoxelNode(attribute); - for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - MetavoxelNode* newChild = new MetavoxelNode(attribute); - newParent->setChild(i, newChild); - int index = MetavoxelNode::getOppositeChildIndex(i); - if (root.isLeaf()) { - newChild->setChild(index, new MetavoxelNode(root.getAttributeValue(attribute))); - } else { - MetavoxelNode* grandchild = root.getChild(i); - grandchild->incrementReferenceCount(); - newChild->setChild(index, grandchild); - } - for (int j = 1; j < MetavoxelNode::CHILD_COUNT; j++) { - MetavoxelNode* newGrandchild = new MetavoxelNode(attribute); - newChild->setChild((index + j) % MetavoxelNode::CHILD_COUNT, newGrandchild); - } - } - return newParent; -} - -FloatAttribute::FloatAttribute(const QString& name) : - SimpleInlineAttribute(name) { -} - -SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, - const SharedObjectPointer& defaultValue) : - InlineAttribute(name, defaultValue), - _metaObject(metaObject) { - -} - -void SharedObjectAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (isLeaf) { - in >> *((SharedObjectPointer*)&value); - } -} - -void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (isLeaf) { - out << decodeInline(value); - } -} - -bool SharedObjectAttribute::deepEqual(void* first, void* second) const { - SharedObjectPointer firstObject = decodeInline(first); - SharedObjectPointer secondObject = decodeInline(second); - return firstObject ? firstObject->equals(secondObject) : !secondObject; -} - -bool SharedObjectAttribute::merge(void*& parent, void* children[], bool postRead) const { - SharedObjectPointer firstChild = decodeInline(children[0]); - for (int i = 1; i < MERGE_COUNT; i++) { - if (firstChild != decodeInline(children[i])) { - *(SharedObjectPointer*)&parent = _defaultValue; - return false; - } - } - *(SharedObjectPointer*)&parent = firstChild; - return true; -} - -void* SharedObjectAttribute::createFromVariant(const QVariant& value) const { - return create(encodeInline(value.value())); -} - -QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const { - SharedObjectEditor* editor = new SharedObjectEditor(_metaObject, parent); - editor->setObject(_defaultValue); - return editor; -} - -SharedObjectSetAttribute::SharedObjectSetAttribute(const QString& name, const QMetaObject* metaObject) : - InlineAttribute(name), - _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); -} - -MetavoxelNode* SharedObjectSetAttribute::createMetavoxelNode( - const AttributeValue& value, const MetavoxelNode* original) const { - return new MetavoxelNode(value, original); -} - -static bool setsEqual(const SharedObjectSet& firstSet, const SharedObjectSet& secondSet) { - if (firstSet.size() != secondSet.size()) { - return false; - } - // some hackiness here: we assume that the local ids of the first set correspond to the remote ids of the second, - // so that this will work with the tests - foreach (const SharedObjectPointer& firstObject, firstSet) { - int id = firstObject->getID(); - bool found = false; - foreach (const SharedObjectPointer& secondObject, secondSet) { - if (secondObject->getRemoteID() == id) { - if (!firstObject->equals(secondObject)) { - return false; - } - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; -} - -bool SharedObjectSetAttribute::deepEqual(void* first, void* second) const { - return setsEqual(decodeInline(first), decodeInline(second)); -} - -MetavoxelNode* SharedObjectSetAttribute::expandMetavoxelRoot(const MetavoxelNode& root) { - AttributePointer attribute(this); - MetavoxelNode* newParent = new MetavoxelNode(attribute); - for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - MetavoxelNode* newChild = new MetavoxelNode(root.getAttributeValue(attribute)); - newParent->setChild(i, newChild); - if (root.isLeaf()) { - continue; - } - MetavoxelNode* grandchild = root.getChild(i); - grandchild->incrementReferenceCount(); - int index = MetavoxelNode::getOppositeChildIndex(i); - newChild->setChild(index, grandchild); - for (int j = 1; j < MetavoxelNode::CHILD_COUNT; j++) { - MetavoxelNode* newGrandchild = new MetavoxelNode(attribute); - newChild->setChild((index + j) % MetavoxelNode::CHILD_COUNT, newGrandchild); - } - } - return newParent; -} - -bool SharedObjectSetAttribute::merge(void*& parent, void* children[], bool postRead) const { - for (int i = 0; i < MERGE_COUNT; i++) { - if (!decodeInline(children[i]).isEmpty()) { - return false; - } - } - return true; -} - -AttributeValue SharedObjectSetAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - -QWidget* SharedObjectSetAttribute::createEditor(QWidget* parent) const { - return new SharedObjectEditor(_metaObject, parent); -} - -SpannerSetAttribute::SpannerSetAttribute(const QString& name, const QMetaObject* metaObject) : - SharedObjectSetAttribute(name, metaObject) { -} - -void SpannerSetAttribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { - forever { - SharedObjectPointer object; - state.base.stream >> object; - if (!object) { - break; - } - data.insert(state.base.attribute, object); - } - // even if the root is empty, it should still exist - if (!data.getRoot(state.base.attribute)) { - data.createRoot(state.base.attribute); - } -} - -void SpannerSetAttribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { - state.base.visit = Spanner::getAndIncrementNextVisit(); - root.writeSpanners(state); - state.base.stream << SharedObjectPointer(); -} - -void SpannerSetAttribute::readMetavoxelDelta(MetavoxelData& data, - const MetavoxelNode& reference, MetavoxelStreamState& state) { - readMetavoxelSubdivision(data, state); -} - -static void writeDeltaSubdivision(SharedObjectSet& oldSet, SharedObjectSet& newSet, Bitstream& stream) { - for (SharedObjectSet::iterator newIt = newSet.begin(); newIt != newSet.end(); ) { - SharedObjectSet::iterator oldIt = oldSet.find(*newIt); - if (oldIt == oldSet.end()) { - stream << *newIt; // added - newIt = newSet.erase(newIt); - - } else { - oldSet.erase(oldIt); - newIt++; - } - } - foreach (const SharedObjectPointer& object, oldSet) { - stream << object; // removed - } - stream << SharedObjectPointer(); - foreach (const SharedObjectPointer& object, newSet) { - object->maybeWriteSubdivision(stream); - } - stream << SharedObjectPointer(); -} - -void SpannerSetAttribute::writeMetavoxelDelta(const MetavoxelNode& root, - const MetavoxelNode& reference, MetavoxelStreamState& state) { - SharedObjectSet oldSet, newSet; - reference.getSpanners(this, state.minimum, state.size, state.base.referenceLOD, oldSet); - root.getSpanners(this, state.minimum, state.size, state.base.lod, newSet); - writeDeltaSubdivision(oldSet, newSet, state.base.stream); -} - -void SpannerSetAttribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { - forever { - SharedObjectPointer object; - state.base.stream >> object; - if (!object) { - break; - } - data.toggle(state.base.attribute, object); - } - forever { - SharedObjectPointer object; - state.base.stream >> object; - if (!object) { - break; - } - SharedObjectPointer newObject = object->readSubdivision(state.base.stream); - if (newObject != object) { - data.replace(state.base.attribute, object, newObject); - state.base.stream.addSubdividedObject(newObject); - } - } - // even if the root is empty, it should still exist - if (!data.getRoot(state.base.attribute)) { - data.createRoot(state.base.attribute); - } -} - -void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { - SharedObjectSet oldSet, newSet; - root.getSpanners(this, state.minimum, state.size, state.base.referenceLOD, oldSet); - root.getSpanners(this, state.minimum, state.size, state.base.lod, newSet); - writeDeltaSubdivision(oldSet, newSet, state.base.stream); -} - -bool SpannerSetAttribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, - const glm::vec3& minimum, float size, const MetavoxelLOD& lod) { - - SharedObjectSet firstSet; - firstRoot.getSpanners(this, minimum, size, lod, firstSet); - SharedObjectSet secondSet; - secondRoot.getSpanners(this, minimum, size, lod, secondSet); - return setsEqual(firstSet, secondSet); -} diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h deleted file mode 100644 index ea0ec263e4..0000000000 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ /dev/null @@ -1,409 +0,0 @@ -// -// AttributeRegistry.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/6/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AttributeRegistry_h -#define hifi_AttributeRegistry_h - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Bitstream.h" -#include "SharedObject.h" - -class QScriptContext; -class QScriptEngine; -class QScriptValue; - -class Attribute; -class DataBlock; -class MetavoxelData; -class MetavoxelLOD; -class MetavoxelNode; -class MetavoxelStreamState; - -typedef SharedObjectPointerTemplate AttributePointer; - -Q_DECLARE_METATYPE(AttributePointer) - -/// Maintains information about metavoxel attribute types. -class AttributeRegistry { -public: - - /// Returns a pointer to the singleton registry instance. - static AttributeRegistry* getInstance(); - - AttributeRegistry(); - - /// Configures the supplied script engine with the global AttributeRegistry property. - void configureScriptEngine(QScriptEngine* engine); - - /// Registers an attribute with the system. The registry assumes ownership of the object. - /// \return either the pointer passed as an argument, if the attribute wasn't already registered, or the existing - /// attribute - AttributePointer registerAttribute(Attribute* attribute) { return registerAttribute(AttributePointer(attribute)); } - - /// Registers an attribute with the system. - /// \return either the pointer passed as an argument, if the attribute wasn't already registered, or the existing - /// attribute - AttributePointer registerAttribute(AttributePointer attribute); - - /// Deregisters an attribute. - void deregisterAttribute(const QString& name); - - /// Retrieves an attribute by name. - AttributePointer getAttribute(const QString& name); - - /// Returns a reference to the attribute hash. - const QHash& getAttributes() const { return _attributes; } - - /// Returns a reference to the attributes lock. - QReadWriteLock& getAttributesLock() { return _attributesLock; } - - /// Returns a reference to the standard SharedObjectPointer "guide" attribute. - const AttributePointer& getGuideAttribute() const { return _guideAttribute; } - - /// Returns a reference to the standard SharedObjectPointer "renderer" attribute. - const AttributePointer& getRendererAttribute() const { return _rendererAttribute; } - - /// Returns a reference to the standard SharedObjectSet "spanners" attribute. - const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; } - -private: - - static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); - - QHash _attributes; - QReadWriteLock _attributesLock; - - AttributePointer _guideAttribute; - AttributePointer _rendererAttribute; - AttributePointer _spannersAttribute; -}; - -/// Converts a value to a void pointer. -template inline void* encodeInline(T value) { - return *(void**)&value; -} - -/// Extracts a value from a void pointer. -template inline T decodeInline(void* value) { - return *(T*)&value; -} - -/// Pairs an attribute value with its type. -class AttributeValue { -public: - - AttributeValue(const AttributePointer& attribute = AttributePointer()); - AttributeValue(const AttributePointer& attribute, void* value); - - AttributePointer getAttribute() const { return _attribute; } - void* getValue() const { return _value; } - - template void setInlineValue(T value) { _value = encodeInline(value); } - template T getInlineValue() const { return decodeInline(_value); } - - void* copy() const; - - bool isDefault() const; - - bool operator==(const AttributeValue& other) const; - bool operator==(void* other) const; - - bool operator!=(const AttributeValue& other) const; - bool operator!=(void* other) const; - -protected: - - AttributePointer _attribute; - void* _value; -}; - -// Assumes ownership of an attribute value. -class OwnedAttributeValue : public AttributeValue { -public: - - /// Assumes ownership of the specified value. It will be destroyed when this is destroyed or reassigned. - OwnedAttributeValue(const AttributePointer& attribute, void* value); - - /// Creates an owned attribute with a copy of the specified attribute's default value. - OwnedAttributeValue(const AttributePointer& attribute = AttributePointer()); - - /// Creates an owned attribute with a copy of the specified other value. - OwnedAttributeValue(const AttributeValue& other); - - /// Creates an owned attribute with a copy of the specified other value. - OwnedAttributeValue(const OwnedAttributeValue& other); - - /// Destroys the current value, if any. - ~OwnedAttributeValue(); - - /// Sets this attribute to a mix of the first and second provided. - void mix(const AttributeValue& first, const AttributeValue& second, float alpha); - - /// Sets this attribute to a blend of the source and destination. - void blend(const AttributeValue& source, const AttributeValue& dest); - - /// Destroys the current value, if any, and copies the specified other value. - OwnedAttributeValue& operator=(const AttributeValue& other); - - /// Destroys the current value, if any, and copies the specified other value. - OwnedAttributeValue& operator=(const OwnedAttributeValue& other); -}; - -Q_DECLARE_METATYPE(OwnedAttributeValue) - -/// Represents a registered attribute. -class Attribute : public SharedObject { - Q_OBJECT - Q_PROPERTY(float lodThresholdMultiplier MEMBER _lodThresholdMultiplier) - Q_PROPERTY(bool userFacing MEMBER _userFacing) - -public: - - static const int MERGE_COUNT = 8; - - Attribute(const QString& name); - virtual ~Attribute(); - - Q_INVOKABLE QString getName() const { return objectName(); } - - float getLODThresholdMultiplier() const { return _lodThresholdMultiplier; } - void setLODThresholdMultiplier(float multiplier) { _lodThresholdMultiplier = multiplier; } - - bool isUserFacing() const { return _userFacing; } - void setUserFacing(bool userFacing) { _userFacing = userFacing; } - - void* create() const { return create(getDefaultValue()); } - virtual void* create(void* copy) const = 0; - virtual void destroy(void* value) const = 0; - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const = 0; - virtual void write(Bitstream& out, void* value, bool isLeaf) const = 0; - - virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } - - virtual void readSubdivided(MetavoxelStreamState& state, void*& value, - const MetavoxelStreamState& ancestorState, void* ancestorValue, bool isLeaf) const; - virtual void writeSubdivided(MetavoxelStreamState& state, void* value, - const MetavoxelStreamState& ancestorState, void* ancestorValue, bool isLeaf) const; - - virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - - virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state); - virtual void writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state); - - virtual void readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); - - virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state); - virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); - - virtual bool equal(void* first, void* second) const = 0; - - virtual bool deepEqual(void* first, void* second) const { return equal(first, second); } - - virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, - const glm::vec3& minimum, float size, const MetavoxelLOD& lod); - - /// Expands the specified root, doubling its size in each dimension. - /// \return a new node representing the result - virtual MetavoxelNode* expandMetavoxelRoot(const MetavoxelNode& root); - - /// Merges the value of a parent and its children. - /// \param postRead whether or not the merge is happening after a read - /// \return whether or not the children and parent values are all equal - virtual bool merge(void*& parent, void* children[], bool postRead = false) const = 0; - - /// Given the parent value, returns the value that children should inherit (either the parent value or the default). - virtual AttributeValue inherit(const AttributeValue& parentValue) const { return parentValue; } - - /// Mixes the first and the second, returning a new value with the result. - virtual void* mix(void* first, void* second, float alpha) const = 0; - - /// Blends the source with the destination, returning a new value with the result. - virtual void* blend(void* source, void* dest) const = 0; - - virtual void* getDefaultValue() const = 0; - - virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } - - virtual void* createFromVariant(const QVariant& value) const { return create(); } - - /// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable. - /// The widget should have a single "user" property that will be used to get/set the value. - virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; } - -private: - - float _lodThresholdMultiplier; - bool _userFacing; -}; - -/// A simple attribute class that stores its values inline. -template class InlineAttribute : public Attribute { -public: - - InlineAttribute(const QString& name, const T& defaultValue = T()) : Attribute(name), _defaultValue(defaultValue) { } - - virtual void* create(void* copy) const { void* value; new (&value) T(*(T*)©); return value; } - virtual void destroy(void* value) const { ((T*)&value)->~T(); } - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual bool equal(void* first, void* second) const { return decodeInline(first) == decodeInline(second); } - - virtual void* mix(void* first, void* second, float alpha) const { return create(alpha < 0.5f ? first : second); } - - virtual void* blend(void* source, void* dest) const { return create(source); } - - virtual void* getDefaultValue() const { return encodeInline(_defaultValue); } - -protected: - - T _defaultValue; -}; - -template inline void InlineAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (isLeaf) { - value = getDefaultValue(); - in.read(&value, bits); - } -} - -template inline void InlineAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (isLeaf) { - out.write(&value, bits); - } -} - -/// Provides averaging using the +=, ==, and / operators. -template class SimpleInlineAttribute : public InlineAttribute { -public: - - SimpleInlineAttribute(const QString& name, const T& defaultValue = T()) : InlineAttribute(name, defaultValue) { } - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; -}; - -template inline bool SimpleInlineAttribute::merge( - void*& parent, void* children[], bool postRead) const { - T firstValue = decodeInline(children[0]); - T totalValue = firstValue; - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - T value = decodeInline(children[i]); - totalValue += value; - allChildrenEqual &= (firstValue == value); - } - parent = encodeInline(totalValue / Attribute::MERGE_COUNT); - return allChildrenEqual; -} - -/// A simple float attribute. -class FloatAttribute : public SimpleInlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE FloatAttribute(const QString& name = QString()); -}; - -/// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). -class SharedObjectAttribute : public InlineAttribute { - Q_OBJECT - Q_PROPERTY(const QMetaObject* metaObject MEMBER _metaObject) - -public: - - 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; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual bool deepEqual(void* first, void* second) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual void* createFromVariant(const QVariant& value) const; - - virtual QWidget* createEditor(QWidget* parent = NULL) const; - -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 void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - - virtual bool deepEqual(void* first, void* second) const; - - virtual MetavoxelNode* expandMetavoxelRoot(const MetavoxelNode& root); - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; - - virtual QWidget* createEditor(QWidget* parent = NULL) const; - -private: - - const QMetaObject* _metaObject; -}; - -/// An attribute that takes the form of a set of spanners. -class SpannerSetAttribute : public SharedObjectSetAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE SpannerSetAttribute(const QString& name = QString(), - const QMetaObject* metaObject = &SharedObject::staticMetaObject); - - virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state); - virtual void writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state); - - virtual void readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); - - virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state); - virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); - - virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, - const glm::vec3& minimum, float size, const MetavoxelLOD& lod); -}; - -#endif // hifi_AttributeRegistry_h diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp deleted file mode 100644 index 9c672a415e..0000000000 --- a/libraries/metavoxels/src/Bitstream.cpp +++ /dev/null @@ -1,3552 +0,0 @@ -// -// Bitstream.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/2/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "AttributeRegistry.h" -#include "Bitstream.h" -#include "ScriptCache.h" - -REGISTER_SIMPLE_TYPE_STREAMER(bool) -REGISTER_SIMPLE_TYPE_STREAMER(int) -REGISTER_SIMPLE_TYPE_STREAMER(uint) -REGISTER_SIMPLE_TYPE_STREAMER(float) -REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) -REGISTER_SIMPLE_TYPE_STREAMER(QColor) -REGISTER_SIMPLE_TYPE_STREAMER(QScriptValue) -REGISTER_SIMPLE_TYPE_STREAMER(QString) -REGISTER_SIMPLE_TYPE_STREAMER(QVariant) -REGISTER_SIMPLE_TYPE_STREAMER(QUrl) -REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) -REGISTER_SIMPLE_TYPE_STREAMER(QVariantHash) -REGISTER_SIMPLE_TYPE_STREAMER(SharedObjectPointer) - -// some types don't quite work with our macro -static int vec3Streamer = Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); -static int quatStreamer = Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); -static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId(), - new SimpleTypeStreamer()); - -static int genericValueStreamer = Bitstream::registerTypeStreamer( - qRegisterMetaType(), new GenericValueStreamer()); - -static int qVariantPairListMetaTypeId = qRegisterMetaType(); - -IDStreamer::IDStreamer(Bitstream& stream) : - _stream(stream), - _bits(1) { -} - -static int getBitsForHighestValue(int highestValue) { - // if this turns out to be a bottleneck, there are fancier ways to do it (get the position of the highest set bit): - // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious - int bits = 0; - while (highestValue != 0) { - bits++; - highestValue >>= 1; - } - return bits; -} - -void IDStreamer::setBitsFromValue(int value) { - _bits = getBitsForHighestValue(value + 1); -} - -IDStreamer& IDStreamer::operator<<(int value) { - _stream.write(&value, _bits); - if (value == (1 << _bits) - 1) { - _bits++; - } - return *this; -} - -IDStreamer& IDStreamer::operator>>(int& value) { - value = 0; - _stream.read(&value, _bits); - if (value == (1 << _bits) - 1) { - _bits++; - } - return *this; -} - -void Bitstream::preThreadingInit() { - getObjectStreamers(); - getEnumStreamers(); - getEnumStreamersByName(); -} - -int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { - getMetaObjects().insert(className, metaObject); - - // register it as a subclass of itself and all of its superclasses - for (const QMetaObject* superClass = metaObject; superClass; superClass = superClass->superClass()) { - getMetaObjectSubClasses().insert(superClass, metaObject); - } - return 0; -} - -int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { - streamer->_type = type; - if (!streamer->_self) { - streamer->_self = TypeStreamerPointer(streamer); - } - getTypeStreamers().insert(type, streamer); - return 0; -} - -const TypeStreamer* Bitstream::getTypeStreamer(int type) { - return getTypeStreamers().value(type); -} - -const ObjectStreamer* Bitstream::getObjectStreamer(const QMetaObject* metaObject) { - return getObjectStreamers().value(metaObject); -} - -const QMetaObject* Bitstream::getMetaObject(const QByteArray& className) { - return getMetaObjects().value(className); -} - -QList Bitstream::getMetaObjectSubClasses(const QMetaObject* metaObject) { - return getMetaObjectSubClasses().values(metaObject); -} - -QScriptValue sharedObjectPointerToScriptValue(QScriptEngine* engine, const SharedObjectPointer& pointer) { - return pointer ? engine->newQObject(pointer.data()) : engine->nullValue(); -} - -void sharedObjectPointerFromScriptValue(const QScriptValue& object, SharedObjectPointer& pointer) { - pointer = qobject_cast(object.toQObject()); -} - -void Bitstream::registerTypes(QScriptEngine* engine) { - foreach (const QMetaObject* metaObject, getMetaObjects()) { - engine->globalObject().setProperty(metaObject->className(), engine->newQMetaObject(metaObject)); - } - qScriptRegisterMetaType(engine, sharedObjectPointerToScriptValue, sharedObjectPointerFromScriptValue); -} - -Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, GenericsMode genericsMode, QObject* parent) : - QObject(parent), - _underlying(underlying), - _byte(0), - _position(0), - _metadataType(metadataType), - _genericsMode(genericsMode), - _context(NULL), - _objectStreamerStreamer(*this), - _typeStreamerStreamer(*this), - _attributeStreamer(*this), - _scriptStringStreamer(*this), - _sharedObjectStreamer(*this) { -} - -void Bitstream::addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject) { - _metaObjectSubstitutions.insert(className, metaObject); -} - -void Bitstream::addTypeSubstitution(const QByteArray& typeName, int type) { - _typeStreamerSubstitutions.insert(typeName, getTypeStreamers().value(type)); -} - -void Bitstream::addTypeSubstitution(const QByteArray& typeName, const char* replacementTypeName) { - const TypeStreamer* streamer = getTypeStreamers().value(QMetaType::type(replacementTypeName)); - if (!streamer) { - streamer = getEnumStreamersByName().value(replacementTypeName); - } - _typeStreamerSubstitutions.insert(typeName, streamer); -} - -const int LAST_BIT_POSITION = BITS_IN_BYTE - 1; - -Bitstream& Bitstream::write(const void* data, int bits, int offset) { - const quint8* source = (const quint8*)data; - while (bits > 0) { - int bitsToWrite = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits)); - _byte |= ((*source >> offset) & ((1 << bitsToWrite) - 1)) << _position; - if ((_position += bitsToWrite) == BITS_IN_BYTE) { - flush(); - } - if ((offset += bitsToWrite) == BITS_IN_BYTE) { - source++; - offset = 0; - } - bits -= bitsToWrite; - } - return *this; -} - -Bitstream& Bitstream::read(void* data, int bits, int offset) { - quint8* dest = (quint8*)data; - while (bits > 0) { - if (_position == 0) { - _underlying >> _byte; - } - int bitsToRead = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits)); - int mask = ((1 << bitsToRead) - 1) << offset; - *dest = (*dest & ~mask) | (((_byte >> _position) << offset) & mask); - _position = (_position + bitsToRead) & LAST_BIT_POSITION; - if ((offset += bitsToRead) == BITS_IN_BYTE) { - dest++; - offset = 0; - } - bits -= bitsToRead; - } - return *this; -} - -void Bitstream::flush() { - if (_position != 0) { - _underlying << _byte; - reset(); - } -} - -void Bitstream::reset() { - _byte = 0; - _position = 0; -} - -Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { - WriteMappings mappings = { _objectStreamerStreamer.getAndResetTransientOffsets(), - _typeStreamerStreamer.getAndResetTransientOffsets(), - _attributeStreamer.getAndResetTransientOffsets(), - _scriptStringStreamer.getAndResetTransientOffsets(), - _sharedObjectStreamer.getAndResetTransientOffsets() }; - return mappings; -} - -void Bitstream::persistWriteMappings(const WriteMappings& mappings) { - _objectStreamerStreamer.persistTransientOffsets(mappings.objectStreamerOffsets); - _typeStreamerStreamer.persistTransientOffsets(mappings.typeStreamerOffsets); - _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); - _scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets); - _sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets); - - // find out when shared objects are deleted in order to clear their mappings - for (QHash::const_iterator it = mappings.sharedObjectOffsets.constBegin(); - it != mappings.sharedObjectOffsets.constEnd(); it++) { - if (!it.key()) { - continue; - } - connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*))); - QPointer& reference = _sharedObjectReferences[it.key()->getOriginID()]; - if (reference && reference != it.key()) { - // the object has been replaced by a successor, so we can forget about the original - _sharedObjectStreamer.removePersistentID(reference); - reference->disconnect(this); - } - reference = it.key(); - } -} - -void Bitstream::persistAndResetWriteMappings() { - persistWriteMappings(getAndResetWriteMappings()); -} - -Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { - ReadMappings mappings = { _objectStreamerStreamer.getAndResetTransientValues(), - _typeStreamerStreamer.getAndResetTransientValues(), - _attributeStreamer.getAndResetTransientValues(), - _scriptStringStreamer.getAndResetTransientValues(), - _sharedObjectStreamer.getAndResetTransientValues(), - _subdividedObjects }; - _subdividedObjects.clear(); - return mappings; -} - -void Bitstream::persistReadMappings(const ReadMappings& mappings) { - _objectStreamerStreamer.persistTransientValues(mappings.objectStreamerValues); - _typeStreamerStreamer.persistTransientValues(mappings.typeStreamerValues); - _attributeStreamer.persistTransientValues(mappings.attributeValues); - _scriptStringStreamer.persistTransientValues(mappings.scriptStringValues); - _sharedObjectStreamer.persistTransientValues(mappings.sharedObjectValues); - - for (QHash::const_iterator it = mappings.sharedObjectValues.constBegin(); - it != mappings.sharedObjectValues.constEnd(); it++) { - if (!it.value()) { - continue; - } - QPointer& reference = _sharedObjectReferences[it.value()->getRemoteOriginID()]; - if (reference && reference != it.value()) { - // the object has been replaced by a successor, so we can forget about the original - _sharedObjectStreamer.removePersistentValue(reference.data()); - } - reference = it.value(); - _weakSharedObjectHash.remove(it.value()->getRemoteID()); - } - foreach (const SharedObjectPointer& object, mappings.subdividedObjects) { - QPointer& reference = _sharedObjectReferences[object->getRemoteOriginID()]; - if (reference && reference != object) { - int id = _sharedObjectStreamer.removePersistentValue(reference.data()); - if (id != 0) { - _sharedObjectStreamer.insertPersistentValue(id, object); - } - } - reference = object; - } -} - -void Bitstream::persistAndResetReadMappings() { - persistReadMappings(getAndResetReadMappings()); -} - -void Bitstream::copyPersistentMappings(const Bitstream& other) { - _objectStreamerStreamer.copyPersistentMappings(other._objectStreamerStreamer); - _typeStreamerStreamer.copyPersistentMappings(other._typeStreamerStreamer); - _attributeStreamer.copyPersistentMappings(other._attributeStreamer); - _scriptStringStreamer.copyPersistentMappings(other._scriptStringStreamer); - _sharedObjectStreamer.copyPersistentMappings(other._sharedObjectStreamer); - _sharedObjectReferences = other._sharedObjectReferences; - _weakSharedObjectHash = other._weakSharedObjectHash; -} - -void Bitstream::clearPersistentMappings() { - _objectStreamerStreamer.clearPersistentMappings(); - _typeStreamerStreamer.clearPersistentMappings(); - _attributeStreamer.clearPersistentMappings(); - _scriptStringStreamer.clearPersistentMappings(); - _sharedObjectStreamer.clearPersistentMappings(); - _sharedObjectReferences.clear(); - _weakSharedObjectHash.clear(); -} - -void Bitstream::clearSharedObject(int id) { - SharedObjectPointer object = _sharedObjectStreamer.takePersistentValue(id); - if (object) { - _weakSharedObjectHash.remove(object->getRemoteID()); - } -} - -void Bitstream::writeDelta(bool value, bool reference) { - *this << value; -} - -void Bitstream::readDelta(bool& value, bool reference) { - *this >> value; -} - -void Bitstream::writeDelta(const QVariant& value, const QVariant& reference) { - // QVariant only handles == for built-in types; we need to use our custom operators - const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); - if (value.userType() == reference.userType() && (!streamer || streamer->equal(value, reference))) { - *this << false; - return; - } - *this << true; - _typeStreamerStreamer << streamer; - streamer->writeRawDelta(*this, value, reference); -} - -void Bitstream::writeRawDelta(const QVariant& value, const QVariant& reference) { - const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); - _typeStreamerStreamer << streamer; - streamer->writeRawDelta(*this, value, reference); -} - -void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) { - TypeStreamerPointer typeStreamer; - _typeStreamerStreamer >> typeStreamer; - typeStreamer->readRawDelta(*this, value, reference); -} - -void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { - if (!value) { - _objectStreamerStreamer << NULL; - return; - } - const QMetaObject* metaObject = value->metaObject(); - const ObjectStreamer* streamer = (metaObject == &GenericSharedObject::staticMetaObject) ? - static_cast(value)->getStreamer().data() : getObjectStreamers().value(metaObject); - _objectStreamerStreamer << streamer; - streamer->writeRawDelta(*this, value, reference); -} - -void Bitstream::readRawDelta(QObject*& value, const QObject* reference) { - ObjectStreamerPointer streamer; - _objectStreamerStreamer >> streamer; - value = streamer ? streamer->readRawDelta(*this, reference) : NULL; -} - -void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& reference) { - if (reference.isUndefined() || reference.isNull()) { - *this << value; - - } else if (reference.isBool()) { - if (value.isBool()) { - *this << false; - *this << value.toBool(); - - } else { - *this << true; - *this << value; - } - } else if (reference.isNumber()) { - if (value.isNumber()) { - *this << false; - *this << value.toNumber(); - - } else { - *this << true; - *this << value; - } - } else if (reference.isString()) { - if (value.isString()) { - *this << false; - *this << value.toString(); - - } else { - *this << true; - *this << value; - } - } else if (reference.isVariant()) { - if (value.isVariant()) { - *this << false; - writeRawDelta(value.toVariant(), reference.toVariant()); - - } else { - *this << true; - *this << value; - } - } else if (reference.isQObject()) { - if (value.isQObject()) { - *this << false; - writeRawDelta(value.toQObject(), reference.toQObject()); - - } else { - *this << true; - *this << value; - } - } else if (reference.isQMetaObject()) { - if (value.isQMetaObject()) { - *this << false; - *this << value.toQMetaObject(); - - } else { - *this << true; - *this << value; - } - } else if (reference.isDate()) { - if (value.isDate()) { - *this << false; - *this << value.toDateTime(); - - } else { - *this << true; - *this << value; - } - } else if (reference.isRegExp()) { - if (value.isRegExp()) { - *this << false; - *this << value.toRegExp(); - - } else { - *this << true; - *this << value; - } - } else if (reference.isArray()) { - if (value.isArray()) { - *this << false; - int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); - *this << length; - int referenceLength = reference.property(DependencyManager::get()->getLengthString()).toInt32(); - for (int i = 0; i < length; i++) { - if (i < referenceLength) { - writeDelta(value.property(i), reference.property(i)); - } else { - *this << value.property(i); - } - } - } else { - *this << true; - *this << value; - } - } else if (reference.isObject()) { - if (value.isObject() && !(value.isArray() || value.isRegExp() || value.isDate() || - value.isQMetaObject() || value.isQObject() || value.isVariant())) { - *this << false; - for (QScriptValueIterator it(value); it.hasNext(); ) { - it.next(); - QScriptValue referenceValue = reference.property(it.scriptName()); - if (it.value() != referenceValue) { - *this << it.scriptName(); - writeRawDelta(it.value(), referenceValue); - } - } - for (QScriptValueIterator it(reference); it.hasNext(); ) { - it.next(); - if (!value.property(it.scriptName()).isValid()) { - *this << it.scriptName(); - writeRawDelta(QScriptValue(), it.value()); - } - } - *this << QScriptString(); - - } else { - *this << true; - *this << value; - } - } else { - *this << value; - } -} - -void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) { - if (reference.isUndefined() || reference.isNull()) { - *this >> value; - - } else if (reference.isBool()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - bool boolValue; - *this >> boolValue; - value = QScriptValue(boolValue); - } - } else if (reference.isNumber()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - qsreal numberValue; - *this >> numberValue; - value = QScriptValue(numberValue); - } - } else if (reference.isString()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - QString stringValue; - *this >> stringValue; - value = QScriptValue(stringValue); - } - } else if (reference.isVariant()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - QVariant variant; - readRawDelta(variant, reference.toVariant()); - value = DependencyManager::get()->getEngine()->newVariant(variant); - } - } else if (reference.isQObject()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - QObject* object; - readRawDelta(object, reference.toQObject()); - value = DependencyManager::get()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); - } - } else if (reference.isQMetaObject()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - const QMetaObject* metaObject; - *this >> metaObject; - value = DependencyManager::get()->getEngine()->newQMetaObject(metaObject); - } - } else if (reference.isDate()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - QDateTime dateTime; - *this >> dateTime; - value = DependencyManager::get()->getEngine()->newDate(dateTime); - } - } else if (reference.isRegExp()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - QRegExp regExp; - *this >> regExp; - value = DependencyManager::get()->getEngine()->newRegExp(regExp); - } - } else if (reference.isArray()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - int length; - *this >> length; - value = DependencyManager::get()->getEngine()->newArray(length); - int referenceLength = reference.property(DependencyManager::get()->getLengthString()).toInt32(); - for (int i = 0; i < length; i++) { - QScriptValue element; - if (i < referenceLength) { - readDelta(element, reference.property(i)); - } else { - *this >> element; - } - value.setProperty(i, element); - } - } - } else if (reference.isObject()) { - bool typeChanged; - *this >> typeChanged; - if (typeChanged) { - *this >> value; - - } else { - // start by shallow-copying the reference - value = DependencyManager::get()->getEngine()->newObject(); - for (QScriptValueIterator it(reference); it.hasNext(); ) { - it.next(); - value.setProperty(it.scriptName(), it.value()); - } - // then apply the requested changes - forever { - QScriptString name; - *this >> name; - if (!name.isValid()) { - break; - } - QScriptValue scriptValue; - readRawDelta(scriptValue, reference.property(name)); - value.setProperty(name, scriptValue); - } - } - } else { - *this >> value; - } -} - -void Bitstream::writeAligned(const QByteArray& data) { - flush(); - _underlying.device()->write(data); -} - -QByteArray Bitstream::readAligned(int bytes) { - reset(); - return _underlying.device()->read(bytes); -} - -Bitstream& Bitstream::operator<<(bool value) { - if (value) { - _byte |= (1 << _position); - } - if (++_position == BITS_IN_BYTE) { - flush(); - } - return *this; -} - -Bitstream& Bitstream::operator>>(bool& value) { - if (_position == 0) { - _underlying >> _byte; - } - value = _byte & (1 << _position); - _position = (_position + 1) & LAST_BIT_POSITION; - return *this; -} - -Bitstream& Bitstream::operator<<(int value) { - return write(&value, 32); -} - -Bitstream& Bitstream::operator>>(int& value) { - qint32 sizedValue; - read(&sizedValue, 32); - value = sizedValue; - return *this; -} - -Bitstream& Bitstream::operator<<(uint value) { - return write(&value, 32); -} - -Bitstream& Bitstream::operator>>(uint& value) { - quint32 sizedValue; - read(&sizedValue, 32); - value = sizedValue; - return *this; -} - -Bitstream& Bitstream::operator<<(qint64 value) { - return write(&value, 64); -} - -Bitstream& Bitstream::operator>>(qint64& value) { - return read(&value, 64); -} - -Bitstream& Bitstream::operator<<(float value) { - return write(&value, 32); -} - -Bitstream& Bitstream::operator>>(float& value) { - return read(&value, 32); -} - -Bitstream& Bitstream::operator<<(double value) { - return write(&value, 64); -} - -Bitstream& Bitstream::operator>>(double& value) { - return read(&value, 64); -} - -Bitstream& Bitstream::operator<<(const glm::vec3& value) { - return *this << value.x << value.y << value.z; -} - -Bitstream& Bitstream::operator>>(glm::vec3& value) { - return *this >> value.x >> value.y >> value.z; -} - -Bitstream& Bitstream::operator<<(const glm::quat& value) { - return *this << value.w << value.x << value.y << value.z; -} - -Bitstream& Bitstream::operator>>(glm::quat& value) { - return *this >> value.w >> value.x >> value.y >> value.z; -} - -Bitstream& Bitstream::operator<<(const QByteArray& string) { - *this << string.size(); - return write(string.constData(), string.size() * BITS_IN_BYTE); -} - -Bitstream& Bitstream::operator>>(QByteArray& string) { - int size; - *this >> size; - string.resize(size); - return read(string.data(), size * BITS_IN_BYTE); -} - -Bitstream& Bitstream::operator<<(const QColor& color) { - return *this << (int)color.rgba(); -} - -Bitstream& Bitstream::operator>>(QColor& color) { - int rgba; - *this >> rgba; - color.setRgba(rgba); - return *this; -} - -Bitstream& Bitstream::operator<<(const QString& string) { - *this << string.size(); - return write(string.constData(), string.size() * sizeof(QChar) * BITS_IN_BYTE); -} - -Bitstream& Bitstream::operator>>(QString& string) { - int size; - *this >> size; - string.resize(size); - return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE); -} - -Bitstream& Bitstream::operator<<(const QUrl& url) { - return *this << url.toString(); -} - -Bitstream& Bitstream::operator>>(QUrl& url) { - QString string; - *this >> string; - url = string; - return *this; -} - -Bitstream& Bitstream::operator<<(const QDateTime& dateTime) { - return *this << dateTime.toMSecsSinceEpoch(); -} - -Bitstream& Bitstream::operator>>(QDateTime& dateTime) { - qint64 msecsSinceEpoch; - *this >> msecsSinceEpoch; - dateTime = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch); - return *this; -} - -Bitstream& Bitstream::operator<<(const QRegExp& regExp) { - *this << regExp.pattern(); - Qt::CaseSensitivity caseSensitivity = regExp.caseSensitivity(); - write(&caseSensitivity, 1); - QRegExp::PatternSyntax syntax = regExp.patternSyntax(); - write(&syntax, 3); - return *this << regExp.isMinimal(); -} - -Bitstream& Bitstream::operator>>(QRegExp& regExp) { - QString pattern; - *this >> pattern; - Qt::CaseSensitivity caseSensitivity = (Qt::CaseSensitivity)0; - read(&caseSensitivity, 1); - QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)0; - read(&syntax, 3); - regExp = QRegExp(pattern, caseSensitivity, syntax); - bool minimal; - *this >> minimal; - regExp.setMinimal(minimal); - return *this; -} - -Bitstream& Bitstream::operator<<(const QVariant& value) { - if (!value.isValid()) { - _typeStreamerStreamer << NULL; - return *this; - } - const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); - if (streamer) { - streamer->writeVariant(*this, value); - } else { - qWarning() << "Non-streamable type: " << value.typeName() << "\n"; - } - return *this; -} - -Bitstream& Bitstream::operator>>(QVariant& value) { - TypeStreamerPointer streamer; - _typeStreamerStreamer >> streamer; - if (!streamer) { - value = QVariant(); - } else { - value = streamer->readVariant(*this); - } - return *this; -} - -Bitstream& Bitstream::operator<<(const AttributeValue& attributeValue) { - _attributeStreamer << attributeValue.getAttribute(); - if (attributeValue.getAttribute()) { - attributeValue.getAttribute()->write(*this, attributeValue.getValue(), true); - } - return *this; -} - -Bitstream& Bitstream::operator>>(OwnedAttributeValue& attributeValue) { - AttributePointer attribute; - _attributeStreamer >> attribute; - if (attribute) { - void* value = attribute->create(); - attribute->read(*this, value, true); - attributeValue = AttributeValue(attribute, value); - attribute->destroy(value); - - } else { - attributeValue = AttributeValue(); - } - return *this; -} - -Bitstream& Bitstream::operator<<(const GenericValue& value) { - value.getStreamer()->write(*this, value.getValue()); - return *this; -} - -Bitstream& Bitstream::operator>>(GenericValue& value) { - value = GenericValue(); - return *this; -} - -Bitstream& Bitstream::operator<<(const QObject* object) { - if (!object) { - _objectStreamerStreamer << NULL; - return *this; - } - const QMetaObject* metaObject = object->metaObject(); - const ObjectStreamer* streamer = (metaObject == &GenericSharedObject::staticMetaObject) ? - static_cast(object)->getStreamer().data() : getObjectStreamers().value(metaObject); - _objectStreamerStreamer << streamer; - streamer->write(*this, object); - return *this; -} - -Bitstream& Bitstream::operator>>(QObject*& object) { - ObjectStreamerPointer streamer; - _objectStreamerStreamer >> streamer; - object = streamer ? streamer->read(*this) : NULL; - return *this; -} - -Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) { - _objectStreamerStreamer << getObjectStreamers().value(metaObject); - return *this; -} - -Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { - ObjectStreamerPointer streamer; - _objectStreamerStreamer >> streamer; - metaObject = streamer->getMetaObject(); - return *this; -} - -Bitstream& Bitstream::operator<<(const ObjectStreamer* streamer) { - _objectStreamerStreamer << streamer; - return *this; -} - -Bitstream& Bitstream::operator>>(const ObjectStreamer*& streamer) { - ObjectStreamerPointer objectStreamer; - _objectStreamerStreamer >> objectStreamer; - streamer = objectStreamer.data(); - return *this; -} - -Bitstream& Bitstream::operator>>(ObjectStreamerPointer& streamer) { - _objectStreamerStreamer >> streamer; - return *this; -} - -Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { - _typeStreamerStreamer << streamer; - return *this; -} - -Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { - TypeStreamerPointer typeStreamer; - _typeStreamerStreamer >> typeStreamer; - streamer = typeStreamer.data(); - return *this; -} - -Bitstream& Bitstream::operator>>(TypeStreamerPointer& streamer) { - _typeStreamerStreamer >> streamer; - return *this; -} - -Bitstream& Bitstream::operator<<(const AttributePointer& attribute) { - _attributeStreamer << attribute; - return *this; -} - -Bitstream& Bitstream::operator>>(AttributePointer& attribute) { - _attributeStreamer >> attribute; - return *this; -} - -Bitstream& Bitstream::operator<<(const QScriptString& string) { - _scriptStringStreamer << string; - return *this; -} - -Bitstream& Bitstream::operator>>(QScriptString& string) { - _scriptStringStreamer >> string; - return *this; -} - -enum ScriptValueType { - INVALID_SCRIPT_VALUE, - UNDEFINED_SCRIPT_VALUE, - NULL_SCRIPT_VALUE, - BOOL_SCRIPT_VALUE, - NUMBER_SCRIPT_VALUE, - STRING_SCRIPT_VALUE, - VARIANT_SCRIPT_VALUE, - QOBJECT_SCRIPT_VALUE, - QMETAOBJECT_SCRIPT_VALUE, - DATE_SCRIPT_VALUE, - REGEXP_SCRIPT_VALUE, - ARRAY_SCRIPT_VALUE, - OBJECT_SCRIPT_VALUE -}; - -const int SCRIPT_VALUE_BITS = 4; - -static void writeScriptValueType(Bitstream& out, ScriptValueType type) { - out.write(&type, SCRIPT_VALUE_BITS); -} - -static ScriptValueType readScriptValueType(Bitstream& in) { - ScriptValueType type = (ScriptValueType)0; - in.read(&type, SCRIPT_VALUE_BITS); - return type; -} - -Bitstream& Bitstream::operator<<(const QScriptValue& value) { - if (value.isUndefined()) { - writeScriptValueType(*this, UNDEFINED_SCRIPT_VALUE); - - } else if (value.isNull()) { - writeScriptValueType(*this, NULL_SCRIPT_VALUE); - - } else if (value.isBool()) { - writeScriptValueType(*this, BOOL_SCRIPT_VALUE); - *this << value.toBool(); - - } else if (value.isNumber()) { - writeScriptValueType(*this, NUMBER_SCRIPT_VALUE); - *this << value.toNumber(); - - } else if (value.isString()) { - writeScriptValueType(*this, STRING_SCRIPT_VALUE); - *this << value.toString(); - - } else if (value.isVariant()) { - writeScriptValueType(*this, VARIANT_SCRIPT_VALUE); - *this << value.toVariant(); - - } else if (value.isQObject()) { - writeScriptValueType(*this, QOBJECT_SCRIPT_VALUE); - *this << value.toQObject(); - - } else if (value.isQMetaObject()) { - writeScriptValueType(*this, QMETAOBJECT_SCRIPT_VALUE); - *this << value.toQMetaObject(); - - } else if (value.isDate()) { - writeScriptValueType(*this, DATE_SCRIPT_VALUE); - *this << value.toDateTime(); - - } else if (value.isRegExp()) { - writeScriptValueType(*this, REGEXP_SCRIPT_VALUE); - *this << value.toRegExp(); - - } else if (value.isArray()) { - writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); - int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); - *this << length; - for (int i = 0; i < length; i++) { - *this << value.property(i); - } - } else if (value.isObject()) { - writeScriptValueType(*this, OBJECT_SCRIPT_VALUE); - for (QScriptValueIterator it(value); it.hasNext(); ) { - it.next(); - *this << it.scriptName(); - *this << it.value(); - } - *this << QScriptString(); - - } else { - writeScriptValueType(*this, INVALID_SCRIPT_VALUE); - } - return *this; -} - -Bitstream& Bitstream::operator>>(QScriptValue& value) { - switch (readScriptValueType(*this)) { - case UNDEFINED_SCRIPT_VALUE: - value = QScriptValue(QScriptValue::UndefinedValue); - break; - - case NULL_SCRIPT_VALUE: - value = QScriptValue(QScriptValue::NullValue); - break; - - case BOOL_SCRIPT_VALUE: { - bool boolValue; - *this >> boolValue; - value = QScriptValue(boolValue); - break; - } - case NUMBER_SCRIPT_VALUE: { - qsreal numberValue; - *this >> numberValue; - value = QScriptValue(numberValue); - break; - } - case STRING_SCRIPT_VALUE: { - QString stringValue; - *this >> stringValue; - value = QScriptValue(stringValue); - break; - } - case VARIANT_SCRIPT_VALUE: { - QVariant variantValue; - *this >> variantValue; - value = DependencyManager::get()->getEngine()->newVariant(variantValue); - break; - } - case QOBJECT_SCRIPT_VALUE: { - QObject* object; - *this >> object; - DependencyManager::get()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); - break; - } - case QMETAOBJECT_SCRIPT_VALUE: { - const QMetaObject* metaObject; - *this >> metaObject; - DependencyManager::get()->getEngine()->newQMetaObject(metaObject); - break; - } - case DATE_SCRIPT_VALUE: { - QDateTime dateTime; - *this >> dateTime; - value = DependencyManager::get()->getEngine()->newDate(dateTime); - break; - } - case REGEXP_SCRIPT_VALUE: { - QRegExp regExp; - *this >> regExp; - value = DependencyManager::get()->getEngine()->newRegExp(regExp); - break; - } - case ARRAY_SCRIPT_VALUE: { - int length; - *this >> length; - value = DependencyManager::get()->getEngine()->newArray(length); - for (int i = 0; i < length; i++) { - QScriptValue element; - *this >> element; - value.setProperty(i, element); - } - break; - } - case OBJECT_SCRIPT_VALUE: { - value = DependencyManager::get()->getEngine()->newObject(); - forever { - QScriptString name; - *this >> name; - if (!name.isValid()) { - break; - } - QScriptValue scriptValue; - *this >> scriptValue; - value.setProperty(name, scriptValue); - } - break; - } - default: - value = QScriptValue(); - break; - } - return *this; -} - -Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) { - _sharedObjectStreamer << object; - return *this; -} - -Bitstream& Bitstream::operator>>(SharedObjectPointer& object) { - _sharedObjectStreamer >> object; - return *this; -} - -Bitstream& Bitstream::operator<(const ObjectStreamer* streamer) { - if (!streamer) { - return *this << QByteArray(); - } - const char* name = streamer->getName(); - *this << QByteArray::fromRawData(name, strlen(name)); - if (_metadataType != NO_METADATA) { - streamer->writeMetadata(*this, _metadataType == FULL_METADATA); - } - return *this; -} - -static MappedObjectStreamer* createMappedObjectStreamer(const QMetaObject* metaObject, - const QVector& properties) { - for (const QMetaObject* super = metaObject; super; super = super->superClass()) { - if (super == &SharedObject::staticMetaObject) { - return new SharedObjectStreamer(metaObject, properties); - } - } - return new MappedObjectStreamer(metaObject, properties); -} - -Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) { - QByteArray className; - *this >> className; - if (className.isEmpty()) { - streamer = ObjectStreamerPointer(); - return *this; - } - const QMetaObject* metaObject = _metaObjectSubstitutions.value(className); - if (!metaObject) { - metaObject = getMetaObjects().value(className); - } - // start out with the streamer for the named class, if any - if (metaObject) { - streamer = getObjectStreamers().value(metaObject)->getSelf(); - } else { - streamer = ObjectStreamerPointer(); - } - if (_metadataType == NO_METADATA) { - if (!metaObject) { - throw BitstreamException(QString("Unknown class name: ") + className); - } - return *this; - } - if (_genericsMode == ALL_GENERICS) { - streamer = readGenericObjectStreamer(className); - return *this; - } - if (!metaObject && _genericsMode == FALLBACK_GENERICS) { - streamer = readGenericObjectStreamer(className); - return *this; - } - int propertyCount; - *this >> propertyCount; - QVector properties(propertyCount); - for (int i = 0; i < propertyCount; i++) { - TypeStreamerPointer typeStreamer; - *this >> typeStreamer; - QMetaProperty property = QMetaProperty(); - if (_metadataType == FULL_METADATA) { - QByteArray propertyName; - *this >> propertyName; - if (metaObject) { - property = metaObject->property(metaObject->indexOfProperty(propertyName)); - } - } - properties[i] = StreamerPropertyPair(typeStreamer, property); - } - // for hash metadata, check the names/types of the properties as well as the name hash against our own class - if (_metadataType == HASH_METADATA) { - QCryptographicHash hash(QCryptographicHash::Md5); - bool matches = true; - if (metaObject) { - const QVector& localProperties = streamer->getProperties(); - if (localProperties.size() == properties.size()) { - for (int i = 0; i < localProperties.size(); i++) { - const StreamerPropertyPair& localProperty = localProperties.at(i); - if (localProperty.first != properties.at(i).first) { - matches = false; - break; - } - hash.addData(localProperty.second.name(), strlen(localProperty.second.name()) + 1); - } - } else { - matches = false; - } - } - QByteArray localHashResult = hash.result(); - QByteArray remoteHashResult(localHashResult.size(), 0); - read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (metaObject && matches && localHashResult == remoteHashResult) { - return *this; - } - } else if (metaObject) { - const QVector& localProperties = streamer->getProperties(); - if (localProperties.size() != properties.size()) { - streamer = ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties)); - return *this; - } - for (int i = 0; i < localProperties.size(); i++) { - const StreamerPropertyPair& property = properties.at(i); - const StreamerPropertyPair& localProperty = localProperties.at(i); - if (property.first != localProperty.first || - property.second.propertyIndex() != localProperty.second.propertyIndex()) { - streamer = ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties)); - return *this; - } - } - return *this; - } - streamer = ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties)); - return *this; -} - -Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { - if (!streamer) { - *this << QByteArray(); - return *this; - } - const char* typeName = streamer->getName(); - *this << QByteArray::fromRawData(typeName, strlen(typeName)); - if (_metadataType != NO_METADATA) { - *this << (int)streamer->getCategory(); - streamer->writeMetadata(*this, _metadataType == FULL_METADATA); - } - return *this; -} - -Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { - QByteArray typeName; - *this >> typeName; - if (typeName.isEmpty()) { - streamer = TypeStreamerPointer(); - return *this; - } - const TypeStreamer* baseStreamer = _typeStreamerSubstitutions.value(typeName); - if (!baseStreamer) { - baseStreamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); - if (!baseStreamer) { - baseStreamer = getEnumStreamersByName().value(typeName); - } - } - // start out with the base, if any - if (baseStreamer) { - streamer = baseStreamer->getSelf(); - } else { - streamer = TypeStreamerPointer(); - } - if (_metadataType == NO_METADATA) { - if (!baseStreamer) { - throw BitstreamException(QString("Unknown type name: ") + typeName); - } - return *this; - } - int category; - *this >> category; - if (category == TypeStreamer::SIMPLE_CATEGORY) { - if (!streamer) { - throw BitstreamException(QString("Unknown type name: ") + typeName); - } - return *this; - } - if (_genericsMode == ALL_GENERICS) { - streamer = readGenericTypeStreamer(typeName, category); - return *this; - } - if (!baseStreamer) { - if (_genericsMode == FALLBACK_GENERICS) { - streamer = readGenericTypeStreamer(typeName, category); - return *this; - } - baseStreamer = getInvalidTypeStreamer(); - } - switch (category) { - case TypeStreamer::ENUM_CATEGORY: { - if (_metadataType == FULL_METADATA) { - int keyCount; - *this >> keyCount; - QMetaEnum metaEnum = baseStreamer->getMetaEnum(); - QHash mappings; - bool matches = (keyCount == metaEnum.keyCount()); - int highestValue = 0; - for (int i = 0; i < keyCount; i++) { - QByteArray key; - int value; - *this >> key >> value; - highestValue = qMax(value, highestValue); - int localValue = metaEnum.keyToValue(key); - if (localValue != -1) { - mappings.insert(value, localValue); - } - matches &= (value == localValue); - } - if (!matches) { - streamer = TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer, - getBitsForHighestValue(highestValue), mappings)); - } - } else { - int bits; - *this >> bits; - QCryptographicHash hash(QCryptographicHash::Md5); - if (baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) { - QMetaEnum metaEnum = baseStreamer->getMetaEnum(); - for (int i = 0; i < metaEnum.keyCount(); i++) { - hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); - qint32 value = metaEnum.value(i); - hash.addData((const char*)&value, sizeof(qint32)); - } - } - QByteArray localHashResult = hash.result(); - QByteArray remoteHashResult(localHashResult.size(), 0); - read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (localHashResult != remoteHashResult) { - streamer = TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer, bits, QHash())); - } - } - return *this; - } - case TypeStreamer::LIST_CATEGORY: - case TypeStreamer::SET_CATEGORY: { - TypeStreamerPointer valueStreamer; - *this >> valueStreamer; - if (!(baseStreamer->getCategory() == category && valueStreamer == baseStreamer->getValueStreamer())) { - streamer = TypeStreamerPointer(category == TypeStreamer::LIST_CATEGORY ? - new MappedListTypeStreamer(baseStreamer, valueStreamer) : - new MappedSetTypeStreamer(baseStreamer, valueStreamer)); - } - return *this; - } - case TypeStreamer::MAP_CATEGORY: { - TypeStreamerPointer keyStreamer, valueStreamer; - *this >> keyStreamer >> valueStreamer; - if (!(baseStreamer->getCategory() == TypeStreamer::MAP_CATEGORY && - keyStreamer == baseStreamer->getKeyStreamer() && valueStreamer == baseStreamer->getValueStreamer())) { - streamer = TypeStreamerPointer(new MappedMapTypeStreamer(baseStreamer, keyStreamer, valueStreamer)); - } - return *this; - } - } - // streamable type - int fieldCount; - *this >> fieldCount; - QVector fields(fieldCount); - for (int i = 0; i < fieldCount; i++) { - TypeStreamerPointer typeStreamer; - *this >> typeStreamer; - int index = -1; - if (_metadataType == FULL_METADATA) { - QByteArray fieldName; - *this >> fieldName; - index = baseStreamer->getFieldIndex(fieldName); - } - fields[i] = StreamerIndexPair(typeStreamer, index); - } - // for hash metadata, check the names/types of the fields as well as the name hash against our own class - if (_metadataType == HASH_METADATA) { - QCryptographicHash hash(QCryptographicHash::Md5); - bool matches = true; - const QVector& localFields = baseStreamer->getMetaFields(); - if (fieldCount != localFields.size()) { - matches = false; - - } else { - if (fieldCount == 0) { - return *this; - } - for (int i = 0; i < fieldCount; i++) { - const MetaField& localField = localFields.at(i); - if (fields.at(i).first != localField.getStreamer()) { - matches = false; - break; - } - hash.addData(localField.getName().constData(), localField.getName().size() + 1); - } - } - QByteArray localHashResult = hash.result(); - QByteArray remoteHashResult(localHashResult.size(), 0); - read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (matches && localHashResult == remoteHashResult) { - // since everything is the same, we can use the default streamer - return *this; - } - } - // if all fields are the same type and in the right order, we can use the (more efficient) default streamer - const QVector& localFields = baseStreamer->getMetaFields(); - if (fieldCount != localFields.size()) { - streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); - return *this; - } - for (int i = 0; i < fieldCount; i++) { - const StreamerIndexPair& field = fields.at(i); - if (field.first != localFields.at(i).getStreamer() || field.second != i) { - streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); - return *this; - } - } - return *this; -} - -Bitstream& Bitstream::operator<(const AttributePointer& attribute) { - return *this << (QObject*)attribute.data(); -} - -Bitstream& Bitstream::operator>(AttributePointer& attribute) { - QObject* object; - *this >> object; - attribute = AttributeRegistry::getInstance()->registerAttribute(static_cast(object)); - return *this; -} - -const QString INVALID_STRING("%INVALID%"); - -Bitstream& Bitstream::operator<(const QScriptString& string) { - return *this << (string.isValid() ? string.toString() : INVALID_STRING); -} - -Bitstream& Bitstream::operator>(QScriptString& string) { - QString rawString; - *this >> rawString; - string = (rawString == INVALID_STRING) ? QScriptString() : - DependencyManager::get()->getEngine()->toStringHandle(rawString); - return *this; -} - -Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { - if (!object) { - return *this << (int)0; - } - *this << object->getID(); - *this << object->getOriginID(); - QPointer reference = _sharedObjectReferences.value(object->getOriginID()); - if (reference) { - *this << true; - writeRawDelta((const QObject*)object.data(), (const QObject*)reference.data()); - } else { - *this << false; - *this << (QObject*)object.data(); - } - return *this; -} - -Bitstream& Bitstream::operator>(SharedObjectPointer& object) { - int id; - *this >> id; - if (id == 0) { - object = SharedObjectPointer(); - return *this; - } - int originID; - *this >> originID; - bool delta; - *this >> delta; - QPointer reference = _sharedObjectReferences.value(originID); - QPointer& pointer = _weakSharedObjectHash[id]; - if (pointer) { - ObjectStreamerPointer objectStreamer; - _objectStreamerStreamer >> objectStreamer; - if (delta) { - if (!reference) { - throw BitstreamException(QString("Delta without reference [id=%1, originID=%2]").arg(id).arg(originID)); - } - objectStreamer->readRawDelta(*this, reference.data(), pointer.data()); - } else { - objectStreamer->read(*this, pointer.data()); - } - } else { - QObject* rawObject; - if (delta) { - if (!reference) { - throw BitstreamException(QString("Delta without reference [id=%1, originID=%2]").arg(id).arg(originID)); - } - readRawDelta(rawObject, (const QObject*)reference.data()); - } else { - *this >> rawObject; - } - pointer = static_cast(rawObject); - if (pointer) { - if (reference) { - pointer->setOriginID(reference->getOriginID()); - } - pointer->setRemoteID(id); - pointer->setRemoteOriginID(originID); - } else { - qDebug() << "Null object" << pointer << reference << id; - } - } - object = static_cast(pointer.data()); - return *this; -} - -void Bitstream::clearSharedObject(QObject* object) { - SharedObject* sharedObject = static_cast(object); - _sharedObjectReferences.remove(sharedObject->getOriginID()); - int id = _sharedObjectStreamer.takePersistentID(sharedObject); - if (id != 0) { - emit sharedObjectCleared(id); - } -} - -const int MD5_HASH_SIZE = 16; - -ObjectStreamerPointer Bitstream::readGenericObjectStreamer(const QByteArray& name) { - int propertyCount; - *this >> propertyCount; - QVector properties(propertyCount); - QByteArray hash; - if (propertyCount > 0) { - for (int i = 0; i < propertyCount; i++) { - TypeStreamerPointer streamer; - *this >> streamer; - QByteArray name; - if (_metadataType == FULL_METADATA) { - *this >> name; - } - properties[i] = StreamerNamePair(streamer, name); - } - if (_metadataType == HASH_METADATA) { - hash.resize(MD5_HASH_SIZE); - read(hash.data(), hash.size() * BITS_IN_BYTE); - } - } - ObjectStreamerPointer streamer = ObjectStreamerPointer(new GenericObjectStreamer(name, properties, hash)); - static_cast(streamer.data())->_weakSelf = streamer; - return streamer; -} - -TypeStreamerPointer Bitstream::readGenericTypeStreamer(const QByteArray& name, int category) { - TypeStreamerPointer streamer; - switch (category) { - case TypeStreamer::ENUM_CATEGORY: { - QVector values; - int bits; - QByteArray hash; - if (_metadataType == FULL_METADATA) { - int keyCount; - *this >> keyCount; - values.resize(keyCount); - int highestValue = 0; - for (int i = 0; i < keyCount; i++) { - QByteArray name; - int value; - *this >> name >> value; - values[i] = NameIntPair(name, value); - highestValue = qMax(highestValue, value); - } - bits = getBitsForHighestValue(highestValue); - - } else { - *this >> bits; - hash.resize(MD5_HASH_SIZE); - read(hash.data(), hash.size() * BITS_IN_BYTE); - } - streamer = TypeStreamerPointer(new GenericEnumTypeStreamer(name, values, bits, hash)); - break; - } - case TypeStreamer::STREAMABLE_CATEGORY: { - int fieldCount; - *this >> fieldCount; - QVector fields(fieldCount); - QByteArray hash; - if (fieldCount == 0) { - streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(name, fields, hash)); - break; - } - for (int i = 0; i < fieldCount; i++) { - TypeStreamerPointer streamer; - *this >> streamer; - QByteArray name; - if (_metadataType == FULL_METADATA) { - *this >> name; - } - fields[i] = StreamerNamePair(streamer, name); - } - if (_metadataType == HASH_METADATA) { - hash.resize(MD5_HASH_SIZE); - read(hash.data(), hash.size() * BITS_IN_BYTE); - } - streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(name, fields, hash)); - break; - } - case TypeStreamer::LIST_CATEGORY: - case TypeStreamer::SET_CATEGORY: { - TypeStreamerPointer valueStreamer; - *this >> valueStreamer; - streamer = TypeStreamerPointer(category == TypeStreamer::LIST_CATEGORY ? - new GenericListTypeStreamer(name, valueStreamer) : new GenericSetTypeStreamer(name, valueStreamer)); - break; - } - case TypeStreamer::MAP_CATEGORY: { - TypeStreamerPointer keyStreamer, valueStreamer; - *this >> keyStreamer >> valueStreamer; - streamer = TypeStreamerPointer(new GenericMapTypeStreamer(name, keyStreamer, valueStreamer)); - break; - } - } - static_cast(streamer.data())->_weakSelf = streamer; - return streamer; -} - -QHash& Bitstream::getMetaObjects() { - static QHash metaObjects; - return metaObjects; -} - -QMultiHash& Bitstream::getMetaObjectSubClasses() { - static QMultiHash metaObjectSubClasses; - return metaObjectSubClasses; -} - -const QHash& Bitstream::getObjectStreamers() { - static QHash objectStreamers = createObjectStreamers(); - return objectStreamers; -} - -QHash Bitstream::createObjectStreamers() { - QHash objectStreamers; - foreach (const QMetaObject* metaObject, getMetaObjects()) { - QVector properties; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; - } - const TypeStreamer* streamer; - if (property.isEnumType()) { - QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(ScopeNamePair( - QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), - QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); - } else { - streamer = getTypeStreamers().value(property.userType()); - } - if (streamer) { - properties.append(StreamerPropertyPair(streamer->getSelf(), property)); - } - } - ObjectStreamerPointer streamer = ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties)); - streamer->_self = streamer; - objectStreamers.insert(metaObject, streamer.data()); - } - return objectStreamers; -} - -QHash& Bitstream::getTypeStreamers() { - static QHash typeStreamers; - return typeStreamers; -} - -const QHash& Bitstream::getEnumStreamers() { - static QHash enumStreamers = createEnumStreamers(); - return enumStreamers; -} - -static QByteArray getEnumName(const char* scope, const char* name) { - return QByteArray(scope) + "::" + name; -} - -QHash Bitstream::createEnumStreamers() { - QHash enumStreamers; - foreach (const QMetaObject* metaObject, getMetaObjects()) { - for (int i = 0; i < metaObject->enumeratorCount(); i++) { - QMetaEnum metaEnum = metaObject->enumerator(i); - const TypeStreamer*& streamer = enumStreamers[ScopeNamePair(metaEnum.scope(), metaEnum.name())]; - if (!streamer) { - // look for a streamer registered by name - streamer = getTypeStreamers().value(QMetaType::type(getEnumName(metaEnum.scope(), metaEnum.name()))); - if (!streamer) { - streamer = new EnumTypeStreamer(metaEnum); - } - } - } - } - return enumStreamers; -} - -const QHash& Bitstream::getEnumStreamersByName() { - static QHash enumStreamersByName = createEnumStreamersByName(); - return enumStreamersByName; -} - -QHash Bitstream::createEnumStreamersByName() { - QHash enumStreamersByName; - foreach (const TypeStreamer* streamer, getEnumStreamers()) { - enumStreamersByName.insert(streamer->getName(), streamer); - } - return enumStreamersByName; -} - -const TypeStreamer* Bitstream::getInvalidTypeStreamer() { - const TypeStreamer* streamer = createInvalidTypeStreamer(); - return streamer; -} - -const TypeStreamer* Bitstream::createInvalidTypeStreamer() { - TypeStreamer* streamer = new TypeStreamer(); - streamer->_type = QMetaType::UnknownType; - streamer->_self = TypeStreamerPointer(streamer); - return streamer; -} - -BitstreamException::BitstreamException(const QString& description) : - _description(description) { -} - -QJsonValue JSONWriter::getData(bool value) { - return value; -} - -QJsonValue JSONWriter::getData(int value) { - return value; -} - -QJsonValue JSONWriter::getData(uint value) { - return (int)value; -} - -QJsonValue JSONWriter::getData(float value) { - return (double)value; -} - -QJsonValue JSONWriter::getData(const QByteArray& value) { - return QString(value.toPercentEncoding()); -} - -QJsonValue JSONWriter::getData(const QColor& value) { - return value.name(); -} - -QJsonValue JSONWriter::getData(const QScriptValue& value) { - QJsonObject object; - if (value.isUndefined()) { - object.insert("type", QString("UNDEFINED")); - - } else if (value.isNull()) { - object.insert("type", QString("NULL")); - - } else if (value.isBool()) { - object.insert("type", QString("BOOL")); - object.insert("value", value.toBool()); - - } else if (value.isNumber()) { - object.insert("type", QString("NUMBER")); - object.insert("value", value.toNumber()); - - } else if (value.isString()) { - object.insert("type", QString("STRING")); - object.insert("value", value.toString()); - - } else if (value.isVariant()) { - object.insert("type", QString("VARIANT")); - object.insert("value", getData(value.toVariant())); - - } else if (value.isQObject()) { - object.insert("type", QString("QOBJECT")); - object.insert("value", getData(value.toQObject())); - - } else if (value.isQMetaObject()) { - object.insert("type", QString("QMETAOBJECT")); - object.insert("value", getData(value.toQMetaObject())); - - } else if (value.isDate()) { - object.insert("type", QString("DATE")); - object.insert("value", getData(value.toDateTime())); - - } else if (value.isRegExp()) { - object.insert("type", QString("REGEXP")); - object.insert("value", getData(value.toRegExp())); - - } else if (value.isArray()) { - object.insert("type", QString("ARRAY")); - QJsonArray array; - int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); - for (int i = 0; i < length; i++) { - array.append(getData(value.property(i))); - } - object.insert("value", array); - - } else if (value.isObject()) { - object.insert("type", QString("OBJECT")); - QJsonObject valueObject; - for (QScriptValueIterator it(value); it.hasNext(); ) { - it.next(); - valueObject.insert(it.name(), getData(it.value())); - } - object.insert("value", valueObject); - - } else { - object.insert("type", QString("INVALID")); - } - return object; -} - -QJsonValue JSONWriter::getData(const QString& value) { - return value; -} - -QJsonValue JSONWriter::getData(const QUrl& value) { - return value.toString(); -} - -QJsonValue JSONWriter::getData(const QDateTime& value) { - return (qsreal)value.toMSecsSinceEpoch(); -} - -QJsonValue JSONWriter::getData(const QRegExp& value) { - QJsonObject object; - object.insert("pattern", value.pattern()); - object.insert("caseSensitivity", (int)value.caseSensitivity()); - object.insert("patternSyntax", (int)value.patternSyntax()); - object.insert("minimal", value.isMinimal()); - return object; -} - -QJsonValue JSONWriter::getData(const glm::vec3& value) { - QJsonArray array; - array.append(value.x); - array.append(value.y); - array.append(value.z); - return array; -} - -QJsonValue JSONWriter::getData(const glm::quat& value) { - QJsonArray array; - array.append(value.x); - array.append(value.y); - array.append(value.z); - array.append(value.w); - return array; -} - -QJsonValue JSONWriter::getData(const QMetaObject* metaObject) { - if (!metaObject) { - return QJsonValue(); - } - const ObjectStreamer* streamer = Bitstream::getObjectStreamers().value(metaObject); - addObjectStreamer(streamer); - return QString(streamer->getName()); -} - -QJsonValue JSONWriter::getData(const QVariant& value) { - if (!value.isValid()) { - return QJsonValue(); - } - const TypeStreamer* streamer = Bitstream::getTypeStreamers().value(value.userType()); - if (streamer) { - return streamer->getJSONVariantData(*this, value); - } else { - qWarning() << "Non-streamable type:" << value.typeName(); - return QJsonValue(); - } -} - -QJsonValue JSONWriter::getData(const SharedObjectPointer& object) { - if (object) { - addSharedObject(object); - return object->getID(); - } else { - return 0; - } -} - -QJsonValue JSONWriter::getData(const QObject* object) { - if (!object) { - return QJsonValue(); - } - const QMetaObject* metaObject = object->metaObject(); - const ObjectStreamer* streamer = (metaObject == &GenericSharedObject::staticMetaObject) ? - static_cast(object)->getStreamer().data() : - Bitstream::getObjectStreamers().value(metaObject); - return streamer->getJSONData(*this, object); -} - -QJsonValue JSONWriter::getData(const GenericValue& value) { - return value.getStreamer()->getJSONData(*this, value.getValue()); -} - -void JSONWriter::addTypeStreamer(const TypeStreamer* streamer) { - if (!_typeStreamerNames.contains(streamer->getName())) { - _typeStreamerNames.insert(streamer->getName()); - - QJsonValue metadata = streamer->getJSONMetadata(*this); - if (!metadata.isNull()) { - _typeStreamers.append(metadata); - } - } -} - -void JSONWriter::addObjectStreamer(const ObjectStreamer* streamer) { - if (!_objectStreamerNames.contains(streamer->getName())) { - _objectStreamerNames.insert(streamer->getName()); - _objectStreamers.append(streamer->getJSONMetadata(*this)); - } -} - -void JSONWriter::addSharedObject(const SharedObjectPointer& object) { - if (!_sharedObjectIDs.contains(object->getID())) { - _sharedObjectIDs.insert(object->getID()); - - QJsonObject sharedObject; - sharedObject.insert("id", object->getID()); - sharedObject.insert("data", getData(static_cast(object.data()))); - _sharedObjects.append(sharedObject); - } -} - -QJsonDocument JSONWriter::getDocument() const { - QJsonObject top; - top.insert("contents", _contents); - top.insert("objects", _sharedObjects); - top.insert("classes", _objectStreamers); - top.insert("types", _typeStreamers); - return QJsonDocument(top); -} - -JSONReader::JSONReader(const QJsonDocument& document, Bitstream::GenericsMode genericsMode) { - // create and map the type streamers in order - QJsonObject top = document.object(); - foreach (const QJsonValue& element, top.value("types").toArray()) { - QJsonObject type = element.toObject(); - QString name = type.value("name").toString(); - QByteArray latinName = name.toLatin1(); - const TypeStreamer* baseStreamer = Bitstream::getTypeStreamers().value(QMetaType::type(latinName)); - if (!baseStreamer) { - baseStreamer = Bitstream::getEnumStreamersByName().value(latinName); - } - if (!baseStreamer && genericsMode == Bitstream::NO_GENERICS) { - continue; // no built-in type and no generics allowed; we give up - } - QString category = type.value("category").toString(); - if (!baseStreamer || genericsMode == Bitstream::ALL_GENERICS) { - // create a generic streamer - TypeStreamerPointer streamer; - if (category == "ENUM") { - QVector values; - int highestValue = 0; - foreach (const QJsonValue& value, type.value("values").toArray()) { - QJsonObject object = value.toObject(); - int intValue = object.value("value").toInt(); - highestValue = qMax(intValue, highestValue); - values.append(NameIntPair(object.value("key").toString().toLatin1(), intValue)); - } - streamer = TypeStreamerPointer(new GenericEnumTypeStreamer(latinName, - values, getBitsForHighestValue(highestValue), QByteArray())); - - } else if (category == "STREAMABLE") { - QVector fields; - foreach (const QJsonValue& field, type.value("fields").toArray()) { - QJsonObject object = field.toObject(); - fields.append(StreamerNamePair(getTypeStreamer(object.value("type").toString()), - object.value("name").toString().toLatin1())); - } - streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(latinName, - fields, QByteArray())); - - } else if (category == "LIST") { - streamer = TypeStreamerPointer(new GenericListTypeStreamer(latinName, - getTypeStreamer(type.value("valueType").toString()))); - - } else if (category == "SET") { - streamer = TypeStreamerPointer(new GenericSetTypeStreamer(latinName, - getTypeStreamer(type.value("valueType").toString()))); - - } else if (category == "MAP") { - streamer = TypeStreamerPointer(new GenericMapTypeStreamer(latinName, - getTypeStreamer(type.value("keyType").toString()), - getTypeStreamer(type.value("valueType").toString()))); - } - _typeStreamers.insert(name, streamer); - static_cast(streamer.data())->_weakSelf = streamer; - continue; - } - // create a mapped streamer, determining along the way whether it matches our base - if (category == "ENUM") { - QHash mappings; - int highestValue = 0; - QMetaEnum metaEnum = baseStreamer->getMetaEnum(); - QJsonArray array = type.value("values").toArray(); - bool matches = (array.size() == metaEnum.keyCount()); - foreach (const QJsonValue& value, array) { - QJsonObject object = value.toObject(); - int intValue = object.value("value").toInt(); - highestValue = qMax(intValue, highestValue); - int mapping = metaEnum.keyToValue(object.value("key").toString().toLatin1()); - if (mapping != -1) { - mappings.insert(intValue, mapping); - } - matches &= (intValue == mapping); - } - // if everything matches our built-in enum, we can use that, which will be faster - if (matches) { - _typeStreamers.insert(name, baseStreamer->getSelf()); - } else { - _typeStreamers.insert(name, TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer, - getBitsForHighestValue(highestValue), mappings))); - } - } else if (category == "STREAMABLE") { - QVector fields; - QJsonArray array = type.value("fields").toArray(); - const QVector& metaFields = baseStreamer->getMetaFields(); - bool matches = (array.size() == metaFields.size()); - for (int i = 0; i < array.size(); i++) { - QJsonObject object = array.at(i).toObject(); - TypeStreamerPointer streamer = getTypeStreamer(object.value("type").toString()); - int index = baseStreamer->getFieldIndex(object.value("name").toString().toLatin1()); - fields.append(StreamerIndexPair(streamer, index)); - matches &= (index == i && streamer == metaFields.at(i).getStreamer()); - } - // if everything matches our built-in streamable, we can use that, which will be faster - if (matches) { - _typeStreamers.insert(name, baseStreamer->getSelf()); - } else { - _typeStreamers.insert(name, TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields))); - } - } else if (category == "LIST") { - TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString()); - if (valueStreamer == baseStreamer->getValueStreamer()) { - _typeStreamers.insert(name, baseStreamer->getSelf()); - - } else { - _typeStreamers.insert(name, TypeStreamerPointer(new MappedListTypeStreamer(baseStreamer, valueStreamer))); - } - } else if (category == "SET") { - TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString()); - if (valueStreamer == baseStreamer->getValueStreamer()) { - _typeStreamers.insert(name, baseStreamer->getSelf()); - - } else { - _typeStreamers.insert(name, TypeStreamerPointer(new MappedSetTypeStreamer(baseStreamer, valueStreamer))); - } - } else if (category == "MAP") { - TypeStreamerPointer keyStreamer = getTypeStreamer(type.value("keyType").toString()); - TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString()); - if (keyStreamer == baseStreamer->getKeyStreamer() && valueStreamer == baseStreamer->getValueStreamer()) { - _typeStreamers.insert(name, baseStreamer->getSelf()); - - } else { - _typeStreamers.insert(name, TypeStreamerPointer(new MappedMapTypeStreamer( - baseStreamer, keyStreamer, valueStreamer))); - } - } - } - - // create and map the object streamers in order - foreach (const QJsonValue& element, top.value("classes").toArray()) { - QJsonObject clazz = element.toObject(); - QString name = clazz.value("name").toString(); - QByteArray latinName = name.toLatin1(); - const ObjectStreamer* baseStreamer = Bitstream::getObjectStreamers().value( - Bitstream::getMetaObjects().value(latinName)); - if (!baseStreamer && genericsMode == Bitstream::NO_GENERICS) { - continue; // no built-in class and no generics allowed; we give up - } - if (!baseStreamer || genericsMode == Bitstream::ALL_GENERICS) { - // create a generic streamer - QVector properties; - foreach (const QJsonValue& property, clazz.value("properties").toArray()) { - QJsonObject object = property.toObject(); - properties.append(StreamerNamePair(getTypeStreamer(object.value("type").toString()), - object.value("name").toString().toLatin1())); - } - ObjectStreamerPointer streamer = ObjectStreamerPointer(new GenericObjectStreamer( - latinName, properties, QByteArray())); - _objectStreamers.insert(name, streamer); - static_cast(streamer.data())->_weakSelf = streamer; - continue; - } - // create a mapped streamer, determining along the way whether it matches our base - const QMetaObject* metaObject = baseStreamer->getMetaObject(); - const QVector& baseProperties = baseStreamer->getProperties(); - QVector properties; - QJsonArray propertyArray = clazz.value("properties").toArray(); - bool matches = (baseProperties.size() == propertyArray.size()); - for (int i = 0; i < propertyArray.size(); i++) { - QJsonObject object = propertyArray.at(i).toObject(); - TypeStreamerPointer typeStreamer = getTypeStreamer(object.value("type").toString()); - QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty( - object.value("name").toString().toLatin1())); - properties.append(StreamerPropertyPair(typeStreamer, metaProperty)); - - const StreamerPropertyPair& baseProperty = baseProperties.at(i); - matches &= (typeStreamer == baseProperty.first && - metaProperty.propertyIndex() == baseProperty.second.propertyIndex()); - } - // if everything matches our built-in type, we can use that directly, which will be faster - if (matches) { - _objectStreamers.insert(name, baseStreamer->getSelf()); - } else { - _objectStreamers.insert(name, ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties))); - } - } - - // create and map the objects in order - foreach (const QJsonValue& element, top.value("objects").toArray()) { - QJsonObject object = element.toObject(); - int id = object.value("id").toInt(); - QObject* qObject; - putData(object.value("data"), qObject); - if (qObject) { - _sharedObjects.insert(id, static_cast(qObject)); - } - } - - // prepare the contents for extraction - _contents = top.value("contents").toArray(); - _contentsIterator = _contents.constBegin(); -} - -void JSONReader::putData(const QJsonValue& data, bool& value) { - value = data.toBool(); -} - -void JSONReader::putData(const QJsonValue& data, int& value) { - value = data.toInt(); -} - -void JSONReader::putData(const QJsonValue& data, uint& value) { - value = data.toInt(); -} - -void JSONReader::putData(const QJsonValue& data, float& value) { - value = data.toDouble(); -} - -void JSONReader::putData(const QJsonValue& data, QByteArray& value) { - value = QByteArray::fromPercentEncoding(data.toString().toLatin1()); -} - -void JSONReader::putData(const QJsonValue& data, QColor& value) { - value.setNamedColor(data.toString()); -} - -void JSONReader::putData(const QJsonValue& data, QScriptValue& value) { - QJsonObject object = data.toObject(); - QString type = object.value("type").toString(); - if (type == "UNDEFINED") { - value = QScriptValue(QScriptValue::UndefinedValue); - - } else if (type == "NULL") { - value = QScriptValue(QScriptValue::NullValue); - - } else if (type == "BOOL") { - value = QScriptValue(object.value("value").toBool()); - - } else if (type == "NUMBER") { - value = QScriptValue(object.value("value").toDouble()); - - } else if (type == "STRING") { - value = QScriptValue(object.value("value").toString()); - - } else if (type == "VARIANT") { - QVariant variant; - putData(object.value("value"), variant); - value = DependencyManager::get()->getEngine()->newVariant(variant); - - } else if (type == "QOBJECT") { - QObject* qObject; - putData(object.value("value"), qObject); - value = DependencyManager::get()->getEngine()->newQObject(qObject, QScriptEngine::ScriptOwnership); - - } else if (type == "QMETAOBJECT") { - const QMetaObject* metaObject; - putData(object.value("value"), metaObject); - value = DependencyManager::get()->getEngine()->newQMetaObject(metaObject); - - } else if (type == "DATE") { - QDateTime dateTime; - putData(object.value("value"), dateTime); - value = DependencyManager::get()->getEngine()->newDate(dateTime); - - } else if (type == "REGEXP") { - QRegExp regExp; - putData(object.value("value"), regExp); - value = DependencyManager::get()->getEngine()->newRegExp(regExp); - - } else if (type == "ARRAY") { - QJsonArray array = object.value("value").toArray(); - value = DependencyManager::get()->getEngine()->newArray(array.size()); - for (int i = 0; i < array.size(); i++) { - QScriptValue element; - putData(array.at(i), element); - value.setProperty(i, element); - } - } else if (type == "OBJECT") { - QJsonObject jsonObject = object.value("value").toObject(); - value = DependencyManager::get()->getEngine()->newObject(); - for (QJsonObject::const_iterator it = jsonObject.constBegin(); it != jsonObject.constEnd(); it++) { - QScriptValue element; - putData(it.value(), element); - value.setProperty(it.key(), element); - } - } else { - value = QScriptValue(); - } -} - -void JSONReader::putData(const QJsonValue& data, QString& value) { - value = data.toString(); -} - -void JSONReader::putData(const QJsonValue& data, QUrl& value) { - value = data.toString(); -} - -void JSONReader::putData(const QJsonValue& data, QDateTime& value) { - value.setMSecsSinceEpoch((qint64)data.toDouble()); -} - -void JSONReader::putData(const QJsonValue& data, QRegExp& value) { - QJsonObject object = data.toObject(); - value = QRegExp(object.value("pattern").toString(), (Qt::CaseSensitivity)object.value("caseSensitivity").toInt(), - (QRegExp::PatternSyntax)object.value("patternSyntax").toInt()); - value.setMinimal(object.value("minimal").toBool()); -} - -void JSONReader::putData(const QJsonValue& data, glm::vec3& value) { - QJsonArray array = data.toArray(); - value = glm::vec3(array.at(0).toDouble(), array.at(1).toDouble(), array.at(2).toDouble()); -} - -void JSONReader::putData(const QJsonValue& data, glm::quat& value) { - QJsonArray array = data.toArray(); - value = glm::quat(array.at(0).toDouble(), array.at(1).toDouble(), array.at(2).toDouble(), array.at(3).toDouble()); -} - -void JSONReader::putData(const QJsonValue& data, const QMetaObject*& value) { - ObjectStreamerPointer streamer = _objectStreamers.value(data.toString()); - value = streamer ? streamer->getMetaObject() : NULL; -} - -void JSONReader::putData(const QJsonValue& data, QVariant& value) { - QJsonObject object = data.toObject(); - QString type = object.value("type").toString(); - TypeStreamerPointer streamer = getTypeStreamer(type); - if (streamer) { - streamer->putJSONVariantData(*this, object.value("value"), value); - } else { - value = QVariant(); - } -} - -void JSONReader::putData(const QJsonValue& data, SharedObjectPointer& value) { - value = _sharedObjects.value(data.toInt()); -} - -void JSONReader::putData(const QJsonValue& data, QObject*& value) { - QJsonObject object = data.toObject(); - ObjectStreamerPointer streamer = _objectStreamers.value(object.value("class").toString()); - value = streamer ? streamer->putJSONData(*this, object) : NULL; -} - -TypeStreamerPointer JSONReader::getTypeStreamer(const QString& name) const { - TypeStreamerPointer streamer = _typeStreamers.value(name); - if (!streamer) { - const TypeStreamer* defaultStreamer = Bitstream::getTypeStreamers().value(QMetaType::type(name.toLatin1())); - if (defaultStreamer) { - streamer = defaultStreamer->getSelf(); - } else { - qWarning() << "Unknown type:" << name; - } - } - return streamer; -} - -ObjectStreamer::ObjectStreamer(const QMetaObject* metaObject) : - _metaObject(metaObject) { -} - -ObjectStreamer::~ObjectStreamer() { -} - -const QVector& ObjectStreamer::getProperties() const { - static QVector emptyProperties; - return emptyProperties; -} - -MappedObjectStreamer::MappedObjectStreamer(const QMetaObject* metaObject, const QVector& properties) : - ObjectStreamer(metaObject), - _properties(properties) { -} - -const char* MappedObjectStreamer::getName() const { - return _metaObject->className(); -} - -const QVector& MappedObjectStreamer::getProperties() const { - return _properties; -} - -void MappedObjectStreamer::writeMetadata(Bitstream& out, bool full) const { - out << _properties.size(); - if (_properties.isEmpty()) { - return; - } - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const StreamerPropertyPair& property, _properties) { - out << property.first.data(); - if (full) { - out << QByteArray::fromRawData(property.second.name(), strlen(property.second.name())); - } else { - hash.addData(property.second.name(), strlen(property.second.name()) + 1); - } - } - if (!full) { - QByteArray hashResult = hash.result(); - out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); - } -} - -QJsonObject MappedObjectStreamer::getJSONMetadata(JSONWriter& writer) const { - QJsonObject metadata; - metadata.insert("name", QString(_metaObject->className())); - QJsonArray properties; - foreach (const StreamerPropertyPair& property, _properties) { - QJsonObject object; - writer.addTypeStreamer(property.first.data()); - object.insert("type", QString(property.first->getName())); - object.insert("name", QString(property.second.name())); - properties.append(object); - } - metadata.insert("properties", properties); - return metadata; -} - -QJsonObject MappedObjectStreamer::getJSONData(JSONWriter& writer, const QObject* object) const { - QJsonObject data; - writer.addObjectStreamer(this); - data.insert("class", QString(_metaObject->className())); - QJsonArray properties; - foreach (const StreamerPropertyPair& property, _properties) { - properties.append(property.first->getJSONData(writer, property.second.read(object))); - } - data.insert("properties", properties); - return data; -} - -QObject* MappedObjectStreamer::putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const { - if (!_metaObject) { - return NULL; - } - QObject* object = _metaObject->newInstance(); - QJsonArray properties = jsonObject.value("properties").toArray(); - for (int i = 0; i < _properties.size(); i++) { - const StreamerPropertyPair& property = _properties.at(i); - if (property.second.isValid()) { - QVariant value; - property.first->putJSONData(reader, properties.at(i), value); - property.second.write(object, value); - } - } - return object; -} - -bool MappedObjectStreamer::equal(const QObject* first, const QObject* second) const { - foreach (const StreamerPropertyPair& property, _properties) { - if (!property.first->equal(property.second.read(first), property.second.read(second))) { - return false; - } - } - return true; -} - -void MappedObjectStreamer::write(Bitstream& out, const QObject* object) const { - foreach (const StreamerPropertyPair& property, _properties) { - property.first->write(out, property.second.read(object)); - } -} - -void MappedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const { - foreach (const StreamerPropertyPair& property, _properties) { - property.first->writeDelta(out, property.second.read(object), (reference && reference->metaObject() == _metaObject) ? - property.second.read(reference) : QVariant()); - } -} - -QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { - bool reread = (object != NULL); - if (!object && _metaObject) { - object = _metaObject->newInstance(); - } - foreach (const StreamerPropertyPair& property, _properties) { - QVariant value = property.first->read(in); - if (property.second.isValid() && object && !reread) { - property.second.write(object, value); - } - } - return object; -} - -QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { - bool reread = (object != NULL); - if (!object && _metaObject) { - object = _metaObject->newInstance(); - } - foreach (const StreamerPropertyPair& property, _properties) { - QVariant value; - property.first->readDelta(in, value, (property.second.isValid() && reference && - reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant()); - if (property.second.isValid() && object && !reread) { - property.second.write(object, value); - } - } - return object; -} - -SharedObjectStreamer::SharedObjectStreamer(const QMetaObject* metaObject, const QVector& properties) : - MappedObjectStreamer(metaObject, properties) { -} - -void SharedObjectStreamer::write(Bitstream& out, const QObject* object) const { - MappedObjectStreamer::write(out, object); - static_cast(object)->writeExtra(out); -} - -void SharedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const { - MappedObjectStreamer::writeRawDelta(out, object, reference); - static_cast(object)->writeExtraDelta(out, static_cast(reference)); -} - -QObject* SharedObjectStreamer::read(Bitstream& in, QObject* object) const { - QObject* result = MappedObjectStreamer::read(in, object); - static_cast(result)->readExtra(in, object != NULL); - return result; -} - -QObject* SharedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { - QObject* result = MappedObjectStreamer::readRawDelta(in, reference, object); - static_cast(result)->readExtraDelta(in, static_cast(reference), object != NULL); - return result; -} - -GenericObjectStreamer::GenericObjectStreamer(const QByteArray& name, const QVector& properties, - const QByteArray& hash) : - ObjectStreamer(&GenericSharedObject::staticMetaObject), - _name(name), - _properties(properties), - _hash(hash) { -} - -const char* GenericObjectStreamer::getName() const { - return _name.constData(); -} - -void GenericObjectStreamer::writeMetadata(Bitstream& out, bool full) const { - out << _properties.size(); - if (_properties.isEmpty()) { - return; - } - foreach (const StreamerNamePair& property, _properties) { - out << property.first.data(); - if (full) { - out << property.second; - } - } - if (!full) { - if (_hash.isEmpty()) { - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const StreamerNamePair& property, _properties) { - hash.addData(property.second.constData(), property.second.size() + 1); - } - const_cast(this)->_hash = hash.result(); - } - out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); - } -} - -QJsonObject GenericObjectStreamer::getJSONMetadata(JSONWriter& writer) const { - QJsonObject metadata; - metadata.insert("name", QString(_name)); - QJsonArray properties; - foreach (const StreamerNamePair& property, _properties) { - QJsonObject object; - writer.addTypeStreamer(property.first.data()); - object.insert("type", QString(property.first->getName())); - object.insert("name", QString(property.second)); - properties.append(object); - } - metadata.insert("properties", properties); - return metadata; -} - -QJsonObject GenericObjectStreamer::getJSONData(JSONWriter& writer, const QObject* object) const { - QJsonObject data; - writer.addObjectStreamer(this); - data.insert("class", QString(_name)); - QJsonArray properties; - const QVariantList& values = static_cast(object)->getValues(); - for (int i = 0; i < _properties.size(); i++) { - properties.append(_properties.at(i).first->getJSONData(writer, values.at(i))); - } - data.insert("properties", properties); - return data; -} - -QObject* GenericObjectStreamer::putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const { - GenericSharedObject* object = new GenericSharedObject(_weakSelf); - QJsonArray properties = jsonObject.value("properties").toArray(); - QVariantList values; - for (int i = 0; i < _properties.size(); i++) { - QVariant value; - _properties.at(i).first->putJSONData(reader, properties.at(i), value); - values.append(value); - } - object->setValues(values); - return object; -} - -bool GenericObjectStreamer::equal(const QObject* first, const QObject* second) const { - const QVariantList& firstValues = static_cast(first)->getValues(); - const QVariantList& secondValues = static_cast(second)->getValues(); - for (int i = 0; i < _properties.size(); i++) { - if (!_properties.at(i).first->equal(firstValues.at(i), secondValues.at(i))) { - return false; - } - } - return true; -} - -void GenericObjectStreamer::write(Bitstream& out, const QObject* object) const { - const QVariantList& values = static_cast(object)->getValues(); - for (int i = 0; i < _properties.size(); i++) { - _properties.at(i).first->write(out, values.at(i)); - } -} - -void GenericObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const { - const GenericSharedObject* genericObject = static_cast(object); - const GenericSharedObject* genericReference = (reference && - reference->metaObject() == &GenericSharedObject::staticMetaObject) ? - static_cast(reference) : NULL; - for (int i = 0; i < _properties.size(); i++) { - _properties.at(i).first->writeDelta(out, genericObject->getValues().at(i), - (genericReference && genericReference->getStreamer() == genericObject->getStreamer()) ? - genericReference->getValues().at(i) : QVariant()); - } -} - -QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { - bool reread = (object != NULL); - if (!object) { - object = new GenericSharedObject(_weakSelf); - } - QVariantList values; - foreach (const StreamerNamePair& property, _properties) { - values.append(property.first->read(in)); - } - if (!reread) { - static_cast(object)->setValues(values); - } - return object; -} - -QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { - bool reread = (object != NULL); - if (!object) { - object = new GenericSharedObject(_weakSelf); - } - QVariantList values; - for (int i = 0; i < _properties.size(); i++) { - const StreamerNamePair& property = _properties.at(i); - QVariant value; - property.first->readDelta(in, value, reference ? - static_cast(reference)->getValues().at(i) : QVariant()); - values.append(value); - } - if (!reread) { - static_cast(object)->setValues(values); - } - return object; -} - -MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : - _name(name), - _streamer(streamer) { -} - -GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : - _streamer(streamer), - _value(value) { -} - -bool GenericValue::operator==(const GenericValue& other) const { - return _streamer == other._streamer && _value == other._value; -} - -GenericSharedObject::GenericSharedObject(const ObjectStreamerPointer& streamer) : - _streamer(streamer) { -} - -TypeStreamer::~TypeStreamer() { -} - -const char* TypeStreamer::getName() const { - return QMetaType::typeName(_type); -} - -const TypeStreamer* TypeStreamer::getStreamerToWrite(const QVariant& value) const { - return this; -} - -void TypeStreamer::writeMetadata(Bitstream& out, bool full) const { - if (getCategory() != STREAMABLE_CATEGORY) { - return; - } - // streamable type - const QVector& metaFields = getMetaFields(); - out << metaFields.size(); - if (metaFields.isEmpty()) { - return; - } - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const MetaField& metaField, metaFields) { - out << metaField.getStreamer(); - if (full) { - out << metaField.getName(); - } else { - hash.addData(metaField.getName().constData(), metaField.getName().size() + 1); - } - } - if (!full) { - QByteArray hashResult = hash.result(); - out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); - } -} - -QJsonValue TypeStreamer::getJSONMetadata(JSONWriter& writer) const { - Category category = getCategory(); - switch (category) { - case STREAMABLE_CATEGORY: { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString("STREAMABLE")); - QJsonArray fields; - foreach (const MetaField& metaField, getMetaFields()) { - QJsonObject field; - writer.addTypeStreamer(metaField.getStreamer()); - field.insert("type", QString(metaField.getStreamer()->getName())); - field.insert("name", QString(metaField.getName())); - fields.append(field); - } - metadata.insert("fields", fields); - return metadata; - } - case LIST_CATEGORY: - case SET_CATEGORY: { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString(category == LIST_CATEGORY ? "LIST" : "SET")); - const TypeStreamer* valueStreamer = getValueStreamer(); - writer.addTypeStreamer(valueStreamer); - metadata.insert("valueType", QString(valueStreamer->getName())); - return metadata; - } - case MAP_CATEGORY: { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString("MAP")); - const TypeStreamer* keyStreamer = getKeyStreamer(); - writer.addTypeStreamer(keyStreamer); - metadata.insert("keyType", QString(keyStreamer->getName())); - const TypeStreamer* valueStreamer = getValueStreamer(); - writer.addTypeStreamer(valueStreamer); - metadata.insert("valueType", QString(valueStreamer->getName())); - return metadata; - } - default: - return QJsonValue(); - } -} - -QJsonValue TypeStreamer::getJSONData(JSONWriter& writer, const QVariant& value) const { - return QJsonValue(); -} - -QJsonValue TypeStreamer::getJSONVariantData(JSONWriter& writer, const QVariant& value) const { - writer.addTypeStreamer(this); - QJsonObject data; - data.insert("type", QString(getName())); - data.insert("value", getJSONData(writer, value)); - return data; -} - -void TypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - value = QVariant(); -} - -void TypeStreamer::putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - putJSONData(reader, data, value); -} - -bool TypeStreamer::equal(const QVariant& first, const QVariant& second) const { - return first == second; -} - -void TypeStreamer::write(Bitstream& out, const QVariant& value) const { - // nothing by default -} - -QVariant TypeStreamer::read(Bitstream& in) const { - return QVariant(); -} - -void TypeStreamer::writeVariant(Bitstream& out, const QVariant& value) const { - out << this; - write(out, value); -} - -QVariant TypeStreamer::readVariant(Bitstream& in) const { - return read(in); -} - -void TypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { - if (value == reference) { - out << false; - } else { - out << true; - writeRawDelta(out, value, reference); - } -} - -void TypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - bool changed; - in >> changed; - if (changed) { - readRawDelta(in, value, reference); - } else { - value = reference; - } -} - -void TypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { - // nothing by default -} - -void TypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - value = reference; -} - -void TypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) const { - // nothing by default -} - -const QVector& TypeStreamer::getMetaFields() const { - static QVector emptyMetaFields; - return emptyMetaFields; -} - -int TypeStreamer::getFieldIndex(const QByteArray& name) const { - return -1; -} - -void TypeStreamer::setField(QVariant& object, int index, const QVariant& value) const { - // nothing by default -} - -QVariant TypeStreamer::getField(const QVariant& object, int index) const { - return QVariant(); -} - -TypeStreamer::Category TypeStreamer::getCategory() const { - return SIMPLE_CATEGORY; -} - -int TypeStreamer::getBits() const { - return 0; -} - -QMetaEnum TypeStreamer::getMetaEnum() const { - return QMetaEnum(); -} - -const TypeStreamer* TypeStreamer::getKeyStreamer() const { - return NULL; -} - -const TypeStreamer* TypeStreamer::getValueStreamer() const { - return NULL; -} - -void TypeStreamer::insert(QVariant& object, const QVariant& element) const { - // nothing by default -} - -void TypeStreamer::insert(QVariant& object, const QVariant& key, const QVariant& value) const { - // nothing by default -} - -bool TypeStreamer::remove(QVariant& object, const QVariant& key) const { - return false; -} - -QVariant TypeStreamer::getValue(const QVariant& object, const QVariant& key) const { - return QVariant(); -} - -void TypeStreamer::prune(QVariant& object, int size) const { - // nothing by default -} - -QVariant TypeStreamer::getValue(const QVariant& object, int index) const { - return QVariant(); -} - -void TypeStreamer::setValue(QVariant& object, int index, const QVariant& value) const { - // nothing by default -} - -QDebug& operator<<(QDebug& debug, const TypeStreamer* typeStreamer) { - return debug << (typeStreamer ? QMetaType::typeName(typeStreamer->getType()) : "null"); -} - -QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject) { - return debug << (metaObject ? metaObject->className() : "null"); -} - -EnumTypeStreamer::EnumTypeStreamer(const QMetaObject* metaObject, const char* name) : - _metaObject(metaObject), - _enumName(name), - _name(getEnumName(metaObject->className(), name)), - _bits(-1) { - - _type = QMetaType::Int; - _self = TypeStreamerPointer(this); -} - -EnumTypeStreamer::EnumTypeStreamer(const QMetaEnum& metaEnum) : - _name(getEnumName(metaEnum.scope(), metaEnum.name())), - _metaEnum(metaEnum), - _bits(-1) { - - _type = QMetaType::Int; - _self = TypeStreamerPointer(this); -} - -const char* EnumTypeStreamer::getName() const { - return _name.constData(); -} - -void EnumTypeStreamer::writeMetadata(Bitstream& out, bool full) const { - QMetaEnum metaEnum = getMetaEnum(); - if (full) { - out << metaEnum.keyCount(); - for (int i = 0; i < metaEnum.keyCount(); i++) { - out << QByteArray::fromRawData(metaEnum.key(i), strlen(metaEnum.key(i))); - out << metaEnum.value(i); - } - } else { - out << getBits(); - QCryptographicHash hash(QCryptographicHash::Md5); - for (int i = 0; i < metaEnum.keyCount(); i++) { - hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); - qint32 value = metaEnum.value(i); - hash.addData((const char*)&value, sizeof(qint32)); - } - QByteArray hashResult = hash.result(); - out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); - } -} - -QJsonValue EnumTypeStreamer::getJSONMetadata(JSONWriter& writer) const { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString("ENUM")); - QJsonArray values; - QMetaEnum metaEnum = getMetaEnum(); - for (int i = 0; i < metaEnum.keyCount(); i++) { - QJsonObject value; - value.insert("key", QString(metaEnum.key(i))); - value.insert("value", metaEnum.value(i)); - values.append(value); - } - metadata.insert("values", values); - return metadata; -} - -QJsonValue EnumTypeStreamer::getJSONData(JSONWriter& writer, const QVariant& value) const { - return value.toInt(); -} - -void EnumTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - value = data.toInt(); -} - -TypeStreamer::Category EnumTypeStreamer::getCategory() const { - return ENUM_CATEGORY; -} - -int EnumTypeStreamer::getBits() const { - if (_bits == -1) { - int highestValue = 0; - QMetaEnum metaEnum = getMetaEnum(); - for (int j = 0; j < metaEnum.keyCount(); j++) { - highestValue = qMax(highestValue, metaEnum.value(j)); - } - const_cast(this)->_bits = getBitsForHighestValue(highestValue); - } - return _bits; -} - -QMetaEnum EnumTypeStreamer::getMetaEnum() const { - if (!_metaEnum.isValid()) { - const_cast(this)->_metaEnum = _metaObject->enumerator(_metaObject->indexOfEnumerator(_enumName)); - } - return _metaEnum; -} - -bool EnumTypeStreamer::equal(const QVariant& first, const QVariant& second) const { - return first.toInt() == second.toInt(); -} - -void EnumTypeStreamer::write(Bitstream& out, const QVariant& value) const { - int intValue = value.toInt(); - out.write(&intValue, getBits()); -} - -QVariant EnumTypeStreamer::read(Bitstream& in) const { - int intValue = 0; - in.read(&intValue, getBits()); - return intValue; -} - -void EnumTypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { - int intValue = value.toInt(), intReference = reference.toInt(); - if (intValue == intReference) { - out << false; - } else { - out << true; - out.write(&intValue, getBits()); - } -} - -void EnumTypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - bool changed; - in >> changed; - if (changed) { - int intValue = 0; - in.read(&intValue, getBits()); - value = intValue; - } else { - value = reference; - } -} - -void EnumTypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { - int intValue = value.toInt(); - out.write(&intValue, getBits()); -} - -void EnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - int intValue = 0; - in.read(&intValue, getBits()); - value = intValue; -} - -void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) const { - if (getMetaEnum().isFlag()) { - int combined = 0; - for (QHash::const_iterator it = mappings.constBegin(); it != mappings.constEnd(); it++) { - if (value & it.key()) { - combined |= it.value(); - } - } - object = combined; - - } else { - object = mappings.value(value); - } -} - -MappedEnumTypeStreamer::MappedEnumTypeStreamer(const TypeStreamer* baseStreamer, int bits, const QHash& mappings) : - _baseStreamer(baseStreamer), - _bits(bits), - _mappings(mappings) { -} - -void MappedEnumTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - value = QVariant(_baseStreamer->getType(), 0); - _baseStreamer->setEnumValue(value, data.toInt(), _mappings); -} - -QVariant MappedEnumTypeStreamer::read(Bitstream& in) const { - QVariant object = QVariant(_baseStreamer->getType(), 0); - int value = 0; - in.read(&value, _bits); - _baseStreamer->setEnumValue(object, value, _mappings); - return object; -} - -void MappedEnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - int value = 0; - in.read(&value, _bits); - _baseStreamer->setEnumValue(object, value, _mappings); -} - -GenericTypeStreamer::GenericTypeStreamer(const QByteArray& name) : - _name(name) { -} - -const char* GenericTypeStreamer::getName() const { - return _name.constData(); -} - -void GenericTypeStreamer::putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - QVariant containedValue; - putJSONData(reader, data, containedValue); - value = QVariant::fromValue(GenericValue(_weakSelf, containedValue)); -} - -QVariant GenericTypeStreamer::readVariant(Bitstream& in) const { - return QVariant::fromValue(GenericValue(_weakSelf, read(in))); -} - -GenericEnumTypeStreamer::GenericEnumTypeStreamer(const QByteArray& name, const QVector& values, - int bits, const QByteArray& hash) : - GenericTypeStreamer(name), - _values(values), - _bits(bits), - _hash(hash) { - - _type = qMetaTypeId(); -} - -void GenericEnumTypeStreamer::writeMetadata(Bitstream& out, bool full) const { - if (full) { - out << _values.size(); - foreach (const NameIntPair& value, _values) { - out << value.first << value.second; - } - } else { - out << _bits; - if (_hash.isEmpty()) { - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const NameIntPair& value, _values) { - hash.addData(value.first.constData(), value.first.size() + 1); - qint32 intValue = value.second; - hash.addData((const char*)&intValue, sizeof(qint32)); - } - const_cast(this)->_hash = hash.result(); - } - out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); - } -} - -QJsonValue GenericEnumTypeStreamer::getJSONMetadata(JSONWriter& writer) const { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString("ENUM")); - QJsonArray values; - foreach (const NameIntPair& value, _values) { - QJsonObject object; - object.insert("key", QString(value.first)); - object.insert("value", value.second); - values.append(object); - } - metadata.insert("values", values); - return metadata; -} - -QJsonValue GenericEnumTypeStreamer::getJSONData(JSONWriter& writer, const QVariant& value) const { - return value.toInt(); -} - -void GenericEnumTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - value = data.toInt(); -} - -void GenericEnumTypeStreamer::write(Bitstream& out, const QVariant& value) const { - int intValue = value.toInt(); - out.write(&intValue, _bits); -} - -QVariant GenericEnumTypeStreamer::read(Bitstream& in) const { - int intValue = 0; - in.read(&intValue, _bits); - return intValue; -} - -TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { - return ENUM_CATEGORY; -} - -MappedStreamableTypeStreamer::MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, - const QVector& fields) : - _baseStreamer(baseStreamer), - _fields(fields) { -} - -void MappedStreamableTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - value = QVariant(_baseStreamer->getType(), 0); - QJsonArray array = data.toArray(); - for (int i = 0; i < _fields.size(); i++) { - const StreamerIndexPair& pair = _fields.at(i); - if (pair.second != -1) { - QVariant element; - pair.first->putJSONData(reader, array.at(i), element); - _baseStreamer->setField(value, pair.second, element); - } - } -} - -QVariant MappedStreamableTypeStreamer::read(Bitstream& in) const { - QVariant object = QVariant(_baseStreamer->getType(), 0); - foreach (const StreamerIndexPair& pair, _fields) { - QVariant value = pair.first->read(in); - if (pair.second != -1) { - _baseStreamer->setField(object, pair.second, value); - } - } - return object; -} - -void MappedStreamableTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - foreach (const StreamerIndexPair& pair, _fields) { - QVariant value; - if (pair.second != -1) { - pair.first->readDelta(in, value, _baseStreamer->getField(reference, pair.second)); - _baseStreamer->setField(object, pair.second, value); - } else { - pair.first->readDelta(in, value, QVariant()); - } - } -} - -GenericStreamableTypeStreamer::GenericStreamableTypeStreamer(const QByteArray& name, - const QVector& fields, const QByteArray& hash) : - GenericTypeStreamer(name), - _fields(fields), - _hash(hash) { - - _type = qMetaTypeId(); -} - -void GenericStreamableTypeStreamer::writeMetadata(Bitstream& out, bool full) const { - out << _fields.size(); - if (_fields.isEmpty()) { - return; - } - foreach (const StreamerNamePair& field, _fields) { - out << field.first.data(); - if (full) { - out << field.second; - } - } - if (!full) { - if (_hash.isEmpty()) { - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const StreamerNamePair& field, _fields) { - hash.addData(field.second.constData(), field.second.size() + 1); - } - const_cast(this)->_hash = hash.result(); - } - out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); - } -} - -QJsonValue GenericStreamableTypeStreamer::getJSONMetadata(JSONWriter& writer) const { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString("STREAMABLE")); - QJsonArray fields; - foreach (const StreamerNamePair& field, _fields) { - QJsonObject object; - writer.addTypeStreamer(field.first.data()); - object.insert("type", QString(field.first->getName())); - object.insert("name", QString(field.second)); - fields.append(object); - } - metadata.insert("fields", fields); - return metadata; -} - -QJsonValue GenericStreamableTypeStreamer::getJSONData(JSONWriter& writer, const QVariant& value) const { - QVariantList values = value.toList(); - QJsonArray array; - for (int i = 0; i < _fields.size(); i++) { - array.append(_fields.at(i).first->getJSONData(writer, values.at(i))); - } - return array; -} - -void GenericStreamableTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - QJsonArray array = data.toArray(); - QVariantList values; - for (int i = 0; i < _fields.size(); i++) { - QVariant element; - _fields.at(i).first->putJSONData(reader, array.at(i), element); - values.append(element); - } - value = values; -} - -void GenericStreamableTypeStreamer::write(Bitstream& out, const QVariant& value) const { - QVariantList values = value.toList(); - for (int i = 0; i < _fields.size(); i++) { - _fields.at(i).first->write(out, values.at(i)); - } -} - -QVariant GenericStreamableTypeStreamer::read(Bitstream& in) const { - QVariantList values; - foreach (const StreamerNamePair& field, _fields) { - values.append(field.first->read(in)); - } - return values; -} - -TypeStreamer::Category GenericStreamableTypeStreamer::getCategory() const { - return STREAMABLE_CATEGORY; -} - -MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : - _baseStreamer(baseStreamer), - _valueStreamer(valueStreamer) { -} - -void MappedListTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - value = QVariant(_baseStreamer->getType(), 0); - foreach (const QJsonValue& element, data.toArray()) { - QVariant elementValue; - _valueStreamer->putJSONData(reader, element, elementValue); - _baseStreamer->insert(value, elementValue); - } -} - -QVariant MappedListTypeStreamer::read(Bitstream& in) const { - QVariant object = QVariant(_baseStreamer->getType(), 0); - int size; - in >> size; - for (int i = 0; i < size; i++) { - QVariant value = _valueStreamer->read(in); - _baseStreamer->insert(object, value); - } - return object; -} - -void MappedListTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - object = reference; - int size, referenceSize; - in >> size >> referenceSize; - if (size < referenceSize) { - _baseStreamer->prune(object, size); - } - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, i)); - _baseStreamer->setValue(object, i, value); - } else { - _baseStreamer->insert(object, _valueStreamer->read(in)); - } - } -} - -GenericListTypeStreamer::GenericListTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer) : - GenericTypeStreamer(name), - _valueStreamer(valueStreamer) { - - _type = qMetaTypeId(); -} - -void GenericListTypeStreamer::writeMetadata(Bitstream& out, bool full) const { - out << _valueStreamer.data(); -} - -QJsonValue GenericListTypeStreamer::getJSONMetadata(JSONWriter& writer) const { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString("LIST")); - writer.addTypeStreamer(_valueStreamer.data()); - metadata.insert("valueType", QString(_valueStreamer->getName())); - return metadata; -} - -QJsonValue GenericListTypeStreamer::getJSONData(JSONWriter& writer, const QVariant& value) const { - QVariantList values = value.toList(); - QJsonArray array; - foreach (const QVariant& element, values) { - array.append(_valueStreamer->getJSONData(writer, element)); - } - return array; -} - -void GenericListTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - QJsonArray array = data.toArray(); - QVariantList values; - foreach (const QJsonValue& element, array) { - QVariant elementValue; - _valueStreamer->putJSONData(reader, element, elementValue); - values.append(elementValue); - } - value = values; -} - -void GenericListTypeStreamer::write(Bitstream& out, const QVariant& value) const { - QVariantList values = value.toList(); - out << values.size(); - foreach (const QVariant& element, values) { - _valueStreamer->write(out, element); - } -} - -QVariant GenericListTypeStreamer::read(Bitstream& in) const { - QVariantList values; - int size; - in >> size; - for (int i = 0; i < size; i++) { - values.append(_valueStreamer->read(in)); - } - return values; -} - -TypeStreamer::Category GenericListTypeStreamer::getCategory() const { - return LIST_CATEGORY; -} - -MappedSetTypeStreamer::MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : - MappedListTypeStreamer(baseStreamer, valueStreamer) { -} - -void MappedSetTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - object = reference; - int addedOrRemoved; - in >> addedOrRemoved; - for (int i = 0; i < addedOrRemoved; i++) { - QVariant value = _valueStreamer->read(in); - if (!_baseStreamer->remove(object, value)) { - _baseStreamer->insert(object, value); - } - } -} - -GenericSetTypeStreamer::GenericSetTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer) : - GenericListTypeStreamer(name, valueStreamer) { -} - -QJsonValue GenericSetTypeStreamer::getJSONMetadata(JSONWriter& writer) const { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString("SET")); - writer.addTypeStreamer(_valueStreamer.data()); - metadata.insert("valueType", QString(_valueStreamer->getName())); - return metadata; -} - -TypeStreamer::Category GenericSetTypeStreamer::getCategory() const { - return SET_CATEGORY; -} - -MappedMapTypeStreamer::MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer, - const TypeStreamerPointer& valueStreamer) : - _baseStreamer(baseStreamer), - _keyStreamer(keyStreamer), - _valueStreamer(valueStreamer) { -} - -void MappedMapTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - value = QVariant(_baseStreamer->getType(), 0); - foreach (const QJsonValue& element, data.toArray()) { - QJsonArray pair = element.toArray(); - QVariant elementKey; - _keyStreamer->putJSONData(reader, pair.at(0), elementKey); - QVariant elementValue; - _valueStreamer->putJSONData(reader, pair.at(1), elementValue); - _baseStreamer->insert(value, elementKey, elementValue); - } -} - -QVariant MappedMapTypeStreamer::read(Bitstream& in) const { - QVariant object = QVariant(_baseStreamer->getType(), 0); - int size; - in >> size; - for (int i = 0; i < size; i++) { - QVariant key = _keyStreamer->read(in); - QVariant value = _valueStreamer->read(in); - _baseStreamer->insert(object, key, value); - } - return object; -} - -void MappedMapTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - object = reference; - int added; - in >> added; - for (int i = 0; i < added; i++) { - QVariant key = _keyStreamer->read(in); - QVariant value = _valueStreamer->read(in); - _baseStreamer->insert(object, key, value); - } - int modified; - in >> modified; - for (int i = 0; i < modified; i++) { - QVariant key = _keyStreamer->read(in); - QVariant value; - _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, key)); - _baseStreamer->insert(object, key, value); - } - int removed; - in >> removed; - for (int i = 0; i < removed; i++) { - QVariant key = _keyStreamer->read(in); - _baseStreamer->remove(object, key); - } -} - -GenericMapTypeStreamer::GenericMapTypeStreamer(const QByteArray& name, const TypeStreamerPointer& keyStreamer, - const TypeStreamerPointer& valueStreamer) : - GenericTypeStreamer(name), - _keyStreamer(keyStreamer), - _valueStreamer(valueStreamer) { - - _type = qMetaTypeId(); -} - -void GenericMapTypeStreamer::writeMetadata(Bitstream& out, bool full) const { - out << _keyStreamer.data() << _valueStreamer.data(); -} - -QJsonValue GenericMapTypeStreamer::getJSONMetadata(JSONWriter& writer) const { - QJsonObject metadata; - metadata.insert("name", QString(getName())); - metadata.insert("category", QString("MAP")); - writer.addTypeStreamer(_keyStreamer.data()); - metadata.insert("keyType", QString(_keyStreamer->getName())); - writer.addTypeStreamer(_valueStreamer.data()); - metadata.insert("valueType", QString(_valueStreamer->getName())); - return metadata; -} - -QJsonValue GenericMapTypeStreamer::getJSONData(JSONWriter& writer, const QVariant& value) const { - QVariantPairList values = value.value(); - QJsonArray array; - foreach (const QVariantPair& pair, values) { - QJsonArray pairArray; - pairArray.append(_keyStreamer->getJSONData(writer, pair.first)); - pairArray.append(_valueStreamer->getJSONData(writer, pair.second)); - array.append(pairArray); - } - return array; -} - -void GenericMapTypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - QJsonArray array = data.toArray(); - QVariantPairList values; - foreach (const QJsonValue& element, array) { - QJsonArray pair = element.toArray(); - QVariant elementKey; - _keyStreamer->putJSONData(reader, pair.at(0), elementKey); - QVariant elementValue; - _valueStreamer->putJSONData(reader, pair.at(1), elementValue); - values.append(QVariantPair(elementKey, elementValue)); - } - value = QVariant::fromValue(values); -} - -void GenericMapTypeStreamer::write(Bitstream& out, const QVariant& value) const { - QVariantPairList values = value.value(); - out << values.size(); - foreach (const QVariantPair& pair, values) { - _keyStreamer->write(out, pair.first); - _valueStreamer->write(out, pair.second); - } -} - -QVariant GenericMapTypeStreamer::read(Bitstream& in) const { - QVariantPairList values; - int size; - in >> size; - for (int i = 0; i < size; i++) { - QVariant key = _keyStreamer->read(in); - QVariant value = _valueStreamer->read(in); - values.append(QVariantPair(key, value)); - } - return QVariant::fromValue(values); -} - -TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { - return MAP_CATEGORY; -} - -QJsonValue GenericValueStreamer::getJSONVariantData(JSONWriter& writer, const QVariant& value) const { - GenericValue genericValue = value.value(); - writer.addTypeStreamer(genericValue.getStreamer().data()); - QJsonObject data; - data.insert("type", QString(genericValue.getStreamer()->getName())); - data.insert("value", genericValue.getStreamer()->getJSONData(writer, genericValue.getValue())); - return data; -} - -void GenericValueStreamer::writeVariant(Bitstream& out, const QVariant& value) const { - GenericValue genericValue = value.value(); - out << genericValue.getStreamer().data(); - genericValue.getStreamer()->write(out, genericValue.getValue()); -} - diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h deleted file mode 100644 index 121aa6c672..0000000000 --- a/libraries/metavoxels/src/Bitstream.h +++ /dev/null @@ -1,1781 +0,0 @@ -// -// Bitstream.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/2/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Bitstream_h -#define hifi_Bitstream_h - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "SharedObject.h" - -class QByteArray; -class QColor; -class QDataStream; -class QScriptEngine; -class QScriptValue; -class QUrl; - -class Attribute; -class AttributeValue; -class Bitstream; -class GenericValue; -class JSONWriter; -class ObjectReader; -class ObjectStreamer; -class OwnedAttributeValue; -class TypeStreamer; - -typedef SharedObjectPointerTemplate AttributePointer; - -typedef QPair ScopeNamePair; -typedef QPair NameIntPair; -typedef QSharedPointer ObjectStreamerPointer; -typedef QWeakPointer WeakObjectStreamerPointer; -typedef QSharedPointer TypeStreamerPointer; -typedef QWeakPointer WeakTypeStreamerPointer; - -typedef QPair QVariantPair; -typedef QList QVariantPairList; - -Q_DECLARE_METATYPE(QVariantPairList) - -/// 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 -/// us to use the minimum number of bits to encode the IDs. -class IDStreamer { -public: - - IDStreamer(Bitstream& stream); - - void setBitsFromValue(int value); - - IDStreamer& operator<<(int value); - IDStreamer& operator>>(int& value); - -private: - - Bitstream& _stream; - int _bits; -}; - -/// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When -/// subsequently streamed, only the ID is sent. -template class RepeatedValueStreamer { -public: - - RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream), - _lastPersistentID(0), _lastTransientOffset(0) { } - - QHash getAndResetTransientOffsets(); - - void persistTransientOffsets(const QHash& transientOffsets); - - QHash getAndResetTransientValues(); - - void persistTransientValues(const QHash& transientValues); - - void removePersistentID(P value) { _persistentIDs.remove(value); } - - int takePersistentID(P value) { return _persistentIDs.take(value); } - - int removePersistentValue(V value) { int id = _valueIDs.take(value); _persistentValues.remove(id); return id; } - - V takePersistentValue(int id) { V value = _persistentValues.take(id); _valueIDs.remove(value); return value; } - - void insertPersistentValue(int id, V value) { _valueIDs.insert(value, id); _persistentValues.insert(id, value); } - - void copyPersistentMappings(const RepeatedValueStreamer& other); - void clearPersistentMappings(); - - RepeatedValueStreamer& operator<<(K value); - RepeatedValueStreamer& operator>>(V& value); - -private: - - Bitstream& _stream; - IDStreamer _idStreamer; - int _lastPersistentID; - int _lastTransientOffset; - QHash _persistentIDs; - QHash _transientOffsets; - QHash _persistentValues; - QHash _transientValues; - QHash _valueIDs; -}; - -template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { - QHash transientOffsets; - _transientOffsets.swap(transientOffsets); - _lastTransientOffset = 0; - _idStreamer.setBitsFromValue(_lastPersistentID); - return transientOffsets; -} - -template inline void RepeatedValueStreamer::persistTransientOffsets( - const QHash& transientOffsets) { - int oldLastPersistentID = _lastPersistentID; - for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { - int& id = _persistentIDs[it.key()]; - if (id == 0) { - id = oldLastPersistentID + it.value(); - _lastPersistentID = qMax(_lastPersistentID, id); - } - } - _idStreamer.setBitsFromValue(_lastPersistentID); -} - -template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { - QHash transientValues; - _transientValues.swap(transientValues); - _idStreamer.setBitsFromValue(_lastPersistentID); - return transientValues; -} - -template inline void RepeatedValueStreamer::persistTransientValues( - const QHash& transientValues) { - int oldLastPersistentID = _lastPersistentID; - for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { - int& id = _valueIDs[it.value()]; - if (id == 0) { - id = oldLastPersistentID + it.key(); - _lastPersistentID = qMax(_lastPersistentID, id); - _persistentValues.insert(id, it.value()); - } - } - _idStreamer.setBitsFromValue(_lastPersistentID); -} - -template inline RepeatedValueStreamer& - RepeatedValueStreamer::operator<<(K value) { - int id = _persistentIDs.value(value); - if (id == 0) { - int& offset = _transientOffsets[value]; - if (offset == 0) { - _idStreamer << (_lastPersistentID + (offset = ++_lastTransientOffset)); - _stream < value; - - } else { - _idStreamer << (_lastPersistentID + offset); - } - } else { - _idStreamer << id; - } - return *this; -} - -template inline RepeatedValueStreamer& - RepeatedValueStreamer::operator>>(V& value) { - int id; - _idStreamer >> id; - if (id <= _lastPersistentID) { - value = _persistentValues.value(id); - - } else { - int offset = id - _lastPersistentID; - typename QHash::iterator it = _transientValues.find(offset); - if (it == _transientValues.end()) { - _stream > value; - _transientValues.insert(offset, value); - - } else { - value = *it; - } - } - return *this; -} - -template inline void RepeatedValueStreamer::copyPersistentMappings( - const RepeatedValueStreamer& other) { - _lastPersistentID = other._lastPersistentID; - _idStreamer.setBitsFromValue(_lastPersistentID); - _persistentIDs = other._persistentIDs; - _transientOffsets.clear(); - _lastTransientOffset = 0; - _persistentValues = other._persistentValues; - _transientValues.clear(); - _valueIDs = other._valueIDs; -} - -template inline void RepeatedValueStreamer::clearPersistentMappings() { - _lastPersistentID = 0; - _idStreamer.setBitsFromValue(_lastPersistentID); - _persistentIDs.clear(); - _transientOffsets.clear(); - _lastTransientOffset = 0; - _persistentValues.clear(); - _transientValues.clear(); - _valueIDs.clear(); -} - -/// A stream for bit-aligned data. Through a combination of code generation, reflection, macros, and templates, provides a -/// serialization mechanism that may be used for both networking and persistent storage. For unreliable networking, the -/// class provides a mapping system that resends mappings for ids until they are acknowledged (and thus persisted). For -/// version-resilient persistence, the class provides a metadata system that maps stored types to local types (or to -/// generic containers), allowing one to add and remove fields to classes without breaking compatibility with previously -/// stored data. -/// -/// The basic usage requires one to create a Bitstream that wraps an underlying QDataStream, specifying the metadata type -/// desired and (for readers) the generics mode. Then, one uses the << or >> operators to write or read values to/from -/// the stream (a stream instance may be used for reading or writing, but not both). For write streams, the flush -/// function should be called on completion to write any partial data. -/// -/// Polymorphic types are supported via the QVariant and QObject*/SharedObjectPointer types. When you write a QVariant or -/// QObject, the type or class name (at minimum) is written to the stream. When you read a QVariant or QObject, the default -/// behavior is to look for a corresponding registered type with the written name. With hash metadata, Bitstream can verify -/// that the local type matches the stream type, throwing the streamed version away if not. With full metadata, Bitstream can -/// create a mapping from the streamed type to the local type that applies the intersection of the two types' fields. -/// -/// To register types for streaming, select from the provided templates and macros. To register a QObject (or SharedObject) -/// subclass, use REGISTER_META_OBJECT in the class's source file. To register a streamable class for use in QVariant, use -/// the STREAMABLE/STREAM/DECLARE_STREAMABLE_METATYPE macros and use the mtc tool to generate the associated implementation -/// code. To register a QObject enum for use outside the QObject's direct properties, use the -/// DECLARE_ENUM_METATYPE/IMPLEMENT_ENUM_METATYPE macro pair. To register a collection type (QList, QVector, QSet, QMap) of -/// streamable types, use the registerCollectionMetaType template function. -/// -/// Delta-streaming is supported through the writeDelta/readDelta functions (analogous to <>), which accept a reference -/// value and stream only the information necessary to turn the reference into the target value. This assumes that the -/// reference value provided when reading is identical to the one used when writing. -/// -/// Special delta handling is provided for objects tracked by SharedObjectPointers. SharedObjects have IDs (representing their -/// unique identity on one system) as well as origin IDs (representing their identity as preserved over the course of -/// mutation). Once a shared object with a given ID is written (and its mapping persisted), that object's state will not be -/// written again; only its ID will be sent. However, if an object with a new ID but the same origin ID is written, that -/// object's state will be encoded as a delta between the previous object and the new one. So, to transmit delta-encoded -/// objects, one should treat each local SharedObject instance as immutable, replacing it when mutated with a cloned instance -/// generated by calling SharedObject::clone(true) and applying the desired changes. -class Bitstream : public QObject { - Q_OBJECT - -public: - - /// Stores a set of mappings from values to ids written. Typically, one would store these mappings along with the send - /// record of the packet that contained them, persisting the mappings if/when the packet is acknowledged by the remote - /// party. - class WriteMappings { - public: - QHash objectStreamerOffsets; - QHash typeStreamerOffsets; - QHash attributeOffsets; - QHash scriptStringOffsets; - QHash sharedObjectOffsets; - }; - - /// Stores a set of mappings from ids to values read. Typically, one would store these mappings along with the receive - /// record of the packet that contained them, persisting the mappings if/when the remote party indicates that it - /// has received the local party's acknowledgement of the packet. - class ReadMappings { - public: - QHash objectStreamerValues; - QHash typeStreamerValues; - QHash attributeValues; - QHash scriptStringValues; - QHash sharedObjectValues; - QVector subdividedObjects; - }; - - /// Performs all of the various lazily initializations (of object streamers, etc.) If multiple threads need to use - /// Bitstream instances, call this beforehand to prevent errors from occurring when multiple threads attempt lazy - /// initialization simultaneously. - static void preThreadingInit(); - - /// Registers a metaobject under its name so that instances of it can be streamed. Consider using the REGISTER_META_OBJECT - /// at the top level of the source file associated with the class rather than calling this function directly. - /// \return zero; the function only returns a value so that it can be used in static initialization - static int registerMetaObject(const char* className, const QMetaObject* metaObject); - - /// Registers a streamer for the specified Qt-registered type. Consider using one of the registration macros (such as - /// REGISTER_SIMPLE_TYPE_STREAMER) at the top level of the associated source file rather than calling this function - /// directly. - /// \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 streamer registered for the supplied type, if any. - static const TypeStreamer* getTypeStreamer(int type); - - /// Returns the streamer registered for the supplied object, if any. - static const ObjectStreamer* getObjectStreamer(const QMetaObject* metaObject); - - /// 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. When registered, metaobjects register - /// themselves as subclasses of all of their parents, mostly in order to allow editors to provide lists of available - /// subclasses. - static QList getMetaObjectSubClasses(const QMetaObject* metaObject); - - /// Configures the supplied script engine with our registered meta-objects, allowing all of them to be instantiated from - /// scripts. - static void registerTypes(QScriptEngine* engine); - - enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA }; - - enum GenericsMode { NO_GENERICS, FALLBACK_GENERICS, ALL_GENERICS }; - - /// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both. - /// \param metadataType the metadata type, which determines the amount of version-resiliency: with no metadata, all types - /// must match exactly; with hash metadata, any types which do not match exactly will be ignored; with full metadata, - /// fields (and enum values, etc.) will be remapped by name - /// \param genericsMode the generics mode, which determines which types will be replaced by generic equivalents: with - /// no generics, no generics will be created; with fallback generics, generics will be created for any unknown types; with - /// all generics, generics will be created for all non-simple types - Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, - GenericsMode = NO_GENERICS, QObject* parent = NULL); - - /// Returns a reference to the underlying data stream. - QDataStream& getUnderlying() { return _underlying; } - - /// Sets the context pointer. - void setContext(void* context) { _context = context; } - - /// Returns the context pointer. - void* getContext() const { return _context; } - - /// Substitutes the supplied metaobject for the given class name's default mapping. This is mostly useful for testing the - /// process of mapping between different types, but may in the future be used for permanently renaming classes. - void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject); - - /// Substitutes the supplied type for the given type name's default mapping. - void addTypeSubstitution(const QByteArray& typeName, int type); - - /// Substitutes the named type for the given type name's default mapping. - void addTypeSubstitution(const QByteArray& typeName, const char* replacementTypeName); - - /// Writes a set of bits to the underlying stream. - /// \param bits the number of bits to write - /// \param offset the offset of the first bit - Bitstream& write(const void* data, int bits, int offset = 0); - - /// Reads a set of bits from the underlying stream. - /// \param bits the number of bits to read - /// \param offset the offset of the first bit - Bitstream& read(void* data, int bits, int offset = 0); - - /// Flushes any unwritten bits to the underlying stream. - void flush(); - - /// Resets to the initial state. - void reset(); - - /// Adds a subdivided object, which will be added to the read mappings and used as a reference if persisted. - void addSubdividedObject(const SharedObjectPointer& object) { _subdividedObjects.append(object); } - - /// Returns the set of transient mappings gathered during writing and resets them. - WriteMappings getAndResetWriteMappings(); - - /// Persists a set of write mappings recorded earlier. - void persistWriteMappings(const WriteMappings& mappings); - - /// Immediately persists and resets the write mappings. - void persistAndResetWriteMappings(); - - /// Returns the set of transient mappings gathered during reading and resets them. - ReadMappings getAndResetReadMappings(); - - /// Persists a set of read mappings recorded earlier. - void persistReadMappings(const ReadMappings& mappings); - - /// Immediately persists and resets the read mappings. - void persistAndResetReadMappings(); - - /// Copies the persistent mappings from the specified other stream. - void copyPersistentMappings(const Bitstream& other); - - /// Clears the persistent mappings for this stream. - void clearPersistentMappings(); - - /// Returns a reference to the weak hash storing shared objects for this stream. - const WeakSharedObjectHash& getWeakSharedObjectHash() const { return _weakSharedObjectHash; } - - /// Removes a shared object from the read mappings. - void clearSharedObject(int id); - - void writeDelta(bool value, bool reference); - void readDelta(bool& value, bool reference); - - void writeDelta(const QVariant& value, const QVariant& reference); - - template void writeDelta(const T& value, const T& reference); - template void readDelta(T& value, const T& reference); - - void writeRawDelta(const QVariant& value, const QVariant& reference); - void readRawDelta(QVariant& value, const QVariant& reference); - - void writeRawDelta(const QObject* value, const QObject* reference); - void readRawDelta(QObject*& value, const QObject* reference); - - void writeRawDelta(const QScriptValue& value, const QScriptValue& reference); - void readRawDelta(QScriptValue& value, const QScriptValue& reference); - - template void writeRawDelta(const T& value, const T& reference); - template void readRawDelta(T& value, const T& reference); - - template void writeRawDelta(const QList& value, const QList& reference); - template void readRawDelta(QList& value, const QList& reference); - - template void writeRawDelta(const QVector& value, const QVector& reference); - template void readRawDelta(QVector& value, const QVector& reference); - - template void writeRawDelta(const QSet& value, const QSet& reference); - template void readRawDelta(QSet& value, const QSet& reference); - - template void writeRawDelta(const QHash& value, const QHash& reference); - template void readRawDelta(QHash& value, const QHash& reference); - - /// Writes the specified array aligned on byte boundaries to avoid the inefficiency - /// of bit-twiddling (at the cost of up to seven bits of wasted space). - void writeAligned(const QByteArray& data); - QByteArray readAligned(int bytes); - - Bitstream& operator<<(bool value); - Bitstream& operator>>(bool& value); - - Bitstream& operator<<(int value); - Bitstream& operator>>(int& value); - - Bitstream& operator<<(uint value); - Bitstream& operator>>(uint& value); - - Bitstream& operator<<(qint64 value); - Bitstream& operator>>(qint64& value); - - Bitstream& operator<<(float value); - Bitstream& operator>>(float& value); - - Bitstream& operator<<(double value); - Bitstream& operator>>(double& value); - - Bitstream& operator<<(const glm::vec3& value); - Bitstream& operator>>(glm::vec3& value); - - Bitstream& operator<<(const glm::quat& value); - Bitstream& operator>>(glm::quat& value); - - Bitstream& operator<<(const QByteArray& string); - Bitstream& operator>>(QByteArray& string); - - Bitstream& operator<<(const QColor& color); - Bitstream& operator>>(QColor& color); - - Bitstream& operator<<(const QString& string); - Bitstream& operator>>(QString& string); - - Bitstream& operator<<(const QUrl& url); - Bitstream& operator>>(QUrl& url); - - Bitstream& operator<<(const QDateTime& dateTime); - Bitstream& operator>>(QDateTime& dateTime); - - Bitstream& operator<<(const QRegExp& regExp); - Bitstream& operator>>(QRegExp& regExp); - - Bitstream& operator<<(const QVariant& value); - Bitstream& operator>>(QVariant& value); - - Bitstream& operator<<(const AttributeValue& attributeValue); - Bitstream& operator>>(OwnedAttributeValue& attributeValue); - - Bitstream& operator<<(const GenericValue& value); - Bitstream& operator>>(GenericValue& value); - - template Bitstream& operator<<(const QList& list); - template Bitstream& operator>>(QList& list); - - template Bitstream& operator<<(const QVector& list); - template Bitstream& operator>>(QVector& list); - - template Bitstream& operator<<(const QSet& set); - template Bitstream& operator>>(QSet& set); - - template Bitstream& operator<<(const QHash& hash); - template Bitstream& operator>>(QHash& hash); - - Bitstream& operator<<(const QObject* object); - Bitstream& operator>>(QObject*& object); - - Bitstream& operator<<(const QMetaObject* metaObject); - Bitstream& operator>>(const QMetaObject*& metaObject); - - Bitstream& operator<<(const ObjectStreamer* streamer); - Bitstream& operator>>(const ObjectStreamer*& streamer); - Bitstream& operator>>(ObjectStreamerPointer& streamer); - - Bitstream& operator<<(const TypeStreamer* streamer); - Bitstream& operator>>(const TypeStreamer*& streamer); - Bitstream& operator>>(TypeStreamerPointer& streamer); - - Bitstream& operator<<(const AttributePointer& attribute); - Bitstream& operator>>(AttributePointer& attribute); - - Bitstream& operator<<(const QScriptString& string); - Bitstream& operator>>(QScriptString& string); - - Bitstream& operator<<(const QScriptValue& value); - Bitstream& operator>>(QScriptValue& value); - - Bitstream& operator<<(const SharedObjectPointer& object); - Bitstream& operator>>(SharedObjectPointer& object); - - Bitstream& operator<(const ObjectStreamer* streamer); - Bitstream& operator>(ObjectStreamerPointer& streamer); - - Bitstream& operator<(const TypeStreamer* streamer); - Bitstream& operator>(TypeStreamerPointer& streamer); - - Bitstream& operator<(const AttributePointer& attribute); - Bitstream& operator>(AttributePointer& attribute); - - Bitstream& operator<(const QScriptString& string); - Bitstream& operator>(QScriptString& string); - - Bitstream& operator<(const SharedObjectPointer& object); - Bitstream& operator>(SharedObjectPointer& object); - -signals: - - void sharedObjectCleared(int id); - -private slots: - - void clearSharedObject(QObject* object); - -private: - - friend class JSONReader; - friend class JSONWriter; - - ObjectStreamerPointer readGenericObjectStreamer(const QByteArray& name); - TypeStreamerPointer readGenericTypeStreamer(const QByteArray& name, int category); - - QDataStream& _underlying; - quint8 _byte; - int _position; - - MetadataType _metadataType; - GenericsMode _genericsMode; - - void* _context; - - RepeatedValueStreamer _objectStreamerStreamer; - RepeatedValueStreamer _typeStreamerStreamer; - RepeatedValueStreamer _attributeStreamer; - RepeatedValueStreamer _scriptStringStreamer; - RepeatedValueStreamer _sharedObjectStreamer; - - QVector _subdividedObjects; - - WeakSharedObjectHash _sharedObjectReferences; - - WeakSharedObjectHash _weakSharedObjectHash; - - QHash _metaObjectSubstitutions; - QHash _typeStreamerSubstitutions; - - static QHash& getMetaObjects(); - static QMultiHash& getMetaObjectSubClasses(); - static QHash& getTypeStreamers(); - - static const QHash& getObjectStreamers(); - static QHash createObjectStreamers(); - - static const QHash& getEnumStreamers(); - static QHash createEnumStreamers(); - - static const QHash& getEnumStreamersByName(); - static QHash createEnumStreamersByName(); - - static const TypeStreamer* getInvalidTypeStreamer(); - static const TypeStreamer* createInvalidTypeStreamer(); -}; - -template inline void Bitstream::writeDelta(const T& value, const T& reference) { - if (value == reference) { - *this << false; - } else { - *this << true; - writeRawDelta(value, reference); - } -} - -template inline void Bitstream::readDelta(T& value, const T& reference) { - bool changed; - *this >> changed; - if (changed) { - readRawDelta(value, reference); - } else { - value = reference; - } -} - -template inline void Bitstream::writeRawDelta(const T& value, const T& reference) { - *this << value; -} - -template inline void Bitstream::readRawDelta(T& value, const T& reference) { - *this >> value; -} - -template inline void Bitstream::writeRawDelta(const QList& value, const QList& reference) { - *this << value.size(); - *this << reference.size(); - for (int i = 0; i < value.size(); i++) { - if (i < reference.size()) { - writeDelta(value.at(i), reference.at(i)); - } else { - *this << value.at(i); - } - } -} - -template inline void Bitstream::readRawDelta(QList& value, const QList& reference) { - value = reference; - int size, referenceSize; - *this >> size >> referenceSize; - if (size < value.size()) { - value.erase(value.begin() + size, value.end()); - } - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - readDelta(value[i], reference.at(i)); - } else { - T element; - *this >> element; - value.append(element); - } - } -} - -template inline void Bitstream::writeRawDelta(const QVector& value, const QVector& reference) { - *this << value.size(); - *this << reference.size(); - for (int i = 0; i < value.size(); i++) { - if (i < reference.size()) { - writeDelta(value.at(i), reference.at(i)); - } else { - *this << value.at(i); - } - } -} - -template inline void Bitstream::readRawDelta(QVector& value, const QVector& reference) { - value = reference; - int size, referenceSize; - *this >> size >> referenceSize; - if (size < value.size()) { - value.erase(value.begin() + size, value.end()); - } - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - readDelta(value[i], reference.at(i)); - } else { - T element; - *this >> element; - value.append(element); - } - } -} - -template inline void Bitstream::writeRawDelta(const QSet& value, const QSet& reference) { - int addedOrRemoved = 0; - foreach (const T& element, value) { - if (!reference.contains(element)) { - addedOrRemoved++; - } - } - foreach (const T& element, reference) { - if (!value.contains(element)) { - addedOrRemoved++; - } - } - *this << addedOrRemoved; - foreach (const T& element, value) { - if (!reference.contains(element)) { - *this << element; - } - } - foreach (const T& element, reference) { - if (!value.contains(element)) { - *this << element; - } - } -} - -template inline void Bitstream::readRawDelta(QSet& value, const QSet& reference) { - value = reference; - int addedOrRemoved; - *this >> addedOrRemoved; - for (int i = 0; i < addedOrRemoved; i++) { - T element; - *this >> element; - if (!value.remove(element)) { - value.insert(element); - } - } -} - -template inline void Bitstream::writeRawDelta(const QHash& value, const QHash& reference) { - int added = 0; - int modified = 0; - for (typename QHash::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { - typename QHash::const_iterator previous = reference.find(it.key()); - if (previous == reference.constEnd()) { - added++; - } else if (previous.value() != it.value()) { - modified++; - } - } - *this << added; - for (typename QHash::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { - if (!reference.contains(it.key())) { - *this << it.key(); - *this << it.value(); - } - } - *this << modified; - for (typename QHash::const_iterator it = value.constBegin(); it != value.constEnd(); it++) { - typename QHash::const_iterator previous = reference.find(it.key()); - if (previous != reference.constEnd() && previous.value() != it.value()) { - *this << it.key(); - writeDelta(it.value(), previous.value()); - } - } - int removed = 0; - for (typename QHash::const_iterator it = reference.constBegin(); it != reference.constEnd(); it++) { - if (!value.contains(it.key())) { - removed++; - } - } - *this << removed; - for (typename QHash::const_iterator it = reference.constBegin(); it != reference.constEnd(); it++) { - if (!value.contains(it.key())) { - *this << it.key(); - } - } -} - -template inline void Bitstream::readRawDelta(QHash& value, const QHash& reference) { - value = reference; - int added; - *this >> added; - for (int i = 0; i < added; i++) { - K key; - V mapping; - *this >> key >> mapping; - value.insert(key, mapping); - } - int modified; - *this >> modified; - for (int i = 0; i < modified; i++) { - K key; - *this >> key; - V& mapping = value[key]; - V newMapping; - readDelta(newMapping, mapping); - mapping = newMapping; - } - int removed; - *this >> removed; - for (int i = 0; i < removed; i++) { - K key; - *this >> key; - value.remove(key); - } -} - -template inline Bitstream& Bitstream::operator<<(const QList& list) { - *this << list.size(); - foreach (const T& entry, list) { - *this << entry; - } - return *this; -} - -template inline Bitstream& Bitstream::operator>>(QList& list) { - int size; - *this >> size; - list.clear(); - list.reserve(size); - for (int i = 0; i < size; i++) { - T entry; - *this >> entry; - list.append(entry); - } - return *this; -} - -template inline Bitstream& Bitstream::operator<<(const QVector& vector) { - *this << vector.size(); - foreach (const T& entry, vector) { - *this << entry; - } - return *this; -} - -template inline Bitstream& Bitstream::operator>>(QVector& vector) { - int size; - *this >> size; - vector.clear(); - vector.reserve(size); - for (int i = 0; i < size; i++) { - T entry; - *this >> entry; - vector.append(entry); - } - 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++) { - *this << it.key(); - *this << it.value(); - } - return *this; -} - -template inline Bitstream& Bitstream::operator>>(QHash& hash) { - int size; - *this >> size; - hash.clear(); - hash.reserve(size); - for (int i = 0; i < size; i++) { - K key; - V value; - *this >> key; - *this >> value; - hash.insertMulti(key, value); - } - return *this; -} - -/// Thrown for unrecoverable errors. -class BitstreamException { -public: - - BitstreamException(const QString& description); - - const QString& getDescription() const { return _description; } - -private: - - QString _description; -}; - -/// Provides a means of writing Bitstream-able data to JSON rather than the usual binary format in a manner that allows it to -/// be manipulated and re-read, converted to binary, etc. To use, create a JSONWriter, stream values in using the << operator, -/// and call getDocument to obtain the JSON data. -class JSONWriter { -public: - - QJsonValue getData(bool value); - QJsonValue getData(int value); - QJsonValue getData(uint value); - QJsonValue getData(float value); - QJsonValue getData(const QByteArray& value); - QJsonValue getData(const QColor& value); - QJsonValue getData(const QScriptValue& value); - QJsonValue getData(const QString& value); - QJsonValue getData(const QUrl& value); - QJsonValue getData(const QDateTime& value); - QJsonValue getData(const QRegExp& value); - QJsonValue getData(const glm::vec3& value); - QJsonValue getData(const glm::quat& value); - QJsonValue getData(const QMetaObject* value); - QJsonValue getData(const QVariant& value); - QJsonValue getData(const SharedObjectPointer& value); - QJsonValue getData(const QObject* value); - QJsonValue getData(const GenericValue& value); - - template QJsonValue getData(const T& value) { return QJsonValue(); } - - template QJsonValue getData(const QList& list); - template QJsonValue getData(const QVector& list); - template QJsonValue getData(const QSet& set); - template QJsonValue getData(const QHash& hash); - - template JSONWriter& operator<<(const T& value) { _contents.append(getData(value)); return *this; } - - void appendToContents(const QJsonValue& value) { _contents.append(value); } - - void addSharedObject(const SharedObjectPointer& object); - void addObjectStreamer(const ObjectStreamer* streamer); - void addTypeStreamer(const TypeStreamer* streamer); - - QJsonDocument getDocument() const; - -private: - - QJsonArray _contents; - - QSet _sharedObjectIDs; - QJsonArray _sharedObjects; - - QSet _objectStreamerNames; - QJsonArray _objectStreamers; - - QSet _typeStreamerNames; - QJsonArray _typeStreamers; -}; - -template inline QJsonValue JSONWriter::getData(const QList& list) { - QJsonArray array; - foreach (const T& value, list) { - array.append(getData(value)); - } - return array; -} - -template inline QJsonValue JSONWriter::getData(const QVector& vector) { - QJsonArray array; - foreach (const T& value, vector) { - array.append(getData(value)); - } - return array; -} - -template inline QJsonValue JSONWriter::getData(const QSet& set) { - QJsonArray array; - foreach (const T& value, set) { - array.append(getData(value)); - } - return array; -} - -template inline QJsonValue JSONWriter::getData(const QHash& hash) { - QJsonArray array; - for (typename QHash::const_iterator it = hash.constBegin(); it != hash.constEnd(); it++) { - QJsonArray pair; - pair.append(getData(it.key())); - pair.append(getData(it.value())); - array.append(pair); - } - return array; -} - -/// Reads a document written by JSONWriter. To use, create a JSONReader and stream values out using the >> operator. -class JSONReader { -public: - - /// Creates a reader to read from the supplied document. - /// \param genericsMode the generics mode to use: NO_GENERICS to map all types in the document to built-in types, - /// FALLBACK_GENERICS to use generic containers where no matching built-in type is found, or ALL_GENERICS to - /// read all types to generic containers - JSONReader(const QJsonDocument& document, Bitstream::GenericsMode genericsMode = Bitstream::NO_GENERICS); - - void putData(const QJsonValue& data, bool& value); - void putData(const QJsonValue& data, int& value); - void putData(const QJsonValue& data, uint& value); - void putData(const QJsonValue& data, float& value); - void putData(const QJsonValue& data, QByteArray& value); - void putData(const QJsonValue& data, QColor& value); - void putData(const QJsonValue& data, QScriptValue& value); - void putData(const QJsonValue& data, QString& value); - void putData(const QJsonValue& data, QUrl& value); - void putData(const QJsonValue& data, QDateTime& value); - void putData(const QJsonValue& data, QRegExp& value); - void putData(const QJsonValue& data, glm::vec3& value); - void putData(const QJsonValue& data, glm::quat& value); - void putData(const QJsonValue& data, const QMetaObject*& value); - void putData(const QJsonValue& data, QVariant& value); - void putData(const QJsonValue& data, SharedObjectPointer& value); - void putData(const QJsonValue& data, QObject*& value); - - template void putData(const QJsonValue& data, T& value) { value = T(); } - - template void putData(const QJsonValue& data, QList& list); - template void putData(const QJsonValue& data, QVector& list); - template void putData(const QJsonValue& data, QSet& set); - template void putData(const QJsonValue& data, QHash& hash); - - template JSONReader& operator>>(T& value) { putData(*_contentsIterator++, value); return *this; } - - QJsonValue retrieveNextFromContents() { return *_contentsIterator++; } - - TypeStreamerPointer getTypeStreamer(const QString& name) const; - ObjectStreamerPointer getObjectStreamer(const QString& name) const { return _objectStreamers.value(name); } - SharedObjectPointer getSharedObject(int id) const { return _sharedObjects.value(id); } - -private: - - QJsonArray _contents; - QJsonArray::const_iterator _contentsIterator; - - QHash _typeStreamers; - QHash _objectStreamers; - QHash _sharedObjects; -}; - -template inline void JSONReader::putData(const QJsonValue& data, QList& list) { - list.clear(); - foreach (const QJsonValue& element, data.toArray()) { - T value; - putData(element, value); - list.append(value); - } -} - -template inline void JSONReader::putData(const QJsonValue& data, QVector& list) { - list.clear(); - foreach (const QJsonValue& element, data.toArray()) { - T value; - putData(element, value); - list.append(value); - } -} - -template inline void JSONReader::putData(const QJsonValue& data, QSet& set) { - set.clear(); - foreach (const QJsonValue& element, data.toArray()) { - T value; - putData(element, value); - set.insert(value); - } -} - -template inline void JSONReader::putData(const QJsonValue& data, QHash& hash) { - hash.clear(); - foreach (const QJsonValue& element, data.toArray()) { - QJsonArray pair = element.toArray(); - K key; - putData(pair.at(0), key); - V value; - putData(pair.at(1), value); - hash.insert(key, value); - } -} - -typedef QPair StreamerPropertyPair; - -/// Contains the information required to stream an object. -class ObjectStreamer { -public: - - ObjectStreamer(const QMetaObject* metaObject); - virtual ~ObjectStreamer(); - - const QMetaObject* getMetaObject() const { return _metaObject; } - const ObjectStreamerPointer& getSelf() const { return _self; } - - virtual const char* getName() const = 0; - virtual const QVector& getProperties() const; - virtual void writeMetadata(Bitstream& out, bool full) const = 0; - virtual QJsonObject getJSONMetadata(JSONWriter& writer) const = 0; - virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const = 0; - virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const = 0; - - virtual bool equal(const QObject* first, const QObject* second) const = 0; - virtual void write(Bitstream& out, const QObject* object) const = 0; - virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const = 0; - virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0; - virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const = 0; - -protected: - - friend class Bitstream; - - const QMetaObject* _metaObject; - ObjectStreamerPointer _self; ///< set/used for built-in classes (never deleted), to obtain shared pointers -}; - -/// A streamer that maps to a local class. -class MappedObjectStreamer : public ObjectStreamer { -public: - - MappedObjectStreamer(const QMetaObject* metaObject, const QVector& properties); - - virtual const char* getName() const; - virtual const QVector& getProperties() const; - virtual void writeMetadata(Bitstream& out, bool full) const; - virtual QJsonObject getJSONMetadata(JSONWriter& writer) const; - virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const; - virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const; - virtual bool equal(const QObject* first, const QObject* second) const; - virtual void write(Bitstream& out, const QObject* object) const; - virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; - virtual QObject* read(Bitstream& in, QObject* object = NULL) const; - virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; - -private: - - QVector _properties; -}; - -/// A streamer that maps to a local shared object class. Shared objects can write extra, non-property data. -class SharedObjectStreamer : public MappedObjectStreamer { -public: - - SharedObjectStreamer(const QMetaObject* metaObject, const QVector& properties); - - virtual void write(Bitstream& out, const QObject* object) const; - virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; - virtual QObject* read(Bitstream& in, QObject* object = NULL) const; - virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; -}; - -typedef QPair StreamerNamePair; - -/// A streamer for generic objects. -class GenericObjectStreamer : public ObjectStreamer { -public: - - GenericObjectStreamer(const QByteArray& name, const QVector& properties, const QByteArray& hash); - - virtual const char* getName() const; - virtual void writeMetadata(Bitstream& out, bool full) const; - virtual QJsonObject getJSONMetadata(JSONWriter& writer) const; - virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const; - virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const; - virtual bool equal(const QObject* first, const QObject* second) const; - virtual void write(Bitstream& out, const QObject* object) const; - virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; - virtual QObject* read(Bitstream& in, QObject* object = NULL) const; - virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; - -private: - - friend class Bitstream; - friend class JSONReader; - - QByteArray _name; - WeakObjectStreamerPointer _weakSelf; ///< promoted to strong references in GenericSharedObject when reading - QVector _properties; - QByteArray _hash; -}; - -/// Describes a metatype field. -class MetaField { -public: - - MetaField(const QByteArray& name = QByteArray(), const TypeStreamer* streamer = NULL); - - const QByteArray& getName() const { return _name; } - const TypeStreamer* getStreamer() const { return _streamer; } - -private: - - QByteArray _name; - const TypeStreamer* _streamer; -}; - -Q_DECLARE_METATYPE(const QMetaObject*) - -/// Macro for registering streamable meta-objects. Typically, one would use this macro at the top level of the source file -/// associated with the class. The class should have a no-argument constructor flagged with Q_INVOKABLE. -#define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); - -/// Contains a value along with a pointer to its streamer. This is stored in QVariants when using fallback generics and -/// no mapping to a built-in type can be found, or when using all generics and the value is any non-simple type. -class GenericValue { -public: - - GenericValue(const TypeStreamerPointer& streamer = TypeStreamerPointer(), const QVariant& value = QVariant()); - - const TypeStreamerPointer& getStreamer() const { return _streamer; } - const QVariant& getValue() const { return _value; } - - bool operator==(const GenericValue& other) const; - -private: - - TypeStreamerPointer _streamer; - QVariant _value; -}; - -Q_DECLARE_METATYPE(GenericValue) - -/// Contains a list of property values along with a pointer to their metadata. This is stored when using fallback generics -/// and no mapping to a built-in class can be found, or for all QObjects when using all generics. -class GenericSharedObject : public SharedObject { - Q_OBJECT - -public: - - GenericSharedObject(const ObjectStreamerPointer& streamer); - - const ObjectStreamerPointer& getStreamer() const { return _streamer; } - - void setValues(const QVariantList& values) { _values = values; } - const QVariantList& getValues() const { return _values; } - -private: - - ObjectStreamerPointer _streamer; - QVariantList _values; -}; - -/// Interface for objects that can write values to and read values from bitstreams. -class TypeStreamer { -public: - - enum Category { SIMPLE_CATEGORY, ENUM_CATEGORY, STREAMABLE_CATEGORY, LIST_CATEGORY, SET_CATEGORY, MAP_CATEGORY }; - - virtual ~TypeStreamer(); - - int getType() const { return _type; } - - const TypeStreamerPointer& getSelf() const { return _self; } - - virtual const char* getName() const; - - virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; - - virtual void writeMetadata(Bitstream& out, bool full) const; - - virtual QJsonValue getJSONMetadata(JSONWriter& writer) const; - virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const; - virtual QJsonValue getJSONVariantData(JSONWriter& writer, const QVariant& value) const; - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual void putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - - virtual bool equal(const QVariant& first, const QVariant& second) const; - - virtual void write(Bitstream& out, const QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - - virtual void writeVariant(Bitstream& out, const QVariant& value) const; - virtual QVariant readVariant(Bitstream& in) const; - - virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; - virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; - - virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; - virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; - - virtual void setEnumValue(QVariant& object, int value, const QHash& mappings) const; - - virtual const QVector& getMetaFields() const; - virtual int getFieldIndex(const QByteArray& name) const; - virtual void setField(QVariant& object, int index, const QVariant& value) const; - virtual QVariant getField(const QVariant& object, int index) const; - - virtual Category getCategory() const; - - virtual int getBits() const; - virtual QMetaEnum getMetaEnum() const; - - virtual const TypeStreamer* getKeyStreamer() const; - virtual const TypeStreamer* getValueStreamer() const; - - virtual void insert(QVariant& object, const QVariant& value) const; - virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const; - virtual bool remove(QVariant& object, const QVariant& key) const; - - virtual QVariant getValue(const QVariant& object, const QVariant& key) const; - - virtual void prune(QVariant& object, int size) const; - virtual QVariant getValue(const QVariant& object, int index) const; - virtual void setValue(QVariant& object, int index, const QVariant& value) const; - -protected: - - friend class Bitstream; - - int _type; - TypeStreamerPointer _self; ///< set/used for built-in types (never deleted), to obtain shared pointers -}; - -QDebug& operator<<(QDebug& debug, const TypeStreamer* typeStreamer); - -QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject); - -/// A streamer that works with Bitstream's operators. -template class SimpleTypeStreamer : public TypeStreamer { -public: - - virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const { - return writer.getData(value.value()); } - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { - T rawValue; reader.putData(data, rawValue); value = QVariant::fromValue(rawValue); } - virtual bool equal(const QVariant& first, const QVariant& second) const { return first.value() == second.value(); } - virtual void write(Bitstream& out, const QVariant& value) const { out << value.value(); } - virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); } - virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { - out.writeDelta(value.value(), reference.value()); } - virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - T rawValue; in.readDelta(rawValue, reference.value()); value = QVariant::fromValue(rawValue); } - virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { - out.writeRawDelta(value.value(), reference.value()); } - virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - T rawValue; in.readRawDelta(rawValue, reference.value()); value = QVariant::fromValue(rawValue); } -}; - -/// A streamer class for enumerated types. -class EnumTypeStreamer : public TypeStreamer { -public: - - EnumTypeStreamer(const QMetaObject* metaObject, const char* name); - EnumTypeStreamer(const QMetaEnum& metaEnum); - - virtual const char* getName() const; - virtual void writeMetadata(Bitstream& out, bool full) const; - virtual QJsonValue getJSONMetadata(JSONWriter& writer) const; - virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const; - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual Category getCategory() const; - virtual int getBits() const; - virtual QMetaEnum getMetaEnum() const; - virtual bool equal(const QVariant& first, const QVariant& second) const; - virtual void write(Bitstream& out, const QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; - virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; - virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; - virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; - virtual void setEnumValue(QVariant& object, int value, const QHash& mappings) const; - -private: - - const QMetaObject* _metaObject; - const char* _enumName; - QByteArray _name; - QMetaEnum _metaEnum; - int _bits; -}; - -/// A streamer class for enums that maps to a local type. -class MappedEnumTypeStreamer : public TypeStreamer { -public: - - MappedEnumTypeStreamer(const TypeStreamer* baseStreamer, int bits, const QHash& mappings); - - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; - -private: - - const TypeStreamer* _baseStreamer; - int _bits; - QHash _mappings; -}; - -/// Base class for generic type streamers, which contain all the metadata required to write out a type. -class GenericTypeStreamer : public TypeStreamer { -public: - - GenericTypeStreamer(const QByteArray& name); - - virtual const char* getName() const; - virtual void putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual QVariant readVariant(Bitstream& in) const; - -protected: - - friend class Bitstream; - friend class JSONReader; - - QByteArray _name; - WeakTypeStreamerPointer _weakSelf; ///< promoted to strong references in GenericValue when reading -}; - -/// A streamer for generic enums. -class GenericEnumTypeStreamer : public GenericTypeStreamer { -public: - - GenericEnumTypeStreamer(const QByteArray& name, const QVector& values, int bits, const QByteArray& hash); - - virtual void writeMetadata(Bitstream& out, bool full) const; - virtual QJsonValue getJSONMetadata(JSONWriter& writer) const; - virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const; - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual void write(Bitstream& out, const QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual Category getCategory() const; - -private: - - QVector _values; - int _bits; - QByteArray _hash; -}; - -/// A streamer for types compiled by mtc. -template class StreamableTypeStreamer : public SimpleTypeStreamer { -public: - - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::STREAMABLE_CATEGORY; } - virtual const QVector& getMetaFields() const { return T::getMetaFields(); } - virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); } - virtual void setField(QVariant& object, int index, const QVariant& value) const { - static_cast(object.data())->setField(index, value); } - virtual QVariant getField(const QVariant& object, int index) const { - return static_cast(object.constData())->getField(index); } -}; - -typedef QPair StreamerIndexPair; - -/// A streamer class for streamables that maps to a local type. -class MappedStreamableTypeStreamer : public TypeStreamer { -public: - - MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, const QVector& fields); - - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; - -private: - - const TypeStreamer* _baseStreamer; - QVector _fields; -}; - -/// A streamer for generic enums. -class GenericStreamableTypeStreamer : public GenericTypeStreamer { -public: - - GenericStreamableTypeStreamer(const QByteArray& name, const QVector& fields, const QByteArray& hash); - - virtual void writeMetadata(Bitstream& out, bool full) const; - virtual QJsonValue getJSONMetadata(JSONWriter& writer) const; - virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const; - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual void write(Bitstream& out, const QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual Category getCategory() const; - -private: - - QVector _fields; - QByteArray _hash; -}; - -/// Base template for collection streamers. -template class CollectionTypeStreamer : public SimpleTypeStreamer { -}; - -/// A streamer for list types. -template class CollectionTypeStreamer > : public SimpleTypeStreamer > { -public: - - virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } - virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } - virtual void insert(QVariant& object, const QVariant& value) const { - static_cast*>(object.data())->append(value.value()); } - virtual void prune(QVariant& object, int size) const { - QList* list = static_cast*>(object.data()); list->erase(list->begin() + size, list->end()); } - virtual QVariant getValue(const QVariant& object, int index) const { - return QVariant::fromValue(static_cast*>(object.constData())->at(index)); } - virtual void setValue(QVariant& object, int index, const QVariant& value) const { - static_cast*>(object.data())->replace(index, value.value()); } -}; - -/// A streamer for vector types. -template class CollectionTypeStreamer > : public SimpleTypeStreamer > { -public: - - virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } - virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } - virtual void insert(QVariant& object, const QVariant& value) const { - static_cast*>(object.data())->append(value.value()); } - virtual void prune(QVariant& object, int size) const { - QVector* list = static_cast*>(object.data()); list->erase(list->begin() + size, list->end()); } - virtual QVariant getValue(const QVariant& object, int index) const { - return QVariant::fromValue(static_cast*>(object.constData())->at(index)); } - virtual void setValue(QVariant& object, int index, const QVariant& value) const { - static_cast*>(object.data())->replace(index, value.value()); } -}; - -/// A streamer for lists that maps to a local type. -class MappedListTypeStreamer : public TypeStreamer { -public: - - MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer); - - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; - -protected: - - const TypeStreamer* _baseStreamer; - TypeStreamerPointer _valueStreamer; -}; - -/// A streamer for generic lists. -class GenericListTypeStreamer : public GenericTypeStreamer { -public: - - GenericListTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer); - - virtual void writeMetadata(Bitstream& out, bool full) const; - virtual QJsonValue getJSONMetadata(JSONWriter& writer) const; - virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const; - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual void write(Bitstream& out, const QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual Category getCategory() const; - -protected: - - TypeStreamerPointer _valueStreamer; -}; - -/// A streamer for set types. -template class CollectionTypeStreamer > : public SimpleTypeStreamer > { -public: - - virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::SET_CATEGORY; } - virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } - virtual void insert(QVariant& object, const QVariant& value) const { - static_cast*>(object.data())->insert(value.value()); } - virtual bool remove(QVariant& object, const QVariant& key) const { - return static_cast*>(object.data())->remove(key.value()); } -}; - -/// A streamer for sets that maps to a local type. -class MappedSetTypeStreamer : public MappedListTypeStreamer { -public: - - MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer); - - virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; -}; - -/// A streamer for generic sets. -class GenericSetTypeStreamer : public GenericListTypeStreamer { -public: - - GenericSetTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer); - - virtual QJsonValue getJSONMetadata(JSONWriter& writer) const; - virtual Category getCategory() const; -}; - -/// A streamer for hash types. -template class CollectionTypeStreamer > : public SimpleTypeStreamer > { -public: - - virtual void writeMetadata(Bitstream& out, bool full) const { out << getKeyStreamer() << getValueStreamer(); } - virtual TypeStreamer::Category getCategory() const { return TypeStreamer::MAP_CATEGORY; } - virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } - virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } - virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const { - static_cast*>(object.data())->insert(key.value(), value.value()); } - virtual bool remove(QVariant& object, const QVariant& key) const { - return static_cast*>(object.data())->remove(key.value()); } - virtual QVariant getValue(const QVariant& object, const QVariant& key) const { - return QVariant::fromValue(static_cast*>(object.constData())->value(key.value())); } -}; - -/// A streamer for maps that maps to a local type. -class MappedMapTypeStreamer : public TypeStreamer { -public: - - MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer, - const TypeStreamerPointer& valueStreamer); - - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; - -private: - - const TypeStreamer* _baseStreamer; - TypeStreamerPointer _keyStreamer; - TypeStreamerPointer _valueStreamer; -}; - -/// A streamer for generic maps. -class GenericMapTypeStreamer : public GenericTypeStreamer { -public: - - GenericMapTypeStreamer(const QByteArray& name, const TypeStreamerPointer& keyStreamer, - const TypeStreamerPointer& valueStreamer); - - virtual void writeMetadata(Bitstream& out, bool full) const; - virtual QJsonValue getJSONMetadata(JSONWriter& writer) const; - virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const; - virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; - virtual void write(Bitstream& out, const QVariant& value) const; - virtual QVariant read(Bitstream& in) const; - virtual Category getCategory() const; - -private: - - TypeStreamerPointer _keyStreamer; - TypeStreamerPointer _valueStreamer; -}; - -/// A streamer class for generic values. -class GenericValueStreamer : public SimpleTypeStreamer { -public: - - QJsonValue getJSONVariantData(JSONWriter& writer, const QVariant& value) const; - virtual void writeVariant(Bitstream& out, const QVariant& value) const; -}; - -/// Macro for registering simple type streamers. Typically, one would use this at the top level of the source file -/// associated with the type. -#define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \ - Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); - -/// Macro for registering collection type (QList, QVector, QSet, QMap) streamers. Typically, one would use this at the top -/// level of the source file associated with the type. -#define REGISTER_COLLECTION_TYPE_STREAMER(X) static int x##Streamer = \ - Bitstream::registerTypeStreamer(qMetaTypeId(), new CollectionTypeStreamer()); - -/// Declares the metatype and the streaming operators. Typically, one would use this immediately after the definition of a -/// type flagged as STREAMABLE in its header file. The type should have a no-argument constructor. The last lines of this -/// macro ensure that the generated file will be included in the link phase. -#ifdef _WIN32 -#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ - Bitstream& operator<<(Bitstream& out, const X& obj); \ - Bitstream& operator>>(Bitstream& in, X& obj); \ - template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \ - template<> void Bitstream::readRawDelta(X& value, const X& reference); \ - template<> QJsonValue JSONWriter::getData(const X& value); \ - template<> void JSONReader::putData(const QJsonValue& data, X& value); \ - bool operator==(const X& first, const X& second); \ - bool operator!=(const X& first, const X& second); \ - static const int* _TypePtr##X = &X::Type; -#elif __GNUC__ -#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ - Bitstream& operator<<(Bitstream& out, const X& obj); \ - Bitstream& operator>>(Bitstream& in, X& obj); \ - template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \ - template<> void Bitstream::readRawDelta(X& value, const X& reference); \ - template<> QJsonValue JSONWriter::getData(const X& value); \ - template<> void JSONReader::putData(const QJsonValue& data, X& value); \ - bool operator==(const X& first, const X& second); \ - bool operator!=(const X& first, const X& second); \ - __attribute__((unused)) static const int* _TypePtr##X = &X::Type; -#else -#define STRINGIFY(x) #x -#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ - Bitstream& operator<<(Bitstream& out, const X& obj); \ - Bitstream& operator>>(Bitstream& in, X& obj); \ - template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \ - template<> void Bitstream::readRawDelta(X& value, const X& reference); \ - template<> QJsonValue JSONWriter::getData(const X& value); \ - template<> void JSONReader::putData(const QJsonValue& data, X& value); \ - bool operator==(const X& first, const X& second); \ - bool operator!=(const X& first, const X& second); \ - static const int* _TypePtr##X = &X::Type; \ - _Pragma(STRINGIFY(unused(_TypePtr##X))) -#endif - -/// Declares an enum metatype. This is used when one desires to use an enum defined in a QObject outside of that class's -/// direct properties. Typically, one would use this immediately after the definition of the QObject containing the enum -/// in its header file. -#define DECLARE_ENUM_METATYPE(S, N) Q_DECLARE_METATYPE(S::N) \ - Bitstream& operator<<(Bitstream& out, const S::N& obj); \ - Bitstream& operator>>(Bitstream& in, S::N& obj); \ - template<> inline void Bitstream::writeRawDelta(const S::N& value, const S::N& reference) { *this << value; } \ - template<> inline void Bitstream::readRawDelta(S::N& value, const S::N& reference) { *this >> value; } \ - template<> inline QJsonValue JSONWriter::getData(const S::N& value) { return (int)value; } \ - template<> inline void JSONReader::putData(const QJsonValue& data, S::N& value) { value = (S::N)data.toInt(); } - -/// Implements an enum metatype. This performs the implementation of the previous macro, and would normally be used at the -/// top level of the source file associated with the QObject containing the enum. -#define IMPLEMENT_ENUM_METATYPE(S, N) \ - static int S##N##MetaTypeId = registerEnumMetaType(&S::staticMetaObject, #N); \ - Bitstream& operator<<(Bitstream& out, const S::N& obj) { \ - static int bits = Bitstream::getTypeStreamer(qMetaTypeId())->getBits(); \ - return out.write(&obj, bits); \ - } \ - Bitstream& operator>>(Bitstream& in, S::N& obj) { \ - static int bits = Bitstream::getTypeStreamer(qMetaTypeId())->getBits(); \ - obj = (S::N)0; \ - return in.read(&obj, bits); \ - } - -/// Registers a simple type and its streamer. This would typically be used at the top level of the source file associated with -/// the type, and combines the registration of the type with the Qt metatype system with the registration of a simple type -/// streamer. -/// \return the metatype id -template int registerSimpleMetaType() { - int type = qRegisterMetaType(); - Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer()); - return type; -} - -/// Registers an enum type and its streamer. Rather than using this directly, consider using the IMPLEMENT_ENUM_METATYPE -/// macro. -/// \return the metatype id -template int registerEnumMetaType(const QMetaObject* metaObject, const char* name) { - int type = qRegisterMetaType(); - Bitstream::registerTypeStreamer(type, new EnumTypeStreamer(metaObject, name)); - return type; -} - -/// Registers a streamable type and its streamer. Rather than using this directly, consider using the -/// DECLARE_STREAMABLE_METATYPE macro and the mtc code generation tool. -/// \return the metatype id -template int registerStreamableMetaType() { - int type = qRegisterMetaType(); - Bitstream::registerTypeStreamer(type, new StreamableTypeStreamer()); - return type; -} - -/// Registers a collection type and its streamer. This would typically be used at the top level of the source file associated -/// with the type, and combines the registration of the type with the Qt metatype system with the registration of a collection -/// type streamer. -/// \return the metatype id -template int registerCollectionMetaType() { - int type = qRegisterMetaType(); - Bitstream::registerTypeStreamer(type, new CollectionTypeStreamer()); - return type; -} - -/// Flags a class as streamable. Use as you would Q_OBJECT: as the first line of a class definition, before any members or -/// access qualifiers. Typically, one would follow the definition with DECLARE_STREAMABLE_METATYPE. The mtc tool looks for -/// this macro in order to generate streaming (etc.) code for types. -#define STREAMABLE public: \ - static const int Type; \ - static const QVector& getMetaFields(); \ - static int getFieldIndex(const QByteArray& name); \ - void setField(int index, const QVariant& value); \ - QVariant getField(int index) const; \ - private: \ - static QHash createFieldIndices(); - -/// Flags a (public) field within or base class of a streamable type as streaming. Use before all base classes or fields that -/// you want to stream. -#define STREAM - -#endif // hifi_Bitstream_h diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp deleted file mode 100644 index ae4a96ad4f..0000000000 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ /dev/null @@ -1,952 +0,0 @@ -// -// DatagramSequencer.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/20/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include - -#include - -#include "DatagramSequencer.h" -#include "MetavoxelMessages.h" - -// in sequencer parlance, a "packet" may consist of multiple datagrams. clarify when we refer to actual datagrams -const int MAX_DATAGRAM_SIZE = MAX_PACKET_SIZE; - -const int DEFAULT_MAX_PACKET_SIZE = 3000; - -// the default slow-start threshold, which will be lowered quickly when we first encounter packet loss -const float DEFAULT_SLOW_START_THRESHOLD = 1000.0f; - -DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader, QObject* parent) : - QObject(parent), - _outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly), - _outputStream(_outgoingPacketStream, Bitstream::NO_METADATA, Bitstream::NO_GENERICS, this), - _incomingDatagramStream(&_incomingDatagramBuffer), - _datagramHeaderSize(datagramHeader.size()), - _outgoingPacketNumber(0), - _outgoingDatagram(MAX_DATAGRAM_SIZE, 0), - _outgoingDatagramBuffer(&_outgoingDatagram), - _outgoingDatagramStream(&_outgoingDatagramBuffer), - _incomingPacketNumber(0), - _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), - _inputStream(_incomingPacketStream, Bitstream::NO_METADATA, Bitstream::NO_GENERICS, this), - _receivedHighPriorityMessages(0), - _maxPacketSize(DEFAULT_MAX_PACKET_SIZE), - _packetsPerGroup(1.0f), - _packetsToWrite(0.0f), - _slowStartThreshold(DEFAULT_SLOW_START_THRESHOLD), - _packetRateIncreasePacketNumber(0), - _packetRateDecreasePacketNumber(0), - _packetDropCount(0) { - - _outgoingPacketStream.setByteOrder(QDataStream::LittleEndian); - _incomingDatagramStream.setByteOrder(QDataStream::LittleEndian); - _incomingPacketStream.setByteOrder(QDataStream::LittleEndian); - _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); -} - -void DatagramSequencer::sendHighPriorityMessage(const QVariant& data) { - HighPriorityMessage message = { data, _outgoingPacketNumber + 1 }; - _highPriorityMessages.append(message); -} - -ReliableChannel* DatagramSequencer::getReliableOutputChannel(int index) { - ReliableChannel*& channel = _reliableOutputChannels[index]; - if (!channel) { - channel = new ReliableChannel(this, index, true); - } - return channel; -} - -ReliableChannel* DatagramSequencer::getReliableInputChannel(int index) { - ReliableChannel*& channel = _reliableInputChannels[index]; - if (!channel) { - channel = new ReliableChannel(this, index, false); - } - return channel; -} - -void DatagramSequencer::addReliableChannelStats(int& sendProgress, int& sendTotal, - int& receiveProgress, int& receiveTotal) const { - foreach (ReliableChannel* channel, _reliableOutputChannels) { - int sent, total; - if (channel->getMessageSendProgress(sent, total)) { - sendProgress += sent; - sendTotal += total; - } - } - foreach (ReliableChannel* channel, _reliableInputChannels) { - int received, total; - if (channel->getMessageReceiveProgress(received, total)) { - receiveProgress += received; - receiveTotal += total; - } - } -} - -int DatagramSequencer::notePacketGroup(int desiredPackets) { - // figure out how much data we have enqueued and increase the number of packets desired - int totalAvailable = 0; - foreach (ReliableChannel* channel, _reliableOutputChannels) { - totalAvailable += channel->getBytesAvailable(); - } - desiredPackets += (totalAvailable / _maxPacketSize); - - // increment our packet counter and subtract/return the integer portion - _packetsToWrite += _packetsPerGroup; - int wholePackets = (int)_packetsToWrite; - _packetsToWrite -= wholePackets; - wholePackets = qMin(wholePackets, desiredPackets); - - // if we don't want to send any more, push out the rate increase number past the group - if (desiredPackets <= _packetsPerGroup) { - _packetRateIncreasePacketNumber = _outgoingPacketNumber + wholePackets + 1; - } - - // likewise, if we're only sending one packet, don't let its loss cause rate decrease - if (wholePackets == 1) { - _packetRateDecreasePacketNumber = _outgoingPacketNumber + 2; - } - - return wholePackets; -} - -Bitstream& DatagramSequencer::startPacket() { - // start with the list of acknowledgements - _outgoingPacketStream << (quint32)_receiveRecords.size(); - foreach (const ReceiveRecord& record, _receiveRecords) { - _outgoingPacketStream << (quint32)record.packetNumber; - } - - // return the stream, allowing the caller to write the rest - return _outputStream; -} - -void DatagramSequencer::endPacket() { - // write the high-priority messages - _outputStream << _highPriorityMessages.size(); - foreach (const HighPriorityMessage& message, _highPriorityMessages) { - _outputStream << message.data; - } - _outputStream.flush(); - - // if we have space remaining, send some data from our reliable channels - int remaining = _maxPacketSize - _outgoingPacketStream.device()->pos(); - const int MINIMUM_RELIABLE_SIZE = sizeof(quint32) * 5; // count, channel number, segment count, offset, size - QVector spans; - if (remaining > MINIMUM_RELIABLE_SIZE) { - appendReliableData(remaining, spans); - } else { - _outgoingPacketStream << (quint32)0; - } - - sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos()), spans); - _outgoingPacketStream.device()->seek(0); -} - -void DatagramSequencer::cancelPacket() { - _outputStream.reset(); - _outputStream.getAndResetWriteMappings(); - _outgoingPacketStream.device()->seek(0); -} - -/// Simple RAII-style object to keep a device open when in scope. -class QIODeviceOpener { -public: - - QIODeviceOpener(QIODevice* device, QIODevice::OpenMode mode) : _device(device) { _device->open(mode); } - ~QIODeviceOpener() { _device->close(); } - -private: - - QIODevice* _device; -}; - -void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { - _incomingDatagramBuffer.setData(datagram.constData() + _datagramHeaderSize, datagram.size() - _datagramHeaderSize); - QIODeviceOpener opener(&_incomingDatagramBuffer, QIODevice::ReadOnly); - - // read the sequence number - int sequenceNumber; - _incomingDatagramStream >> sequenceNumber; - - // if it's less than the last, ignore - if (sequenceNumber < _incomingPacketNumber) { - return; - } - - // read the size and offset - quint32 packetSize, offset; - _incomingDatagramStream >> packetSize >> offset; - - // if it's greater, reset - if (sequenceNumber > _incomingPacketNumber) { - _incomingPacketNumber = sequenceNumber; - _incomingPacketData.resize(packetSize); - _offsetsReceived.clear(); - _offsetsReceived.insert(offset); - _remainingBytes = packetSize; - - } else { - // make sure it's not a duplicate - if (_offsetsReceived.contains(offset)) { - return; - } - _offsetsReceived.insert(offset); - } - - // copy in the data - memcpy(_incomingPacketData.data() + offset, _incomingDatagramBuffer.data().constData() + _incomingDatagramBuffer.pos(), - _incomingDatagramBuffer.bytesAvailable()); - - // see if we're done - if ((_remainingBytes -= _incomingDatagramBuffer.bytesAvailable()) > 0) { - return; - } - - // read the list of acknowledged packets - quint32 acknowledgementCount; - _incomingPacketStream >> acknowledgementCount; - for (quint32 i = 0; i < acknowledgementCount; i++) { - quint32 packetNumber; - _incomingPacketStream >> packetNumber; - if (_sendRecords.isEmpty()) { - continue; - } - int index = packetNumber - _sendRecords.first().packetNumber; - if (index < 0 || index >= _sendRecords.size()) { - continue; - } - QList::iterator it = _sendRecords.begin(); - for (int i = 0; i < index; i++) { - sendRecordLost(*it++); - } - sendRecordAcknowledged(*it); - emit sendAcknowledged(index); - _sendRecords.erase(_sendRecords.begin(), it + 1); - } - - try { - // alert external parties so that they can read the middle - emit readyToRead(_inputStream); - - // read and dispatch the high-priority messages - int highPriorityMessageCount; - _inputStream >> highPriorityMessageCount; - int newHighPriorityMessages = highPriorityMessageCount - _receivedHighPriorityMessages; - for (int i = 0; i < highPriorityMessageCount; i++) { - QVariant data; - _inputStream >> data; - if (i >= _receivedHighPriorityMessages) { - emit receivedHighPriorityMessage(data); - } - } - _receivedHighPriorityMessages = highPriorityMessageCount; - - // read the reliable data, if any - quint32 reliableChannels; - _incomingPacketStream >> reliableChannels; - for (quint32 i = 0; i < reliableChannels; i++) { - quint32 channelIndex; - _incomingPacketStream >> channelIndex; - getReliableInputChannel(channelIndex)->readData(_incomingPacketStream); - } - - // record the receipt - ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings(), newHighPriorityMessages }; - _receiveRecords.append(record); - - emit receiveRecorded(); - - } catch (const BitstreamException& e) { - qWarning() << "Error reading datagram:" << e.getDescription(); - } - - _incomingPacketStream.device()->seek(0); - _inputStream.reset(); -} - -void DatagramSequencer::sendClearSharedObjectMessage(int id) { - // 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::clearReliableChannel(QObject* object) { - ReliableChannel* channel = static_cast(object); - (channel->isOutput() ? _reliableOutputChannels : _reliableInputChannels).remove(channel->getIndex()); -} - -void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { - // stop acknowledging the recorded packets - while (!_receiveRecords.isEmpty() && _receiveRecords.first().packetNumber <= record.lastReceivedPacketNumber) { - emit receiveAcknowledged(0); - const ReceiveRecord& received = _receiveRecords.first(); - _inputStream.persistReadMappings(received.mappings); - _receivedHighPriorityMessages -= received.newHighPriorityMessages; - _receiveRecords.removeFirst(); - } - _outputStream.persistWriteMappings(record.mappings); - - // remove the received high priority messages - for (int i = _highPriorityMessages.size() - 1; i >= 0; i--) { - if (_highPriorityMessages.at(i).firstPacketNumber <= record.packetNumber) { - _highPriorityMessages.erase(_highPriorityMessages.begin(), _highPriorityMessages.begin() + i + 1); - break; - } - } - - // acknowledge the received spans - foreach (const ChannelSpan& span, record.spans) { - ReliableChannel* channel = _reliableOutputChannels.value(span.channel); - if (channel) { - channel->spanAcknowledged(span); - } - } - - // increase the packet rate with every ack until we pass the slow start threshold; then, every round trip - if (record.packetNumber >= _packetRateIncreasePacketNumber) { - if (_packetsPerGroup >= _slowStartThreshold) { - _packetRateIncreasePacketNumber = _outgoingPacketNumber + 1; - } - _packetsPerGroup += 1.0f; - } -} - -void DatagramSequencer::sendRecordLost(const SendRecord& record) { - // notify the channels of their lost spans - foreach (const ChannelSpan& span, record.spans) { - ReliableChannel* channel = _reliableOutputChannels.value(span.channel); - if (channel) { - channel->spanLost(record.packetNumber, _outgoingPacketNumber + 1); - } - } - - // if we've lost three in a row, halve the rate and remember as threshold - if (_packetDropCount == 0 || record.packetNumber == _lastPacketDropped + 1) { - _packetDropCount++; - _lastPacketDropped = record.packetNumber; - const int CONSECUTIVE_DROPS_BEFORE_REDUCTION = 1; - if (_packetDropCount >= CONSECUTIVE_DROPS_BEFORE_REDUCTION && record.packetNumber >= _packetRateDecreasePacketNumber) { - _packetsPerGroup = qMax(_packetsPerGroup * 0.5f, 1.0f); - _slowStartThreshold = _packetsPerGroup; - _packetRateDecreasePacketNumber = _outgoingPacketNumber + 1; - } - } else { - _packetDropCount = 0; - } -} - -void DatagramSequencer::appendReliableData(int bytes, QVector& spans) { - // gather total number of bytes to write, priority - int totalBytes = 0; - float totalPriority = 0.0f; - int totalChannels = 0; - foreach (ReliableChannel* channel, _reliableOutputChannels) { - int channelBytes = channel->getBytesAvailable(); - if (channelBytes > 0) { - totalBytes += channelBytes; - totalPriority += channel->getPriority(); - totalChannels++; - } - } - _outgoingPacketStream << (quint32)totalChannels; - if (totalChannels == 0) { - return; - } - totalBytes = qMin(bytes, totalBytes); - - foreach (ReliableChannel* channel, _reliableOutputChannels) { - int channelBytes = channel->getBytesAvailable(); - if (channelBytes == 0) { - continue; - } - _outgoingPacketStream << (quint32)channel->getIndex(); - channelBytes = qMin(channelBytes, (int)(totalBytes * channel->getPriority() / totalPriority)); - channel->writeData(_outgoingPacketStream, channelBytes, spans); - totalBytes -= channelBytes; - totalPriority -= channel->getPriority(); - } -} - -void DatagramSequencer::sendPacket(const QByteArray& packet, const QVector& spans) { - QIODeviceOpener opener(&_outgoingDatagramBuffer, QIODevice::WriteOnly); - - // increment the packet number - _outgoingPacketNumber++; - - // record the send - SendRecord record = { _outgoingPacketNumber, _receiveRecords.isEmpty() ? 0 : _receiveRecords.last().packetNumber, - _outputStream.getAndResetWriteMappings(), spans }; - _sendRecords.append(record); - - emit sendRecorded(); - - // write the sequence number and size, which are the same between all fragments - _outgoingDatagramBuffer.seek(_datagramHeaderSize); - _outgoingDatagramStream << (quint32)_outgoingPacketNumber; - _outgoingDatagramStream << (quint32)packet.size(); - int initialPosition = _outgoingDatagramBuffer.pos(); - - // break the packet into MTU-sized datagrams - int offset = 0; - do { - _outgoingDatagramBuffer.seek(initialPosition); - _outgoingDatagramStream << (quint32)offset; - - int payloadSize = qMin((int)(_outgoingDatagram.size() - _outgoingDatagramBuffer.pos()), packet.size() - offset); - memcpy(_outgoingDatagram.data() + _outgoingDatagramBuffer.pos(), packet.constData() + offset, payloadSize); - - emit readyToWrite(QByteArray::fromRawData(_outgoingDatagram.constData(), _outgoingDatagramBuffer.pos() + payloadSize)); - - offset += payloadSize; - - } while(offset < packet.size()); -} - -const int INITIAL_CIRCULAR_BUFFER_CAPACITY = 16; - -CircularBuffer::CircularBuffer(QObject* parent) : - QIODevice(parent), - _data(INITIAL_CIRCULAR_BUFFER_CAPACITY, 0), - _position(0), - _size(0), - _offset(0) { -} - -void CircularBuffer::append(const char* data, int length) { - // resize to fit - int oldSize = _size; - resize(_size + length); - - // write our data in up to two segments: one from the position to the end, one from the beginning - int end = (_position + oldSize) % _data.size(); - int firstSegment = qMin(length, _data.size() - end); - memcpy(_data.data() + end, data, firstSegment); - int secondSegment = length - firstSegment; - if (secondSegment > 0) { - memcpy(_data.data(), data + firstSegment, secondSegment); - } -} - -void CircularBuffer::remove(int length) { - _position = (_position + length) % _data.size(); - _size -= length; -} - -QByteArray CircularBuffer::readBytes(int offset, int length) const { - 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); - memcpy(data, _data.constData() + start, firstSegment); - int secondSegment = length - firstSegment; - if (secondSegment > 0) { - 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); - } -} - -void CircularBuffer::writeToStream(int offset, int length, QDataStream& out) const { - // write in up to two segments - int start = (_position + offset) % _data.size(); - int firstSegment = qMin(length, _data.size() - start); - out.writeRawData(_data.constData() + start, firstSegment); - int secondSegment = length - firstSegment; - if (secondSegment > 0) { - out.writeRawData(_data.constData(), secondSegment); - } -} - -void CircularBuffer::readFromStream(int offset, int length, QDataStream& in) { - // resize to fit - int requiredSize = offset + length; - if (requiredSize > _size) { - resize(requiredSize); - } - - // read in up to two segments - int start = (_position + offset) % _data.size(); - int firstSegment = qMin(length, _data.size() - start); - in.readRawData(_data.data() + start, firstSegment); - int secondSegment = length - firstSegment; - if (secondSegment > 0) { - in.readRawData(_data.data(), secondSegment); - } -} - -void CircularBuffer::appendToBuffer(int offset, int length, CircularBuffer& buffer) const { - // append in up to two segments - int start = (_position + offset) % _data.size(); - int firstSegment = qMin(length, _data.size() - start); - buffer.append(_data.constData() + start, firstSegment); - int secondSegment = length - firstSegment; - if (secondSegment > 0) { - buffer.append(_data.constData(), secondSegment); - } -} - -bool CircularBuffer::atEnd() const { - return _offset >= _size; -} - -qint64 CircularBuffer::bytesAvailable() const { - return _size - _offset; -} - -bool CircularBuffer::canReadLine() const { - for (int offset = _offset; offset < _size; offset++) { - if (_data.at((_position + offset) % _data.size()) == '\n') { - return true; - } - } - return false; -} - -bool CircularBuffer::open(OpenMode flags) { - return QIODevice::open(flags | QIODevice::Unbuffered); -} - -qint64 CircularBuffer::pos() const { - return _offset; -} - -bool CircularBuffer::seek(qint64 pos) { - if (pos < 0 || pos > _size) { - return false; - } - _offset = pos; - return true; -} - -qint64 CircularBuffer::size() const { - return _size; -} - -qint64 CircularBuffer::readData(char* data, qint64 length) { - int readable = qMin((int)length, _size - _offset); - - // read in up to two segments - int start = (_position + _offset) % _data.size(); - int firstSegment = qMin((int)length, _data.size() - start); - memcpy(data, _data.constData() + start, firstSegment); - int secondSegment = length - firstSegment; - if (secondSegment > 0) { - memcpy(data + firstSegment, _data.constData(), secondSegment); - } - _offset += readable; - return readable; -} - -qint64 CircularBuffer::writeData(const char* data, qint64 length) { - // resize to fit - int requiredSize = _offset + length; - if (requiredSize > _size) { - resize(requiredSize); - } - - // write in up to two segments - int start = (_position + _offset) % _data.size(); - int firstSegment = qMin((int)length, _data.size() - start); - memcpy(_data.data() + start, data, firstSegment); - int secondSegment = length - firstSegment; - if (secondSegment > 0) { - memcpy(_data.data(), data + firstSegment, secondSegment); - } - _offset += length; - return length; -} - -void CircularBuffer::resize(int size) { - if (size > _data.size()) { - // double our capacity until we can fit the desired length - int newCapacity = _data.size(); - do { - newCapacity *= 2; - } while (size > newCapacity); - - int oldCapacity = _data.size(); - _data.resize(newCapacity); - - int trailing = _position + _size - oldCapacity; - if (trailing > 0) { - memcpy(_data.data() + oldCapacity, _data.constData(), trailing); - } - } - _size = size; -} - -SpanList::SpanList() : _totalSet(0) { -} - -int SpanList::set(int offset, int length) { - // if we intersect the front of the list, consume beginning spans and return advancement - if (offset <= 0) { - int intersection = offset + length; - return (intersection > 0) ? setSpans(_spans.begin(), intersection) : 0; - } - - // look for an intersection within the list - int position = 0; - for (int i = 0; i < _spans.size(); i++) { - QList::iterator it = _spans.begin() + i; - - // if we intersect the unset portion, contract it - position += it->unset; - if (offset <= position) { - int remove = position - offset; - it->unset -= remove; - - // if we continue into the set portion, expand it and consume following spans - int extra = offset + length - position; - if (extra >= 0) { - extra -= it->set; - it->set += remove; - _totalSet += remove; - if (extra > 0) { - int amount = setSpans(it + 1, extra); - _spans[i].set += amount; - _totalSet += amount; - } - // otherwise, insert a new span - } else { - Span span = { it->unset, length }; - it->unset = -extra; - _spans.insert(it, span); - _totalSet += length; - } - return 0; - } - - // if we intersect the set portion, expand it and consume following spans - position += it->set; - if (offset <= position) { - int extra = offset + length - position; - if (extra > 0) { - int amount = setSpans(it + 1, extra); - _spans[i].set += amount; - _totalSet += amount; - } - return 0; - } - } - - // add to end of list - Span span = { offset - position, length }; - _spans.append(span); - _totalSet += length; - - return 0; -} - -int SpanList::setSpans(QList::iterator it, int length) { - int remainingLength = length; - int totalRemoved = 0; - for (; it != _spans.end(); it = _spans.erase(it)) { - if (remainingLength < it->unset) { - it->unset -= remainingLength; - totalRemoved += remainingLength; - break; - } - int combined = it->unset + it->set; - remainingLength = qMax(remainingLength - combined, 0); - totalRemoved += combined; - _totalSet -= it->set; - } - return qMax(length, totalRemoved); -} - -int ReliableChannel::getBytesAvailable() const { - return _buffer.size() - _acknowledged.getTotalSet(); -} - -void ReliableChannel::startMessage() { - // write a placeholder for the length; we'll fill it in when we know what it is - _messageLengthPlaceholder = _buffer.pos(); - _dataStream << (quint32)0; -} - -void ReliableChannel::endMessage() { - _bitstream.flush(); - _bitstream.persistAndResetWriteMappings(); - - quint32 length = _buffer.pos() - _messageLengthPlaceholder; - _buffer.writeBytes(_messageLengthPlaceholder, sizeof(quint32), (const char*)&length); - - pruneOutgoingMessageStats(); - _outgoingMessageStats.append(OffsetSizePair(getBytesWritten(), length)); -} - -void ReliableChannel::sendMessage(const QVariant& message) { - startMessage(); - _bitstream << message; - endMessage(); -} - -bool ReliableChannel::getMessageSendProgress(int& sent, int& total) { - pruneOutgoingMessageStats(); - if (!_messagesEnabled || _outgoingMessageStats.isEmpty()) { - return false; - } - const OffsetSizePair& stat = _outgoingMessageStats.first(); - sent = qMax(0, stat.second - (stat.first - _offset)); - total = stat.second; - return true; -} - -bool ReliableChannel::getMessageReceiveProgress(int& received, int& total) const { - if (!_messagesEnabled || _buffer.bytesAvailable() < (int)sizeof(quint32)) { - return false; - } - quint32 length; - _buffer.readBytes(_buffer.pos(), sizeof(quint32), (char*)&length); - total = length; - received = _buffer.bytesAvailable(); - return true; -} - -void ReliableChannel::sendClearSharedObjectMessage(int id) { - ClearSharedObjectMessage message = { id }; - sendMessage(QVariant::fromValue(message)); -} - -void ReliableChannel::handleMessage(const QVariant& message, Bitstream& in) { - 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), - _output(output), - _dataStream(&_buffer), - _bitstream(_dataStream, Bitstream::NO_METADATA, Bitstream::NO_GENERICS, this), - _priority(1.0f), - _offset(0), - _writePosition(0), - _writePositionResetPacketNumber(0), - _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&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); - - sequencer->connect(this, SIGNAL(destroyed(QObject*)), SLOT(clearReliableChannel(QObject*))); -} - -void ReliableChannel::writeData(QDataStream& out, int bytes, QVector& spans) { - if (bytes == 0) { - out << (quint32)0; - return; - } - _writePosition %= _buffer.pos(); - while (bytes > 0) { - int position = 0; - for (int i = 0; i < _acknowledged.getSpans().size(); i++) { - const SpanList::Span& span = _acknowledged.getSpans().at(i); - position += span.unset; - if (_writePosition < position) { - int start = qMax(position - span.unset, _writePosition); - int length = qMin(bytes, position - start); - writeSpan(out, start, length, spans); - writeFullSpans(out, bytes - length, i + 1, position + span.set, spans); - out << (quint32)0; - return; - } - position += span.set; - } - int leftover = _buffer.pos() - position; - position = _buffer.pos(); - - if (_writePosition < position && leftover > 0) { - int start = qMax(position - leftover, _writePosition); - int length = qMin(bytes, position - start); - writeSpan(out, start, length, spans); - writeFullSpans(out, bytes - length, 0, 0, spans); - out << (quint32)0; - return; - } - _writePosition = 0; - } -} - -void ReliableChannel::writeFullSpans(QDataStream& out, int bytes, int startingIndex, int position, - QVector& spans) { - int expandedSize = _acknowledged.getSpans().size() + 1; - for (int i = 0; i < expandedSize; i++) { - if (bytes == 0) { - return; - } - int index = (startingIndex + i) % expandedSize; - if (index == _acknowledged.getSpans().size()) { - int leftover = _buffer.pos() - position; - if (leftover > 0) { - int length = qMin(leftover, bytes); - writeSpan(out, position, length, spans); - bytes -= length; - } - position = 0; - - } else { - const SpanList::Span& span = _acknowledged.getSpans().at(index); - int length = qMin(span.unset, bytes); - writeSpan(out, position, length, spans); - bytes -= length; - position += (span.unset + span.set); - } - } -} - -int ReliableChannel::writeSpan(QDataStream& out, int position, int length, QVector& spans) { - DatagramSequencer::ChannelSpan span = { _index, _offset + position, length }; - spans.append(span); - out << (quint32)length; - out << (quint32)span.offset; - _buffer.writeToStream(position, length, out); - _writePosition = position + length; - - return length; -} - -void ReliableChannel::spanAcknowledged(const DatagramSequencer::ChannelSpan& span) { - int advancement = _acknowledged.set(span.offset - _offset, span.length); - if (advancement > 0) { - _buffer.remove(advancement); - _buffer.seek(_buffer.size()); - - _offset += advancement; - _writePosition = qMax(_writePosition - advancement, 0); - } -} - -void ReliableChannel::spanLost(int packetNumber, int nextOutgoingPacketNumber) { - // reset the write position up to once each round trip time - if (packetNumber >= _writePositionResetPacketNumber) { - _writePosition = 0; - _writePositionResetPacketNumber = nextOutgoingPacketNumber; - } -} - -void ReliableChannel::readData(QDataStream& in) { - bool readSome = false; - forever { - quint32 size; - in >> size; - if (size == 0) { - break; - } - quint32 offset; - in >> offset; - - int position = offset - _offset; - int end = position + size; - if (end <= 0) { - in.skipRawData(size); - - } else if (position < 0) { - in.skipRawData(-position); - _assemblyBuffer.readFromStream(0, end, in); - - } else { - _assemblyBuffer.readFromStream(position, size, in); - } - int advancement = _acknowledged.set(position, size); - if (advancement > 0) { - _assemblyBuffer.appendToBuffer(0, advancement, _buffer); - _assemblyBuffer.remove(advancement); - _offset += advancement; - readSome = true; - } - } - if (!readSome) { - return; - } - - 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 (_messagesEnabled) { - quint32 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; - emit receivedMessage(message, _bitstream); - _bitstream.reset(); - _bitstream.persistAndResetReadMappings(); - continue; - } - } - // otherwise, just let whoever's listening know that data is available - } else { - emit _buffer.readyRead(); - } - break; - } - - // prune any read data from the buffer - if (_buffer.pos() > 0) { - _buffer.remove((int)_buffer.pos()); - _buffer.seek(0); - } -} - -void ReliableChannel::pruneOutgoingMessageStats() { - while (!_outgoingMessageStats.isEmpty() && _offset >= _outgoingMessageStats.first().first) { - _outgoingMessageStats.removeFirst(); - } -} - diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h deleted file mode 100644 index 739373d137..0000000000 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ /dev/null @@ -1,445 +0,0 @@ -// -// DatagramSequencer.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/20/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_DatagramSequencer_h -#define hifi_DatagramSequencer_h - -#include -#include -#include -#include -#include -#include - -#include "AttributeRegistry.h" - -class ReliableChannel; - -/// Performs datagram sequencing, packet fragmentation and reassembly. Works with Bitstream to provide methods to send and -/// receive data over UDP with varying reliability and latency characteristics. To use, create a DatagramSequencer with the -/// fixed-size header that will be included with all outgoing datagrams and expected in all incoming ones (the contents of the -/// header are not checked on receive, only skipped over, and may be modified by the party that actually send the -/// datagram--this means that the header may include dynamically generated data, as long as its size remains fixed). Connect -/// the readyToWrite signal to a slot that will actually transmit the datagram to the remote party. When a datagram is -/// received from that party, call receivedDatagram with its contents. -/// -/// A "packet" represents a batch of data sent at one time (split into one or more datagrams sized below the MTU). Packets are -/// received in full and in order or not at all (that is, a packet being assembled is dropped as soon as a fragment from the -/// next packet is received). Packets can be any size, but the larger a packet is, the more likely it is to be dropped--so, -/// it's better to keep packet sizes close to the MTU. To write a packet, call startPacket, write data to the returned -/// Bitstream, then call endPacket (which will result in one or more firings of readyToWrite). Data written in this way is not -/// guaranteed to be received, but if it is received, it will arrive in order. This is a good way to transmit delta state: -/// state that represents the change between the last acknowledged state and the current state (which, if not received, will -/// not be resent as-is; instead, it will be replaced by up-to-date new deltas). -/// -/// There are two methods for sending reliable data. The first, for small messages that require minimum-latency processing, is -/// the high priority messaging system. When you call sendHighPriorityMessage, the message that you send will be included with -/// every outgoing packet until it is acknowledged. When the receiving party first sees the message, it will fire a -/// receivedHighPriorityMessage signal. -/// -/// The second method employs a set of independent reliable channels multiplexed onto the packet stream. These channels are -/// created lazily through the getReliableOutputChannel/getReliableInputChannel functions. Output channels contain buffers -/// to which one may write either arbitrary data (as a QIODevice) or messages (as QVariants), or switch between the two. -/// Each time a packet is sent, data pending for reliable output channels is added, in proportion to their relative priorities, -/// until the packet size limit set by setMaxPacketSize is reached. On the receive side, the streams are reconstructed and -/// (again, depending on whether messages are enabled) either the QIODevice reports that data is available, or, when a complete -/// message is decoded, the receivedMessage signal is fired. -class DatagramSequencer : public QObject { - Q_OBJECT - -public: - - /// Contains the content of a high-priority message along with the number of the first packet in which it was sent. - class HighPriorityMessage { - public: - QVariant data; - int firstPacketNumber; - }; - - /// Creates a new datagram sequencer. - /// \param datagramHeader the content of the header that will be prepended to each outgoing datagram and whose length - /// will be skipped over in each incoming datagram - DatagramSequencer(const QByteArray& datagramHeader = QByteArray(), QObject* parent = NULL); - - /// Returns a reference to the weak hash mapping remote ids to shared objects. - const WeakSharedObjectHash& getWeakSharedObjectHash() const { return _inputStream.getWeakSharedObjectHash(); } - - /// Returns the packet number of the last packet sent. - int getOutgoingPacketNumber() const { return _outgoingPacketNumber; } - - /// Returns the packet number of the last packet received (or the packet currently being assembled). - int getIncomingPacketNumber() const { return _incomingPacketNumber; } - - /// Returns a reference to the stream used to read packets. - Bitstream& getInputStream() { return _inputStream; } - - /// Returns a reference to the stream used to write packets. - Bitstream& getOutputStream() { return _outputStream; } - - /// Returns a reference to the outgoing packet data. - const QByteArray& getOutgoingPacketData() const { return _outgoingPacketData; } - - /// Returns the packet number of the sent packet at the specified index. - int getSentPacketNumber(int index) const { return _sendRecords.at(index).packetNumber; } - - /// Adds a message to the high priority queue. Will be sent with every outgoing packet until received. - void sendHighPriorityMessage(const QVariant& data); - - /// Returns a reference to the list of high priority messages not yet acknowledged. - const QList& getHighPriorityMessages() const { return _highPriorityMessages; } - - /// Sets the maximum packet size. This is a soft limit that determines how much - /// reliable data we include with each transmission. - void setMaxPacketSize(int maxPacketSize) { _maxPacketSize = maxPacketSize; } - - int getMaxPacketSize() const { return _maxPacketSize; } - - /// Returns the output channel at the specified index, creating it if necessary. - ReliableChannel* getReliableOutputChannel(int index = 0); - - /// Returns the intput channel at the specified index, creating it if necessary. - ReliableChannel* getReliableInputChannel(int index = 0); - - /// Returns a reference to the stored receive mappings at the specified index. - const Bitstream::ReadMappings& getReadMappings(int index) const { return _receiveRecords.at(index).mappings; } - - /// Adds stats for all reliable channels to the referenced variables. - void addReliableChannelStats(int& sendProgress, int& sendTotal, int& receiveProgress, int& receiveTotal) const; - - /// Notes that we're sending a group of packets. - /// \param desiredPackets the number of packets we'd like to write in the group - /// \return the number of packets to write in the group - int notePacketGroup(int desiredPackets = 1); - - /// Starts a new packet for transmission. - /// \return a reference to the Bitstream to use for writing to the packet - Bitstream& startPacket(); - - /// Sends the packet currently being written. - void endPacket(); - - /// Cancels the packet currently being written. - void cancelPacket(); - - /// Processes a datagram received from the other party, emitting readyToRead when the entire packet - /// has been successfully assembled. - Q_INVOKABLE void receivedDatagram(const QByteArray& datagram); - -signals: - - /// Emitted when a datagram is ready to be transmitted. - void readyToWrite(const QByteArray& datagram); - - /// Emitted when a packet is available to read. - void readyToRead(Bitstream& input); - - /// Emitted when we've received a high-priority message. - void receivedHighPriorityMessage(const QVariant& data); - - /// Emitted when we've recorded the transmission of a packet. - void sendRecorded(); - - /// Emitted when we've recorded the receipt of a packet (that is, at the end of packet processing). - void receiveRecorded(); - - /// Emitted when a sent packet has been acknowledged by the remote side. - /// \param index the index of the packet in our list of send records - void sendAcknowledged(int index); - - /// Emitted when our acknowledgement of a received packet has been acknowledged by the remote side. - /// \param index the index of the packet in our list of receive records - void receiveAcknowledged(int index); - -private slots: - - void sendClearSharedObjectMessage(int id); - void handleHighPriorityMessage(const QVariant& data); - void clearReliableChannel(QObject* object); - -private: - - friend class ReliableChannel; - - class ChannelSpan { - public: - int channel; - int offset; - int length; - }; - - class SendRecord { - public: - int packetNumber; - int lastReceivedPacketNumber; - Bitstream::WriteMappings mappings; - QVector spans; - }; - - class ReceiveRecord { - public: - int packetNumber; - Bitstream::ReadMappings mappings; - int newHighPriorityMessages; - - bool operator<(const ReceiveRecord& other) const { return packetNumber < other.packetNumber; } - }; - - /// Notes that the described send was acknowledged by the other party. - void sendRecordAcknowledged(const SendRecord& record); - - /// Notes that the described send was lost in transit. - void sendRecordLost(const SendRecord& record); - - /// Appends some reliable data to the outgoing packet. - void appendReliableData(int bytes, QVector& spans); - - /// Sends a packet to the other party, fragmenting it into multiple datagrams (and emitting - /// readyToWrite) as necessary. - void sendPacket(const QByteArray& packet, const QVector& spans); - - QList _sendRecords; - QList _receiveRecords; - - QByteArray _outgoingPacketData; - QDataStream _outgoingPacketStream; - Bitstream _outputStream; - - QBuffer _incomingDatagramBuffer; - QDataStream _incomingDatagramStream; - int _datagramHeaderSize; - - int _outgoingPacketNumber; - QByteArray _outgoingDatagram; - QBuffer _outgoingDatagramBuffer; - QDataStream _outgoingDatagramStream; - - int _incomingPacketNumber; - QByteArray _incomingPacketData; - QDataStream _incomingPacketStream; - Bitstream _inputStream; - QSet _offsetsReceived; - int _remainingBytes; - - QList _highPriorityMessages; - int _receivedHighPriorityMessages; - - int _maxPacketSize; - - float _packetsPerGroup; - float _packetsToWrite; - float _slowStartThreshold; - int _packetRateIncreasePacketNumber; - int _packetRateDecreasePacketNumber; - int _packetDropCount; - int _lastPacketDropped; - - QHash _reliableOutputChannels; - QHash _reliableInputChannels; -}; - -/// A circular buffer, where one may efficiently append data to the end or remove data from the beginning. -class CircularBuffer : public QIODevice { -public: - - CircularBuffer(QObject* parent = NULL); - - /// Appends data to the end of the buffer. - void append(const QByteArray& data) { append(data.constData(), data.size()); } - - /// Appends data to the end of the buffer. - void append(const char* data, int length); - - /// Removes data from the beginning of the buffer. - void remove(int length); - - /// 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; - - /// Reads part of the buffer from the supplied stream. - void readFromStream(int offset, int length, QDataStream& in); - - /// Appends part of the buffer to the supplied other buffer. - void appendToBuffer(int offset, int length, CircularBuffer& buffer) const; - - virtual bool atEnd() const; - virtual qint64 bytesAvailable() const; - virtual bool canReadLine() const; - virtual bool open(OpenMode flags); - virtual qint64 pos() const; - virtual bool seek(qint64 pos); - virtual qint64 size() const; - -protected: - - virtual qint64 readData(char* data, qint64 length); - virtual qint64 writeData(const char* data, qint64 length); - -private: - - void resize(int size); - - QByteArray _data; - int _position; - int _size; - int _offset; -}; - -/// A list of contiguous spans, alternating between set and unset. Conceptually, the list is preceeded by a set -/// span of infinite length and followed by an unset span of infinite length. Within those bounds, it alternates -/// between unset and set. -class SpanList { -public: - - class Span { - public: - int unset; - int set; - }; - - SpanList(); - - const QList& getSpans() const { return _spans; } - - /// Returns the total length set. - int getTotalSet() const { return _totalSet; } - - /// Sets a region of the list. - /// \return the advancement of the set length at the beginning of the list - int set(int offset, int length); - -private: - - /// Sets the spans starting at the specified iterator, consuming at least the given length. - /// \return the actual amount set, which may be greater if we ran into an existing set span - int setSpans(QList::iterator it, int length); - - QList _spans; - int _totalSet; -}; - -/// Represents a single reliable channel multiplexed onto the datagram sequence. -class ReliableChannel : public QObject { - Q_OBJECT - -public: - - /// Returns the channel's index in the sequencer's channel map. - int getIndex() const { return _index; } - - /// Checks whether this is an output channel. - bool isOutput() const { return _output; } - - /// Returns a reference to the buffer used to write/read data to/from this channel. - CircularBuffer& getBuffer() { return _buffer; } - - /// Returns a reference to the data stream created on this channel's buffer. - QDataStream& getDataStream() { return _dataStream; } - - /// Returns a reference to the bitstream created on this channel's data stream. - Bitstream& getBitstream() { return _bitstream; } - - /// Sets the channel priority, which determines how much of this channel's data (in proportion to the other channels) to - /// include in each outgoing packet. - void setPriority(float priority) { _priority = priority; } - float getPriority() const { return _priority; } - - /// Returns the number of bytes available to read from this channel. - int getBytesAvailable() const; - - /// Returns the offset, which represents the total number of bytes acknowledged - /// (on the write end) or received completely (on the read end). - int getOffset() const { return _offset; } - - /// Returns the total number of bytes written to this channel. - int getBytesWritten() const { return _offset + _buffer.pos(); } - - /// Sets whether we expect to write/read framed messages. - void setMessagesEnabled(bool enabled) { _messagesEnabled = enabled; } - bool getMessagesEnabled() const { return _messagesEnabled; } - - /// Starts a framed message on this channel. - void startMessage(); - - /// Ends a framed message on this channel. - void endMessage(); - - /// Sends a framed message on this channel (convenience function that calls startMessage, - /// writes the message to the bitstream, then calls endMessage). - void sendMessage(const QVariant& message); - - /// Determines the number of bytes uploaded towards the currently pending message. - /// \return true if there is a message pending, in which case the sent and total arguments will be set - bool getMessageSendProgress(int& sent, int& total); - - /// Determines the number of bytes downloaded towards the currently pending message. - /// \return true if there is a message pending, in which case the received and total arguments will be set - bool getMessageReceiveProgress(int& received, int& total) const; - -signals: - - /// Fired when a framed message has been received on this channel. - void receivedMessage(const QVariant& message, Bitstream& in); - -private slots: - - void sendClearSharedObjectMessage(int id); - void handleMessage(const QVariant& message, Bitstream& in); - -private: - - friend class DatagramSequencer; - - ReliableChannel(DatagramSequencer* sequencer, int index, bool output); - - void writeData(QDataStream& out, int bytes, QVector& spans); - void writeFullSpans(QDataStream& out, int bytes, int startingIndex, int position, - QVector& spans); - int writeSpan(QDataStream& out, int position, int length, QVector& spans); - - void spanAcknowledged(const DatagramSequencer::ChannelSpan& span); - void spanLost(int packetNumber, int nextOutgoingPacketNumber); - - void readData(QDataStream& in); - - void pruneOutgoingMessageStats(); - - int _index; - bool _output; - CircularBuffer _buffer; - CircularBuffer _assemblyBuffer; - QDataStream _dataStream; - Bitstream _bitstream; - float _priority; - - int _offset; - int _writePosition; - int _writePositionResetPacketNumber; - SpanList _acknowledged; - bool _messagesEnabled; - int _messageLengthPlaceholder; ///< the location in the buffer of the message length for the current message - - typedef QPair OffsetSizePair; - QVector _outgoingMessageStats; - - int _messageReceivedOffset; ///< when reached, indicates that the most recent sent message has been received - int _messageSize; ///< the size of the most recent sent message; only valid when _messageReceivedOffset has been set -}; - -#endif // hifi_DatagramSequencer_h diff --git a/libraries/metavoxels/src/Endpoint.cpp b/libraries/metavoxels/src/Endpoint.cpp deleted file mode 100644 index a8a8f922cd..0000000000 --- a/libraries/metavoxels/src/Endpoint.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// -// Endpoint.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 6/26/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include "Endpoint.h" - -Endpoint::Endpoint(const SharedNodePointer& node, PacketRecord* baselineSendRecord, PacketRecord* baselineReceiveRecord) : - _node(node), - _sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData), this) { - - connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&))); - connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&))); - connect(&_sequencer, SIGNAL(sendRecorded()), SLOT(recordSend())); - connect(&_sequencer, SIGNAL(receiveRecorded()), SLOT(recordReceive())); - connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); - connect(&_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int))); - - // insert the baseline send and receive records - _sendRecords.append(baselineSendRecord); - _receiveRecords.append(baselineReceiveRecord); -} - -Endpoint::~Endpoint() { - foreach (PacketRecord* record, _sendRecords) { - delete record; - } - foreach (PacketRecord* record, _receiveRecords) { - delete record; - } -} - -void Endpoint::update() { - int packetsToSend = _sequencer.notePacketGroup(); - for (int i = 0; i < packetsToSend; i++) { - Bitstream& out = _sequencer.startPacket(); - writeUpdateMessage(out); - _sequencer.endPacket(); - } -} - -int Endpoint::parseData(const QByteArray& packet) { - // process through sequencer - QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); - return packet.size(); -} - -void Endpoint::sendDatagram(const QByteArray& data) { - DependencyManager::get()->writeDatagram(data, _node); -} - -void Endpoint::readMessage(Bitstream& in) { - QVariant message; - in >> message; - handleMessage(message, in); -} - -void Endpoint::handleMessage(const QVariant& message, Bitstream& in) { - if (message.userType() == QMetaType::QVariantList) { - foreach (const QVariant& element, message.toList()) { - handleMessage(element, in); - } - } -} - -void Endpoint::recordSend() { - _sendRecords.append(maybeCreateSendRecord()); -} - -void Endpoint::recordReceive() { - _receiveRecords.append(maybeCreateReceiveRecord()); -} - -void Endpoint::clearSendRecordsBefore(int index) { - QList::iterator end = _sendRecords.begin() + index + 1; - for (QList::const_iterator it = _sendRecords.begin(); it != end; it++) { - delete *it; - } - _sendRecords.erase(_sendRecords.begin(), end); -} - -void Endpoint::clearReceiveRecordsBefore(int index) { - QList::iterator end = _receiveRecords.begin() + index + 1; - for (QList::const_iterator it = _receiveRecords.begin(); it != end; it++) { - delete *it; - } - _receiveRecords.erase(_receiveRecords.begin(), end); -} - -void Endpoint::writeUpdateMessage(Bitstream& out) { - out << QVariant(); -} - -PacketRecord* Endpoint::maybeCreateSendRecord() const { - return NULL; -} - -PacketRecord* Endpoint::maybeCreateReceiveRecord() const { - return NULL; -} - -PacketRecord::PacketRecord(int packetNumber, const MetavoxelLOD& lod, const MetavoxelData& data) : - _packetNumber(packetNumber), - _lod(lod), - _data(data) { -} - -PacketRecord::~PacketRecord() { -} diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h deleted file mode 100644 index c64f29878d..0000000000 --- a/libraries/metavoxels/src/Endpoint.h +++ /dev/null @@ -1,89 +0,0 @@ -// -// Endpoint.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 6/26/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Endpoint_h -#define hifi_Endpoint_h - -#include - -#include "DatagramSequencer.h" -#include "MetavoxelData.h" -#include "Spanner.h" - -class PacketRecord; - -/// Base class for communication endpoints: clients and server sessions. -class Endpoint : public NodeData { - Q_OBJECT - -public: - - /// The index of the input/output channel used to transmit reliable deltas. - static const int RELIABLE_DELTA_CHANNEL_INDEX = 1; - - Endpoint(const SharedNodePointer& node, PacketRecord* baselineSendRecord = NULL, - PacketRecord* baselineReceiveRecord = NULL); - virtual ~Endpoint(); - - DatagramSequencer& getSequencer() { return _sequencer; } - - virtual void update(); - - virtual int parseData(const QByteArray& packet); - -protected slots: - - virtual void sendDatagram(const QByteArray& data); - virtual void readMessage(Bitstream& in); - virtual void handleMessage(const QVariant& message, Bitstream& in); - - void recordSend(); - virtual void recordReceive(); - - virtual void clearSendRecordsBefore(int index); - virtual void clearReceiveRecordsBefore(int index); - -protected: - - virtual void writeUpdateMessage(Bitstream& out); - - virtual PacketRecord* maybeCreateSendRecord() const; - virtual PacketRecord* maybeCreateReceiveRecord() const; - - PacketRecord* getLastAcknowledgedSendRecord() const { return _sendRecords.first(); } - PacketRecord* getLastAcknowledgedReceiveRecord() const { return _receiveRecords.first(); } - - SharedNodePointer _node; - DatagramSequencer _sequencer; - - QList _sendRecords; - QList _receiveRecords; -}; - -/// Base class for packet records. -class PacketRecord { -public: - - PacketRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData()); - virtual ~PacketRecord(); - - int getPacketNumber() const { return _packetNumber; } - const MetavoxelLOD& getLOD() const { return _lod; } - const MetavoxelData& getData() const { return _data; } - -private: - - int _packetNumber; - MetavoxelLOD _lod; - MetavoxelData _data; -}; - -#endif // hifi_Endpoint_h diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp deleted file mode 100644 index 0fd845d581..0000000000 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ /dev/null @@ -1,446 +0,0 @@ -// -// MetavoxelClientManager.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 6/26/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include - -#include "MetavoxelClientManager.h" -#include "MetavoxelMessages.h" - -MetavoxelClientManager::MetavoxelClientManager() : - _updater(new MetavoxelUpdater(this)) { - QThread* thread = new QThread(this); - _updater->moveToThread(thread); - connect(thread, &QThread::finished, _updater, &QObject::deleteLater); - thread->start(); - QMetaObject::invokeMethod(_updater, "start"); -} - -MetavoxelClientManager::~MetavoxelClientManager() { - if (_updater) { - _updater->thread()->quit(); - _updater->thread()->wait(); - } -} - -void MetavoxelClientManager::init() { - connect(DependencyManager::get().data(), &NodeList::nodeAdded, - this, &MetavoxelClientManager::maybeAttachClient); - connect(DependencyManager::get().data(), &NodeList::nodeKilled, - this, &MetavoxelClientManager::maybeDeleteClient); -} - -SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(const glm::vec3& origin, - const glm::vec3& direction, const AttributePointer& attribute, float& distance) { - SharedObjectPointer closestSpanner; - float closestDistance = FLT_MAX; - - DependencyManager::get()->eachNode([&](const SharedNodePointer& node){ - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - float clientDistance; - SharedObjectPointer clientSpanner = client->getDataCopy().findFirstRaySpannerIntersection( - origin, direction, attribute, clientDistance - ); - if (clientSpanner && clientDistance < closestDistance) { - closestSpanner = clientSpanner; - closestDistance = clientDistance; - } - } - } - }); - - if (closestSpanner) { - distance = closestDistance; - } - return closestSpanner; -} - -class RayHeightfieldIntersectionVisitor : public RaySpannerIntersectionVisitor { -public: - - float intersectionDistance; - - RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); - - virtual bool visitSpanner(Spanner* spanner, float distance); -}; - -RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, - const glm::vec3& direction, const MetavoxelLOD& lod) : - RaySpannerIntersectionVisitor(origin, direction, QVector() << - AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), lod), - intersectionDistance(FLT_MAX) { -} - -bool RayHeightfieldIntersectionVisitor::visitSpanner(Spanner* spanner, float distance) { - if (spanner->isHeightfield()) { - intersectionDistance = distance; - return false; - } - return true; -} - -bool MetavoxelClientManager::findFirstRayHeightfieldIntersection(const glm::vec3& origin, - const glm::vec3& direction, float& distance) { - RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); - guide(visitor); - if (visitor.intersectionDistance == FLT_MAX) { - return false; - } - distance = visitor.intersectionDistance; - return true; -} - -class HeightfieldHeightVisitor : public SpannerVisitor { -public: - - float height; - - HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); - - virtual bool visit(Spanner* spanner); - virtual int visit(MetavoxelInfo& info); - -private: - - glm::vec3 _location; -}; - -HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), lod), - height(-FLT_MAX), - _location(location) { -} - -bool HeightfieldHeightVisitor::visit(Spanner* spanner) { - height = qMax(height, spanner->getHeight(_location)); - return true; -} - -static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); - -int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { - if (_location.x < info.minimum.x || _location.z < info.minimum.z || _location.x > info.minimum.x + info.size || - _location.z > info.minimum.z + info.size) { - return STOP_RECURSION; - } - SpannerVisitor::visit(info); - return (height == -FLT_MAX) ? (info.isLeaf ? STOP_RECURSION : REVERSE_ORDER) : SHORT_CIRCUIT; -} - -float MetavoxelClientManager::getHeightfieldHeight(const glm::vec3& location) { - HeightfieldHeightVisitor visitor(getLOD(), location); - guide(visitor); - return visitor.height; -} - -void MetavoxelClientManager::paintHeightfieldHeight(const glm::vec3& position, float radius, float height) { - MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldHeightEdit(position, radius, height)) }; - applyEdit(edit, true); -} - -void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { - QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable)); -} - -MetavoxelLOD MetavoxelClientManager::getLOD() { - return MetavoxelLOD(); -} - -void MetavoxelClientManager::maybeAttachClient(const SharedNodePointer& node) { - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = createClient(node); - client->moveToThread(_updater->thread()); - QMetaObject::invokeMethod(_updater, "addClient", Q_ARG(QObject*, client)); - node->setLinkedData(client); - } -} - -void MetavoxelClientManager::maybeDeleteClient(const SharedNodePointer& node) { - if (node->getType() == NodeType::MetavoxelServer) { - // we assume the node is already locked - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - node->setLinkedData(NULL); - client->deleteLater(); - } - } -} - -MetavoxelClient* MetavoxelClientManager::createClient(const SharedNodePointer& node) { - return new MetavoxelClient(node, _updater); -} - -void MetavoxelClientManager::guide(MetavoxelVisitor& visitor) { - DependencyManager::get()->eachNode([&visitor](const SharedNodePointer& node){ - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - client->getDataCopy().guide(visitor); - } - } - }); -} - -MetavoxelUpdater::MetavoxelUpdater(MetavoxelClientManager* clientManager) : - _clientManager(clientManager), - _sendTimer(this) { - - _sendTimer.setSingleShot(true); - connect(&_sendTimer, &QTimer::timeout, this, &MetavoxelUpdater::sendUpdates); -} - -const int SEND_INTERVAL = 33; - -void MetavoxelUpdater::start() { - _lastSend = QDateTime::currentMSecsSinceEpoch(); - _sendTimer.start(SEND_INTERVAL); -} - -void MetavoxelUpdater::addClient(QObject* client) { - _clients.insert(static_cast(client)); - connect(client, &QObject::destroyed, this, &MetavoxelUpdater::removeClient); -} - -void MetavoxelUpdater::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { - // apply to all clients - foreach (MetavoxelClient* client, _clients) { - client->applyEdit(edit, reliable); - } -} - -void MetavoxelUpdater::getStats(QObject* receiver, const QByteArray& method) { - int internal = 0, leaves = 0; - int sendProgress = 0, sendTotal = 0; - int receiveProgress = 0, receiveTotal = 0; - foreach (MetavoxelClient* client, _clients) { - client->getData().countNodes(internal, leaves, _lod); - client->getSequencer().addReliableChannelStats(sendProgress, sendTotal, receiveProgress, receiveTotal); - } - QMetaObject::invokeMethod(receiver, method.constData(), Q_ARG(int, internal), Q_ARG(int, leaves), Q_ARG(int, sendProgress), - Q_ARG(int, sendTotal), Q_ARG(int, receiveProgress), Q_ARG(int, receiveTotal)); -} - -void MetavoxelUpdater::sendUpdates() { - // get the latest LOD from the client manager - _lod = _clientManager->getLOD(); - - // send updates for all clients - foreach (MetavoxelClient* client, _clients) { - client->update(); - } - - // restart the send timer - qint64 now = QDateTime::currentMSecsSinceEpoch(); - int elapsed = now - _lastSend; - _lastSend = now; - - _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL))); -} - -void MetavoxelUpdater::removeClient(QObject* client) { - _clients.remove(static_cast(client)); -} - -MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : - Endpoint(node, new PacketRecord(), new PacketRecord()), - _updater(updater), - _reliableDeltaChannel(NULL), - _reliableDeltaID(0), - _dummyInputStream(_dummyDataStream), - _dummyPacketNumber(0) { - - connect(_sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX), - SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); -} - -MetavoxelData MetavoxelClient::getDataCopy() { - QReadLocker locker(&_dataCopyLock); - return _dataCopy; -} - -void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { - if (reliable) { - _sequencer.getReliableOutputChannel()->sendMessage(QVariant::fromValue(edit)); - - } else { - // apply immediately to local tree - MetavoxelData oldData = _data; - edit.apply(_data, _sequencer.getWeakSharedObjectHash()); - if (_data != oldData) { - dataChanged(oldData); - } - - // start sending it out - _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); - } -} - -PacketRecord* MetavoxelClient::getAcknowledgedSendRecord(int packetNumber) const { - PacketRecord* lastAcknowledged = getLastAcknowledgedSendRecord(); - if (lastAcknowledged->getPacketNumber() == packetNumber) { - return lastAcknowledged; - } - foreach (PacketRecord* record, _clearedSendRecords) { - if (record->getPacketNumber() == packetNumber) { - return record; - } - } - return NULL; -} - -PacketRecord* MetavoxelClient::getAcknowledgedReceiveRecord(int packetNumber) const { - PacketRecord* lastAcknowledged = getLastAcknowledgedReceiveRecord(); - if (lastAcknowledged->getPacketNumber() == packetNumber) { - return lastAcknowledged; - } - foreach (const ClearedReceiveRecord& record, _clearedReceiveRecords) { - if (record.first->getPacketNumber() == packetNumber) { - return record.first; - } - } - return NULL; -} - -void MetavoxelClient::dataChanged(const MetavoxelData& oldData) { - // make thread-safe copy - QWriteLocker locker(&_dataCopyLock); - _dataCopy = _data; -} - -void MetavoxelClient::recordReceive() { - Endpoint::recordReceive(); - - // clear the cleared lists - foreach (PacketRecord* record, _clearedSendRecords) { - delete record; - } - _clearedSendRecords.clear(); - - foreach (const ClearedReceiveRecord& record, _clearedReceiveRecords) { - delete record.first; - } - _clearedReceiveRecords.clear(); -} - -void MetavoxelClient::clearSendRecordsBefore(int index) { - // move to cleared list - QList::iterator end = _sendRecords.begin() + index + 1; - for (QList::const_iterator it = _sendRecords.begin(); it != end; it++) { - _clearedSendRecords.append(*it); - } - _sendRecords.erase(_sendRecords.begin(), end); -} - -void MetavoxelClient::clearReceiveRecordsBefore(int index) { - // copy the mappings on first call per packet - if (_sequencer.getIncomingPacketNumber() > _dummyPacketNumber) { - _dummyPacketNumber = _sequencer.getIncomingPacketNumber(); - _dummyInputStream.copyPersistentMappings(_sequencer.getInputStream()); - } - - // move to cleared list - QList::iterator end = _receiveRecords.begin() + index + 1; - for (QList::const_iterator it = _receiveRecords.begin(); it != end; it++) { - _clearedReceiveRecords.append(ClearedReceiveRecord(*it, _sequencer.getReadMappings(index))); - } - _receiveRecords.erase(_receiveRecords.begin(), end); -} - -void MetavoxelClient::writeUpdateMessage(Bitstream& out) { - ClientStateMessage state = { _updater->getLOD() }; - out << QVariant::fromValue(state); -} - -void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { - int userType = message.userType(); - if (userType == MetavoxelDeltaMessage::Type) { - if (_reliableDeltaChannel) { - MetavoxelData reference = _remoteData; - MetavoxelLOD referenceLOD = _remoteDataLOD; - _remoteData.readDelta(reference, referenceLOD, in, _remoteDataLOD = _reliableDeltaLOD); - _sequencer.getInputStream().persistReadMappings(in.getAndResetReadMappings()); - in.clearPersistentMappings(); - _reliableDeltaChannel = NULL; - - } else { - PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); - _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, - _remoteDataLOD = getLastAcknowledgedSendRecord()->getLOD()); - in.reset(); - } - // copy to local and reapply local edits - MetavoxelData oldData = _data; - _data = _remoteData; - foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) { - if (message.data.userType() == MetavoxelEditMessage::Type) { - message.data.value().apply(_data, _sequencer.getWeakSharedObjectHash()); - } - } - if (_data != oldData) { - dataChanged(oldData); - } - } else if (userType == MetavoxelDeltaPendingMessage::Type) { - // check the id to make sure this is not a delta we've already processed - MetavoxelDeltaPendingMessage pending = message.value(); - if (pending.id > _reliableDeltaID) { - _reliableDeltaID = pending.id; - PacketRecord* sendRecord = getAcknowledgedSendRecord(pending.receivedPacketNumber); - if (!sendRecord) { - qWarning() << "Missing send record for delta" << pending.receivedPacketNumber; - return; - } - _reliableDeltaLOD = sendRecord->getLOD(); - PacketRecord* receiveRecord = getAcknowledgedReceiveRecord(pending.sentPacketNumber); - if (!receiveRecord) { - qWarning() << "Missing receive record for delta" << pending.sentPacketNumber; - return; - } - _remoteDataLOD = receiveRecord->getLOD(); - _remoteData = receiveRecord->getData(); - - _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); - if (receiveRecord == getLastAcknowledgedReceiveRecord()) { - _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); - - } else { - _reliableDeltaChannel->getBitstream().copyPersistentMappings(_dummyInputStream); - foreach (const ClearedReceiveRecord& record, _clearedReceiveRecords) { - _reliableDeltaChannel->getBitstream().persistReadMappings(record.second); - if (record.first == receiveRecord) { - break; - } - } - } - } - } else { - Endpoint::handleMessage(message, in); - } -} - -PacketRecord* MetavoxelClient::maybeCreateSendRecord() const { - return new PacketRecord(_sequencer.getOutgoingPacketNumber(), - _reliableDeltaChannel ? _reliableDeltaLOD : _updater->getLOD()); -} - -PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const { - return new PacketRecord(_sequencer.getIncomingPacketNumber(), _remoteDataLOD, _remoteData); -} - diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h deleted file mode 100644 index 8d029c172f..0000000000 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ /dev/null @@ -1,157 +0,0 @@ -// -// MetavoxelClientManager.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 6/26/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelClientManager_h -#define hifi_MetavoxelClientManager_h - -#include -#include - -#include "Endpoint.h" - -class MetavoxelClient; -class MetavoxelEditMessage; -class MetavoxelUpdater; - -/// Manages the set of connected metavoxel clients. -class MetavoxelClientManager : public QObject { - Q_OBJECT - -public: - - MetavoxelClientManager(); - virtual ~MetavoxelClientManager(); - - virtual void init(); - - MetavoxelUpdater* getUpdater() const { return _updater; } - - SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, - const AttributePointer& attribute, float& distance); - - bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); - - Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); - - Q_INVOKABLE void paintHeightfieldHeight(const glm::vec3& position, float radius, float height); - - Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); - - /// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread. - virtual MetavoxelLOD getLOD(); - -private slots: - - void maybeAttachClient(const SharedNodePointer& node); - void maybeDeleteClient(const SharedNodePointer& node); - -protected: - - virtual MetavoxelClient* createClient(const SharedNodePointer& node); - - void guide(MetavoxelVisitor& visitor); - - MetavoxelUpdater* _updater; -}; - -/// Handles updates in a dedicated thread. -class MetavoxelUpdater : public QObject { - Q_OBJECT - -public: - - MetavoxelUpdater(MetavoxelClientManager* clientManager); - - const MetavoxelLOD& getLOD() const { return _lod; } - - Q_INVOKABLE void start(); - - Q_INVOKABLE void addClient(QObject* client); - - Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable); - - /// Requests a set of statistics. The receiving method should take six integer arguments: internal node count, leaf count, - /// send progress, send total, receive progress, receive total. - Q_INVOKABLE void getStats(QObject* receiver, const QByteArray& method); - -private slots: - - void sendUpdates(); - void removeClient(QObject* client); - -private: - - MetavoxelClientManager* _clientManager; - QSet _clients; - - QTimer _sendTimer; - qint64 _lastSend; - - MetavoxelLOD _lod; -}; - -/// Base class for metavoxel clients. -class MetavoxelClient : public Endpoint { - Q_OBJECT - -public: - - MetavoxelClient(const SharedNodePointer& node, MetavoxelUpdater* updater); - - /// Returns a reference to the most recent data. This function is *not* thread-safe. - const MetavoxelData& getData() const { return _data; } - - /// Returns a copy of the most recent data. This function *is* thread-safe. - MetavoxelData getDataCopy(); - - void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); - -protected: - - PacketRecord* getAcknowledgedSendRecord(int packetNumber) const; - PacketRecord* getAcknowledgedReceiveRecord(int packetNumber) const; - - virtual void dataChanged(const MetavoxelData& oldData); - - virtual void recordReceive(); - - virtual void clearSendRecordsBefore(int index); - virtual void clearReceiveRecordsBefore(int index); - - virtual void writeUpdateMessage(Bitstream& out); - virtual void handleMessage(const QVariant& message, Bitstream& in); - - virtual PacketRecord* maybeCreateSendRecord() const; - virtual PacketRecord* maybeCreateReceiveRecord() const; - - MetavoxelUpdater* _updater; - MetavoxelData _data; - MetavoxelData _remoteData; - MetavoxelLOD _remoteDataLOD; - - ReliableChannel* _reliableDeltaChannel; - MetavoxelLOD _reliableDeltaLOD; - int _reliableDeltaID; - QVariant _reliableDeltaMessage; - - MetavoxelData _dataCopy; - QReadWriteLock _dataCopyLock; - - QDataStream _dummyDataStream; - Bitstream _dummyInputStream; - int _dummyPacketNumber; - QList _clearedSendRecords; - - typedef QPair ClearedReceiveRecord; - QList _clearedReceiveRecords; -}; - -#endif // hifi_MetavoxelClientManager_h diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp deleted file mode 100644 index b8b03226d2..0000000000 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ /dev/null @@ -1,1818 +0,0 @@ -// -// MetavoxelData.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/6/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -#include "MetavoxelData.h" -#include "Spanner.h" - -REGISTER_META_OBJECT(MetavoxelGuide) -REGISTER_META_OBJECT(DefaultMetavoxelGuide) -REGISTER_META_OBJECT(MetavoxelRenderer) -REGISTER_META_OBJECT(DefaultMetavoxelRenderer) - -static int metavoxelDataTypeId = registerSimpleMetaType(); - -MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) : - position(position), - threshold(threshold) { -} - -bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size, float multiplier) const { - float halfSize = size * 0.5f; - return size >= (glm::distance(position, minimum + glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * - threshold * multiplier; -} - -bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, - const MetavoxelLOD& reference, float multiplier) const { - if (position == reference.position && threshold >= reference.threshold) { - return false; // first off, nothing becomes subdivided if it doesn't change - } - if (!shouldSubdivide(minimum, size, multiplier)) { - return false; // this one must be subdivided - } - // TODO: find some way of culling subtrees that can't possibly contain subdivided nodes - return true; -} - -bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec3& minimum, float size, - const MetavoxelLOD& reference, float multiplier) const { - if (position == reference.position && threshold == reference.threshold) { - return false; // first off, nothing becomes subdivided or collapsed if it doesn't change - } - if (!(shouldSubdivide(minimum, size, multiplier) || reference.shouldSubdivide(minimum, size, multiplier))) { - return false; // this one or the reference must be subdivided - } - // TODO: find some way of culling subtrees that can't possibly contain subdivided or collapsed nodes - return true; -} - -bool MetavoxelLOD::shouldSubdivide(const glm::vec2& minimum, float size, float multiplier) const { - float halfSize = size * 0.5f; - return size >= (glm::distance(glm::vec2(position), minimum + glm::vec2(halfSize, halfSize)) - halfSize) * - threshold * multiplier; -} - -bool MetavoxelLOD::becameSubdivided(const glm::vec2& minimum, float size, - const MetavoxelLOD& reference, float multiplier) const { - if (position == reference.position && threshold >= reference.threshold) { - return false; // first off, nothing becomes subdivided if it doesn't change - } - if (!shouldSubdivide(minimum, size, multiplier)) { - return false; // this one must be subdivided - } - // TODO: find some way of culling subtrees that can't possibly contain subdivided nodes - return true; -} - -bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec2& minimum, float size, - const MetavoxelLOD& reference, float multiplier) const { - if (position == reference.position && threshold == reference.threshold) { - return false; // first off, nothing becomes subdivided or collapsed if it doesn't change - } - if (!(shouldSubdivide(minimum, size, multiplier) || reference.shouldSubdivide(minimum, size, multiplier))) { - return false; // this one or the reference must be subdivided - } - // TODO: find some way of culling subtrees that can't possibly contain subdivided or collapsed nodes - return true; -} - -MetavoxelData::MetavoxelData() : _size(1.0f) { -} - -MetavoxelData::MetavoxelData(const MetavoxelData& other) : - _size(other._size), - _roots(other._roots) { - - incrementRootReferenceCounts(); -} - -MetavoxelData::~MetavoxelData() { - decrementRootReferenceCounts(); -} - -MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) { - decrementRootReferenceCounts(); - _size = other._size; - _roots = other._roots; - incrementRootReferenceCounts(); - return *this; -} - -Box MetavoxelData::getBounds() const { - float halfSize = _size * 0.5f; - return Box(glm::vec3(-halfSize, -halfSize, -halfSize), glm::vec3(halfSize, halfSize, halfSize)); -} - -void MetavoxelData::guide(MetavoxelVisitor& visitor) { - // let the visitor know we're about to begin a tour - visitor.prepare(this); - - // start with the root values/defaults (plus the guide attribute) - const QVector& inputs = visitor.getInputs(); - const QVector& outputs = visitor.getOutputs(); - MetavoxelVisitation& firstVisitation = visitor.acquireVisitation(); - firstVisitation.info.minimum = getMinimum(); - firstVisitation.info.size = _size; - for (int i = 0; i < inputs.size(); i++) { - const AttributePointer& input = inputs.at(i); - MetavoxelNode* node = _roots.value(input); - firstVisitation.inputNodes[i] = node; - firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(input) : input; - } - AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute(); - MetavoxelNode* node = _roots.value(guideAttribute); - firstVisitation.inputNodes.last() = node; - firstVisitation.info.inputValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; - for (int i = 0; i < outputs.size(); i++) { - MetavoxelNode* node = _roots.value(outputs.at(i)); - firstVisitation.outputNodes[i] = node; - } - static_cast(firstVisitation.info.inputValues.last().getInlineValue< - SharedObjectPointer>().data())->guide(firstVisitation); - for (int i = 0; i < outputs.size(); i++) { - OwnedAttributeValue& value = firstVisitation.info.outputValues[i]; - if (!value.getAttribute()) { - continue; - } - // replace the old node with the new - MetavoxelNode*& node = _roots[value.getAttribute()]; - if (node) { - node->decrementReferenceCount(value.getAttribute()); - } - node = firstVisitation.outputNodes.at(i); - if (node->isLeaf() && value.isDefault()) { - // immediately remove the new node if redundant - node->decrementReferenceCount(value.getAttribute()); - _roots.remove(value.getAttribute()); - } - value = AttributeValue(); - } - visitor.releaseVisitation(); -} - -void MetavoxelData::guideToDifferent(const MetavoxelData& other, MetavoxelVisitor& visitor) { - // if the other data is smaller, we need to expand it to compare - const MetavoxelData* expandedOther = &other; - if (_size > other._size) { - MetavoxelData* expanded = new MetavoxelData(other); - while (expanded->_size < _size) { - expanded->expand(); - } - expandedOther = expanded; - } - - // let the visitor know we're about to begin a tour - visitor.prepare(this); - - // start with the root values/defaults (plus the guide attribute) - const QVector& inputs = visitor.getInputs(); - const QVector& outputs = visitor.getOutputs(); - MetavoxelVisitation& firstVisitation = visitor.acquireVisitation(); - firstVisitation.compareNodes.resize(inputs.size() + 1); - firstVisitation.info.minimum = getMinimum(); - firstVisitation.info.size = _size; - bool allNodesSame = true; - for (int i = 0; i < inputs.size(); i++) { - const AttributePointer& input = inputs.at(i); - MetavoxelNode* node = _roots.value(input); - firstVisitation.inputNodes[i] = node; - firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(input) : input; - MetavoxelNode* compareNode = expandedOther->_roots.value(input); - firstVisitation.compareNodes[i] = compareNode; - allNodesSame &= (node == compareNode); - } - AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute(); - MetavoxelNode* node = _roots.value(guideAttribute); - firstVisitation.inputNodes.last() = node; - firstVisitation.info.inputValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; - MetavoxelNode* compareNode = expandedOther->_roots.value(guideAttribute); - firstVisitation.compareNodes.last() = compareNode; - allNodesSame &= (node == compareNode); - if (!allNodesSame) { - for (int i = 0; i < outputs.size(); i++) { - MetavoxelNode* node = _roots.value(outputs.at(i)); - firstVisitation.outputNodes[i] = node; - } - static_cast(firstVisitation.info.inputValues.last().getInlineValue< - SharedObjectPointer>().data())->guideToDifferent(firstVisitation); - for (int i = 0; i < outputs.size(); i++) { - OwnedAttributeValue& value = firstVisitation.info.outputValues[i]; - if (!value.getAttribute()) { - continue; - } - // replace the old node with the new - MetavoxelNode*& node = _roots[value.getAttribute()]; - if (node) { - node->decrementReferenceCount(value.getAttribute()); - } - node = firstVisitation.outputNodes.at(i); - if (node->isLeaf() && value.isDefault()) { - // immediately remove the new node if redundant - node->decrementReferenceCount(value.getAttribute()); - _roots.remove(value.getAttribute()); - } - value = AttributeValue(); - } - } - visitor.releaseVisitation(); - - // delete the expanded other if we had to expand - if (expandedOther != &other) { - delete expandedOther; - } -} - -typedef void (*SpannerUpdateFunction)(SharedObjectSet& set, const SharedObjectPointer& object); - -void insertSpanner(SharedObjectSet& set, const SharedObjectPointer& object) { - set.insert(object); -} - -void removeSpanner(SharedObjectSet& set, const SharedObjectPointer& object) { - set.remove(object); -} - -void toggleSpanner(SharedObjectSet& set, const SharedObjectPointer& object) { - if (!set.remove(object)) { - set.insert(object); - } -} - -template class SpannerUpdateVisitor : public MetavoxelVisitor { -public: - - SpannerUpdateVisitor(const AttributePointer& attribute, const Box& bounds, - float granularity, const SharedObjectPointer& object); - - virtual int visit(MetavoxelInfo& info); - -private: - - const AttributePointer& _attribute; - const Box& _bounds; - float _longestSide; - const SharedObjectPointer& _object; -}; - -template SpannerUpdateVisitor::SpannerUpdateVisitor(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) { -} - -template int SpannerUpdateVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (info.size > _longestSide) { - return DEFAULT_ORDER; - } - SharedObjectSet set = info.inputValues.at(0).getInlineValue(); - F(set, _object); - info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); - return STOP_RECURSION; -} - -void MetavoxelData::insert(const AttributePointer& attribute, const SharedObjectPointer& object) { - Spanner* spanner = static_cast(object.data()); - insert(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), 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(); - } - SpannerUpdateVisitor visitor(attribute, bounds, granularity, object); - guide(visitor); -} - -void MetavoxelData::remove(const AttributePointer& attribute, const SharedObjectPointer& object) { - Spanner* spanner = static_cast(object.data()); - remove(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), object); -} - -void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds, - float granularity, const SharedObjectPointer& object) { - SpannerUpdateVisitor visitor(attribute, bounds, granularity, object); - guide(visitor); -} - -void MetavoxelData::toggle(const AttributePointer& attribute, const SharedObjectPointer& object) { - Spanner* spanner = static_cast(object.data()); - toggle(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), object); -} - -void MetavoxelData::toggle(const AttributePointer& attribute, const Box& bounds, - float granularity, const SharedObjectPointer& object) { - SpannerUpdateVisitor visitor(attribute, bounds, granularity, object); - guide(visitor); -} - -void MetavoxelData::replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject, - const SharedObjectPointer& newObject) { - Spanner* spanner = static_cast(oldObject.data()); - replace(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), oldObject, newObject); -} - -class SpannerReplaceVisitor : public MetavoxelVisitor { -public: - - SpannerReplaceVisitor(const AttributePointer& attribute, const Box& bounds, - float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject); - - virtual int visit(MetavoxelInfo& info); - -private: - - const AttributePointer& _attribute; - const Box& _bounds; - float _longestSide; - const SharedObjectPointer& _oldObject; - const SharedObjectPointer& _newObject; -}; - -SpannerReplaceVisitor::SpannerReplaceVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, - const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) : - MetavoxelVisitor(QVector() << attribute, QVector() << attribute), - _attribute(attribute), - _bounds(bounds), - _longestSide(qMax(bounds.getLongestSide(), granularity)), - _oldObject(oldObject), - _newObject(newObject) { -} - -int SpannerReplaceVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (info.size > _longestSide) { - return DEFAULT_ORDER; - } - SharedObjectSet set = info.inputValues.at(0).getInlineValue(); - if (set.remove(_oldObject)) { - set.insert(_newObject); - } - info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); - return STOP_RECURSION; -} - -void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity, - const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) { - Spanner* newSpanner = static_cast(newObject.data()); - if (!newSpanner) { - remove(attribute, bounds, granularity, oldObject); - return; - } - if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) { - // if the bounds have changed, we must remove and reinsert - remove(attribute, bounds, granularity, oldObject); - insert(attribute, newSpanner->getBounds(), newSpanner->getPlacementGranularity(), newObject); - return; - } - SpannerReplaceVisitor visitor(attribute, bounds, granularity, oldObject, newObject); - guide(visitor); -} - -class SpannerFetchVisitor : public SpannerVisitor { -public: - - SpannerFetchVisitor(const AttributePointer& attribute, const Box& bounds, QVector& results); - - virtual bool visit(Spanner* spanner); - - virtual int visit(MetavoxelInfo& info); - -private: - - const Box& _bounds; - QVector& _results; -}; - -SpannerFetchVisitor::SpannerFetchVisitor(const AttributePointer& attribute, const Box& bounds, - QVector& results) : - SpannerVisitor(QVector() << attribute), - _bounds(bounds), - _results(results) { -} - -bool SpannerFetchVisitor::visit(Spanner* spanner) { - if (spanner->getBounds().intersects(_bounds)) { - _results.append(spanner); - } - return true; -} - -int SpannerFetchVisitor::visit(MetavoxelInfo& info) { - return info.getBounds().intersects(_bounds) ? SpannerVisitor::visit(info) : STOP_RECURSION; -} - -void MetavoxelData::getIntersecting(const AttributePointer& attribute, const Box& bounds, - QVector& results) { - SpannerFetchVisitor visitor(attribute, bounds, results); - guide(visitor); -} - -void MetavoxelData::clear(const AttributePointer& attribute) { - MetavoxelNode* node = _roots.take(attribute); - if (node) { - node->decrementReferenceCount(attribute); - } -} - -void MetavoxelData::touch(const AttributePointer& attribute) { - MetavoxelNode* root = _roots.value(attribute); - if (root) { - setRoot(attribute, root->touch(attribute)); - } -} - -class FirstRaySpannerIntersectionVisitor : public RaySpannerIntersectionVisitor { -public: - - FirstRaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const AttributePointer& attribute, const MetavoxelLOD& lod); - - Spanner* getSpanner() const { return _spanner; } - float getDistance() const { return _distance; } - - virtual bool visitSpanner(Spanner* spanner, float distance); - -private: - - Spanner* _spanner; - float _distance; -}; - -FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor( - const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, const MetavoxelLOD& lod) : - RaySpannerIntersectionVisitor(origin, direction, QVector() << attribute, - QVector(), QVector(), lod), - _spanner(NULL) { -} - -bool FirstRaySpannerIntersectionVisitor::visitSpanner(Spanner* spanner, float distance) { - _spanner = spanner; - _distance = distance; - return false; -} - -SharedObjectPointer MetavoxelData::findFirstRaySpannerIntersection( - const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, - float& distance, const MetavoxelLOD& lod) { - FirstRaySpannerIntersectionVisitor visitor(origin, direction, attribute, lod); - guide(visitor); - if (!visitor.getSpanner()) { - return SharedObjectPointer(); - } - distance = visitor.getDistance(); - return SharedObjectPointer(visitor.getSpanner()); -} - -const int X_MAXIMUM_FLAG = 1; -const int Y_MAXIMUM_FLAG = 2; -const int Z_MAXIMUM_FLAG = 4; -const int MAXIMUM_FLAG_MASK = X_MAXIMUM_FLAG | Y_MAXIMUM_FLAG | Z_MAXIMUM_FLAG; - -static glm::vec3 getNextMinimum(const glm::vec3& minimum, float nextSize, int index) { - return minimum + glm::vec3( - (index & X_MAXIMUM_FLAG) ? nextSize : 0.0f, - (index & Y_MAXIMUM_FLAG) ? nextSize : 0.0f, - (index & Z_MAXIMUM_FLAG) ? nextSize : 0.0f); -} - -static void setNode(const AttributeValue& value, MetavoxelNode*& node, MetavoxelNode* other, bool blend) { - if (!blend) { - // if we're not blending, we can just make a shallow copy - if (node) { - node->decrementReferenceCount(value.getAttribute()); - } - (node = other)->incrementReferenceCount(); - return; - } - if (node) { - MetavoxelNode* oldNode = node; - node = new MetavoxelNode(value.getAttribute(), oldNode); - oldNode->decrementReferenceCount(value.getAttribute()); - - } else { - node = new MetavoxelNode(value); - } - OwnedAttributeValue oldValue = node->getAttributeValue(value.getAttribute()); - node->blendAttributeValues(other->getAttributeValue(value.getAttribute()), oldValue); - if (!other->isLeaf()) { - for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - MetavoxelNode* child = node->getChild(i); - setNode(oldValue, child, other->getChild(i), true); - node->setChild(i, child); - } - } - node->mergeChildren(value.getAttribute()); -} - -static void setNode(const AttributeValue& value, MetavoxelNode*& node, const glm::vec3& minimum, float size, - MetavoxelNode* other, const glm::vec3& otherMinimum, float otherSize, bool blend) { - if (otherSize >= size) { - setNode(value, node, other, blend); - return; - } - if (node) { - MetavoxelNode* oldNode = node; - node = new MetavoxelNode(value.getAttribute(), oldNode); - oldNode->decrementReferenceCount(value.getAttribute()); - - } else { - node = new MetavoxelNode(value); - } - int index = 0; - float otherHalfSize = otherSize * 0.5f; - float nextSize = size * 0.5f; - if (otherMinimum.x + otherHalfSize >= minimum.x + nextSize) { - index |= X_MAXIMUM_FLAG; - } - if (otherMinimum.y + otherHalfSize >= minimum.y + nextSize) { - index |= Y_MAXIMUM_FLAG; - } - if (otherMinimum.z + otherHalfSize >= minimum.z + nextSize) { - index |= Z_MAXIMUM_FLAG; - } - if (node->isLeaf()) { - for (int i = 1; i < MetavoxelNode::CHILD_COUNT; i++) { - node->setChild((index + i) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode( - node->getAttributeValue(value.getAttribute()))); - } - } - MetavoxelNode* nextNode = node->getChild(index); - setNode(node->getAttributeValue(value.getAttribute()), nextNode, getNextMinimum(minimum, nextSize, index), - nextSize, other, otherMinimum, otherSize, blend); - node->setChild(index, nextNode); - node->mergeChildren(value.getAttribute()); -} - -void MetavoxelData::set(const glm::vec3& minimum, const MetavoxelData& data, bool blend) { - // expand to fit the entire data - Box bounds(minimum, minimum + glm::vec3(data.getSize(), data.getSize(), data.getSize())); - while (!getBounds().contains(bounds)) { - expand(); - } - - // set/mix each attribute separately - for (QHash::const_iterator it = data._roots.constBegin(); - it != data._roots.constEnd(); it++) { - MetavoxelNode*& root = _roots[it.key()]; - setNode(it.key(), root, getMinimum(), getSize(), it.value(), minimum, data.getSize(), blend); - if (root->isLeaf() && root->getAttributeValue(it.key()).isDefault()) { - _roots.remove(it.key()); - root->decrementReferenceCount(it.key()); - } - } -} - -void MetavoxelData::expand() { - for (QHash::iterator it = _roots.begin(); it != _roots.end(); it++) { - MetavoxelNode* newNode = it.key()->expandMetavoxelRoot(*it.value()); - it.value()->decrementReferenceCount(it.key()); - it.value() = newNode; - } - _size *= 2.0f; -} - -void MetavoxelData::read(Bitstream& in, const MetavoxelLOD& lod) { - // clear out any existing roots - decrementRootReferenceCounts(); - _roots.clear(); - - in >> _size; - - // read in the new roots - forever { - AttributePointer attribute; - in >> attribute; - if (!attribute) { - break; - } - MetavoxelStreamBase base = { attribute, in, lod, lod }; - MetavoxelStreamState state = { base, getMinimum(), _size }; - in.setContext(&base); - attribute->readMetavoxelRoot(*this, state); - in.setContext(NULL); - } -} - -void MetavoxelData::write(Bitstream& out, const MetavoxelLOD& lod) const { - out << _size; - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - out << it.key(); - MetavoxelStreamBase base = { it.key(), out, lod, lod }; - MetavoxelStreamState state = { base, getMinimum(), _size }; - out.setContext(&base); - it.key()->writeMetavoxelRoot(*it.value(), state); - out.setContext(NULL); - } - out << AttributePointer(); -} - -void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, - Bitstream& in, const MetavoxelLOD& lod) { - // shallow copy the reference - *this = reference; - - QHash remainingRoots = _roots; - - bool changed; - in >> changed; - if (changed) { - bool sizeChanged; - in >> sizeChanged; - if (sizeChanged) { - float size; - in >> size; - while (_size < size) { - expand(); - } - } - - glm::vec3 minimum = getMinimum(); - forever { - AttributePointer attribute; - in >> attribute; - if (!attribute) { - break; - } - MetavoxelStreamBase base = { attribute, in, lod, referenceLOD }; - MetavoxelStreamState state = { base, minimum, _size }; - MetavoxelNode* oldRoot = _roots.value(attribute); - in.setContext(&base); - if (oldRoot) { - bool changed; - in >> changed; - if (changed) { - oldRoot->incrementReferenceCount(); - attribute->readMetavoxelDelta(*this, *oldRoot, state); - oldRoot->decrementReferenceCount(attribute); - } else { - attribute->readMetavoxelSubdivision(*this, state); - } - remainingRoots.remove(attribute); - - } else { - attribute->readMetavoxelRoot(*this, state); - } - in.setContext(NULL); - } - - forever { - AttributePointer attribute; - in >> attribute; - if (!attribute) { - break; - } - _roots.take(attribute)->decrementReferenceCount(attribute); - remainingRoots.remove(attribute); - } - } - - // read subdivisions for the remaining roots if there's any chance of a collapse - if (!(lod.position == referenceLOD.position && lod.threshold <= referenceLOD.threshold)) { - glm::vec3 minimum = getMinimum(); - for (QHash::const_iterator it = remainingRoots.constBegin(); - it != remainingRoots.constEnd(); it++) { - MetavoxelStreamBase base = { it.key(), in, lod, referenceLOD }; - MetavoxelStreamState state = { base, minimum, _size }; - in.setContext(&base); - it.key()->readMetavoxelSubdivision(*this, state); - in.setContext(NULL); - } - } -} - -void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, - Bitstream& out, const MetavoxelLOD& lod) const { - // first things first: there might be no change whatsoever - glm::vec3 minimum = getMinimum(); - bool becameSubdivided = lod.becameSubdivided(minimum, _size, referenceLOD); - if (_size == reference._size && _roots == reference._roots && !becameSubdivided) { - out << false; - return; - } - out << true; - - // compare the size; if changed (rare), we must compare to the expanded reference - const MetavoxelData* expandedReference = &reference; - if (_size == reference._size) { - out << false; - } else { - out << true; - out << _size; - - MetavoxelData* expanded = new MetavoxelData(reference); - while (expanded->_size < _size) { - expanded->expand(); - } - expandedReference = expanded; - } - - // write the added/changed/subdivided roots - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); - MetavoxelStreamBase base = { it.key(), out, lod, referenceLOD }; - MetavoxelStreamState state = { base, minimum, _size }; - out.setContext(&base); - if (it.value() != referenceRoot || becameSubdivided) { - out << it.key(); - if (referenceRoot) { - if (it.value() == referenceRoot) { - out << false; - it.key()->writeMetavoxelSubdivision(*it.value(), state); - } else { - out << true; - it.key()->writeMetavoxelDelta(*it.value(), *referenceRoot, state); - } - } else { - it.key()->writeMetavoxelRoot(*it.value(), state); - } - } - out.setContext(NULL); - } - out << AttributePointer(); - - // same with nodes removed - for (QHash::const_iterator it = expandedReference->_roots.constBegin(); - it != expandedReference->_roots.constEnd(); it++) { - if (!_roots.contains(it.key())) { - out << it.key(); - } - } - out << AttributePointer(); - - // delete the expanded reference if we had to expand - if (expandedReference != &reference) { - delete expandedReference; - } -} - -void MetavoxelData::setRoot(const AttributePointer& attribute, MetavoxelNode* root) { - MetavoxelNode*& rootReference = _roots[attribute]; - if (rootReference) { - rootReference->decrementReferenceCount(attribute); - } - rootReference = root; -} - -MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) { - MetavoxelNode* root = new MetavoxelNode(attribute); - setRoot(attribute, root); - return root; -} - -bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod) const { - if (_size != other._size) { - return false; - } - if (_roots.size() != other._roots.size()) { - return false; - } - glm::vec3 minimum = getMinimum(); - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - MetavoxelNode* otherNode = other._roots.value(it.key()); - if (!(otherNode && it.key()->metavoxelRootsEqual(*it.value(), *otherNode, minimum, _size, lod))) { - return false; - } - } - return true; -} - -void MetavoxelData::countNodes(int& internal, int& leaves, const MetavoxelLOD& lod) const { - glm::vec3 minimum = getMinimum(); - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - it.value()->countNodes(it.key(), minimum, _size, lod, internal, leaves); - } -} - -void MetavoxelData::dumpStats(QDebug debug) const { - QDebugStateSaver saver(debug); - debug.nospace() << "[size=" << _size << ", roots=["; - int totalInternal = 0, totalLeaves = 0; - glm::vec3 minimum = getMinimum(); - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - if (it != _roots.constBegin()) { - debug << ", "; - } - debug << it.key()->getName() << " (" << it.key()->metaObject()->className() << "): "; - int internal = 0, leaves = 0; - it.value()->countNodes(it.key(), minimum, _size, MetavoxelLOD(), internal, leaves); - debug << internal << " internal, " << leaves << " leaves, " << (internal + leaves) << " total"; - totalInternal += internal; - totalLeaves += leaves; - } - debug << "], totalInternal=" << totalInternal << ", totalLeaves=" << totalLeaves << - ", grandTotal=" << (totalInternal + totalLeaves) << "]"; -} - -bool MetavoxelData::operator==(const MetavoxelData& other) const { - return _size == other._size && _roots == other._roots; -} - -bool MetavoxelData::operator!=(const MetavoxelData& other) const { - return _size != other._size || _roots != other._roots; -} - -void MetavoxelData::incrementRootReferenceCounts() { - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - it.value()->incrementReferenceCount(); - } -} - -void MetavoxelData::decrementRootReferenceCounts() { - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - it.value()->decrementReferenceCount(it.key()); - } -} - -Bitstream& operator<<(Bitstream& out, const MetavoxelData& data) { - data.write(out); - return out; -} - -Bitstream& operator>>(Bitstream& in, MetavoxelData& data) { - data.read(in); - return in; -} - -template<> void Bitstream::writeDelta(const MetavoxelData& value, const MetavoxelData& reference) { - value.writeDelta(reference, MetavoxelLOD(), *this, MetavoxelLOD()); -} - -template<> void Bitstream::readDelta(MetavoxelData& value, const MetavoxelData& reference) { - value.readDelta(reference, MetavoxelLOD(), *this, MetavoxelLOD()); -} - -bool MetavoxelStreamState::shouldSubdivide() const { - return base.lod.shouldSubdivide(minimum, size, base.attribute->getLODThresholdMultiplier()); -} - -bool MetavoxelStreamState::shouldSubdivideReference() const { - return base.referenceLOD.shouldSubdivide(minimum, size, base.attribute->getLODThresholdMultiplier()); -} - -bool MetavoxelStreamState::becameSubdivided() const { - return base.lod.becameSubdivided(minimum, size, base.referenceLOD, base.attribute->getLODThresholdMultiplier()); -} - -bool MetavoxelStreamState::becameSubdividedOrCollapsed() const { - return base.lod.becameSubdividedOrCollapsed(minimum, size, base.referenceLOD, base.attribute->getLODThresholdMultiplier()); -} - -void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) { - minimum = getNextMinimum(lastMinimum, size, index); -} - -int MetavoxelNode::getOppositeChildIndex(int index) { - return index ^ MAXIMUM_FLAG_MASK; -} - -MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue, const MetavoxelNode* copyChildren) : - _referenceCount(1) { - - _attributeValue = attributeValue.copy(); - if (copyChildren) { - for (int i = 0; i < CHILD_COUNT; i++) { - if ((_children[i] = copyChildren->_children[i])) { - _children[i]->incrementReferenceCount(); - } - } - } else { - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i] = NULL; - } - } -} - -MetavoxelNode::MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy) : - _referenceCount(1) { - - _attributeValue = attribute->create(copy->_attributeValue); - for (int i = 0; i < CHILD_COUNT; i++) { - if ((_children[i] = copy->_children[i])) { - _children[i]->incrementReferenceCount(); - } - } -} - -void MetavoxelNode::setAttributeValue(const AttributeValue& attributeValue) { - attributeValue.getAttribute()->destroy(_attributeValue); - _attributeValue = attributeValue.copy(); -} - -void MetavoxelNode::blendAttributeValues(const AttributeValue& source, const AttributeValue& dest) { - source.getAttribute()->destroy(_attributeValue); - _attributeValue = source.getAttribute()->blend(source.getValue(), dest.getValue()); -} - -AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribute) const { - return AttributeValue(attribute, _attributeValue); -} - -void MetavoxelNode::mergeChildren(const AttributePointer& attribute, bool postRead) { - if (isLeaf()) { - return; - } - void* childValues[CHILD_COUNT]; - bool allLeaves = true; - for (int i = 0; i < CHILD_COUNT; i++) { - childValues[i] = _children[i]->_attributeValue; - allLeaves &= _children[i]->isLeaf(); - } - if (attribute->merge(_attributeValue, childValues, postRead) && allLeaves && !postRead) { - clearChildren(attribute); - } -} - -bool MetavoxelNode::isLeaf() const { - for (int i = 0; i < CHILD_COUNT; i++) { - if (_children[i]) { - return false; - } - } - return true; -} - -void MetavoxelNode::read(MetavoxelStreamState& state) { - clearChildren(state.base.attribute); - - if (!state.shouldSubdivide()) { - state.base.attribute->read(state.base.stream, _attributeValue, true); - return; - } - bool leaf; - state.base.stream >> leaf; - state.base.attribute->read(state.base.stream, _attributeValue, leaf); - if (!leaf) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i] = new MetavoxelNode(state.base.attribute); - _children[i]->read(nextState); - } - mergeChildren(state.base.attribute, true); - } -} - -void MetavoxelNode::write(MetavoxelStreamState& state) const { - if (!state.shouldSubdivide()) { - state.base.attribute->write(state.base.stream, _attributeValue, true); - return; - } - bool leaf = isLeaf(); - state.base.stream << leaf; - state.base.attribute->write(state.base.stream, _attributeValue, leaf); - if (!leaf) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->write(nextState); - } - } -} - -void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) { - clearChildren(state.base.attribute); - - if (!state.shouldSubdivide()) { - state.base.attribute->readDelta(state.base.stream, _attributeValue, reference._attributeValue, true); - return; - } - bool leaf; - state.base.stream >> leaf; - state.base.attribute->readDelta(state.base.stream, _attributeValue, reference._attributeValue, leaf); - if (!leaf) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - if (reference.isLeaf() || !state.shouldSubdivideReference()) { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i] = new MetavoxelNode(state.base.attribute); - _children[i]->read(nextState); - } - } else { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - bool changed; - state.base.stream >> changed; - if (changed) { - _children[i] = new MetavoxelNode(state.base.attribute); - _children[i]->readDelta(*reference._children[i], nextState); - } else { - if (nextState.becameSubdividedOrCollapsed()) { - _children[i] = reference._children[i]->readSubdivision(nextState); - if (_children[i] == reference._children[i]) { - _children[i]->incrementReferenceCount(); - } - } else { - _children[i] = reference._children[i]; - _children[i]->incrementReferenceCount(); - } - } - } - } - mergeChildren(state.base.attribute, true); - } -} - -void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const { - if (!state.shouldSubdivide()) { - state.base.attribute->writeDelta(state.base.stream, _attributeValue, reference._attributeValue, true); - return; - } - bool leaf = isLeaf(); - state.base.stream << leaf; - state.base.attribute->writeDelta(state.base.stream, _attributeValue, reference._attributeValue, leaf); - if (!leaf) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - if (reference.isLeaf() || !state.shouldSubdivideReference()) { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->write(nextState); - } - } else { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (_children[i] == reference._children[i]) { - state.base.stream << false; - if (nextState.becameSubdivided()) { - _children[i]->writeSubdivision(nextState); - } - } else { - state.base.stream << true; - _children[i]->writeDelta(*reference._children[i], nextState); - } - } - } - } -} - -MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { - if (state.shouldSubdivide()) { - if (!state.shouldSubdivideReference()) { - bool leaf; - state.base.stream >> leaf; - if (leaf) { - return isLeaf() ? this : new MetavoxelNode(getAttributeValue(state.base.attribute)); - - } else { - MetavoxelNode* newNode = new MetavoxelNode(getAttributeValue(state.base.attribute)); - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - newNode->_children[i] = new MetavoxelNode(state.base.attribute); - newNode->_children[i]->readSubdivided(nextState, state, _attributeValue); - } - return newNode; - } - } else if (!isLeaf()) { - MetavoxelNode* node = this; - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (nextState.becameSubdividedOrCollapsed()) { - MetavoxelNode* child = _children[i]->readSubdivision(nextState); - if (child != _children[i]) { - if (node == this) { - node = new MetavoxelNode(state.base.attribute, this); - } - node->_children[i] = child; - _children[i]->decrementReferenceCount(state.base.attribute); - } - } - } - if (node != this) { - node->mergeChildren(state.base.attribute, true); - } - return node; - } - } else if (!isLeaf()) { - return new MetavoxelNode(getAttributeValue(state.base.attribute)); - } - return this; -} - -void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const { - if (!state.shouldSubdivide()) { - return; - } - bool leaf = isLeaf(); - if (!state.shouldSubdivideReference()) { - state.base.stream << leaf; - if (!leaf) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->writeSubdivided(nextState, state, _attributeValue); - } - } - } else if (!leaf) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (nextState.becameSubdivided()) { - _children[i]->writeSubdivision(nextState); - } - } - } -} - -void MetavoxelNode::readSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, - void* ancestorValue) { - clearChildren(state.base.attribute); - - if (!state.shouldSubdivide()) { - state.base.attribute->readSubdivided(state, _attributeValue, ancestorState, ancestorValue, true); - return; - } - bool leaf; - state.base.stream >> leaf; - state.base.attribute->readSubdivided(state, _attributeValue, ancestorState, ancestorValue, leaf); - if (!leaf) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i] = new MetavoxelNode(state.base.attribute); - _children[i]->readSubdivided(nextState, ancestorState, ancestorValue); - } - mergeChildren(state.base.attribute, true); - } -} - -void MetavoxelNode::writeSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, - void* ancestorValue) const { - if (!state.shouldSubdivide()) { - state.base.attribute->writeSubdivided(state, _attributeValue, ancestorState, ancestorValue, true); - return; - } - bool leaf = isLeaf(); - state.base.stream << leaf; - state.base.attribute->writeSubdivided(state, _attributeValue, ancestorState, ancestorValue, leaf); - if (!leaf) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->writeSubdivided(nextState, ancestorState, ancestorValue); - } - } -} - -void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const { - foreach (const SharedObjectPointer& object, decodeInline(_attributeValue)) { - if (static_cast(object.data())->testAndSetVisited(state.base.visit)) { - state.base.stream << object; - } - } - if (!state.shouldSubdivide() || isLeaf()) { - return; - } - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->writeSpanners(nextState); - } -} - -void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) { - if (!_referenceCount.deref()) { - destroy(attribute); - delete this; - } -} - -void MetavoxelNode::destroy(const AttributePointer& attribute) { - attribute->destroy(_attributeValue); - for (int i = 0; i < CHILD_COUNT; i++) { - if (_children[i]) { - _children[i]->decrementReferenceCount(attribute); - } - } -} - -bool MetavoxelNode::clearChildren(const AttributePointer& attribute) { - bool cleared = false; - for (int i = 0; i < CHILD_COUNT; i++) { - if (_children[i]) { - _children[i]->decrementReferenceCount(attribute); - _children[i] = NULL; - cleared = true; - } - } - return cleared; -} - -bool MetavoxelNode::deepEquals(const AttributePointer& attribute, const MetavoxelNode& other, - const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const { - if (!attribute->deepEqual(_attributeValue, other._attributeValue)) { - return false; - } - if (!lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) { - return true; - } - bool leaf = isLeaf(), otherLeaf = other.isLeaf(); - if (leaf && otherLeaf) { - return true; - } - if (leaf || otherLeaf) { - return false; - } - float nextSize = size * 0.5f; - for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextMinimum = getNextMinimum(minimum, nextSize, i); - if (!_children[i]->deepEquals(attribute, *(other._children[i]), nextMinimum, nextSize, lod)) { - return false; - } - } - return true; -} - -void MetavoxelNode::getSpanners(const AttributePointer& attribute, const glm::vec3& minimum, - float size, const MetavoxelLOD& lod, SharedObjectSet& results) const { - results.unite(decodeInline(_attributeValue)); - if (isLeaf() || !lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) { - return; - } - float nextSize = size * 0.5f; - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->getSpanners(attribute, getNextMinimum(minimum, nextSize, i), nextSize, lod, results); - } -} - -void MetavoxelNode::countNodes(const AttributePointer& attribute, const glm::vec3& minimum, - float size, const MetavoxelLOD& lod, int& internal, int& leaves) const { - if (isLeaf() || !lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) { - leaves++; - return; - } - internal++; - float nextSize = size * 0.5f; - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->countNodes(attribute, getNextMinimum(minimum, nextSize, i), nextSize, lod, internal, leaves); - } -} - -MetavoxelNode* MetavoxelNode::touch(const AttributePointer& attribute) const { - MetavoxelNode* node = new MetavoxelNode(getAttributeValue(attribute)); - for (int i = 0; i < CHILD_COUNT; i++) { - if (_children[i]) { - node->setChild(i, _children[i]->touch(attribute)); - } - } - return node; -} - -MetavoxelInfo::MetavoxelInfo(MetavoxelInfo* parentInfo, int inputValuesSize, int outputValuesSize) : - parentInfo(parentInfo), - inputValues(inputValuesSize), - outputValues(outputValuesSize) { -} - -MetavoxelInfo::MetavoxelInfo() { -} - -int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth, - int fifth, int sixth, int seventh, int eighth) { - return first | (second << 3) | (third << 6) | (fourth << 9) | - (fifth << 12) | (sixth << 15) | (seventh << 18) | (eighth << 21); -} - -class IndexDistance { -public: - int index; - float distance; -}; - -bool operator<(const IndexDistance& first, const IndexDistance& second) { - return first.distance < second.distance; -} - -int MetavoxelVisitor::encodeOrder(const glm::vec3& direction) { - QList indexDistances; - for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - IndexDistance indexDistance = { i, glm::dot(direction, getNextMinimum(glm::vec3(), 1.0f, i)) }; - indexDistances.append(indexDistance); - } - qStableSort(indexDistances); - return encodeOrder(indexDistances.at(0).index, indexDistances.at(1).index, indexDistances.at(2).index, - indexDistances.at(3).index, indexDistances.at(4).index, indexDistances.at(5).index, - indexDistances.at(6).index, indexDistances.at(7).index); -} - -const int ORDER_ELEMENT_BITS = 3; -const int ORDER_ELEMENT_MASK = (1 << ORDER_ELEMENT_BITS) - 1; - -int MetavoxelVisitor::encodeRandomOrder() { - // see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm - int order = 0; - int randomValues = rand(); - for (int i = 0, iShift = 0; i < MetavoxelNode::CHILD_COUNT; i++, iShift += ORDER_ELEMENT_BITS) { - int j = (randomValues >> iShift) % (i + 1); - int jShift = j * ORDER_ELEMENT_BITS; - if (j != i) { - int jValue = (order >> jShift) & ORDER_ELEMENT_MASK; - order |= (jValue << iShift); - } - order = (order & ~(ORDER_ELEMENT_MASK << jShift)) | (i << jShift); - } - return order; -} - -const int MetavoxelVisitor::DEFAULT_ORDER = encodeOrder(0, 1, 2, 3, 4, 5, 6, 7); -const int MetavoxelVisitor::STOP_RECURSION = 0; -const int MetavoxelVisitor::SHORT_CIRCUIT = -1; -const int MetavoxelVisitor::ALL_NODES = 1 << 24; -const int MetavoxelVisitor::ALL_NODES_REST = 1 << 25; - -MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, - const QVector& outputs, const MetavoxelLOD& lod) : - _inputs(inputs), - _outputs(outputs), - _lod(lod), - _minimumLODThresholdMultiplier(FLT_MAX), - _depth(-1) { - - // find the minimum LOD threshold multiplier over all attributes - foreach (const AttributePointer& attribute, _inputs) { - _minimumLODThresholdMultiplier = qMin(attribute->getLODThresholdMultiplier(), _minimumLODThresholdMultiplier); - } - foreach (const AttributePointer& attribute, _outputs) { - _minimumLODThresholdMultiplier = qMin(attribute->getLODThresholdMultiplier(), _minimumLODThresholdMultiplier); - } -} - -MetavoxelVisitor::~MetavoxelVisitor() { -} - -void MetavoxelVisitor::prepare(MetavoxelData* data) { - _data = data; -} - -bool MetavoxelVisitor::postVisit(MetavoxelInfo& info) { - return false; -} - -MetavoxelVisitation& MetavoxelVisitor::acquireVisitation() { - if (++_depth >= _visitations.size()) { - _visitations.append(MetavoxelVisitation(_depth == 0 ? NULL : &_visitations[_depth - 1], - this, _inputs.size() + 1, _outputs.size())); - } - return _visitations[_depth]; -} - -SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& inputs, - const QVector& outputs, const MetavoxelLOD& lod, int order) : - MetavoxelVisitor(inputs + spannerInputs, outputs, lod), - _spannerInputCount(spannerInputs.size()), - _order(order) { -} - -void SpannerVisitor::prepare(MetavoxelData* data) { - MetavoxelVisitor::prepare(data); - _visit = Spanner::getAndIncrementNextVisit(); -} - -int SpannerVisitor::visit(MetavoxelInfo& info) { - for (int end = _inputs.size(), i = end - _spannerInputCount; i < end; i++) { - foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { - Spanner* spanner = static_cast(object.data()); - if (spanner->testAndSetVisited(_visit) && !visit(spanner)) { - return SHORT_CIRCUIT; - } - } - } - return info.isLeaf ? STOP_RECURSION : _order; -} - -RayIntersectionVisitor::RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : - MetavoxelVisitor(inputs, outputs, lod), - _origin(origin), - _direction(direction), - _order(encodeOrder(direction)) { -} - -int RayIntersectionVisitor::visit(MetavoxelInfo& info) { - float distance; - if (!info.getBounds().findRayIntersection(_origin, _direction, distance)) { - return STOP_RECURSION; - } - return visit(info, distance); -} - -RaySpannerIntersectionVisitor::RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const QVector& spannerInputs, const QVector& inputs, - const QVector& outputs, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, inputs + spannerInputs, outputs, lod), - _spannerInputCount(spannerInputs.size()) { -} - -void RaySpannerIntersectionVisitor::prepare(MetavoxelData* data) { - MetavoxelVisitor::prepare(data); - _visit = Spanner::getAndIncrementNextVisit(); -} - -class SpannerDistance { -public: - Spanner* spanner; - float distance; -}; - -bool operator<(const SpannerDistance& first, const SpannerDistance& second) { - return first.distance < second.distance; -} - -int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { - QVarLengthArray spannerDistances; - for (int end = _inputs.size(), i = end - _spannerInputCount; i < end; i++) { - foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { - Spanner* spanner = static_cast(object.data()); - if (spanner->testAndSetVisited(_visit)) { - SpannerDistance spannerDistance = { spanner }; - if (spanner->findRayIntersection(_origin, _direction, spannerDistance.distance)) { - spannerDistances.append(spannerDistance); - } - } - } - qStableSort(spannerDistances); - foreach (const SpannerDistance& spannerDistance, spannerDistances) { - if (!visitSpanner(spannerDistance.spanner, spannerDistance.distance)) { - return SHORT_CIRCUIT; - } - } - } - return info.isLeaf ? STOP_RECURSION : _order; -} - -bool MetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { - return guide(visitation); -} - -DefaultMetavoxelGuide::DefaultMetavoxelGuide() { -} - -static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int encodedOrder) { - MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation(); - nextVisitation.info.size = visitation.info.size * 0.5f; - for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - // the encoded order tells us the child indices for each iteration - int index = encodedOrder & ORDER_ELEMENT_MASK; - encodedOrder >>= ORDER_ELEMENT_BITS; - for (int j = 0; j < visitation.inputNodes.size(); j++) { - MetavoxelNode* node = visitation.inputNodes.at(j); - const AttributeValue& parentValue = visitation.info.inputValues.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * - parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; - nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? - child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue); - } - for (int j = 0; j < visitation.outputNodes.size(); j++) { - MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * - visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; - nextVisitation.outputNodes[j] = child; - } - nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index); - if (!static_cast(nextVisitation.info.inputValues.last().getInlineValue< - SharedObjectPointer>().data())->guide(nextVisitation)) { - visitation.visitor->releaseVisitation(); - return false; - } - for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { - OwnedAttributeValue& value = nextVisitation.info.outputValues[j]; - if (!value.getAttribute()) { - continue; - } - // replace the child - OwnedAttributeValue& parentValue = visitation.info.outputValues[j]; - if (!parentValue.getAttribute()) { - // shallow-copy the parent node on first change - parentValue = value; - MetavoxelNode*& node = visitation.outputNodes[j]; - if (node) { - node = new MetavoxelNode(value.getAttribute(), node); - } else { - // create leaf with inherited value - node = new MetavoxelNode(value.getAttribute()->inherit(visitation.getInheritedOutputValue(j))); - } - } - MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = node->getChild(index); - if (child) { - child->decrementReferenceCount(value.getAttribute()); - } else { - // it's a leaf; we need to split it up - AttributeValue nodeValue = value.getAttribute()->inherit(node->getAttributeValue(value.getAttribute())); - for (int k = 1; k < MetavoxelNode::CHILD_COUNT; k++) { - node->setChild((index + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue)); - } - } - node->setChild(index, nextVisitation.outputNodes.at(j)); - value = AttributeValue(); - } - } - for (int i = 0; i < visitation.outputNodes.size(); i++) { - OwnedAttributeValue& value = visitation.info.outputValues[i]; - if (value.getAttribute()) { - MetavoxelNode* node = visitation.outputNodes.at(i); - node->mergeChildren(value.getAttribute()); - value = node->getAttributeValue(value.getAttribute()); - } - } - visitation.visitor->releaseVisitation(); - visitation.info.outputValues.swap(nextVisitation.info.outputValues); - bool changed = visitation.visitor->postVisit(visitation.info); - visitation.info.outputValues.swap(nextVisitation.info.outputValues); - if (changed) { - for (int i = 0; i < visitation.outputNodes.size(); i++) { - OwnedAttributeValue& newValue = nextVisitation.info.outputValues[i]; - if (!newValue.getAttribute()) { - continue; - } - OwnedAttributeValue& value = visitation.info.outputValues[i]; - MetavoxelNode*& node = visitation.outputNodes[i]; - if (value.getAttribute()) { - node->setAttributeValue(value = newValue); - - } else if (!(node && node->isLeaf() && newValue.getAttribute()->equal( - newValue.getValue(), node->getAttributeValue()))) { - node = newValue.getAttribute()->createMetavoxelNode(value = newValue, node); - } - newValue = AttributeValue(); - } - } - return true; -} - -bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute - float halfSize = visitation.info.size * 0.5f; - visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum + - glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold; - visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase * - visitation.visitor->getMinimumLODThresholdMultiplier()); - visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); - int encodedOrder = visitation.visitor->visit(visitation.info); - if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { - return false; - } - for (int i = 0; i < visitation.outputNodes.size(); i++) { - OwnedAttributeValue& value = visitation.info.outputValues[i]; - if (!value.getAttribute()) { - continue; - } - MetavoxelNode*& node = visitation.outputNodes[i]; - if (node && node->isLeaf() && value.getAttribute()->equal(value.getValue(), node->getAttributeValue())) { - // "set" to same value; disregard - value = AttributeValue(); - } else { - node = value.getAttribute()->createMetavoxelNode(value, node); - } - } - if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) { - return true; - } - return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, encodedOrder)); -} - -bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { - // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute - float halfSize = visitation.info.size * 0.5f; - visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum + - glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold; - visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase * - visitation.visitor->getMinimumLODThresholdMultiplier()); - visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); - int encodedOrder = visitation.visitor->visit(visitation.info); - if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { - return false; - } - for (int i = 0; i < visitation.outputNodes.size(); i++) { - OwnedAttributeValue& value = visitation.info.outputValues[i]; - if (!value.getAttribute()) { - continue; - } - MetavoxelNode*& node = visitation.outputNodes[i]; - if (node && node->isLeaf() && value.getAttribute()->equal(value.getValue(), node->getAttributeValue())) { - // "set" to same value; disregard - value = AttributeValue(); - } else { - node = value.getAttribute()->createMetavoxelNode(value, node); - } - } - if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) { - return true; - } - if (encodedOrder & MetavoxelVisitor::ALL_NODES_REST) { - return defaultGuideToChildren(visitation, encodedOrder); - } - bool onlyVisitDifferent = !(encodedOrder & MetavoxelVisitor::ALL_NODES); - MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation(); - nextVisitation.compareNodes.resize(visitation.compareNodes.size()); - nextVisitation.info.size = visitation.info.size * 0.5f; - for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - // the encoded order tells us the child indices for each iteration - int index = encodedOrder & ORDER_ELEMENT_MASK; - encodedOrder >>= ORDER_ELEMENT_BITS; - bool allNodesSame = onlyVisitDifferent; - for (int j = 0; j < visitation.inputNodes.size(); j++) { - MetavoxelNode* node = visitation.inputNodes.at(j); - const AttributeValue& parentValue = visitation.info.inputValues.at(j); - bool expand = (visitation.info.size >= visitation.info.lodBase * - parentValue.getAttribute()->getLODThresholdMultiplier()); - MetavoxelNode* child = (node && expand) ? node->getChild(index) : NULL; - nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? - child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue); - MetavoxelNode* compareNode = visitation.compareNodes.at(j); - MetavoxelNode* compareChild = (compareNode && expand) ? compareNode->getChild(index) : NULL; - nextVisitation.compareNodes[j] = compareChild; - allNodesSame &= (child == compareChild); - } - if (allNodesSame) { - continue; - } - for (int j = 0; j < visitation.outputNodes.size(); j++) { - MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * - visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; - nextVisitation.outputNodes[j] = child; - } - nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index); - if (!static_cast(nextVisitation.info.inputValues.last().getInlineValue< - SharedObjectPointer>().data())->guideToDifferent(nextVisitation)) { - visitation.visitor->releaseVisitation(); - return false; - } - for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { - OwnedAttributeValue& value = nextVisitation.info.outputValues[j]; - if (!value.getAttribute()) { - continue; - } - // replace the child - OwnedAttributeValue& parentValue = visitation.info.outputValues[j]; - if (!parentValue.getAttribute()) { - // shallow-copy the parent node on first change - parentValue = value; - MetavoxelNode*& node = visitation.outputNodes[j]; - if (node) { - node = new MetavoxelNode(value.getAttribute(), node); - } else { - // create leaf with inherited value - node = new MetavoxelNode(value.getAttribute()->inherit(visitation.getInheritedOutputValue(j))); - } - } - MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = node->getChild(index); - if (child) { - child->decrementReferenceCount(value.getAttribute()); - } else { - // it's a leaf; we need to split it up - AttributeValue nodeValue = value.getAttribute()->inherit(node->getAttributeValue(value.getAttribute())); - for (int k = 1; k < MetavoxelNode::CHILD_COUNT; k++) { - node->setChild((index + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue)); - } - } - node->setChild(index, nextVisitation.outputNodes.at(j)); - value = AttributeValue(); - } - } - for (int i = 0; i < visitation.outputNodes.size(); i++) { - OwnedAttributeValue& value = visitation.info.outputValues[i]; - if (value.getAttribute()) { - MetavoxelNode* node = visitation.outputNodes.at(i); - node->mergeChildren(value.getAttribute()); - value = node->getAttributeValue(value.getAttribute()); - } - } - visitation.visitor->releaseVisitation(); - visitation.info.outputValues.swap(nextVisitation.info.outputValues); - bool changed = visitation.visitor->postVisit(visitation.info); - visitation.info.outputValues.swap(nextVisitation.info.outputValues); - if (changed) { - for (int i = 0; i < visitation.outputNodes.size(); i++) { - OwnedAttributeValue& newValue = nextVisitation.info.outputValues[i]; - if (!newValue.getAttribute()) { - continue; - } - OwnedAttributeValue& value = visitation.info.outputValues[i]; - MetavoxelNode*& node = visitation.outputNodes[i]; - if (value.getAttribute()) { - node->setAttributeValue(value = newValue); - - } else if (!(node && node->isLeaf() && newValue.getAttribute()->equal( - newValue.getValue(), node->getAttributeValue()))) { - node = newValue.getAttribute()->createMetavoxelNode(value = newValue, node); - } - newValue = AttributeValue(); - } - } - return true; -} - -MetavoxelVisitation::MetavoxelVisitation(MetavoxelVisitation* previous, - MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize) : - previous(previous), - visitor(visitor), - inputNodes(inputNodesSize), - outputNodes(outputNodesSize), - info(previous ? &previous->info : NULL, inputNodesSize, outputNodesSize) { -} - -MetavoxelVisitation::MetavoxelVisitation() { -} - -bool MetavoxelVisitation::isInputLeaf(int index) const { - MetavoxelNode* node = inputNodes.at(index); - return !node || node->isLeaf() || info.size < info.lodBase * - info.inputValues.at(index).getAttribute()->getLODThresholdMultiplier(); -} - -bool MetavoxelVisitation::allInputNodesLeaves() const { - foreach (MetavoxelNode* node, inputNodes) { - if (node && !node->isLeaf()) { - return false; - } - } - return true; -} - -AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const { - for (const MetavoxelVisitation* visitation = previous; visitation; visitation = visitation->previous) { - MetavoxelNode* node = visitation->outputNodes.at(index); - if (node) { - return node->getAttributeValue(visitor->getOutputs().at(index)); - } - } - return AttributeValue(visitor->getOutputs().at(index)); -} - -MetavoxelRenderer::MetavoxelRenderer() : - _implementation(NULL) { -} - -MetavoxelRendererImplementation* MetavoxelRenderer::getImplementation() { - QMutexLocker locker(&_implementationMutex); - if (!_implementation) { - QByteArray className = getImplementationClassName(); - const QMetaObject* metaObject = Bitstream::getMetaObject(className); - if (!metaObject) { - qDebug() << "Unknown class name:" << className; - metaObject = &MetavoxelRendererImplementation::staticMetaObject; - } - _implementation = static_cast(metaObject->newInstance()); - connect(this, &QObject::destroyed, _implementation, &QObject::deleteLater); - _implementation->init(this); - } - return _implementation; -} - -MetavoxelRendererImplementation::MetavoxelRendererImplementation() { -} - -void MetavoxelRendererImplementation::init(MetavoxelRenderer* renderer) { - _renderer = renderer; -} - -void MetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, - MetavoxelInfo& info, const MetavoxelLOD& lod) { - // nothing by default -} - -void MetavoxelRendererImplementation::simulate(MetavoxelData& data, float deltaTime, - MetavoxelInfo& info, const MetavoxelLOD& lod) { - // nothing by default -} - -void MetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { - // nothing by default -} - -QByteArray MetavoxelRenderer::getImplementationClassName() const { - return "MetavoxelRendererImplementation"; -} - -DefaultMetavoxelRenderer::DefaultMetavoxelRenderer() { -} - -QByteArray DefaultMetavoxelRenderer::getImplementationClassName() const { - return "DefaultMetavoxelRendererImplementation"; -} - diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h deleted file mode 100644 index 806c5bf2ae..0000000000 --- a/libraries/metavoxels/src/MetavoxelData.h +++ /dev/null @@ -1,539 +0,0 @@ -// -// MetavoxelData.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/6/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelData_h -#define hifi_MetavoxelData_h - -#include -#include -#include -#include -#include - -#include - -#include "AttributeRegistry.h" -#include "MetavoxelUtil.h" - -class MetavoxelInfo; -class MetavoxelNode; -class MetavoxelRendererImplementation; -class MetavoxelVisitation; -class MetavoxelVisitor; -class Spanner; - -/// Determines whether to subdivide each node when traversing. Contains the position (presumed to be of the viewer) and a -/// threshold value, where lower thresholds cause smaller/more distant voxels to be subdivided. -class MetavoxelLOD { - STREAMABLE - -public: - STREAM glm::vec3 position; - STREAM float threshold; - - MetavoxelLOD(const glm::vec3& position = glm::vec3(), float threshold = 0.0f); - - bool isValid() const { return threshold > 0.0f; } - - /// Checks whether, according to this LOD, we should subdivide the described voxel. - bool shouldSubdivide(const glm::vec3& minimum, float size, float multiplier = 1.0f) const; - - /// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference. - bool becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference, float multiplier = 1.0f) const; - - /// Checks whether the node or any of the nodes underneath it have had subdivision - /// enabled or disabled as compared to the reference. - bool becameSubdividedOrCollapsed(const glm::vec3& minimum, float size, - const MetavoxelLOD& reference, float multiplier = 1.0f) const; - - /// Checks whether, according to this LOD, we should subdivide the described region. - bool shouldSubdivide(const glm::vec2& minimum, float size, float multiplier = 1.0f) const; - - /// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference. - bool becameSubdivided(const glm::vec2& minimum, float size, const MetavoxelLOD& reference, float multiplier = 1.0f) const; - - /// Checks whether the node or any of the nodes underneath it have had subdivision - /// enabled or disabled as compared to the reference. - bool becameSubdividedOrCollapsed(const glm::vec2& minimum, float size, - const MetavoxelLOD& reference, float multiplier = 1.0f) const; -}; - -DECLARE_STREAMABLE_METATYPE(MetavoxelLOD) - -/// The base metavoxel representation shared between server and client. Contains a size (for all dimensions) and a set of -/// octrees for different attributes. -class MetavoxelData { -public: - - MetavoxelData(); - MetavoxelData(const MetavoxelData& other); - ~MetavoxelData(); - - MetavoxelData& operator=(const MetavoxelData& other); - - /// Sets the size in all dimensions. - void setSize(float size) { _size = size; } - float getSize() const { return _size; } - - /// Returns the minimum extent of the octrees (which are centered about the origin). - glm::vec3 getMinimum() const { return glm::vec3(_size, _size, _size) * -0.5f; } - - /// Returns the bounds of the octrees. - Box getBounds() const; - - /// Applies the specified visitor to the contained voxels. - void guide(MetavoxelVisitor& visitor); - - /// Guides the specified visitor to the voxels that differ from those of the specified other. - void guideToDifferent(const MetavoxelData& other, MetavoxelVisitor& visitor); - - /// Inserts a spanner into the specified attribute layer. - void insert(const AttributePointer& attribute, const SharedObjectPointer& object); - void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); - - /// Removes a spanner from the specified attribute layer. - void remove(const AttributePointer& attribute, const SharedObjectPointer& object); - void remove(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); - - /// Toggles the existence of a spanner in the specified attribute layer (removes if present, adds if not). - void toggle(const AttributePointer& attribute, const SharedObjectPointer& object); - void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); - - /// Replaces a spanner in the specified attribute layer. - void replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject, - const SharedObjectPointer& newObject); - void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, - const SharedObjectPointer& newObject); - - /// Retrieves all spanners that intersect the specified bounds. - void getIntersecting(const AttributePointer& attribute, const Box& bounds, QVector& results); - - /// Clears all data in the specified attribute layer. - void clear(const AttributePointer& attribute); - - /// "Touches" all data in the specified attribute layer, making it look as if it has changed. - void touch(const AttributePointer& attribute); - - /// Convenience function that finds the first spanner intersecting the provided ray. - SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, - const AttributePointer& attribute, float& distance, const MetavoxelLOD& lod = MetavoxelLOD()); - - /// Sets part of the data. - void set(const glm::vec3& minimum, const MetavoxelData& data, bool blend = false); - - /// Expands the tree, doubling its size in all dimensions (that is, increasing its volume eightfold). - void expand(); - - void read(Bitstream& in, const MetavoxelLOD& lod = MetavoxelLOD()); - void write(Bitstream& out, const MetavoxelLOD& lod = MetavoxelLOD()) const; - - void readDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& in, const MetavoxelLOD& lod); - void writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, - Bitstream& out, const MetavoxelLOD& lod) const; - - void setRoot(const AttributePointer& attribute, MetavoxelNode* root); - MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); } - MetavoxelNode* createRoot(const AttributePointer& attribute); - - /// Performs a deep comparison between this data and the specified other (as opposed to the == operator, which does a - /// shallow comparison). - bool deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod = MetavoxelLOD()) const; - - /// Counts the nodes in the data. - void countNodes(int& internalNodes, int& leaves, const MetavoxelLOD& lod = MetavoxelLOD()) const; - - void dumpStats(QDebug debug = QDebug(QtDebugMsg)) const; - - bool operator==(const MetavoxelData& other) const; - bool operator!=(const MetavoxelData& other) const; - -private: - - friend class MetavoxelVisitation; - - void incrementRootReferenceCounts(); - void decrementRootReferenceCounts(); - - float _size; - QHash _roots; -}; - -Bitstream& operator<<(Bitstream& out, const MetavoxelData& data); - -Bitstream& operator>>(Bitstream& in, MetavoxelData& data); - -template<> void Bitstream::writeDelta(const MetavoxelData& value, const MetavoxelData& reference); - -template<> void Bitstream::readDelta(MetavoxelData& value, const MetavoxelData& reference); - -Q_DECLARE_METATYPE(MetavoxelData) - -/// Holds the base state used in streaming metavoxel data. -class MetavoxelStreamBase { -public: - const AttributePointer& attribute; - Bitstream& stream; - const MetavoxelLOD& lod; - const MetavoxelLOD& referenceLOD; - int visit; -}; - -/// Holds the state used in streaming a metavoxel node. -class MetavoxelStreamState { -public: - MetavoxelStreamBase& base; - glm::vec3 minimum; - float size; - - bool shouldSubdivide() const; - bool shouldSubdivideReference() const; - bool becameSubdivided() const; - bool becameSubdividedOrCollapsed() const; - - void setMinimum(const glm::vec3& lastMinimum, int index); -}; - -/// A single node within a metavoxel layer. -class MetavoxelNode { -public: - - static const int CHILD_COUNT = 8; - - static int getOppositeChildIndex(int index); - - MetavoxelNode(const AttributeValue& attributeValue, const MetavoxelNode* copyChildren = NULL); - MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy); - - void setAttributeValue(const AttributeValue& attributeValue); - - void blendAttributeValues(const AttributeValue& source, const AttributeValue& dest); - - AttributeValue getAttributeValue(const AttributePointer& attribute) const; - void* getAttributeValue() const { return _attributeValue; } - - void mergeChildren(const AttributePointer& attribute, bool postRead = false); - - MetavoxelNode* getChild(int index) const { return _children[index]; } - void setChild(int index, MetavoxelNode* child) { _children[index] = child; } - - bool isLeaf() const; - - void read(MetavoxelStreamState& state); - void write(MetavoxelStreamState& state) const; - - void readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state); - void writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; - - MetavoxelNode* readSubdivision(MetavoxelStreamState& state); - void writeSubdivision(MetavoxelStreamState& state) const; - - void readSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, void* ancestorValue); - void writeSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, void* ancestorValue) const; - - void writeSpanners(MetavoxelStreamState& state) const; - - /// Increments the node's reference count. - void incrementReferenceCount() { _referenceCount.ref(); } - - /// Decrements the node's reference count. If the resulting reference count is zero, destroys the node - /// and calls delete this. - void decrementReferenceCount(const AttributePointer& attribute); - - void destroy(const AttributePointer& attribute); - - bool clearChildren(const AttributePointer& attribute); - - /// Performs a deep comparison between this and the specified other node. - bool deepEquals(const AttributePointer& attribute, const MetavoxelNode& other, - const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const; - - /// Retrieves all spanners satisfying the LOD constraint, placing them in the provided set. - void getSpanners(const AttributePointer& attribute, const glm::vec3& minimum, - float size, const MetavoxelLOD& lod, SharedObjectSet& results) const; - - void countNodes(const AttributePointer& attribute, const glm::vec3& minimum, - float size, const MetavoxelLOD& lod, int& internalNodes, int& leaves) const; - - MetavoxelNode* touch(const AttributePointer& attribute) const; - -private: - Q_DISABLE_COPY(MetavoxelNode) - - friend class MetavoxelVisitation; - - QAtomicInt _referenceCount; - void* _attributeValue; - MetavoxelNode* _children[CHILD_COUNT]; -}; - -/// Contains information about a metavoxel (explicit or procedural). -class MetavoxelInfo { -public: - - MetavoxelInfo* parentInfo; - 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; - float lodBase; - bool isLODLeaf; - bool isLeaf; - - MetavoxelInfo(MetavoxelInfo* parentInfo, int inputValuesSize, int outputValuesSize); - MetavoxelInfo(); - - Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); } - glm::vec3 getCenter() const { return minimum + glm::vec3(size, size, size) * 0.5f; } -}; - -/// Base class for visitors to metavoxels. -class MetavoxelVisitor { -public: - - /// Encodes a visitation order sequence for the children of a metavoxel. - static int encodeOrder(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth); - - /// Encodes a visitation order sequence that visits each child as sorted along the specified direction. - static int encodeOrder(const glm::vec3& direction); - - /// Returns a random visitation order sequence. - static int encodeRandomOrder(); - - /// The default visitation order. - static const int DEFAULT_ORDER; - - /// A special "order" that instructs the guide to stop recursion. - static const int STOP_RECURSION; - - /// A special "order" that short-circuits the tour. - static const int SHORT_CIRCUIT; - - /// A flag combined with an order that instructs us to return to visiting all nodes (rather than the different ones) for - /// just this level. - static const int ALL_NODES; - - /// A flag combined with an order that instructs us to return to visiting all nodes (rather than the different ones) for - /// this level and all beneath it. - static const int ALL_NODES_REST; - - MetavoxelVisitor(const QVector& inputs, - const QVector& outputs = QVector(), - const MetavoxelLOD& lod = MetavoxelLOD()); - virtual ~MetavoxelVisitor(); - - /// Returns a reference to the list of input attributes desired. - const QVector& getInputs() const { return _inputs; } - - /// Returns a reference to the list of output attributes provided. - const QVector& getOutputs() const { return _outputs; } - - /// Returns a reference to the level of detail that will determine subdivision levels. - const MetavoxelLOD& getLOD() const { return _lod; } - - void setLOD(const MetavoxelLOD& lod) { _lod = lod; } - - float getMinimumLODThresholdMultiplier() const { return _minimumLODThresholdMultiplier; } - - /// Prepares for a new tour of the metavoxel data. - virtual void prepare(MetavoxelData* data); - - /// Visits a metavoxel. - /// \param info the metavoxel data - /// \return the encoded order in which to traverse the children, zero to stop recursion, or -1 to short-circuit the tour. - /// If child traversal is requested, postVisit will be called after we return from traversing the children and have merged - /// their values - virtual int visit(MetavoxelInfo& info) = 0; - - /// Called after we have visited all of a metavoxel's children. - /// \return whether or not any outputs were set in the info - virtual bool postVisit(MetavoxelInfo& info); - - /// Acquires the next visitation, incrementing the depth. - MetavoxelVisitation& acquireVisitation(); - - /// Releases the current visitation, decrementing the depth. - void releaseVisitation() { _depth--; } - -protected: - - QVector _inputs; - QVector _outputs; - MetavoxelLOD _lod; - float _minimumLODThresholdMultiplier; - MetavoxelData* _data; - QList _visitations; - int _depth; -}; - -/// Base class for visitors to spanners. -class SpannerVisitor : public MetavoxelVisitor { -public: - - SpannerVisitor(const QVector& spannerInputs, - const QVector& inputs = QVector(), - const QVector& outputs = QVector(), - const MetavoxelLOD& lod = MetavoxelLOD(), - int order = DEFAULT_ORDER); - - /// Visits a spanner. - /// \return true to continue, false to short-circuit the tour - virtual bool visit(Spanner* spanner) = 0; - - virtual void prepare(MetavoxelData* data); - virtual int visit(MetavoxelInfo& info); - -protected: - - int _spannerInputCount; - int _order; - int _visit; -}; - -/// Base class for ray intersection visitors. -class RayIntersectionVisitor : public MetavoxelVisitor { -public: - - RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const QVector& inputs, - const QVector& outputs = QVector(), - const MetavoxelLOD& lod = MetavoxelLOD()); - - /// Visits a metavoxel that the ray intersects. - virtual int visit(MetavoxelInfo& info, float distance) = 0; - - virtual int visit(MetavoxelInfo& info); - -protected: - - glm::vec3 _origin; - glm::vec3 _direction; - int _order; -}; - -/// Base class for ray intersection spanner visitors. -class RaySpannerIntersectionVisitor : public RayIntersectionVisitor { -public: - - RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const QVector& spannerInputs, - const QVector& inputs = QVector(), - const QVector& outputs = QVector(), - const MetavoxelLOD& lod = MetavoxelLOD()); - - /// Visits a spanner that the ray intersects. - /// \return true to continue, false to short-circuit the tour - virtual bool visitSpanner(Spanner* spanner, float distance) = 0; - - virtual void prepare(MetavoxelData* data); - virtual int visit(MetavoxelInfo& info, float distance); - -protected: - - int _spannerInputCount; - int _visit; -}; - -/// Interface for objects that guide metavoxel visitors. -class MetavoxelGuide : public SharedObject { - Q_OBJECT - -public: - - /// Guides the specified visitor to the contained voxels. - /// \return true to keep going, false to short circuit the tour - virtual bool guide(MetavoxelVisitation& visitation) = 0; - - /// Guides the specified visitor to the voxels that differ from a reference. - /// \return true to keep going, false to short circuit the tour - virtual bool guideToDifferent(MetavoxelVisitation& visitation); -}; - -/// Guides visitors through the explicit content of the system. -class DefaultMetavoxelGuide : public MetavoxelGuide { - Q_OBJECT - -public: - - Q_INVOKABLE DefaultMetavoxelGuide(); - - virtual bool guide(MetavoxelVisitation& visitation); - virtual bool guideToDifferent(MetavoxelVisitation& visitation); -}; - -/// Contains the state associated with a visit to a metavoxel system. -class MetavoxelVisitation { -public: - - MetavoxelVisitation* previous; - MetavoxelVisitor* visitor; - QVector inputNodes; - QVector outputNodes; - QVector compareNodes; - MetavoxelInfo info; - - MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize); - MetavoxelVisitation(); - - bool isInputLeaf(int index) const; - bool allInputNodesLeaves() const; - AttributeValue getInheritedOutputValue(int index) const; -}; - -/// Base class for objects that render metavoxels. -class MetavoxelRenderer : public SharedObject { - Q_OBJECT - -public: - - MetavoxelRenderer(); - - /// Returns a pointer to the implementation, creating it if necessary. - MetavoxelRendererImplementation* getImplementation(); - -protected: - - MetavoxelRendererImplementation* _implementation; - QMutex _implementationMutex; - - /// Returns the name of the class to instantiate for the implementation. - virtual QByteArray getImplementationClassName() const; -}; - -/// Base class for renderer implementations. -class MetavoxelRendererImplementation : public SharedObject { - Q_OBJECT - -public: - - Q_INVOKABLE MetavoxelRendererImplementation(); - - virtual void init(MetavoxelRenderer* renderer); - virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); - virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod); - virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod); - -protected: - - MetavoxelRenderer* _renderer; -}; - -/// The standard, usual renderer. -class DefaultMetavoxelRenderer : public MetavoxelRenderer { - Q_OBJECT - -public: - - Q_INVOKABLE DefaultMetavoxelRenderer(); - - virtual QByteArray getImplementationClassName() const; -}; - -#endif // hifi_MetavoxelData_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp deleted file mode 100644 index 98da28cafa..0000000000 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ /dev/null @@ -1,282 +0,0 @@ -// -// MetavoxelMessages.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 1/24/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include "MetavoxelMessages.h" -#include "Spanner.h" - -void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - static_cast(edit.data())->apply(data, objects); -} - -MetavoxelEdit::~MetavoxelEdit() { -} - -void MetavoxelEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - // nothing by default -} - -BoxSetEdit::BoxSetEdit(const Box& region, float granularity, const OwnedAttributeValue& value) : - region(region), granularity(granularity), value(value) { -} - -class BoxSetEditVisitor : public MetavoxelVisitor { -public: - - BoxSetEditVisitor(const BoxSetEdit& edit); - - virtual int visit(MetavoxelInfo& info); - -private: - - const BoxSetEdit& _edit; -}; - -BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) : - MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), - _edit(edit) { -} - -int 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); - glm::vec3 size = maximum - minimum; - if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { - return STOP_RECURSION; // disjoint - } - float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); - if (volume >= 1.0f) { - info.outputValues[0] = _edit.value; - return STOP_RECURSION; // entirely contained - } - if (info.size <= _edit.granularity) { - if (volume >= 0.5f) { - info.outputValues[0] = _edit.value; - } - return STOP_RECURSION; // reached granularity limit; take best guess - } - return DEFAULT_ORDER; // subdivide -} - -void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - // expand to fit the entire edit - while (!data.getBounds().contains(region)) { - data.expand(); - } - - BoxSetEditVisitor setVisitor(*this); - data.guide(setVisitor); -} - -GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) : - value(value) { -} - -class GlobalSetEditVisitor : public MetavoxelVisitor { -public: - - GlobalSetEditVisitor(const GlobalSetEdit& edit); - - virtual int visit(MetavoxelInfo& info); - -private: - - const GlobalSetEdit& _edit; -}; - -GlobalSetEditVisitor::GlobalSetEditVisitor(const GlobalSetEdit& edit) : - MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), - _edit(edit) { -} - -int GlobalSetEditVisitor::visit(MetavoxelInfo& info) { - info.outputValues[0] = _edit.value; - return STOP_RECURSION; // entirely contained -} - -void GlobalSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - GlobalSetEditVisitor visitor(*this); - data.guide(visitor); -} - -InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) : - attribute(attribute), - spanner(spanner) { -} - -void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - data.insert(attribute, spanner); -} - -RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) : - attribute(attribute), - id(id) { -} - -void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - SharedObject* object = objects.value(id); - if (object) { - data.remove(attribute, object); - } -} - -ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : - attribute(attribute) { -} - -void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - data.clear(attribute); -} - -SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bool blend) : - minimum(minimum), - data(data), - blend(blend) { -} - -void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - data.set(minimum, this->data, blend); -} - -PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, - float height, bool set, bool erase, float granularity) : - position(position), - radius(radius), - height(height), - set(set), - erase(erase), - granularity(granularity) { -} - -void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - // increase the extents slightly to include neighboring tiles - const float RADIUS_EXTENSION = 1.1f; - glm::vec3 extents = glm::vec3(radius, radius, radius) * RADIUS_EXTENSION; - QVector results; - data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), - Box(position - extents, position + extents), results); - - foreach (const SharedObjectPointer& spanner, results) { - Spanner* newSpanner = static_cast(spanner.data())->paintHeight(position, radius, - height, set, erase, granularity); - if (newSpanner != spanner) { - data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); - } - } -} - -MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& averageColor) : - material(material), - averageColor(averageColor) { -} - -HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner, - const SharedObjectPointer& material, const QColor& averageColor, bool paint, bool voxelize, float granularity) : - MaterialEdit(material, averageColor), - spanner(spanner), - paint(paint), - voxelize(voxelize), - granularity(granularity) { -} - -class SpannerProjectionFetchVisitor : public SpannerVisitor { -public: - - SpannerProjectionFetchVisitor(const Box& bounds, QVector& results); - - virtual bool visit(Spanner* spanner); - -private: - - const Box& _bounds; - QVector& _results; - float _closestDistance; -}; - -SpannerProjectionFetchVisitor::SpannerProjectionFetchVisitor(const Box& bounds, QVector& results) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), - _bounds(bounds), - _results(results), - _closestDistance(FLT_MAX) { -} - -bool SpannerProjectionFetchVisitor::visit(Spanner* spanner) { - Heightfield* heightfield = qobject_cast(spanner); - if (!heightfield) { - return true; - } - glm::mat4 transform = glm::scale(1.0f / glm::vec3(heightfield->getScale(), - heightfield->getScale() * heightfield->getAspectY(), - heightfield->getScale() * heightfield->getAspectZ())) * - glm::mat4_cast(glm::inverse(heightfield->getRotation())) * glm::translate(-heightfield->getTranslation()); - Box transformedBounds = transform * _bounds; - if (transformedBounds.maximum.x < 0.0f || transformedBounds.maximum.z < 0.0f || - transformedBounds.minimum.x > 1.0f || transformedBounds.minimum.z > 1.0f) { - return true; - } - float distance = qMin(glm::abs(transformedBounds.minimum.y), glm::abs(transformedBounds.maximum.y)); - if (distance < _closestDistance) { - _results.clear(); - _results.append(spanner); - _closestDistance = distance; - } - return true; -} - -void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - // make sure the color meets our transparency requirements - QColor color = averageColor; - if (paint) { - color.setAlphaF(1.0f); - - } else if (color.alphaF() < 0.5f) { - color = QColor(0, 0, 0, 0); - } - QVector results; - data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), - static_cast(spanner.data())->getBounds(), results); - - // if there's nothing intersecting directly, find the closest heightfield that intersects the projection - if (results.isEmpty()) { - SpannerProjectionFetchVisitor visitor(static_cast(spanner.data())->getBounds(), results); - data.guide(visitor); - } - - foreach (const SharedObjectPointer& result, results) { - Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, - color, paint, voxelize, granularity); - if (newResult != result) { - data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newResult); - } - } -} - -FillHeightfieldHeightEdit::FillHeightfieldHeightEdit(const glm::vec3& position, float radius, float granularity) : - position(position), - radius(radius), - granularity(granularity) { -} - -void FillHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - glm::vec3 extents = glm::vec3(radius, radius, radius); - QVector results; - data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), - Box(position - extents, position + extents), results); - - foreach (const SharedObjectPointer& spanner, results) { - Spanner* newSpanner = static_cast(spanner.data())->fillHeight(position, radius, granularity); - if (newSpanner != spanner) { - data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); - } - } -} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h deleted file mode 100644 index 7fbe2b4243..0000000000 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ /dev/null @@ -1,269 +0,0 @@ -// -// MetavoxelMessages.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/31/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelMessages_h -#define hifi_MetavoxelMessages_h - -#include "MetavoxelData.h" - -/// Requests to close the session. -class CloseSessionMessage { - STREAMABLE -}; - -DECLARE_STREAMABLE_METATYPE(CloseSessionMessage) - -/// Clears the mapping for a shared object. -class ClearSharedObjectMessage { - STREAMABLE - -public: - - STREAM int id; -}; - -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 - -public: - - STREAM MetavoxelLOD lod; -}; - -DECLARE_STREAMABLE_METATYPE(ClientStateMessage) - -/// A message preceding metavoxel delta information. The actual delta will follow it in the stream. -class MetavoxelDeltaMessage { - STREAMABLE -}; - -DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaMessage) - -/// A message indicating that metavoxel delta information is being sent on a reliable channel. -class MetavoxelDeltaPendingMessage { - STREAMABLE - -public: - - STREAM int id; - STREAM int sentPacketNumber; - STREAM int receivedPacketNumber; -}; - -DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaPendingMessage) - -/// A simple streamable edit. -class MetavoxelEditMessage { - STREAMABLE - -public: - - STREAM QVariant edit; - - void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(MetavoxelEditMessage) - -/// Abstract base class for edits. -class MetavoxelEdit { -public: - - virtual ~MetavoxelEdit(); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -/// 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 WeakSharedObjectHash& objects) 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 WeakSharedObjectHash& objects) const; -}; - -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 WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(InsertSpannerEdit) - -/// An edit that removes 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 WeakSharedObjectHash& objects) const; -}; - -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 WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit) - -/// An edit that directly sets part of the metavoxel data. -class SetDataEdit : public MetavoxelEdit { - STREAMABLE - -public: - - STREAM glm::vec3 minimum; - STREAM MetavoxelData data; - STREAM bool blend; - - SetDataEdit(const glm::vec3& minimum = glm::vec3(), const MetavoxelData& data = MetavoxelData(), bool blend = false); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(SetDataEdit) - -/// An edit that sets a region of a heightfield height. -class PaintHeightfieldHeightEdit : public MetavoxelEdit { - STREAMABLE - -public: - - STREAM glm::vec3 position; - STREAM float radius; - STREAM float height; - STREAM bool set; - STREAM bool erase; - STREAM float granularity; - - PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, - float height = 0.0f, bool set = false, bool erase = false, float granularity = 0.0f); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(PaintHeightfieldHeightEdit) - -/// Base class for edits that have materials. -class MaterialEdit : public MetavoxelEdit { - STREAMABLE - -public: - - STREAM SharedObjectPointer material; - STREAM QColor averageColor; - - MaterialEdit(const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); -}; - -DECLARE_STREAMABLE_METATYPE(MaterialEdit) - -/// An edit that sets the materials of a heightfield within a spanner to a value. -class HeightfieldMaterialSpannerEdit : STREAM public MaterialEdit { - STREAMABLE - -public: - - STREAM SharedObjectPointer spanner; - STREAM bool paint; - STREAM bool voxelize; - STREAM float granularity; - - HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(), - const SharedObjectPointer& material = SharedObjectPointer(), - const QColor& averageColor = QColor(), bool paint = false, bool voxelize = false, float granularity = 0.0f); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(HeightfieldMaterialSpannerEdit) - -/// An edit that fills a region of a heightfield height. -class FillHeightfieldHeightEdit : public MetavoxelEdit { - STREAMABLE - -public: - - STREAM glm::vec3 position; - STREAM float radius; - STREAM float granularity; - - FillHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, float granularity = 0.0f); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(FillHeightfieldHeightEdit) - -#endif // hifi_MetavoxelMessages_h diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp deleted file mode 100644 index 4e86e1c636..0000000000 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ /dev/null @@ -1,723 +0,0 @@ -// -// MetavoxelUtil.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/30/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "MetavoxelUtil.h" -#include "ScriptCache.h" -#include "StreamUtils.h" - -static int scriptHashType = qRegisterMetaType(); -static int parameterizedURLType = qRegisterMetaType(); - -REGISTER_SIMPLE_TYPE_STREAMER(ScriptHash) -REGISTER_SIMPLE_TYPE_STREAMER(ParameterizedURL) - -class DelegatingItemEditorFactory : public QItemEditorFactory { -public: - - DelegatingItemEditorFactory(); - - virtual QWidget* createEditor(int userType, QWidget* parent) const; - virtual QByteArray valuePropertyName(int userType) const; - -private: - - const QItemEditorFactory* _parentFactory; -}; - -class DoubleEditor : public QDoubleSpinBox { -public: - - DoubleEditor(QWidget* parent = NULL); -}; - -DoubleEditor::DoubleEditor(QWidget* parent) : QDoubleSpinBox(parent) { - setMinimum(-FLT_MAX); - setMaximum(FLT_MAX); - setSingleStep(0.01); -} - -DelegatingItemEditorFactory::DelegatingItemEditorFactory() : - _parentFactory(QItemEditorFactory::defaultFactory()) { - - QItemEditorFactory::setDefaultFactory(this); -} - -QWidget* DelegatingItemEditorFactory::createEditor(int userType, QWidget* parent) const { - QWidget* editor = QItemEditorFactory::createEditor(userType, parent); - return (!editor) ? _parentFactory->createEditor(userType, parent) : editor; -} - -QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const { - QByteArray propertyName = QItemEditorFactory::valuePropertyName(userType); - return propertyName.isNull() ? _parentFactory->valuePropertyName(userType) : propertyName; -} - -QItemEditorFactory* getItemEditorFactory() { - static QItemEditorFactory* factory = new DelegatingItemEditorFactory(); - return factory; -} - -static QItemEditorCreatorBase* createDoubleEditorCreator() { - QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); - getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); - getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); - 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); - 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); - return creator; -} - -static QItemEditorCreatorBase* createQuatEditorCreator() { - QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); - getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); - return creator; -} - -static QItemEditorCreatorBase* createParameterizedURLEditorCreator() { - QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); - getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); - return creator; -} - -static QItemEditorCreatorBase* doubleEditorCreator = createDoubleEditorCreator(); -static QItemEditorCreatorBase* qMetaObjectEditorCreator = createQMetaObjectEditorCreator(); -static QItemEditorCreatorBase* qColorEditorCreator = createQColorEditorCreator(); -static QItemEditorCreatorBase* qUrlEditorCreator = createQUrlEditorCreator(); -static QItemEditorCreatorBase* vec3EditorCreator = createVec3EditorCreator(); -static QItemEditorCreatorBase* quatEditorCreator = createQuatEditorCreator(); -static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator(); - -QByteArray signal(const char* signature) { - static QByteArray prototype = SIGNAL(dummyMethod()); - QByteArray signal = prototype; - return signal.replace("dummyMethod()", signature); -} - -Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) : - minimum(minimum), maximum(maximum) { -} - -void Box::add(const Box& other) { - minimum = glm::min(minimum, other.minimum); - maximum = glm::max(maximum, other.maximum); -} - -bool Box::contains(const glm::vec3& point) const { - return point.x >= minimum.x && point.x <= maximum.x && - point.y >= minimum.y && point.y <= maximum.y && - point.z >= minimum.z && point.z <= maximum.z; -} - -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 && - 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; -} - -Box Box::getIntersection(const Box& other) const { - return Box(glm::max(minimum, other.minimum), glm::min(maximum, other.maximum)); -} - -bool Box::isEmpty() const { - return minimum.x >= maximum.x || minimum.y >= maximum.y || minimum.z >= maximum.z; -} - -const int X_MAXIMUM_FLAG = 1; -const int Y_MAXIMUM_FLAG = 2; -const int Z_MAXIMUM_FLAG = 4; - -glm::vec3 Box::getVertex(int index) const { - return glm::vec3( - (index & X_MAXIMUM_FLAG) ? maximum.x : minimum.x, - (index & Y_MAXIMUM_FLAG) ? maximum.y : minimum.y, - (index & Z_MAXIMUM_FLAG) ? maximum.z : minimum.z); -} - -// finds the intersection between a ray and the facing plane on one axis -static bool findIntersection(float origin, float direction, float minimum, float maximum, float& distance) { - if (direction > EPSILON) { - distance = (minimum - origin) / direction; - return true; - } else if (direction < -EPSILON) { - distance = (maximum - origin) / direction; - return true; - } - return false; -} - -// determines whether a value is within the extents -static bool isWithin(float value, float minimum, float maximum) { - return value >= minimum && value <= maximum; -} - -bool Box::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - // handle the trivial case where the box contains the origin - if (contains(origin)) { - distance = 0.0f; - return true; - } - // check each axis - float axisDistance; - if ((findIntersection(origin.x, direction.x, minimum.x, maximum.x, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance*direction.y, minimum.y, maximum.y) && - isWithin(origin.z + axisDistance*direction.z, minimum.z, maximum.z))) { - distance = axisDistance; - return true; - } - if ((findIntersection(origin.y, direction.y, minimum.y, maximum.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance*direction.x, minimum.x, maximum.x) && - isWithin(origin.z + axisDistance*direction.z, minimum.z, maximum.z))) { - distance = axisDistance; - return true; - } - if ((findIntersection(origin.z, direction.z, minimum.z, maximum.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance*direction.y, minimum.y, maximum.y) && - isWithin(origin.x + axisDistance*direction.x, minimum.x, maximum.x))) { - distance = axisDistance; - return true; - } - return false; -} - -Box operator*(const glm::mat4& matrix, const Box& box) { - // start with the constant component - Box newBox(glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]), glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2])); - - // for each element, we choose the minimum or maximum based on the matrix sign - if (matrix[0][0] >= 0.0f) { - newBox.minimum.x += matrix[0][0] * box.minimum.x; - newBox.maximum.x += matrix[0][0] * box.maximum.x; - } else { - newBox.minimum.x += matrix[0][0] * box.maximum.x; - newBox.maximum.x += matrix[0][0] * box.minimum.x; - } - if (matrix[1][0] >= 0.0f) { - newBox.minimum.x += matrix[1][0] * box.minimum.y; - newBox.maximum.x += matrix[1][0] * box.maximum.y; - } else { - newBox.minimum.x += matrix[1][0] * box.maximum.y; - newBox.maximum.x += matrix[1][0] * box.minimum.y; - } - if (matrix[2][0] >= 0.0f) { - newBox.minimum.x += matrix[2][0] * box.minimum.z; - newBox.maximum.x += matrix[2][0] * box.maximum.z; - } else { - newBox.minimum.x += matrix[2][0] * box.maximum.z; - newBox.maximum.x += matrix[2][0] * box.minimum.z; - } - - if (matrix[0][1] >= 0.0f) { - newBox.minimum.y += matrix[0][1] * box.minimum.x; - newBox.maximum.y += matrix[0][1] * box.maximum.x; - } else { - newBox.minimum.y += matrix[0][1] * box.maximum.x; - newBox.maximum.y += matrix[0][1] * box.minimum.x; - } - if (matrix[1][1] >= 0.0f) { - newBox.minimum.y += matrix[1][1] * box.minimum.y; - newBox.maximum.y += matrix[1][1] * box.maximum.y; - } else { - newBox.minimum.y += matrix[1][1] * box.maximum.y; - newBox.maximum.y += matrix[1][1] * box.minimum.y; - } - if (matrix[2][1] >= 0.0f) { - newBox.minimum.y += matrix[2][1] * box.minimum.z; - newBox.maximum.y += matrix[2][1] * box.maximum.z; - } else { - newBox.minimum.y += matrix[2][1] * box.maximum.z; - newBox.maximum.y += matrix[2][1] * box.minimum.z; - } - - if (matrix[0][2] >= 0.0f) { - newBox.minimum.z += matrix[0][2] * box.minimum.x; - newBox.maximum.z += matrix[0][2] * box.maximum.x; - } else { - newBox.minimum.z += matrix[0][2] * box.maximum.x; - newBox.maximum.z += matrix[0][2] * box.minimum.x; - } - if (matrix[1][2] >= 0.0f) { - newBox.minimum.z += matrix[1][2] * box.minimum.y; - newBox.maximum.z += matrix[1][2] * box.maximum.y; - } else { - newBox.minimum.z += matrix[1][2] * box.maximum.y; - newBox.maximum.z += matrix[1][2] * box.minimum.y; - } - if (matrix[2][2] >= 0.0f) { - newBox.minimum.z += matrix[2][2] * box.minimum.z; - newBox.maximum.z += matrix[2][2] * box.maximum.z; - } else { - newBox.minimum.z += matrix[2][2] * box.maximum.z; - newBox.maximum.z += matrix[2][2] * box.minimum.z; - } - - return newBox; -} - -QDebug& operator<<(QDebug& dbg, const Box& box) { - return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximum=" << box.maximum << "}"; -} - -AxisExtents::AxisExtents(const glm::vec3& first0, const glm::vec3& first1, const glm::vec3& first2, const glm::vec3& second) : - axis(glm::cross(first2 - first1, first0 - first1)), - minimum(glm::dot(first1, axis)), - maximum(glm::dot(second, axis)) { -} - -AxisExtents::AxisExtents(const glm::vec3& axis, float minimum, float maximum) : - axis(axis), - minimum(minimum), - maximum(maximum) { -} - -void Frustum::set(const glm::vec3& farTopLeft, const glm::vec3& farTopRight, const glm::vec3& farBottomLeft, - const glm::vec3& farBottomRight, const glm::vec3& nearTopLeft, const glm::vec3& nearTopRight, - const glm::vec3& nearBottomLeft, const glm::vec3& nearBottomRight) { - - _vertices[0] = farBottomLeft; - _vertices[1] = farBottomRight; - _vertices[2] = farTopLeft; - _vertices[3] = farTopRight; - _vertices[4] = nearBottomLeft; - _vertices[5] = nearBottomRight; - _vertices[6] = nearTopLeft; - _vertices[7] = nearTopRight; - - // compute the bounds - _bounds.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX); - _bounds.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX); - for (int i = 0; i < VERTEX_COUNT; i++) { - _bounds.minimum = glm::min(_bounds.minimum, _vertices[i]); - _bounds.maximum = glm::max(_bounds.maximum, _vertices[i]); - } - - // compute the extents for each side - _sideExtents[0] = AxisExtents(nearBottomLeft, nearTopLeft, nearTopRight, farBottomLeft); - _sideExtents[1] = AxisExtents(nearBottomLeft, farBottomLeft, farTopLeft, farBottomRight); - _sideExtents[2] = AxisExtents(nearBottomRight, nearTopRight, farTopRight, farBottomLeft); - _sideExtents[3] = AxisExtents(nearBottomLeft, nearBottomRight, farBottomRight, farTopLeft); - _sideExtents[4] = AxisExtents(nearTopLeft, farTopLeft, farTopRight, farBottomRight); - - // the other set of extents are derived from the cross products of the frustum and box edges - glm::vec3 edges[] = { nearBottomRight - nearBottomLeft, nearTopLeft - nearBottomLeft, farBottomLeft - nearBottomLeft, - farBottomRight - nearBottomRight, farTopLeft - nearTopLeft, farTopRight - nearTopRight }; - const int AXIS_COUNT = 3; - for (uint i = 0, extentIndex = 0; i < sizeof(edges) / sizeof(edges[0]); i++) { - for (int j = 0; j < AXIS_COUNT; j++) { - glm::vec3 axis; - axis[j] = 1.0f; - glm::vec3 crossProduct = glm::cross(edges[i], axis); - float minimum = FLT_MAX, maximum = -FLT_MAX; - for (int k = 0; k < VERTEX_COUNT; k++) { - float projection = glm::dot(crossProduct, _vertices[k]); - minimum = glm::min(minimum, projection); - maximum = glm::max(maximum, projection); - } - _crossProductExtents[extentIndex++] = AxisExtents(crossProduct, minimum, maximum); - } - } -} - -Frustum::IntersectionType Frustum::getIntersectionType(const Box& box) const { - // first check the bounds (equivalent to checking frustum vertices against box extents) - if (!_bounds.intersects(box)) { - return NO_INTERSECTION; - } - - // check box vertices against side extents - bool allInside = true; - for (int i = 0; i < SIDE_EXTENT_COUNT; i++) { - const AxisExtents& extents = _sideExtents[i]; - float firstProjection = glm::dot(box.getVertex(0), extents.axis); - if (firstProjection < extents.minimum) { - allInside = false; - for (int j = 1; j < Box::VERTEX_COUNT; j++) { - if (glm::dot(box.getVertex(j), extents.axis) >= extents.minimum) { - goto sideContinue; - } - } - return NO_INTERSECTION; - - } else if (firstProjection > extents.maximum) { - allInside = false; - for (int j = 1; j < Box::VERTEX_COUNT; j++) { - if (glm::dot(box.getVertex(j), extents.axis) <= extents.maximum) { - goto sideContinue; - } - } - return NO_INTERSECTION; - - } else if (allInside) { - for (int j = 1; j < Box::VERTEX_COUNT; j++) { - float projection = glm::dot(box.getVertex(j), extents.axis); - if (projection < extents.minimum || projection > extents.maximum) { - allInside = false; - goto sideContinue; - } - } - } - sideContinue: ; - } - if (allInside) { - return CONTAINS_INTERSECTION; - } - - // check box vertices against cross product extents - for (int i = 0; i < CROSS_PRODUCT_EXTENT_COUNT; i++) { - const AxisExtents& extents = _crossProductExtents[i]; - float firstProjection = glm::dot(box.getVertex(0), extents.axis); - if (firstProjection < extents.minimum) { - for (int j = 1; j < Box::VERTEX_COUNT; j++) { - if (glm::dot(box.getVertex(j), extents.axis) >= extents.minimum) { - goto crossProductContinue; - } - } - return NO_INTERSECTION; - - } else if (firstProjection > extents.maximum) { - for (int j = 1; j < Box::VERTEX_COUNT; j++) { - if (glm::dot(box.getVertex(j), extents.axis) <= extents.maximum) { - goto crossProductContinue; - } - } - return NO_INTERSECTION; - } - crossProductContinue: ; - } - - return PARTIAL_INTERSECTION; -} - -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()); - layout->setAlignment(Qt::AlignTop); - setLayout(layout); - layout->addWidget(_button = new QPushButton()); - connect(_button, SIGNAL(clicked()), SLOT(selectColor())); - setColor(QColor()); -} - -void QColorEditor::setColor(const QColor& color) { - QString name = (_color = color).name(); - _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name())); - _button->setText(name); -} - -void QColorEditor::selectColor() { - QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel); - if (color.isValid()) { - setColor(color); - emit colorChanged(color); - } -} - -Setting::Handle editorURLs("editorURLs"); - -QUrlEditor::QUrlEditor(QWidget* parent) : - QComboBox(parent) { - - setEditable(true); - setInsertPolicy(InsertAtTop); - - // populate initial URL list from settings - addItems(editorURLs.get()); - - 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)); - } - editorURLs.set(urls); -} - -BaseVec3Editor::BaseVec3Editor(QWidget* parent) : QWidget(parent) { - QHBoxLayout* layout = new QHBoxLayout(); - layout->setContentsMargins(QMargins()); - setLayout(layout); - - layout->addWidget(_x = createComponentBox()); - layout->addWidget(_y = createComponentBox()); - layout->addWidget(_z = createComponentBox()); -} - -void BaseVec3Editor::setSingleStep(double singleStep) { - _x->setSingleStep(singleStep); - _y->setSingleStep(singleStep); - _z->setSingleStep(singleStep); -} - -double BaseVec3Editor::getSingleStep() const { - return _x->singleStep(); -} - -QDoubleSpinBox* BaseVec3Editor::createComponentBox() { - QDoubleSpinBox* box = new QDoubleSpinBox(); - box->setMinimum(-FLT_MAX); - box->setMaximum(FLT_MAX); - box->setMinimumWidth(50); - connect(box, SIGNAL(valueChanged(double)), SLOT(updateValue())); - return box; -} - -Vec3Editor::Vec3Editor(QWidget* parent) : BaseVec3Editor(parent) { - setSingleStep(0.01); -} - -static void setComponentValue(QDoubleSpinBox* box, double value) { - box->blockSignals(true); - box->setValue(value); - box->blockSignals(false); -} - -void Vec3Editor::setValue(const glm::vec3& value) { - _value = value; - setComponentValue(_x, value.x); - setComponentValue(_y, value.y); - setComponentValue(_z, value.z); -} - -void Vec3Editor::updateValue() { - emit valueChanged(_value = glm::vec3(_x->value(), _y->value(), _z->value())); -} - -QuatEditor::QuatEditor(QWidget* parent) : BaseVec3Editor(parent) { - _x->setRange(-179.0, 180.0); - _y->setRange(-179.0, 180.0); - _z->setRange(-179.0, 180.0); - - _x->setWrapping(true); - _y->setWrapping(true); - _z->setWrapping(true); -} - -void QuatEditor::setValue(const glm::quat& value) { - if (_value != value) { - glm::vec3 eulers = glm::degrees(safeEulerAngles(_value = value)); - setComponentValue(_x, eulers.x); - setComponentValue(_y, eulers.y); - setComponentValue(_z, eulers.z); - } -} - -void QuatEditor::updateValue() { - glm::quat value(glm::radians(glm::vec3(_x->value(), _y->value(), _z->value()))); - if (_value != value) { - emit valueChanged(_value = value); - } -} - -ParameterizedURL::ParameterizedURL(const QUrl& url, const ScriptHash& parameters) : - _url(url), - _parameters(parameters) { -} - -bool ParameterizedURL::operator==(const ParameterizedURL& other) const { - return _url == other._url && _parameters == other._parameters; -} - -bool ParameterizedURL::operator!=(const ParameterizedURL& other) const { - return _url != other._url || _parameters != other._parameters; -} - -uint qHash(const ParameterizedURL& url, uint seed) { - // just hash on the URL, for now - return qHash(url.getURL(), seed); -} - -Bitstream& operator<<(Bitstream& out, const ParameterizedURL& url) { - out << url.getURL(); - out << url.getParameters(); - return out; -} - -Bitstream& operator>>(Bitstream& in, ParameterizedURL& url) { - QUrl qurl; - in >> qurl; - ScriptHash parameters; - in >> parameters; - url = ParameterizedURL(qurl, parameters); - return in; -} - -ParameterizedURLEditor::ParameterizedURLEditor(QWidget* parent) : - QWidget(parent) { - - QVBoxLayout* layout = new QVBoxLayout(); - layout->setContentsMargins(QMargins()); - setLayout(layout); - - QWidget* lineContainer = new QWidget(); - layout->addWidget(lineContainer); - - QHBoxLayout* lineLayout = new QHBoxLayout(); - lineContainer->setLayout(lineLayout); - lineLayout->setContentsMargins(QMargins()); - - 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())); - lineLayout->addWidget(refresh); -} - -void ParameterizedURLEditor::setURL(const ParameterizedURL& url) { - _urlEditor.setURL((_url = url).getURL()); - updateParameters(); -} - -void ParameterizedURLEditor::updateURL() { - ScriptHash parameters; - if (layout()->count() > 1) { - QFormLayout* form = static_cast(layout()->itemAt(1)); - for (int i = 0; i < form->rowCount(); i++) { - QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); - QByteArray valuePropertyName = widget->property("valuePropertyName").toByteArray(); - const QMetaObject* widgetMetaObject = widget->metaObject(); - QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); - parameters.insert(DependencyManager::get()->getEngine()->toStringHandle( - widget->property("parameterName").toString()), widgetProperty.read(widget)); - } - } - emit urlChanged(_url = ParameterizedURL(_urlEditor.getURL(), parameters)); - if (_program) { - _program->disconnect(this); - } -} - -void ParameterizedURLEditor::updateParameters() { - if (_program) { - _program->disconnect(this); - } - _program = DependencyManager::get()->getProgram(_url.getURL()); - if (_program->isLoaded()) { - continueUpdatingParameters(); - } else { - connect(_program.data(), SIGNAL(loaded()), SLOT(continueUpdatingParameters())); - } -} - -void ParameterizedURLEditor::continueUpdatingParameters() { - QVBoxLayout* layout = static_cast(this->layout()); - if (layout->count() > 1) { - QFormLayout* form = static_cast(layout->takeAt(1)); - for (int i = form->count() - 1; i >= 0; i--) { - QLayoutItem* item = form->takeAt(i); - if (item->widget()) { - delete item->widget(); - } - delete item; - } - delete form; - } - QSharedPointer value = DependencyManager::get()->getValue(_url.getURL()); - const QList& parameters = static_cast(value.data())->getParameterInfo(); - if (parameters.isEmpty()) { - return; - } - QFormLayout* form = new QFormLayout(); - layout->addLayout(form); - foreach (const ParameterInfo& parameter, parameters) { - QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(parameter.type, NULL); - if (widget) { - form->addRow(parameter.name.toString() + ":", widget); - QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(parameter.type); - widget->setProperty("parameterName", parameter.name.toString()); - widget->setProperty("valuePropertyName", valuePropertyName); - const QMetaObject* widgetMetaObject = widget->metaObject(); - QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); - widgetProperty.write(widget, _url.getParameters().value(parameter.name)); - if (widgetProperty.hasNotifySignal()) { - connect(widget, signal(widgetProperty.notifySignal().methodSignature()), SLOT(updateURL())); - } - } - } -} diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h deleted file mode 100644 index 3be54ca60d..0000000000 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ /dev/null @@ -1,362 +0,0 @@ -// -// MetavoxelUtil.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 12/30/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelUtil_h -#define hifi_MetavoxelUtil_h - -#include -#include -#include -#include -#include -#include - -#include - -#include "Bitstream.h" - -class QByteArray; -class QDoubleSpinBox; -class QItemEditorFactory; -class QPushButton; - -class NetworkProgram; - -/// Performs the runtime equivalent of Qt's SIGNAL macro, which is to attach a prefix to the signature. -QByteArray signal(const char* signature); - -/// A streamable axis-aligned bounding box. -class Box { - STREAMABLE - -public: - - static const int VERTEX_COUNT = 8; - - STREAM glm::vec3 minimum; - STREAM glm::vec3 maximum; - - explicit Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); - - void add(const Box& other); - - bool contains(const glm::vec3& point) const; - - bool contains(const Box& other) const; - - bool intersects(const Box& other) const; - - Box getIntersection(const Box& other) const; - - bool isEmpty() const; - - float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); } - - glm::vec3 getVertex(int index) const; - - glm::vec3 getCenter() const { return (minimum + maximum) * 0.5f; } - - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; -}; - -DECLARE_STREAMABLE_METATYPE(Box) - -Box operator*(const glm::mat4& matrix, const Box& box); - -QDebug& operator<<(QDebug& out, const Box& box); - -/// Represents the extents along an axis. -class AxisExtents { -public: - glm::vec3 axis; - float minimum; - float maximum; - - /// Creates a set of extents given three points on the first plane and one on the second. - AxisExtents(const glm::vec3& first0, const glm::vec3& first1, const glm::vec3& first2, const glm::vec3& second); - - AxisExtents(const glm::vec3& axis = glm::vec3(), float minimum = 0.0f, float maximum = 0.0f); -}; - -/// A simple pyramidal frustum for intersection testing. -class Frustum { -public: - - void set(const glm::vec3& farTopLeft, const glm::vec3& farTopRight, const glm::vec3& farBottomLeft, - const glm::vec3& farBottomRight, const glm::vec3& nearTopLeft, const glm::vec3& nearTopRight, - const glm::vec3& nearBottomLeft, const glm::vec3& nearBottomRight); - - enum IntersectionType { NO_INTERSECTION, PARTIAL_INTERSECTION, CONTAINS_INTERSECTION }; - - IntersectionType getIntersectionType(const Box& box) const; - -private: - - static const int VERTEX_COUNT = 8; - static const int SIDE_EXTENT_COUNT = 5; - static const int CROSS_PRODUCT_EXTENT_COUNT = 18; - - glm::vec3 _vertices[VERTEX_COUNT]; - Box _bounds; - AxisExtents _sideExtents[SIDE_EXTENT_COUNT]; - AxisExtents _crossProductExtents[CROSS_PRODUCT_EXTENT_COUNT]; -}; - -/// Returns a pointer to the singleton item editor factory. -QItemEditorFactory* getItemEditorFactory(); - -/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create, -/// this class simply delays the value property name lookup until actually requested. -template class LazyItemEditorCreator : public QItemEditorCreatorBase { -public: - - virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); } - - virtual QByteArray valuePropertyName() const; - -protected: - - QByteArray _valuePropertyName; -}; - -template QByteArray LazyItemEditorCreator::valuePropertyName() const { - if (_valuePropertyName.isNull()) { - const_cast*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name(); - } - return _valuePropertyName; -} - -/// 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 - Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged USER true) - -public: - - QColorEditor(QWidget* parent); - - const QColor& getColor() const { return _color; } - -signals: - - void colorChanged(const QColor& color); - -public slots: - - void setColor(const QColor& color); - -private slots: - - void selectColor(); - -private: - - QPushButton* _button; - 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; -}; - -/// Base class for Vec3Editor and QuatEditor. -class BaseVec3Editor : public QWidget { - Q_OBJECT - -public: - - BaseVec3Editor(QWidget* parent); - - void setSingleStep(double singleStep); - double getSingleStep() const; - -protected slots: - - virtual void updateValue() = 0; - -protected: - - QDoubleSpinBox* createComponentBox(); - - QDoubleSpinBox* _x; - QDoubleSpinBox* _y; - QDoubleSpinBox* _z; -}; - -/// Editor for vector values. -class Vec3Editor : public BaseVec3Editor { - Q_OBJECT - Q_PROPERTY(glm::vec3 value MEMBER _value WRITE setValue NOTIFY valueChanged USER true) - -public: - - Vec3Editor(QWidget* parent); - - const glm::vec3& getValue() const { return _value; } - -signals: - - void valueChanged(const glm::vec3& vector); - -public slots: - - void setValue(const glm::vec3& vector); - -protected: - - virtual void updateValue(); - -private: - - glm::vec3 _value; -}; - -/// Editor for quaternion values. -class QuatEditor : public BaseVec3Editor { - Q_OBJECT - Q_PROPERTY(glm::quat value MEMBER _value WRITE setValue NOTIFY valueChanged USER true) - -public: - - QuatEditor(QWidget* parent); - -signals: - - void valueChanged(const glm::quat& value); - -public slots: - - void setValue(const glm::quat& value); - -protected: - - virtual void updateValue(); - -private: - - glm::quat _value; -}; - -typedef QHash ScriptHash; - -Q_DECLARE_METATYPE(ScriptHash) - -/// Combines a URL with a set of typed parameters. -class ParameterizedURL { -public: - - ParameterizedURL(const QUrl& url = QUrl(), const ScriptHash& parameters = ScriptHash()); - - bool isValid() const { return _url.isValid(); } - - void setURL(const QUrl& url) { _url = url; } - const QUrl& getURL() const { return _url; } - - void setParameters(const ScriptHash& parameters) { _parameters = parameters; } - const ScriptHash& getParameters() const { return _parameters; } - - bool operator==(const ParameterizedURL& other) const; - bool operator!=(const ParameterizedURL& other) const; - -private: - - QUrl _url; - ScriptHash _parameters; -}; - -uint qHash(const ParameterizedURL& url, uint seed = 0); - -Bitstream& operator<<(Bitstream& out, const ParameterizedURL& url); -Bitstream& operator>>(Bitstream& in, ParameterizedURL& url); - -Q_DECLARE_METATYPE(ParameterizedURL) - -/// Allows editing parameterized URLs. -class ParameterizedURLEditor : public QWidget { - Q_OBJECT - Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL NOTIFY urlChanged USER true) - -public: - - ParameterizedURLEditor(QWidget* parent = NULL); - -signals: - - void urlChanged(const ParameterizedURL& url); - -public slots: - - void setURL(const ParameterizedURL& url); - -private slots: - - void updateURL(); - void updateParameters(); - void continueUpdatingParameters(); - -private: - - ParameterizedURL _url; - QSharedPointer _program; - - QUrlEditor _urlEditor; -}; - -#endif // hifi_MetavoxelUtil_h diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp deleted file mode 100644 index b7e81b5381..0000000000 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ /dev/null @@ -1,209 +0,0 @@ -// -// ScriptCache.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 2/4/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include -#include -#include -#include - -#include "AttributeRegistry.h" -#include "ScriptCache.h" - -static int scriptValueMetaTypeId = qRegisterMetaType(); -static bool scriptValueComparators = QMetaType::registerComparators(); - -bool operator==(const QScriptValue& first, const QScriptValue& second) { - if (first.isUndefined()) { - return second.isUndefined(); - - } else if (first.isNull()) { - return second.isNull(); - - } else if (first.isBool()) { - return second.isBool() && first.toBool() == second.toBool(); - - } else if (first.isNumber()) { - return second.isNumber() && first.toNumber() == second.toNumber(); - - } else if (first.isString()) { - return second.isString() && first.toString() == second.toString(); - - } else if (first.isVariant()) { - return second.isVariant() && first.toVariant() == second.toVariant(); - - } else if (first.isQObject()) { - return second.isQObject() && first.toQObject() == second.toQObject(); - - } else if (first.isQMetaObject()) { - return second.isQMetaObject() && first.toQMetaObject() == second.toQMetaObject(); - - } else if (first.isDate()) { - return second.isDate() && first.toDateTime() == second.toDateTime(); - - } else if (first.isRegExp()) { - return second.isRegExp() && first.toRegExp() == second.toRegExp(); - - } else if (first.isArray()) { - if (!second.isArray()) { - return false; - } - int length = first.property(DependencyManager::get()->getLengthString()).toInt32(); - if (second.property(DependencyManager::get()->getLengthString()).toInt32() != length) { - return false; - } - for (int i = 0; i < length; i++) { - if (first.property(i) != second.property(i)) { - return false; - } - } - return true; - - } else if (first.isObject()) { - if (!second.isObject()) { - return false; - } - int propertyCount = 0; - for (QScriptValueIterator it(first); it.hasNext(); ) { - it.next(); - if (second.property(it.scriptName()) != it.value()) { - return false; - } - propertyCount++; - } - // make sure the second has exactly as many properties as the first - for (QScriptValueIterator it(second); it.hasNext(); ) { - it.next(); - if (--propertyCount < 0) { - return false; - } - } - return true; - - } else { - // if none of the above tests apply, first must be invalid - return !second.isValid(); - } -} - -bool operator!=(const QScriptValue& first, const QScriptValue& second) { - return !(first == second); -} - -bool operator<(const QScriptValue& first, const QScriptValue& second) { - return first.lessThan(second); -} - -ScriptCache::ScriptCache() : - _engine(NULL) -{ - setEngine(new QScriptEngine(this)); - - const qint64 SCRIPT_DEFAULT_UNUSED_MAX_SIZE = 50 * BYTES_PER_MEGABYTES; - setUnusedResourceCacheSize(SCRIPT_DEFAULT_UNUSED_MAX_SIZE); -} - -void ScriptCache::setEngine(QScriptEngine* engine) { - if (_engine && _engine->parent() == this) { - delete _engine; - } - AttributeRegistry::getInstance()->configureScriptEngine(_engine = engine); - _parametersString = engine->toStringHandle("parameters"); - _lengthString = engine->toStringHandle("length"); - _nameString = engine->toStringHandle("name"); - _typeString = engine->toStringHandle("type"); - _generatorString = engine->toStringHandle("generator"); -} - -QSharedPointer ScriptCache::getValue(const ParameterizedURL& url) { - QSharedPointer value = _networkValues.value(url); - if (value.isNull()) { - value = QSharedPointer(url.getParameters().isEmpty() ? - (NetworkValue*)new RootNetworkValue(getProgram(url.getURL())) : - (NetworkValue*)new DerivedNetworkValue(getValue(url.getURL()), url.getParameters())); - _networkValues.insert(url, value); - } - return value; -} - -QSharedPointer ScriptCache::createResource(const QUrl& url, - const QSharedPointer& fallback, bool delayLoad, const void* extra) { - return QSharedPointer(new NetworkProgram(this, url), &Resource::allReferencesCleared); -} - -NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) : - Resource(url), - _cache(cache) { -} - -void NetworkProgram::downloadFinished(QNetworkReply* reply) { - _program = QScriptProgram(QTextStream(reply).readAll(), reply->url().toString()); - reply->deleteLater(); - finishedLoading(true); - emit loaded(); -} - -NetworkValue::~NetworkValue() { -} - -RootNetworkValue::RootNetworkValue(const QSharedPointer& program) : - _program(program) { -} - -QScriptValue& RootNetworkValue::getValue() { - if (!_value.isValid() && _program->isLoaded()) { - _value = _program->getCache()->getEngine()->evaluate(_program->getProgram()); - } - return _value; -} - -const QList& RootNetworkValue::getParameterInfo() { - if (isLoaded() && _parameterInfo.isEmpty()) { - ScriptCache* cache = _program->getCache(); - QScriptEngine* engine = cache->getEngine(); - QScriptValue parameters = _value.property(cache->getParametersString()); - if (parameters.isArray()) { - int length = parameters.property(cache->getLengthString()).toInt32(); - for (int i = 0; i < length; i++) { - QScriptValue parameter = parameters.property(i); - ParameterInfo info = { engine->toStringHandle(parameter.property(cache->getNameString()).toString()), - QMetaType::type(parameter.property(cache->getTypeString()).toString().toUtf8().constData()) }; - _parameterInfo.append(info); - } - } - } - return _parameterInfo; -} - -DerivedNetworkValue::DerivedNetworkValue(const QSharedPointer& baseValue, const ScriptHash& parameters) : - _baseValue(baseValue), - _parameters(parameters) { -} - -QScriptValue& DerivedNetworkValue::getValue() { - if (!_value.isValid() && _baseValue->isLoaded()) { - RootNetworkValue* root = static_cast(_baseValue.data()); - ScriptCache* cache = root->getProgram()->getCache(); - QScriptValue generator = _baseValue->getValue().property(cache->getGeneratorString()); - if (generator.isFunction()) { - QScriptValueList arguments; - foreach (const ParameterInfo& parameter, root->getParameterInfo()) { - arguments.append(cache->getEngine()->newVariant(_parameters.value(parameter.name))); - } - _value = generator.call(QScriptValue(), arguments); - - } else { - _value = _baseValue->getValue(); - } - } - return _value; -} diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h deleted file mode 100644 index b9294a7504..0000000000 --- a/libraries/metavoxels/src/ScriptCache.h +++ /dev/null @@ -1,152 +0,0 @@ -// -// ScriptCache.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 2/4/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ScriptCache_h -#define hifi_ScriptCache_h - -#include -#include - -#include - -#include "MetavoxelUtil.h" - -class QScriptEngine; - -class NetworkProgram; -class NetworkValue; - -/// Maintains a cache of loaded scripts. -class ScriptCache : public ResourceCache, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - -public: - ScriptCache(); - - void setEngine(QScriptEngine* engine); - QScriptEngine* getEngine() const { return _engine; } - - /// Loads a script program from the specified URL. - QSharedPointer getProgram(const QUrl& url) { return getResource(url).staticCast(); } - - /// Loads a script value from the specified URL. - QSharedPointer getValue(const ParameterizedURL& url); - - const QScriptString& getParametersString() const { return _parametersString; } - const QScriptString& getLengthString() const { return _lengthString; } - const QScriptString& getNameString() const { return _nameString; } - const QScriptString& getTypeString() const { return _typeString; } - const QScriptString& getGeneratorString() const { return _generatorString; } - -protected: - - virtual QSharedPointer createResource(const QUrl& url, - const QSharedPointer& fallback, bool delayLoad, const void* extra); - -private: - - QScriptEngine* _engine; - QHash > _networkValues; - QScriptString _parametersString; - QScriptString _lengthString; - QScriptString _nameString; - QScriptString _typeString; - QScriptString _generatorString; -}; - -Q_DECLARE_METATYPE(QScriptValue) - -bool operator==(const QScriptValue& first, const QScriptValue& second); -bool operator!=(const QScriptValue& first, const QScriptValue& second); -bool operator<(const QScriptValue& first, const QScriptValue& second); - -/// A program loaded from the network. -class NetworkProgram : public Resource { - Q_OBJECT - -public: - - NetworkProgram(ScriptCache* cache, const QUrl& url); - - ScriptCache* getCache() const { return _cache; } - - const QScriptProgram& getProgram() const { return _program; } - -signals: - - void loaded(); - -protected: - - virtual void downloadFinished(QNetworkReply* reply); - -private: - - ScriptCache* _cache; - QScriptProgram _program; -}; - -/// Abstract base class of values loaded from the network. -class NetworkValue { -public: - - virtual ~NetworkValue(); - - bool isLoaded() { return getValue().isValid(); } - - virtual QScriptValue& getValue() = 0; - -protected: - - QScriptValue _value; -}; - -/// Contains information about a script parameter. -class ParameterInfo { -public: - QScriptString name; - int type; -}; - -/// The direct result of running a program. -class RootNetworkValue : public NetworkValue { -public: - - RootNetworkValue(const QSharedPointer& program); - - const QSharedPointer& getProgram() const { return _program; } - - virtual QScriptValue& getValue(); - - const QList& getParameterInfo(); - -private: - - QSharedPointer _program; - QList _parameterInfo; -}; - -/// The result of running a program's generator using a set of arguments. -class DerivedNetworkValue : public NetworkValue { -public: - - DerivedNetworkValue(const QSharedPointer& baseValue, const ScriptHash& parameters); - - virtual QScriptValue& getValue(); - -private: - - QSharedPointer _baseValue; - ScriptHash _parameters; -}; - -#endif // hifi_ScriptCache_h diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp deleted file mode 100644 index 6369037e2a..0000000000 --- a/libraries/metavoxels/src/SharedObject.cpp +++ /dev/null @@ -1,319 +0,0 @@ -// -// SharedObject.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 2/5/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include - -#include "Bitstream.h" -#include "MetavoxelUtil.h" -#include "SharedObject.h" - -REGISTER_META_OBJECT(SharedObject) - -SharedObject::SharedObject() : - _id(_nextID.fetchAndAddOrdered(1)), - _originID(_id), - _remoteID(0), - _remoteOriginID(0) { - - QWriteLocker locker(&_weakHashLock); - _weakHash.insert(_id, this); -} - -void SharedObject::setID(int id) { - QWriteLocker locker(&_weakHashLock); - _weakHash.remove(_id); - _weakHash.insert(_id = id, this); -} - -void SharedObject::incrementReferenceCount() { - _referenceCount.ref(); -} - -void SharedObject::decrementReferenceCount() { - if (!_referenceCount.deref()) { - { - QWriteLocker locker(&_weakHashLock); - _weakHash.remove(_id); - } - delete this; - } -} - -SharedObject* SharedObject::clone(bool withID, SharedObject* target) const { - // default behavior is to make a copy using the no-arg constructor and copy the stored properties - const QMetaObject* metaObject = this->metaObject(); - if (!target) { - target = static_cast(metaObject->newInstance()); - } - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored()) { - if (property.userType() == qMetaTypeId()) { - SharedObject* value = property.read(this).value().data(); - property.write(target, QVariant::fromValue(value ? value->clone(withID) : value)); - } else { - property.write(target, property.read(this)); - } - } - } - foreach (const QByteArray& propertyName, dynamicPropertyNames()) { - target->setProperty(propertyName, property(propertyName)); - } - if (withID) { - target->setOriginID(_originID); - } - return target; -} - -bool SharedObject::equals(const SharedObject* other, bool sharedAncestry) const { - if (!other) { - return false; - } - if (other == this) { - return true; - } - // default behavior is to compare the properties - const QMetaObject* metaObject = this->metaObject(); - if (metaObject != other->metaObject() && !sharedAncestry) { - return false; - } - // use the streamer, if we have one - const ObjectStreamer* streamer = Bitstream::getObjectStreamer(metaObject); - if (streamer) { - if (!streamer->equal(this, other)) { - return false; - } - } else { - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored() && property.read(this) != property.read(other)) { - return false; - } - } - } - QList dynamicPropertyNames = this->dynamicPropertyNames(); - if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) { - return false; - } - foreach (const QByteArray& propertyName, dynamicPropertyNames) { - if (property(propertyName) != other->property(propertyName)) { - return false; - } - } - return true; -} - -void SharedObject::dump(QDebug debug) const { - debug << this; - const QMetaObject* metaObject = this->metaObject(); - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored()) { - debug << property.name() << property.read(this); - } - } - QList dynamicPropertyNames = this->dynamicPropertyNames(); - foreach (const QByteArray& propertyName, dynamicPropertyNames) { - debug << propertyName << property(propertyName); - } -} - -void SharedObject::writeExtra(Bitstream& out) const { - // nothing by default -} - -void SharedObject::readExtra(Bitstream& in, bool reread) { - // nothing by default -} - -void SharedObject::writeExtraDelta(Bitstream& out, const SharedObject* reference) const { - // nothing by default -} - -void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) { - // nothing by default -} - -void SharedObject::maybeWriteSubdivision(Bitstream& out) { - // nothing by default -} - -SharedObject* SharedObject::readSubdivision(Bitstream& in) { - return this; -} - -QAtomicInt SharedObject::_nextID(1); -WeakSharedObjectHash SharedObject::_weakHash; -QReadWriteLock SharedObject::_weakHashLock; - -void pruneWeakSharedObjectHash(WeakSharedObjectHash& hash) { - for (WeakSharedObjectHash::iterator it = hash.begin(); it != hash.end(); ) { - if (!it.value()) { - it = hash.erase(it); - } else { - it++; - } - } -} - -SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nullable, QWidget* parent) : QWidget(parent) { - QVBoxLayout* layout = new QVBoxLayout(); - layout->setAlignment(Qt::AlignTop); - setLayout(layout); - - QFormLayout* form = new QFormLayout(); - layout->addLayout(form); - - form->addRow("Type:", _type = new QComboBox()); - if (nullable) { - _type->addItem("(none)"); - } - foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) { - // add constructable subclasses - if (metaObject->constructorCount() > 0) { - _type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); - } - } - connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); - updateType(); -} - -void SharedObjectEditor::setObject(const SharedObjectPointer& object) { - _object = object; - const QMetaObject* metaObject = object ? object->metaObject() : NULL; - int index = _type->findData(QVariant::fromValue(metaObject)); - if (index != -1) { - // ensure that we call updateType to obtain the values - if (_type->currentIndex() == index) { - updateType(); - } else { - _type->setCurrentIndex(index); - } - } -} - -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()); - if (property.hasNotifySignal()) { - connect(_object.data(), signal(property.notifySignal().methodSignature()), SLOT(updateProperty())); - } - } -} - -const QMetaObject* getOwningAncestor(const QMetaObject* metaObject, int propertyIndex) { - while (propertyIndex < metaObject->propertyOffset()) { - metaObject = metaObject->superClass(); - } - return metaObject; -} - -void SharedObjectEditor::updateType() { - // delete the existing rows - if (layout()->count() > 1) { - QFormLayout* form = static_cast(layout()->takeAt(1)); - while (!form->isEmpty()) { - QLayoutItem* item = form->takeAt(0); - if (item->widget()) { - delete item->widget(); - } - delete item; - } - 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) { - _object.reset(); - emit objectChanged(_object); - return; - } - QObject* newObject = metaObject->newInstance(); - - QFormLayout* form = new QFormLayout(); - 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 - property.write(newObject, property.read(oldObject)); - } - QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(property.userType(), NULL); - if (widget) { - widget->setProperty("propertyIndex", i); - form->addRow(QByteArray(property.name()) + ':', widget); - QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); - const QMetaObject* widgetMetaObject = widget->metaObject(); - QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); - widgetProperty.write(widget, property.read(newObject)); - 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())); - } - } - } - emit objectChanged(_object = static_cast(newObject)); -} - -void SharedObjectEditor::propertyChanged() { - 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 != sender()) { - continue; - } - detachObject(); - QObject* object = _object.data(); - QMetaProperty property = object->metaObject()->property(widget->property("propertyIndex").toInt()); - QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); - property.write(object, widget->property(valuePropertyName)); - } - emit objectChanged(_object); -} - -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 deleted file mode 100644 index cd46ae9658..0000000000 --- a/libraries/metavoxels/src/SharedObject.h +++ /dev/null @@ -1,268 +0,0 @@ -// -// SharedObject.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 2/5/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_SharedObject_h -#define hifi_SharedObject_h - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class QComboBox; - -class Bitstream; -class SharedObject; - -typedef QHash > WeakSharedObjectHash; - -/// A QObject that may be shared over the network. -class SharedObject : public QObject { - Q_OBJECT - -public: - - /// Returns the weak hash under which all local shared objects are registered. - static const WeakSharedObjectHash& getWeakHash() { return _weakHash; } - - /// Returns a reference to the weak hash lock. - static QReadWriteLock& getWeakHashLock() { return _weakHashLock; } - - Q_INVOKABLE SharedObject(); - - /// Returns the unique local ID for this object. - int getID() const { return _id; } - - void setID(int id); - - /// Returns the local origin ID for this object. - int getOriginID() const { return _originID; } - - void setOriginID(int originID) { _originID = originID; } - - /// Returns the unique remote ID for this object, or zero if this is a local object. - int getRemoteID() const { return _remoteID; } - - void setRemoteID(int remoteID) { _remoteID = remoteID; } - - /// Returns the remote origin ID for this object, or zero if this is a local object. - int getRemoteOriginID() const { return _remoteOriginID; } - - void setRemoteOriginID(int remoteOriginID) { _remoteOriginID = remoteOriginID; } - - int getReferenceCount() const { return _referenceCount.load(); } - void incrementReferenceCount(); - void decrementReferenceCount(); - - /// Creates a new clone of this object. - /// \param withID if true, give the clone the same origin ID as this object - /// \target if non-NULL, a target object to populate (as opposed to creating a new instance of this object's class) - virtual SharedObject* clone(bool withID = false, SharedObject* target = NULL) const; - - /// Tests this object for equality with another. - /// \param sharedAncestry if true and the classes of the objects differ, compare their shared ancestry (assuming that - /// this is an instance of a superclass of the other object's class) rather than simply returning false. - virtual bool equals(const SharedObject* other, bool sharedAncestry = false) const; - - /// Dumps the contents of this object to the debug output. - virtual void dump(QDebug debug = QDebug(QtDebugMsg)) const; - - /// Writes the non-property contents of this object to the specified stream. - virtual void writeExtra(Bitstream& out) const; - - /// Reads the non-property contents of this object from the specified stream. - /// \param reread if true, reread the contents from the stream but don't reapply them - virtual void readExtra(Bitstream& in, bool reread = false); - - /// Writes the delta-encoded non-property contents of this object to the specified stream. - virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; - - /// Reads the delta-encoded non-property contents of this object from the specified stream. - /// \param reread if true, reread the contents from the stream but don't reapply them - virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread = false); - - /// Writes the subdivision of the contents of this object (preceeded by a - /// reference to the object itself) to the specified stream if necessary. - virtual void maybeWriteSubdivision(Bitstream& out); - - /// Reads the subdivision of this object from the specified stream. - /// \return the modified object, or this if no modification was performed - virtual SharedObject* readSubdivision(Bitstream& in); - -private: - - int _id; - int _originID; - int _remoteID; - int _remoteOriginID; - QAtomicInt _referenceCount; - - static QAtomicInt _nextID; - static WeakSharedObjectHash _weakHash; - static QReadWriteLock _weakHashLock; -}; - -/// Removes the null references from the supplied hash. -void pruneWeakSharedObjectHash(WeakSharedObjectHash& hash); - -/// A pointer to a shared object. -template class SharedObjectPointerTemplate { -public: - - SharedObjectPointerTemplate(T* data = NULL); - SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other); - ~SharedObjectPointerTemplate(); - - T* data() const { return _data; } - - /// "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); } - - void reset(); - - bool operator!() const { return !_data; } - operator T*() const { return _data; } - T& operator*() const { return *_data; } - T* operator->() const { return _data; } - - template SharedObjectPointerTemplate staticCast() const; - - 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; -}; - -template inline SharedObjectPointerTemplate::SharedObjectPointerTemplate(T* data) : _data(data) { - if (_data) { - _data->incrementReferenceCount(); - } -} - -template inline SharedObjectPointerTemplate::SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other) : - _data(other._data) { - - if (_data) { - _data->incrementReferenceCount(); - } -} - -template inline SharedObjectPointerTemplate::~SharedObjectPointerTemplate() { - if (_data) { - _data->decrementReferenceCount(); - } -} - -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() { - if (_data) { - _data->decrementReferenceCount(); - } - _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(); - } - 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); -} - -template bool equals(const SharedObjectPointerTemplate& first, - const SharedObjectPointerTemplate& second) { - return first ? first->equals(second) : !second; -} - -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 - Q_PROPERTY(SharedObjectPointer object READ getObject WRITE setObject NOTIFY objectChanged USER true) - -public: - - SharedObjectEditor(const QMetaObject* metaObject, bool nullable = true, QWidget* parent = NULL); - - const SharedObjectPointer& getObject() const { return _object; } - - /// "Detaches" the object pointer, copying it if anyone else is holding a reference. - void detachObject(); - -signals: - - void objectChanged(const SharedObjectPointer& object); - -public slots: - - void setObject(const SharedObjectPointer& object); - -private slots: - - void updateType(); - void propertyChanged(); - void updateProperty(); - -private: - - QComboBox* _type; - SharedObjectPointer _object; -}; - -#endif // hifi_SharedObject_h diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp deleted file mode 100644 index d2dff016e3..0000000000 --- a/libraries/metavoxels/src/Spanner.cpp +++ /dev/null @@ -1,4058 +0,0 @@ -// -// Spanner.cpp -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 11/10/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "MetavoxelData.h" -#include "Spanner.h" - -using namespace std; - -REGISTER_META_OBJECT(Spanner) -REGISTER_META_OBJECT(Heightfield) -REGISTER_META_OBJECT(Sphere) -REGISTER_META_OBJECT(Cuboid) -REGISTER_META_OBJECT(StaticModel) -REGISTER_META_OBJECT(MaterialObject) - -static int heightfieldHeightTypeId = registerSimpleMetaType(); -static int heightfieldColorTypeId = registerSimpleMetaType(); -static int heightfieldMaterialTypeId = registerSimpleMetaType(); - -static QItemEditorCreatorBase* createHeightfieldHeightEditorCreator() { - QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); - getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); - return creator; -} - -static QItemEditorCreatorBase* createHeightfieldColorEditorCreator() { - QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); - getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); - return creator; -} - -static QItemEditorCreatorBase* heightfieldHeightEditorCreator = createHeightfieldHeightEditorCreator(); -static QItemEditorCreatorBase* heightfieldColorEditorCreator = createHeightfieldColorEditorCreator(); - -const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f; -const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f); - -Setting::Handle heightfieldDir("heightDir"); - -Spanner::Spanner() : - _renderer(NULL), - _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), - _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), - _merged(false), - _willBeVoxelized(false) { -} - -void Spanner::setBounds(const Box& bounds) { - if (_bounds == bounds) { - return; - } - emit boundsWillChange(); - emit boundsChanged(_bounds = bounds); -} - -bool Spanner::testAndSetVisited(int visit) { - QMutexLocker locker(&_lastVisitsMutex); - int& lastVisit = _lastVisits[QThread::currentThread()]; - if (lastVisit == visit) { - return false; - } - lastVisit = 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()); - connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater); - _renderer->init(this); - } - return _renderer; -} - -bool Spanner::isHeightfield() const { - return false; -} - -float Spanner::getHeight(const glm::vec3& location) const { - return -FLT_MAX; -} - -bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - return _bounds.findRayIntersection(origin, direction, distance); -} - -Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase, float granularity) { - return this; -} - -Spanner* Spanner::fillHeight(const glm::vec3& position, float radius, float granularity) { - return this; -} - -Spanner* Spanner::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint, bool voxelize, float granularity) { - return this; -} - -bool Spanner::hasOwnColors() const { - return false; -} - -bool Spanner::hasOwnMaterials() const { - return false; -} - -QRgb Spanner::getColorAt(const glm::vec3& point) { - return 0; -} - -int Spanner::getMaterialAt(const glm::vec3& point) { - return 0; -} - -QVector& Spanner::getMaterials() { - static QVector emptyMaterials; - return emptyMaterials; -} - -bool Spanner::contains(const glm::vec3& point) { - return false; -} - -bool Spanner::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - return false; -} - -QByteArray Spanner::getRendererClassName() const { - return "SpannerRendererer"; -} - -QAtomicInt Spanner::_nextVisit(1); - -SpannerRenderer::SpannerRenderer() { -} - -void SpannerRenderer::init(Spanner* spanner) { - _spanner = spanner; -} - -void SpannerRenderer::simulate(float deltaTime) { - // nothing by default -} - -void SpannerRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { - // nothing by default -} - -bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - return false; -} - -Transformable::Transformable() : _scale(1.0f) { -} - -void Transformable::setTranslation(const glm::vec3& translation) { - if (_translation != translation) { - emit translationChanged(_translation = translation); - } -} - -void Transformable::setRotation(const glm::quat& rotation) { - if (_rotation != rotation) { - emit rotationChanged(_rotation = rotation); - } -} - -void Transformable::setScale(float scale) { - if (_scale != scale) { - emit scaleChanged(_scale = scale); - } -} - -ColorTransformable::ColorTransformable() : - _color(Qt::white) { -} - -void ColorTransformable::setColor(const QColor& color) { - if (_color != color) { - emit colorChanged(_color = color); - } -} - -Sphere::Sphere() { - connect(this, SIGNAL(translationChanged(const glm::vec3&)), SLOT(updateBounds())); - connect(this, SIGNAL(scaleChanged(float)), SLOT(updateBounds())); - updateBounds(); -} - -bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance); -} - -bool Sphere::contains(const glm::vec3& point) { - return glm::distance(point, getTranslation()) <= getScale(); -} - -bool Sphere::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - glm::vec3 relativeStart = start - getTranslation(); - glm::vec3 vector = end - start; - float a = glm::dot(vector, vector); - if (a == 0.0f) { - return false; - } - float b = glm::dot(relativeStart, vector); - float radicand = b * b - a * (glm::dot(relativeStart, relativeStart) - getScale() * getScale()); - if (radicand < 0.0f) { - return false; - } - float radical = glm::sqrt(radicand); - float first = (-b - radical) / a; - if (first >= 0.0f && first <= 1.0f) { - distance = first; - normal = glm::normalize(relativeStart + vector * distance); - return true; - } - float second = (-b + radical) / a; - if (second >= 0.0f && second <= 1.0f) { - distance = second; - normal = glm::normalize(relativeStart + vector * distance); - return true; - } - return false; -} - -QByteArray Sphere::getRendererClassName() const { - return "SphereRenderer"; -} - -void Sphere::updateBounds() { - glm::vec3 extent(getScale(), getScale(), getScale()); - setBounds(Box(getTranslation() - extent, getTranslation() + extent)); -} - -Cuboid::Cuboid() : - _aspectY(1.0f), - _aspectZ(1.0f) { - - connect(this, &Cuboid::translationChanged, this, &Cuboid::updateBoundsAndPlanes); - connect(this, &Cuboid::rotationChanged, this, &Cuboid::updateBoundsAndPlanes); - connect(this, &Cuboid::scaleChanged, this, &Cuboid::updateBoundsAndPlanes); - connect(this, &Cuboid::aspectYChanged, this, &Cuboid::updateBoundsAndPlanes); - connect(this, &Cuboid::aspectZChanged, this, &Cuboid::updateBoundsAndPlanes); - updateBoundsAndPlanes(); -} - -void Cuboid::setAspectY(float aspectY) { - if (_aspectY != aspectY) { - emit aspectYChanged(_aspectY = aspectY); - } -} - -void Cuboid::setAspectZ(float aspectZ) { - if (_aspectZ != aspectZ) { - emit aspectZChanged(_aspectZ = aspectZ); - } -} - -bool Cuboid::contains(const glm::vec3& point) { - glm::vec4 point4(point, 1.0f); - for (int i = 0; i < PLANE_COUNT; i++) { - if (glm::dot(_planes[i], point4) > 0.0f) { - return false; - } - } - return true; -} - -bool Cuboid::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - glm::vec4 start4(start, 1.0f); - glm::vec4 vector = glm::vec4(end - start, 0.0f); - for (int i = 0; i < PLANE_COUNT; i++) { - // first check the segment against the plane - float divisor = glm::dot(_planes[i], vector); - if (glm::abs(divisor) < EPSILON) { - continue; - } - float t = -glm::dot(_planes[i], start4) / divisor; - if (t < 0.0f || t > 1.0f) { - continue; - } - // now that we've established that it intersects the plane, check against the other sides - glm::vec4 point = start4 + vector * t; - const int PLANES_PER_AXIS = 2; - int indexOffset = ((i / PLANES_PER_AXIS) + 1) * PLANES_PER_AXIS; - for (int j = 0; j < PLANE_COUNT - PLANES_PER_AXIS; j++) { - if (glm::dot(_planes[(indexOffset + j) % PLANE_COUNT], point) > 0.0f) { - goto outerContinue; - } - } - distance = t; - normal = glm::vec3(_planes[i]); - return true; - - outerContinue: ; - } - return false; -} - -QByteArray Cuboid::getRendererClassName() const { - return "CuboidRenderer"; -} - -void Cuboid::updateBoundsAndPlanes() { - glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ); - glm::mat4 rotationMatrix = glm::mat4_cast(getRotation()); - setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(-extent, extent)); - - glm::vec4 translation4 = glm::vec4(getTranslation(), 1.0f); - _planes[0] = glm::vec4(glm::vec3(rotationMatrix[0]), -glm::dot(rotationMatrix[0], translation4) - getScale()); - _planes[1] = glm::vec4(glm::vec3(-rotationMatrix[0]), glm::dot(rotationMatrix[0], translation4) - getScale()); - _planes[2] = glm::vec4(glm::vec3(rotationMatrix[1]), -glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY); - _planes[3] = glm::vec4(glm::vec3(-rotationMatrix[1]), glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY); - _planes[4] = glm::vec4(glm::vec3(rotationMatrix[2]), -glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ); - _planes[5] = glm::vec4(glm::vec3(-rotationMatrix[2]), glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ); -} - -StaticModel::StaticModel() { -} - -void StaticModel::setURL(const QUrl& url) { - if (_url != url) { - emit urlChanged(_url = url); - } -} - -bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - // delegate to renderer, if we have one - return _renderer ? _renderer->findRayIntersection(origin, direction, distance) : - Spanner::findRayIntersection(origin, direction, distance); -} - -QByteArray StaticModel::getRendererClassName() const { - return "StaticModelRenderer"; -} - -DataBlock::~DataBlock() { -} - -const int HeightfieldData::SHARED_EDGE = 1; - -HeightfieldData::HeightfieldData(int width) : - _width(width) { -} - -const int HEIGHTFIELD_DATA_HEADER_SIZE = sizeof(qint32) * 4; - -static QByteArray encodeHeightfieldHeight(int offsetX, int offsetY, int width, int height, const QVector& contents) { - QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); - qint32* header = (qint32*)inflated.data(); - *header++ = offsetX; - *header++ = offsetY; - *header++ = width; - *header++ = height; - if (!contents.isEmpty()) { - // encode with Paeth filter (see http://en.wikipedia.org/wiki/Portable_Network_Graphics#Filtering) - QVector filteredContents(contents.size()); - const quint16* src = contents.constData(); - quint16* dest = filteredContents.data(); - *dest++ = *src++; - for (quint16* end = dest + width - 1; dest != end; dest++, src++) { - *dest = *src - src[-1]; - } - for (int y = 1; y < height; y++) { - *dest++ = *src - src[-width]; - src++; - for (quint16* end = dest + width - 1; dest != end; dest++, src++) { - int a = src[-1]; - int b = src[-width]; - int c = src[-width - 1]; - int p = a + b - c; - int ad = abs(a - p); - int bd = abs(b - p); - int cd = abs(c - p); - *dest = *src - (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); - } - } - inflated.append((const char*)filteredContents.constData(), filteredContents.size() * sizeof(quint16)); - } - return qCompress(inflated); -} - -static QVector decodeHeightfieldHeight(const QByteArray& encoded, int& offsetX, int& offsetY, - int& width, int& height) { - QByteArray inflated = qUncompress(encoded); - const qint32* header = (const qint32*)inflated.constData(); - offsetX = *header++; - offsetY = *header++; - width = *header++; - height = *header++; - int payloadSize = inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE; - QVector unfiltered(payloadSize / sizeof(quint16)); - if (!unfiltered.isEmpty()) { - quint16* dest = unfiltered.data(); - const quint16* src = (const quint16*)(inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE); - *dest++ = *src++; - for (quint16* end = dest + width - 1; dest != end; dest++, src++) { - *dest = *src + dest[-1]; - } - for (int y = 1; y < height; y++) { - *dest = (*src++) + dest[-width]; - dest++; - for (quint16* end = dest + width - 1; dest != end; dest++, src++) { - int a = dest[-1]; - int b = dest[-width]; - int c = dest[-width - 1]; - int p = a + b - c; - int ad = abs(a - p); - int bd = abs(b - p); - int cd = abs(c - p); - *dest = *src + (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); - } - } - } - return unfiltered; -} - -const int HeightfieldHeight::HEIGHT_BORDER = 1; -const int HeightfieldHeight::HEIGHT_EXTENSION = SHARED_EDGE + 2 * HEIGHT_BORDER; - -HeightfieldHeight::HeightfieldHeight(int width, const QVector& contents) : - HeightfieldData(width), - _contents(contents) { -} - -HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(DataBlockPointer(this)); - _width = reference->getWidth(); - _contents = reference->getContents(); - - int offsetX, offsetY, width, height; - QVector delta = decodeHeightfieldHeight(reference->getEncodedDelta(), offsetX, offsetY, width, height); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - _width = width; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - const quint16* src = delta.constData(); - quint16* dest = _contents.data() + minY * _width + minX; - for (int y = 0; y < height; y++, src += width, dest += _width) { - memcpy(dest, src, width * sizeof(quint16)); - } -} - -void HeightfieldHeight::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - _encoded = encodeHeightfieldHeight(0, 0, _width, _contents.size() / _width, _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); -} - -void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference) { - if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - int height = _contents.size() / _width; - int minX = _width, minY = height; - int maxX = -1, maxY = -1; - const quint16* src = _contents.constData(); - const quint16* ref = reference->getContents().constData(); - for (int y = 0; y < height; y++) { - bool difference = false; - for (int x = 0; x < _width; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - QVector delta; - int deltaWidth = 0, deltaHeight = 0; - if (maxX >= minX) { - deltaWidth = maxX - minX + 1; - deltaHeight = maxY - minY + 1; - delta = QVector(deltaWidth * deltaHeight); - quint16* dest = delta.data(); - src = _contents.constData() + minY * _width + minX; - for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { - memcpy(dest, src, deltaWidth * sizeof(quint16)); - } - } - reference->setEncodedDelta(encodeHeightfieldHeight(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); -} - -void HeightfieldHeight::read(Bitstream& in, int bytes) { - int offsetX, offsetY, height; - _contents = decodeHeightfieldHeight(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); -} - -Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value) { - if (value) { - value->write(out); - } else { - out << 0; - } - return out; -} - -Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value) { - int size; - in >> size; - if (size == 0) { - value = HeightfieldHeightPointer(); - } else { - value = new HeightfieldHeight(in, size); - } - return in; -} - -template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { - if (value) { - value->writeDelta(*this, reference); - } else { - *this << 0; - } -} - -template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { - int size; - *this >> size; - if (size == 0) { - value = HeightfieldHeightPointer(); - } else { - value = new HeightfieldHeight(*this, size, reference); - } -} - -HeightfieldHeightEditor::HeightfieldHeightEditor(QWidget* parent) : - QWidget(parent) { - - QHBoxLayout* layout = new QHBoxLayout(); - setLayout(layout); - - layout->addWidget(_select = new QPushButton("Select")); - connect(_select, &QPushButton::clicked, this, &HeightfieldHeightEditor::select); - - layout->addWidget(_clear = new QPushButton("Clear")); - connect(_clear, &QPushButton::clicked, this, &HeightfieldHeightEditor::clear); - _clear->setEnabled(false); -} - -void HeightfieldHeightEditor::setHeight(const HeightfieldHeightPointer& height) { - if ((_height = height)) { - _clear->setEnabled(true); - } else { - _clear->setEnabled(false); - } -} - -static int getHeightfieldSize(int size) { - return (int)glm::pow(2.0f, glm::round(glm::log((float)size - HeightfieldData::SHARED_EDGE) / - glm::log(2.0f))) + HeightfieldData::SHARED_EDGE; -} - -void HeightfieldHeightEditor::select() { - QString result = QFileDialog::getOpenFileName(this, "Select Height Image", - heightfieldDir.get(), - "Images (*.png *.jpg *.bmp *.raw *.mdr)"); - if (result.isNull()) { - return; - } - heightfieldDir.set(QFileInfo(result).path()); - const quint16 CONVERSION_OFFSET = 1; - QString lowerResult = result.toLower(); - bool isMDR = lowerResult.endsWith(".mdr"); - if (lowerResult.endsWith(".raw") || isMDR) { - QFile input(result); - input.open(QIODevice::ReadOnly); - QDataStream in(&input); - in.setByteOrder(QDataStream::LittleEndian); - if (isMDR) { - const int MDR_HEADER_SIZE = 1024; - input.seek(MDR_HEADER_SIZE); - } - int available = input.bytesAvailable() / sizeof(quint16); - QVector rawContents(available); - for (quint16* height = rawContents.data(), *end = height + available; height != end; height++) { - in >> *height; - } - if (rawContents.isEmpty()) { - QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); - return; - } - int rawSize = glm::sqrt((float)rawContents.size()); - int size = getHeightfieldSize(rawSize) + 2 * HeightfieldHeight::HEIGHT_BORDER; - QVector contents(size * size); - quint16* dest = contents.data() + (size + 1) * HeightfieldHeight::HEIGHT_BORDER; - const quint16* src = rawContents.constData(); - const float CONVERSION_SCALE = 65534.0f / numeric_limits::max(); - for (int i = 0; i < rawSize; i++, dest += size) { - for (quint16* lineDest = dest, *end = dest + rawSize; lineDest != end; lineDest++, src++) { - *lineDest = (*src == 0) ? 0 : (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET; - } - } - emit heightChanged(_height = new HeightfieldHeight(size, contents)); - _clear->setEnabled(true); - return; - } - QImage image; - if (!image.load(result)) { - QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); - return; - } - image = image.convertToFormat(QImage::Format_ARGB32); - int width = getHeightfieldSize(image.width()) + 2 * HeightfieldHeight::HEIGHT_BORDER; - int height = getHeightfieldSize(image.height()) + 2 * HeightfieldHeight::HEIGHT_BORDER; - QVector contents(width * height); - quint16* dest = contents.data() + (width + 1) * HeightfieldHeight::HEIGHT_BORDER; - const float CONVERSION_SCALE = 65534.0f / numeric_limits::max(); - for (int i = 0; i < image.height(); i++, dest += width) { - const QRgb* src = (const QRgb*)image.constScanLine(i); - for (quint16* lineDest = dest, *end = dest + image.width(); lineDest != end; lineDest++, src++) { - *lineDest = (qAlpha(*src) < numeric_limits::max()) ? 0 : - (quint16)(qRed(*src) * CONVERSION_SCALE) + CONVERSION_OFFSET; - } - } - emit heightChanged(_height = new HeightfieldHeight(width, contents)); - _clear->setEnabled(true); -} - -void HeightfieldHeightEditor::clear() { - emit heightChanged(_height = HeightfieldHeightPointer()); - _clear->setEnabled(false); -} - -static QByteArray encodeHeightfieldColor(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); - qint32* header = (qint32*)inflated.data(); - *header++ = offsetX; - *header++ = offsetY; - *header++ = width; - *header++ = height; - if (!contents.isEmpty()) { - QBuffer buffer(&inflated); - buffer.open(QIODevice::WriteOnly | QIODevice::Append); - QImage((const uchar*)contents.constData(), width, height, width * DataBlock::COLOR_BYTES, - QImage::Format_RGB888).save(&buffer, "JPG"); - } - return qCompress(inflated); -} - -static QByteArray decodeHeightfieldColor(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { - QByteArray inflated = qUncompress(encoded); - const qint32* header = (const qint32*)inflated.constData(); - offsetX = *header++; - offsetY = *header++; - width = *header++; - height = *header++; - int payloadSize = inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE; - if (payloadSize == 0) { - return QByteArray(); - } - QImage image = QImage::fromData((const uchar*)inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE, payloadSize, "JPG"); - if (image.format() != QImage::Format_RGB888) { - image = image.convertToFormat(QImage::Format_RGB888); - } - QByteArray contents(width * height * DataBlock::COLOR_BYTES, 0); - char* dest = contents.data(); - int stride = width * DataBlock::COLOR_BYTES; - for (int y = 0; y < height; y++, dest += stride) { - memcpy(dest, image.constScanLine(y), stride); - } - return contents; -} - -HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) : - HeightfieldData(width), - _contents(contents) { -} - -HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(DataBlockPointer(this)); - _width = reference->getWidth(); - _contents = reference->getContents(); - - int offsetX, offsetY, width, height; - QByteArray delta = decodeHeightfieldColor(reference->getEncodedDelta(), offsetX, offsetY, width, height); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - _width = width; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - const char* src = delta.constData(); - char* dest = _contents.data() + (minY * _width + minX) * DataBlock::COLOR_BYTES; - for (int y = 0; y < height; y++, src += width * DataBlock::COLOR_BYTES, dest += _width * DataBlock::COLOR_BYTES) { - memcpy(dest, src, width * DataBlock::COLOR_BYTES); - } -} - -void HeightfieldColor::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - _encoded = encodeHeightfieldColor(0, 0, _width, _contents.size() / (_width * DataBlock::COLOR_BYTES), _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); -} - -void HeightfieldColor::writeDelta(Bitstream& out, const HeightfieldColorPointer& reference) { - if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - int height = _contents.size() / (_width * DataBlock::COLOR_BYTES); - int minX = _width, minY = height; - int maxX = -1, maxY = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int y = 0; y < height; y++) { - bool difference = false; - for (int x = 0; x < _width; x++, src += DataBlock::COLOR_BYTES, ref += DataBlock::COLOR_BYTES) { - if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - QByteArray delta; - int deltaWidth = 0, deltaHeight = 0; - if (maxX >= minX) { - deltaWidth = maxX - minX + 1; - deltaHeight = maxY - minY + 1; - delta = QByteArray(deltaWidth * deltaHeight * DataBlock::COLOR_BYTES, 0); - char* dest = delta.data(); - src = _contents.constData() + (minY * _width + minX) * DataBlock::COLOR_BYTES; - for (int y = 0; y < deltaHeight; y++, src += _width * DataBlock::COLOR_BYTES, - dest += deltaWidth * DataBlock::COLOR_BYTES) { - memcpy(dest, src, deltaWidth * DataBlock::COLOR_BYTES); - } - } - reference->setEncodedDelta(encodeHeightfieldColor(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); -} - -void HeightfieldColor::read(Bitstream& in, int bytes) { - int offsetX, offsetY, height; - _contents = decodeHeightfieldColor(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); -} - -Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value) { - if (value) { - value->write(out); - } else { - out << 0; - } - return out; -} - -Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value) { - int size; - in >> size; - if (size == 0) { - value = HeightfieldColorPointer(); - } else { - value = new HeightfieldColor(in, size); - } - return in; -} - -template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { - if (value) { - value->writeDelta(*this, reference); - } else { - *this << 0; - } -} - -template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { - int size; - *this >> size; - if (size == 0) { - value = HeightfieldColorPointer(); - } else { - value = new HeightfieldColor(*this, size, reference); - } -} - -HeightfieldColorEditor::HeightfieldColorEditor(QWidget* parent) : - QWidget(parent) { - - QHBoxLayout* layout = new QHBoxLayout(); - setLayout(layout); - - layout->addWidget(_select = new QPushButton("Select")); - connect(_select, &QPushButton::clicked, this, &HeightfieldColorEditor::select); - - layout->addWidget(_clear = new QPushButton("Clear")); - connect(_clear, &QPushButton::clicked, this, &HeightfieldColorEditor::clear); - _clear->setEnabled(false); -} - -void HeightfieldColorEditor::setColor(const HeightfieldColorPointer& color) { - if ((_color = color)) { - _clear->setEnabled(true); - } else { - _clear->setEnabled(false); - } -} - -void HeightfieldColorEditor::select() { - QString result = QFileDialog::getOpenFileName(this, "Select Color Image", - heightfieldDir.get(), - "Images (*.png *.jpg *.bmp)"); - if (result.isNull()) { - return; - } - heightfieldDir.get(QFileInfo(result).path()); - QImage image; - if (!image.load(result)) { - QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); - return; - } - image = image.convertToFormat(QImage::Format_RGB888); - int width = getHeightfieldSize(image.width()); - int height = getHeightfieldSize(image.height()); - QByteArray contents(width * height * DataBlock::COLOR_BYTES, 0); - char* dest = contents.data(); - for (int i = 0; i < image.height(); i++, dest += width * DataBlock::COLOR_BYTES) { - memcpy(dest, image.constScanLine(i), image.width() * DataBlock::COLOR_BYTES); - } - emit colorChanged(_color = new HeightfieldColor(width, contents)); - _clear->setEnabled(true); -} - -void HeightfieldColorEditor::clear() { - emit colorChanged(_color = HeightfieldColorPointer()); - _clear->setEnabled(false); -} - -static QByteArray encodeHeightfieldMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); - qint32* header = (qint32*)inflated.data(); - *header++ = offsetX; - *header++ = offsetY; - *header++ = width; - *header++ = height; - inflated.append(contents); - return qCompress(inflated); -} - -static QByteArray decodeHeightfieldMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { - QByteArray inflated = qUncompress(encoded); - const qint32* header = (const qint32*)inflated.constData(); - offsetX = *header++; - offsetY = *header++; - width = *header++; - height = *header++; - return inflated.mid(HEIGHTFIELD_DATA_HEADER_SIZE); -} - -HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents, - const QVector& materials) : - HeightfieldData(width), - _contents(contents), - _materials(materials) { -} - -HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - in.readDelta(_materials, reference->getMaterials()); - reference->setDeltaData(DataBlockPointer(this)); - _width = reference->getWidth(); - _contents = reference->getContents(); - - int offsetX, offsetY, width, height; - QByteArray delta = decodeHeightfieldMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - _width = width; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - const char* src = delta.constData(); - char* dest = _contents.data() + minY * _width + minX; - for (int y = 0; y < height; y++, src += width, dest += _width) { - memcpy(dest, src, width); - } -} - -void HeightfieldMaterial::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - _encoded = encodeHeightfieldMaterial(0, 0, _width, _contents.size() / _width, _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); - out << _materials; -} - -void HeightfieldMaterial::writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference) { - if (!reference) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - if (reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { - reference->setEncodedDelta(encodeHeightfieldMaterial(0, 0, _width, _contents.size() / _width, _contents)); - - } else { - int height = _contents.size() / _width; - int minX = _width, minY = height; - int maxX = -1, maxY = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int y = 0; y < height; y++) { - bool difference = false; - for (int x = 0; x < _width; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - QByteArray delta; - int deltaWidth = 0, deltaHeight = 0; - if (maxX >= minX) { - deltaWidth = maxX - minX + 1; - deltaHeight = maxY - minY + 1; - delta = QByteArray(deltaWidth * deltaHeight, 0); - char* dest = delta.data(); - src = _contents.constData() + minY * _width + minX; - for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { - memcpy(dest, src, deltaWidth); - } - } - reference->setEncodedDelta(encodeHeightfieldMaterial(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); - } - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); - out.writeDelta(_materials, reference->getMaterials()); -} - -void HeightfieldMaterial::read(Bitstream& in, int bytes) { - int offsetX, offsetY, height; - _contents = decodeHeightfieldMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); - in >> _materials; -} - -Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value) { - if (value) { - value->write(out); - } else { - out << 0; - } - return out; -} - -Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value) { - int size; - in >> size; - if (size == 0) { - value = HeightfieldMaterialPointer(); - } else { - value = new HeightfieldMaterial(in, size); - } - return in; -} - -template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, - const HeightfieldMaterialPointer& reference) { - if (value) { - value->writeDelta(*this, reference); - } else { - *this << 0; - } -} - -template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference) { - int size; - *this >> size; - if (size == 0) { - value = HeightfieldMaterialPointer(); - } else { - value = new HeightfieldMaterial(*this, size, reference); - } -} - -MaterialObject::MaterialObject() : - _scaleS(1.0f), - _scaleT(1.0f) { -} - -int getMaterialIndex(const SharedObjectPointer& material, QVector& materials) { - if (!(material && static_cast(material.data())->getDiffuse().isValid())) { - return 0; - } - // first look for a matching existing material, noting the first reusable slot - int firstEmptyIndex = -1; - for (int i = 0; i < materials.size(); i++) { - const SharedObjectPointer& existingMaterial = materials.at(i); - if (existingMaterial) { - if (existingMaterial->equals(material.data())) { - return i + 1; - } - } else if (firstEmptyIndex == -1) { - firstEmptyIndex = i; - } - } - // if nothing found, use the first empty slot or append - if (firstEmptyIndex != -1) { - materials[firstEmptyIndex] = material; - return firstEmptyIndex + 1; - } - if (materials.size() < numeric_limits::max()) { - materials.append(material); - return materials.size(); - } - return -1; -} - -static QHash countIndices(const QByteArray& contents) { - QHash counts; - for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { - if (*src != 0) { - counts[*src]++; - } - } - return counts; -} - -static uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, - QByteArray& contents) { - int index = getMaterialIndex(material, materials); - if (index != -1) { - return index; - } - // last resort: find the least-used material and remove it - QHash counts = countIndices(contents); - uchar materialIndex = 0; - int lowestCount = INT_MAX; - for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { - if (it.value() < lowestCount) { - materialIndex = it.key(); - lowestCount = it.value(); - } - } - contents.replace((char)materialIndex, (char)0); - return materialIndex; -} - -static void clearUnusedMaterials(QVector& materials, const QByteArray& contents) { - QHash counts = countIndices(contents); - for (int i = 0; i < materials.size(); i++) { - if (counts.value(i + 1) == 0) { - materials[i] = SharedObjectPointer(); - } - } - while (!(materials.isEmpty() || materials.last())) { - materials.removeLast(); - } -} - -static QHash countIndices(const QVector& contents) { - QHash counts; - foreach (const StackArray& array, contents) { - if (array.isEmpty()) { - continue; - } - for (const StackArray::Entry* entry = array.getEntryData(), *end = entry + array.getEntryCount(); - entry != end; entry++) { - if (entry->material != 0) { - counts[entry->material]++; - } - } - } - return counts; -} - -static uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, - QVector& contents) { - int index = getMaterialIndex(material, materials); - if (index != -1) { - return index; - } - // last resort: find the least-used material and remove it - QHash counts = countIndices(contents); - uchar materialIndex = 0; - int lowestCount = INT_MAX; - for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { - if (it.value() < lowestCount) { - materialIndex = it.key(); - lowestCount = it.value(); - } - } - for (StackArray* array = contents.data(), *end = array + contents.size(); array != end; array++) { - if (array->isEmpty()) { - continue; - } - for (StackArray::Entry* entry = array->getEntryData(), *end = entry + array->getEntryCount(); - entry != end; entry++) { - if (entry->material == materialIndex) { - entry->material = 0; - } - } - } - return materialIndex; -} - -static void clearUnusedMaterials(QVector& materials, const QVector& contents) { - QHash counts = countIndices(contents); - for (int i = 0; i < materials.size(); i++) { - if (counts.value(i + 1) == 0) { - materials[i] = SharedObjectPointer(); - } - } - while (!(materials.isEmpty() || materials.last())) { - materials.removeLast(); - } -} - -static QByteArray encodeHeightfieldStack(int offsetX, int offsetY, int width, int height, - const QVector& contents) { - QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); - qint32* header = (qint32*)inflated.data(); - *header++ = offsetX; - *header++ = offsetY; - *header++ = width; - *header++ = height; - foreach (const StackArray& stack, contents) { - quint16 entries = stack.getEntryCount(); - inflated.append((const char*)&entries, sizeof(quint16)); - inflated.append(stack); - } - return qCompress(inflated); -} - -static QVector decodeHeightfieldStack(const QByteArray& encoded, - int& offsetX, int& offsetY, int& width, int& height) { - QByteArray inflated = qUncompress(encoded); - const qint32* header = (const qint32*)inflated.constData(); - offsetX = *header++; - offsetY = *header++; - width = *header++; - height = *header++; - const char* src = inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; - QVector contents(width * height); - for (StackArray* dest = contents.data(), *end = dest + contents.size(); dest != end; dest++) { - int entries = *(const quint16*)src; - src += sizeof(quint16); - if (entries > 0) { - int bytes = StackArray::getSize(entries); - *dest = StackArray(src, bytes); - src += bytes; - } - } - return contents; -} - -StackArray::Entry::Entry() : - color(0), - material(0), - hermiteX(0), - hermiteY(0), - hermiteZ(0) { -} - -bool StackArray::Entry::isZero() const { - return color == 0 && material == 0 && hermiteX == 0 && hermiteY == 0 && hermiteZ == 0; -} - -bool StackArray::Entry::isMergeable(const Entry& other) const { - return color == other.color && material == other.material && hermiteX == 0 && hermiteY == 0 && hermiteZ == 0; -} - -static inline void setHermite(quint32& value, const glm::vec3& normal, float position) { - value = qRgba(normal.x * numeric_limits::max(), normal.y * numeric_limits::max(), - normal.z * numeric_limits::max(), position * numeric_limits::max()); -} - -static inline float getHermite(QRgb value, glm::vec3& normal) { - normal.x = (char)qRed(value) / (float)numeric_limits::max(); - normal.y = (char)qGreen(value) / (float)numeric_limits::max(); - normal.z = (char)qBlue(value) / (float)numeric_limits::max(); - float length = glm::length(normal); - if (length > 0.0f) { - normal /= length; - } - return qAlpha(value) / (float)numeric_limits::max(); -} - -void StackArray::Entry::setHermiteX(const glm::vec3& normal, float position) { - setHermite(hermiteX, normal, position); -} - -float StackArray::Entry::getHermiteX(glm::vec3& normal) const { - return getHermite(hermiteX, normal); -} - -void StackArray::Entry::setHermiteY(const glm::vec3& normal, float position) { - setHermite(hermiteY, normal, position); -} - -float StackArray::Entry::getHermiteY(glm::vec3& normal) const { - return getHermite(hermiteY, normal); -} - -void StackArray::Entry::setHermiteZ(const glm::vec3& normal, float position) { - setHermite(hermiteZ, normal, position); -} - -float StackArray::Entry::getHermiteZ(glm::vec3& normal) const { - return getHermite(hermiteZ, normal); -} - -int StackArray::getEntryAlpha(int y, float heightfieldHeight) const { - int count = getEntryCount(); - if (count != 0) { - int relative = y - getPosition(); - if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f || y < heightfieldHeight)) { - return qAlpha(getEntryData()[qMax(relative, 0)].color); - } - } - return (heightfieldHeight != 0.0f && y <= heightfieldHeight) ? numeric_limits::max() : 0; -} - -StackArray::Entry& StackArray::getEntry(int y, float heightfieldHeight) { - static Entry emptyEntry; - int count = getEntryCount(); - if (count != 0) { - int relative = y - getPosition(); - if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f || y < heightfieldHeight)) { - return getEntryData()[qMax(relative, 0)]; - } - } - return emptyEntry; -} - -const StackArray::Entry& StackArray::getEntry(int y, float heightfieldHeight) const { - static Entry emptyEntry; - int count = getEntryCount(); - if (count != 0) { - int relative = y - getPosition(); - if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f || y < heightfieldHeight)) { - return getEntryData()[qMax(relative, 0)]; - } - } - return emptyEntry; -} - -void StackArray::getExtents(int& minimumY, int& maximumY) const { - int count = getEntryCount(); - if (count > 0) { - int position = getPosition(); - minimumY = qMin(minimumY, position); - maximumY = qMax(maximumY, position + count - 1); - } -} - -bool StackArray::hasSetEntries() const { - int count = getEntryCount(); - if (count > 0) { - for (const Entry* entry = getEntryData(), *end = entry + count; entry != end; entry++) { - if (entry->isSet()) { - return true; - } - } - } - return false; -} - -HeightfieldStack::HeightfieldStack(int width, const QVector& contents, - const QVector& materials) : - HeightfieldData(width), - _contents(contents), - _materials(materials) { -} - -HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - in.readDelta(_materials, reference->getMaterials()); - reference->setDeltaData(DataBlockPointer(this)); - _width = reference->getWidth(); - _contents = reference->getContents(); - - int offsetX, offsetY, width, height; - QVector delta = decodeHeightfieldStack(reference->getEncodedDelta(), offsetX, offsetY, width, height); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - _width = width; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - const StackArray* src = delta.constData(); - StackArray* dest = _contents.data() + minY * _width + minX; - for (int y = 0; y < height; y++, src += width, dest += _width) { - const StackArray* lineSrc = src; - for (StackArray* lineDest = dest, *end = dest + width; lineDest != end; lineDest++, lineSrc++) { - *lineDest = *lineSrc; - } - } -} - -void HeightfieldStack::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - _encoded = encodeHeightfieldStack(0, 0, _width, _contents.size() / _width, _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); - out << _materials; -} - -void HeightfieldStack::writeDelta(Bitstream& out, const HeightfieldStackPointer& reference) { - if (!reference) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - if (reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { - reference->setEncodedDelta(encodeHeightfieldStack(0, 0, _width, _contents.size() / _width, _contents)); - - } else { - int height = _contents.size() / _width; - int minX = _width, minY = height; - int maxX = -1, maxY = -1; - const StackArray* src = _contents.constData(); - const StackArray* ref = reference->getContents().constData(); - for (int y = 0; y < height; y++) { - bool difference = false; - for (int x = 0; x < _width; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - QVector delta; - int deltaWidth = 0, deltaHeight = 0; - if (maxX >= minX) { - deltaWidth = maxX - minX + 1; - deltaHeight = maxY - minY + 1; - delta = QVector(deltaWidth * deltaHeight); - StackArray* dest = delta.data(); - src = _contents.constData() + minY * _width + minX; - for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { - const StackArray* lineSrc = src; - for (StackArray* lineDest = dest, *end = dest + deltaWidth; lineDest != end; lineDest++, lineSrc++) { - *lineDest = *lineSrc; - } - } - } - reference->setEncodedDelta(encodeHeightfieldStack(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); - } - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); - out.writeDelta(_materials, reference->getMaterials()); -} - -void HeightfieldStack::read(Bitstream& in, int bytes) { - int offsetX, offsetY, height; - _contents = decodeHeightfieldStack(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); - in >> _materials; -} - -Bitstream& operator<<(Bitstream& out, const HeightfieldStackPointer& value) { - if (value) { - value->write(out); - } else { - out << 0; - } - return out; -} - -Bitstream& operator>>(Bitstream& in, HeightfieldStackPointer& value) { - int size; - in >> size; - if (size == 0) { - value = HeightfieldStackPointer(); - } else { - value = new HeightfieldStack(in, size); - } - return in; -} - -template<> void Bitstream::writeRawDelta(const HeightfieldStackPointer& value, const HeightfieldStackPointer& reference) { - if (value) { - value->writeDelta(*this, reference); - } else { - *this << 0; - } -} - -template<> void Bitstream::readRawDelta(HeightfieldStackPointer& value, const HeightfieldStackPointer& reference) { - int size; - *this >> size; - if (size == 0) { - value = HeightfieldStackPointer(); - } else { - value = new HeightfieldStack(*this, size, reference); - } -} - -bool HeightfieldStreamState::shouldSubdivide() const { - return base.lod.shouldSubdivide(minimum, size); -} - -bool HeightfieldStreamState::shouldSubdivideReference() const { - return base.referenceLOD.shouldSubdivide(minimum, size); -} - -bool HeightfieldStreamState::becameSubdivided() const { - return base.lod.becameSubdivided(minimum, size, base.referenceLOD); -} - -bool HeightfieldStreamState::becameSubdividedOrCollapsed() const { - return base.lod.becameSubdividedOrCollapsed(minimum, size, base.referenceLOD); -} - -const int X_MAXIMUM_FLAG = 1; -const int Y_MAXIMUM_FLAG = 2; - -static glm::vec2 getNextMinimum(const glm::vec2& minimum, float nextSize, int index) { - return minimum + glm::vec2( - (index & X_MAXIMUM_FLAG) ? nextSize : 0.0f, - (index & Y_MAXIMUM_FLAG) ? nextSize : 0.0f); -} - -void HeightfieldStreamState::setMinimum(const glm::vec2& lastMinimum, int index) { - minimum = getNextMinimum(lastMinimum, size, index); -} - -HeightfieldNode::HeightfieldNode(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, - const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack) : - _height(height), - _color(color), - _material(material), - _stack(stack), - _renderer(NULL) { -} - -HeightfieldNode::HeightfieldNode(const HeightfieldNode& other) : - _height(other.getHeight()), - _color(other.getColor()), - _material(other.getMaterial()), - _stack(other.getStack()), - _renderer(NULL) { - - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i] = other.getChild(i); - } -} - -HeightfieldNode::~HeightfieldNode() { - delete _renderer; -} - -const int HEIGHT_LEAF_SIZE = 256 + HeightfieldHeight::HEIGHT_EXTENSION; - -void HeightfieldNode::setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, - const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack) { - clearChildren(); - - int heightWidth = height->getWidth(); - if (heightWidth <= HEIGHT_LEAF_SIZE) { - _height = height; - _color = color; - _material = material; - return; - } - int heightHeight = height->getContents().size() / heightWidth; - int innerChildHeightWidth = (heightWidth - HeightfieldHeight::HEIGHT_EXTENSION) / 2; - int innerChildHeightHeight = (heightHeight - HeightfieldHeight::HEIGHT_EXTENSION) / 2; - int childHeightWidth = innerChildHeightWidth + HeightfieldHeight::HEIGHT_EXTENSION; - int childHeightHeight = innerChildHeightHeight + HeightfieldHeight::HEIGHT_EXTENSION; - - for (int i = 0; i < CHILD_COUNT; i++) { - QVector childHeightContents(childHeightWidth * childHeightHeight); - quint16* heightDest = childHeightContents.data(); - bool maximumX = (i & X_MAXIMUM_FLAG), maximumY = (i & Y_MAXIMUM_FLAG); - const quint16* heightSrc = height->getContents().constData() + (maximumY ? innerChildHeightHeight * heightWidth : 0) + - (maximumX ? innerChildHeightWidth : 0); - for (int z = 0; z < childHeightHeight; z++, heightDest += childHeightWidth, heightSrc += heightWidth) { - memcpy(heightDest, heightSrc, childHeightWidth * sizeof(quint16)); - } - - HeightfieldColorPointer childColor; - if (color) { - int colorWidth = color->getWidth(); - int colorHeight = color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - int innerChildColorWidth = (colorWidth - HeightfieldData::SHARED_EDGE) / 2; - int innerChildColorHeight = (colorHeight - HeightfieldData::SHARED_EDGE) / 2; - int childColorWidth = innerChildColorWidth + HeightfieldData::SHARED_EDGE; - int childColorHeight = innerChildColorHeight + HeightfieldData::SHARED_EDGE; - QByteArray childColorContents(childColorWidth * childColorHeight * DataBlock::COLOR_BYTES, 0); - char* dest = childColorContents.data(); - const char* src = color->getContents().constData() + ((maximumY ? innerChildColorHeight * colorWidth : 0) + - (maximumX ? innerChildColorWidth : 0)) * DataBlock::COLOR_BYTES; - for (int z = 0; z < childColorHeight; z++, dest += childColorWidth * DataBlock::COLOR_BYTES, - src += colorWidth * DataBlock::COLOR_BYTES) { - memcpy(dest, src, childColorWidth * DataBlock::COLOR_BYTES); - } - childColor = new HeightfieldColor(childColorWidth, childColorContents); - } - - HeightfieldMaterialPointer childMaterial; - if (material) { - int materialWidth = material->getWidth(); - int materialHeight = material->getContents().size() / materialWidth; - int innerChildMaterialWidth = (materialWidth - HeightfieldData::SHARED_EDGE) / 2; - int innerChildMaterialHeight = (materialHeight - HeightfieldData::SHARED_EDGE) / 2; - int childMaterialWidth = innerChildMaterialWidth + HeightfieldData::SHARED_EDGE; - int childMaterialHeight = innerChildMaterialHeight + HeightfieldData::SHARED_EDGE; - QByteArray childMaterialContents(childMaterialWidth * childMaterialHeight, 0); - QVector childMaterials; - uchar* dest = (uchar*)childMaterialContents.data(); - const uchar* src = (const uchar*)material->getContents().data() + - (maximumY ? innerChildMaterialHeight * materialWidth : 0) + (maximumX ? innerChildMaterialWidth : 0); - QHash materialMap; - for (int z = 0; z < childMaterialHeight; z++, dest += childMaterialWidth, src += materialWidth) { - const uchar* lineSrc = src; - for (uchar* lineDest = dest, *end = dest + childMaterialWidth; lineDest != end; lineDest++, lineSrc++) { - int value = *lineSrc; - if (value != 0) { - int& mapping = materialMap[value]; - if (mapping == 0) { - childMaterials.append(material->getMaterials().at(value - 1)); - mapping = childMaterials.size(); - } - value = mapping; - } - *lineDest = value; - } - } - childMaterial = new HeightfieldMaterial(childMaterialWidth, childMaterialContents, childMaterials); - } - - HeightfieldStackPointer childStack; - if (stack) { - } - - _children[i] = new HeightfieldNode(); - _children[i]->setContents(HeightfieldHeightPointer(new HeightfieldHeight(childHeightWidth, childHeightContents)), - childColor, childMaterial, childStack); - } - - mergeChildren(); -} - -bool HeightfieldNode::isLeaf() const { - for (int i = 0; i < CHILD_COUNT; i++) { - if (_children[i]) { - return false; - } - } - return true; -} - -bool HeightfieldNode::findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - glm::quat inverseRotation = glm::inverse(rotation); - glm::vec3 inverseScale = 1.0f / scale; - glm::vec3 transformedOrigin = inverseRotation * (origin - translation) * inverseScale; - glm::vec3 transformedDirection = inverseRotation * direction * inverseScale; - float boundsDistance; - if (!Box(glm::vec3(), glm::vec3(1.0f, 1.0f, 1.0f)).findRayIntersection(transformedOrigin, transformedDirection, - boundsDistance)) { - return false; - } - if (!isLeaf()) { - float closestDistance = FLT_MAX; - for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); - float childDistance; - if (_children[i]->findRayIntersection(translation + - rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, - i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, origin, direction, childDistance)) { - closestDistance = qMin(closestDistance, childDistance); - } - } - if (closestDistance == FLT_MAX) { - return false; - } - distance = closestDistance; - return true; - } - float shortestDistance = FLT_MAX; - float heightfieldDistance; - if (findHeightfieldRayIntersection(transformedOrigin, transformedDirection, boundsDistance, heightfieldDistance)) { - shortestDistance = heightfieldDistance; - } - float rendererDistance; - if (_renderer && _renderer->findRayIntersection(translation, rotation, scale, origin, direction, boundsDistance, - rendererDistance)) { - shortestDistance = qMin(shortestDistance, rendererDistance); - } - if (shortestDistance == FLT_MAX) { - return false; - } - distance = shortestDistance; - return true; -} - -const float HERMITE_GRANULARITY = 1.0f / numeric_limits::max(); - -void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const { - if (!_height) { - return; - } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - int highestHeightX = heightWidth - 1; - int highestHeightZ = heightHeight - 1; - - glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); - glm::vec3 center = glm::inverse(rotation) * (position - translation) * inverseScale + glm::vec3(1.0f, 0.0f, 1.0f); - glm::vec3 extents = radius * inverseScale; - - if (center.x + extents.x < 0.0f || center.z + extents.z < 0.0f || - center.x - extents.x > highestHeightX || center.z - extents.z > highestHeightZ) { - return; - } - if (!isLeaf()) { - for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); - _children[i]->getRangeAfterHeightPaint(translation + - rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, - i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, position, radius, height, minimum, maximum); - } - return; - } - glm::vec3 start = glm::clamp(glm::floor(center - extents), glm::vec3(), - glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); - glm::vec3 end = glm::clamp(glm::ceil(center + extents), glm::vec3(), - glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); - - const quint16* lineDest = _height->getContents().constData() + (int)start.z * heightWidth + (int)start.x; - float squaredRadius = extents.x * extents.x; - float squaredRadiusReciprocal = 1.0f / squaredRadius; - float multiplierZ = extents.x / extents.z; - float relativeHeight = height * numeric_limits::max() / scale.y; - for (float z = start.z; z <= end.z; z += 1.0f) { - const quint16* dest = lineDest; - for (float x = start.x; x <= end.x; x += 1.0f, dest++) { - float dx = x - center.x, dz = (z - center.z) * multiplierZ; - float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius) { - // height falls off towards edges - int value = *dest; - if (value != 0) { - value += relativeHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - minimum = qMin(minimum, (float)value); - maximum = qMax(maximum, (float)value); - } - } - } - lineDest += heightWidth; - } - - // make sure we increment in multiples of the voxel size - float voxelStep = scale.x / innerHeightWidth; - float heightIncrement = (1.0f - minimum) * scale.y / numeric_limits::max(); - float incrementSteps = heightIncrement / voxelStep; - if (glm::abs(incrementSteps - glm::round(incrementSteps)) > HERMITE_GRANULARITY) { - minimum = 1.0f - voxelStep * glm::ceil(incrementSteps) * numeric_limits::max() / scale.y; - } -} - -HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius, float height, bool set, bool erase, - float normalizeScale, float normalizeOffset, float granularity) { - if (!_height) { - return this; - } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - int highestHeightX = heightWidth - 1; - int highestHeightZ = heightHeight - 1; - - glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); - glm::vec3 center = glm::inverse(rotation) * (position - translation) * inverseScale + glm::vec3(1.0f, 0.0f, 1.0f); - glm::vec3 extents = radius * inverseScale; - - bool intersects = (center.x + extents.x >= 0.0f && center.z + extents.z >= 0.0f && - center.x - extents.x <= highestHeightX && center.z - extents.z <= highestHeightZ); - if (!intersects && normalizeScale == 1.0f && normalizeOffset == 0.0f) { - return this; - } - if (!isLeaf()) { - HeightfieldNode* newNode = this; - for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); - HeightfieldNode* newChild = _children[i]->paintHeight(translation + - rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, - i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, position, radius, height, set, erase, normalizeScale, normalizeOffset, granularity); - if (_children[i] != newChild) { - if (newNode == this) { - newNode = new HeightfieldNode(*this); - } - newNode->setChild(i, HeightfieldNodePointer(newChild)); - } - } - if (newNode != this) { - newNode->mergeChildren(true, false); - } - return newNode; - } - QVector newHeightContents = _height->getContents(); - - int stackWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; - QVector newStackContents; - QVector newStackMaterials; - if (_stack) { - stackWidth = _stack->getWidth(); - newStackContents = _stack->getContents(); - newStackMaterials = _stack->getMaterials(); - } - int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; - - // renormalize if necessary - maybeRenormalize(scale, normalizeScale, normalizeOffset, innerStackWidth, newHeightContents, newStackContents); - if (!intersects) { - return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), - _color, _material, HeightfieldStackPointer(newStackContents.isEmpty() ? NULL : - new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); - } - - // if the granularity is insufficient, we must subdivide - if (scale.x / innerHeightWidth > granularity || scale.z / innerHeightHeight > granularity) { - HeightfieldNodePointer newNode(subdivide(newHeightContents, newStackContents)); - return newNode->paintHeight(translation, rotation, scale, position, radius, height, set, - erase, 1.0f, 0.0f, granularity); - } - - // now apply the actual change - glm::vec3 start = glm::clamp(glm::floor(center - extents), glm::vec3(), - glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); - glm::vec3 end = glm::clamp(glm::ceil(center + extents), glm::vec3(), - glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); - - quint16* lineDest = newHeightContents.data() + (int)start.z * heightWidth + (int)start.x; - float squaredRadius = extents.x * extents.x; - float squaredRadiusReciprocal = 1.0f / squaredRadius; - float multiplierZ = extents.x / extents.z; - float relativeHeight = height * numeric_limits::max() / scale.y; - quint16 heightValue = erase ? 0 : relativeHeight; - for (float z = start.z; z <= end.z; z += 1.0f) { - quint16* dest = lineDest; - for (float x = start.x; x <= end.x; x += 1.0f, dest++) { - float dx = x - center.x, dz = (z - center.z) * multiplierZ; - float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius) { - if (erase || set) { - *dest = heightValue; - - } else { - // height falls off towards edges - int value = *dest; - if (value != 0) { - *dest = value + relativeHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - } - } - } - } - lineDest += heightWidth; - } - - return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), - _color, _material, HeightfieldStackPointer(newStackContents.isEmpty() ? NULL : - new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); -} - -HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius, float granularity) { - if (!_height) { - return this; - } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - int highestHeightX = heightWidth - 1; - int highestHeightZ = heightHeight - 1; - - glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); - glm::vec3 center = glm::inverse(rotation) * (position - translation) * inverseScale + glm::vec3(1.0f, 0.0f, 1.0f); - glm::vec3 extents = radius * inverseScale; - - if (center.x + extents.x < 0.0f || center.z + extents.z < 0.0f || center.x - extents.x > highestHeightX || - center.z - extents.z > highestHeightZ) { - return this; - } - if (!isLeaf()) { - HeightfieldNode* newNode = this; - for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); - HeightfieldNode* newChild = _children[i]->fillHeight(translation + - rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, - i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, position, radius, granularity); - if (_children[i] != newChild) { - if (newNode == this) { - newNode = new HeightfieldNode(*this); - } - newNode->setChild(i, HeightfieldNodePointer(newChild)); - } - } - if (newNode != this) { - newNode->mergeChildren(true, false); - } - return newNode; - } - if (!_stack) { - return this; - } - - // if the granularity is insufficient, we must subdivide - QVector newHeightContents = _height->getContents(); - QVector newStackContents = _stack->getContents(); - if (scale.x / innerHeightWidth > granularity || scale.z / innerHeightHeight > granularity) { - HeightfieldNodePointer newNode(subdivide(newHeightContents, newStackContents)); - return newNode->fillHeight(translation, rotation, scale, position, radius, granularity); - } - - int stackWidth = _stack->getWidth(); - int stackHeight = newStackContents.size() / stackWidth; - QVector newStackMaterials = _stack->getMaterials(); - - int colorWidth, colorHeight; - QByteArray newColorContents; - if (_color) { - colorWidth = _color->getWidth(); - colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - newColorContents = _color->getContents(); - - } else { - colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; - colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; - newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu); - } - int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; - int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; - - int materialWidth, materialHeight; - QByteArray newMaterialContents; - QVector newMaterialMaterials; - if (_material) { - materialWidth = _material->getWidth(); - materialHeight = _material->getContents().size() / materialWidth; - newMaterialContents = _material->getContents(); - newMaterialMaterials = _material->getMaterials(); - - } else { - materialWidth = colorWidth; - materialHeight = colorHeight; - newMaterialContents = QByteArray(materialWidth * materialHeight, 0); - } - int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; - int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; - - glm::vec3 start = glm::clamp(glm::floor(center - extents), glm::vec3(), - glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); - glm::vec3 end = glm::clamp(glm::ceil(center + extents), glm::vec3(), - glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); - float voxelStep = scale.x / innerHeightWidth; - float voxelScale = scale.y / (numeric_limits::max() * voxelStep); - - quint16* lineDest = newHeightContents.data() + (int)start.z * heightWidth + (int)start.x; - float squaredRadius = extents.x * extents.x; - float multiplierZ = extents.x / extents.z; - float colorStepX = (float)innerColorWidth / innerHeightWidth; - float colorStepZ = (float)innerColorHeight / innerHeightHeight; - float materialStepX = (float)innerMaterialWidth / innerHeightWidth; - float materialStepZ = (float)innerMaterialHeight / innerHeightHeight; - QHash materialMap; - for (float z = start.z; z <= end.z; z += 1.0f) { - quint16* dest = lineDest; - for (float x = start.x; x <= end.x; x += 1.0f, dest++) { - float dx = x - center.x, dz = (z - center.z) * multiplierZ; - float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius && x >= 1.0f && z >= 1.0f && x <= stackWidth && z <= stackHeight) { - int stackX = (int)x - 1, stackZ = (int)z - 1; - StackArray* stackDest = newStackContents.data() + stackZ * stackWidth + stackX; - if (stackDest->isEmpty()) { - continue; - } - int y = stackDest->getPosition() + stackDest->getEntryCount() - 1; - for (const StackArray::Entry* entry = stackDest->getEntryData() + stackDest->getEntryCount() - 1; - entry >= stackDest->getEntryData(); entry--, y--) { - if (!entry->isSet()) { - continue; - } - glm::vec3 normal; - int newHeight = qMax((int)((y + entry->getHermiteY(normal)) / voxelScale), 1); - if (newHeight < *dest) { - break; - } - *dest = newHeight; - for (int colorZ = stackZ * colorStepZ; (int)(colorZ / colorStepZ) == stackZ; colorZ++) { - for (int colorX = stackX * colorStepX; (int)(colorX / colorStepX) == stackX; colorX++) { - uchar* colorDest = (uchar*)newColorContents.data() + - (colorZ * colorWidth + colorX) * DataBlock::COLOR_BYTES; - colorDest[0] = qRed(entry->color); - colorDest[1] = qGreen(entry->color); - colorDest[2] = qBlue(entry->color); - } - } - for (int materialZ = stackZ * materialStepZ; (int)(materialZ / materialStepZ) == stackZ; materialZ++) { - for (int materialX = stackX * materialStepX; (int)(materialX / materialStepX) == stackX; - materialX++) { - int material = entry->material; - if (material != 0) { - int& mapping = materialMap[material]; - if (mapping == 0) { - mapping = getMaterialIndex(newStackMaterials.at(material - 1), - newMaterialMaterials, newMaterialContents); - } - material = mapping; - } - newMaterialContents[materialZ * materialWidth + materialX] = material; - } - } - break; - } - stackDest->clear(); - } - } - lineDest += heightWidth; - } - clearUnusedMaterials(newMaterialMaterials, newMaterialContents); - clearUnusedMaterials(newStackMaterials, newStackContents); - - return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), - HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)), - HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, newMaterialContents, newMaterialMaterials)), - HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); -} - -void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const Box& editBounds, float& minimum, float& maximum) const { - if (!_height) { - return; - } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); - glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); - glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; - Box transformedBounds = inverseTransform * editBounds; - if (transformedBounds.maximum.x < 0.0f || transformedBounds.maximum.z < 0.0f || - transformedBounds.minimum.x > heightWidth - 1 || transformedBounds.minimum.z > heightHeight - 1) { - return; - } - if (!isLeaf()) { - for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); - _children[i]->getRangeAfterEdit(translation + - rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, - i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, editBounds, minimum, maximum); - } - return; - } - glm::vec3 start = glm::floor(transformedBounds.minimum); - glm::vec3 end = glm::ceil(transformedBounds.maximum); - - minimum = qMin(minimum, start.y); - maximum = qMax(maximum, end.y); - - // make sure we increment in multiples of the voxel size - float voxelStep = scale.x / innerHeightWidth; - float heightIncrement = (1.0f - minimum) * scale.y / numeric_limits::max(); - float incrementSteps = heightIncrement / voxelStep; - if (glm::abs(incrementSteps - glm::round(incrementSteps)) > HERMITE_GRANULARITY) { - minimum = 1.0f - voxelStep * glm::ceil(incrementSteps) * numeric_limits::max() / scale.y; - } -} - -HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize, - float normalizeScale, float normalizeOffset, float granularity) { - if (!_height) { - return this; - } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - glm::vec3 expansion(1.0f / innerHeightWidth, 0.0f, 1.0f / innerHeightHeight); - Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(-expansion, scale + expansion); - bool intersects = bounds.intersects(spanner->getBounds()); - if (!intersects && normalizeScale == 1.0f && normalizeOffset == 0.0f) { - return this; - } - if (!isLeaf()) { - HeightfieldNode* newNode = this; - for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); - HeightfieldNode* newChild = _children[i]->setMaterial(translation + - rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, - i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, nextScale, spanner, - material, color, paint, voxelize, normalizeScale, normalizeOffset, granularity); - if (_children[i] != newChild) { - if (newNode == this) { - newNode = new HeightfieldNode(*this); - } - newNode->setChild(i, HeightfieldNodePointer(newChild)); - } - } - if (newNode != this) { - newNode->mergeChildren(); - } - return newNode; - } - int highestHeightX = heightWidth - 1; - int highestHeightZ = heightHeight - 1; - QVector newHeightContents = _height->getContents(); - - int stackWidth, stackHeight; - QVector newStackContents; - QVector newStackMaterials; - if (_stack) { - stackWidth = _stack->getWidth(); - stackHeight = _stack->getContents().size() / stackWidth; - newStackContents = _stack->getContents(); - newStackMaterials = _stack->getMaterials(); - - } else { - stackWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; - stackHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; - newStackContents = QVector(stackWidth * stackHeight); - } - int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; - int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; - - // renormalize if necessary - maybeRenormalize(scale, normalizeScale, normalizeOffset, innerStackWidth, newHeightContents, newStackContents); - if (!intersects) { - return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), - _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); - } - - // if the granularity is insufficient, we must subdivide - if (scale.x / innerHeightWidth > granularity || scale.z / innerHeightHeight > granularity) { - HeightfieldNodePointer newNode(subdivide(newHeightContents, newStackContents)); - return newNode->setMaterial(translation, rotation, scale, spanner, material, color, - paint, voxelize, 1.0f, 0.0f, granularity); - } - - QVector oldHeightContents = newHeightContents; - QVector oldStackContents = newStackContents; - - int colorWidth, colorHeight; - QByteArray newColorContents; - if (_color) { - colorWidth = _color->getWidth(); - colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - newColorContents = _color->getContents(); - - } else { - colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; - colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; - newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu); - } - int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; - int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; - - int materialWidth, materialHeight; - QByteArray newMaterialContents; - QVector newMaterialMaterials; - if (_material) { - materialWidth = _material->getWidth(); - materialHeight = _material->getContents().size() / materialWidth; - newMaterialContents = _material->getContents(); - newMaterialMaterials = _material->getMaterials(); - - } else { - materialWidth = colorWidth; - materialHeight = colorHeight; - newMaterialContents = QByteArray(materialWidth * materialHeight, 0); - } - int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; - int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; - - glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); - glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); - glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; - Box transformedBounds = inverseTransform * spanner->getBounds(); - glm::mat4 transform = glm::inverse(inverseTransform); - - glm::vec3 start = glm::ceil(transformedBounds.maximum); - glm::vec3 end = glm::floor(transformedBounds.minimum); - - float stepX = 1.0f, stepZ = 1.0f; - if (paint) { - stepX = (float)innerHeightWidth / qMax(innerHeightWidth, qMax(innerColorWidth, innerMaterialWidth)); - stepZ = (float)innerHeightHeight / qMax(innerHeightHeight, qMax(innerColorHeight, innerMaterialHeight)); - - } else { - start.x += 1.0f; - start.z += 1.0f; - end.x -= 1.0f; - end.z -= 1.0f; - } - - float startX = glm::clamp(start.x, 0.0f, (float)highestHeightX), endX = glm::clamp(end.x, 0.0f, (float)highestHeightX); - float startZ = glm::clamp(start.z, 0.0f, (float)highestHeightZ), endZ = glm::clamp(end.z, 0.0f, (float)highestHeightZ); - float voxelStep = scale.x / innerHeightWidth; - float voxelScale = scale.y / (numeric_limits::max() * voxelStep); - int newTop = start.y * voxelScale; - int newBottom = end.y * voxelScale; - glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, paint ? 0.0f : newTop / voxelScale, startZ, 1.0f)); - glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(stepX, 0.0f, 0.0f, 0.0f)); - glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, 1.0f / voxelScale, 0.0f, 0.0f)); - glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, stepZ, 0.0f)); - QRgb rgba = color.rgba(); - bool erase = (color.alpha() == 0); - uchar materialMaterialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); - uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, newStackContents); - bool hasOwnColors = spanner->hasOwnColors(); - bool hasOwnMaterials = spanner->hasOwnMaterials(); - QHash materialMappings; - for (float z = startZ; z >= endZ; z -= stepZ, worldStart -= worldStepZ) { - quint16* heightDest = newHeightContents.data() + (int)z * heightWidth; - glm::vec3 worldPos = worldStart; - for (float x = startX; x >= endX; x -= stepX, worldPos -= worldStepX) { - quint16* heightLineDest = heightDest + (int)x; - float distance; - glm::vec3 normal; - - float colorX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerColorWidth / innerHeightWidth; - float colorZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerColorHeight / innerHeightHeight; - uchar* colorDest = (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) ? - ((uchar*)newColorContents.data() + ((int)colorZ * colorWidth + (int)colorX) * DataBlock::COLOR_BYTES) : NULL; - - float materialX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialWidth / innerHeightWidth; - float materialZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialHeight / innerHeightHeight; - char* materialDest = (materialX >= 0.0f && materialX <= innerMaterialWidth && materialZ >= 0.0f && - materialZ <= innerMaterialHeight) ? (newMaterialContents.data() + - (int)materialZ * materialWidth + (int)materialX) : NULL; - - if (paint && *heightLineDest != 0 && spanner->contains(worldPos + worldStepY * (*heightLineDest * voxelScale))) { - if (colorDest) { - colorDest[0] = qRed(rgba); - colorDest[1] = qGreen(rgba); - colorDest[2] = qBlue(rgba); - } - if (materialDest) { - *materialDest = materialMaterialIndex; - } - } - - float stackX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerStackWidth / innerHeightWidth; - float stackZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerStackHeight / innerHeightHeight; - - if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { - StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; - if (paint) { - if (stackDest->isEmpty() || glm::fract(x) != 0.0f || glm::fract(z) != 0.0f) { - continue; - } - glm::vec3 pos = worldPos + worldStepY * (float)stackDest->getPosition(); - for (StackArray::Entry* entryDest = stackDest->getEntryData(), *end = entryDest + - stackDest->getEntryCount(); entryDest != end; entryDest++, pos += worldStepY) { - if (entryDest->isSet() && spanner->contains(pos)) { - entryDest->color = rgba; - entryDest->material = stackMaterialIndex; - } - } - continue; - } - int prepend = 0, append = 0; - if (!stackDest->isEmpty()) { - int oldBottom = stackDest->getPosition(); - int oldTop = oldBottom + stackDest->getEntryCount() - 1; - prepend = qMax(0, oldBottom - newBottom); - append = qMax(0, newTop - oldTop); - if (prepend != 0 || append != 0) { - StackArray newStack(prepend + stackDest->getEntryCount() + append); - memcpy(newStack.getEntryData() + prepend, stackDest->getEntryData(), - stackDest->getEntryCount() * sizeof(StackArray::Entry)); - for (StackArray::Entry* entryDest = newStack.getEntryData(), *end = entryDest + prepend; - entryDest != end; entryDest++) { - entryDest->color = end->color; - entryDest->material = end->material; - } - *stackDest = newStack; - stackDest->setPosition(qMin(oldBottom, newBottom)); - } - } else { - *stackDest = StackArray(newTop - newBottom + 1); - stackDest->setPosition(newBottom); - prepend = stackDest->getEntryCount(); - } - const quint16* oldHeightLineDest = oldHeightContents.constData() + (int)z * heightWidth + (int)x; - if (*heightLineDest != 0) { - float voxelHeight = *heightLineDest * voxelScale; - float left = oldHeightLineDest[-1] * voxelScale; - float right = oldHeightLineDest[1] * voxelScale; - float down = oldHeightLineDest[-heightWidth] * voxelScale; - float up = oldHeightLineDest[heightWidth] * voxelScale; - float deltaX = (left == 0.0f || right == 0.0f) ? 0.0f : (left - right); - float deltaZ = (up == 0.0f || down == 0.0f) ? 0.0f : (down - up); - for (int i = 0, total = prepend + append; i < total; i++) { - int offset = (i < prepend) ? i : stackDest->getEntryCount() - append + (i - prepend); - int y = stackDest->getPosition() + offset; - StackArray::Entry* entryDest = stackDest->getEntryData() + offset; - if (y > voxelHeight) { - if (y <= right) { - entryDest->setHermiteX(glm::normalize(glm::vec3(voxelHeight - right, 1.0f, deltaZ * 0.5f)), - (right == voxelHeight) ? 0.5f : (y - voxelHeight) / (right - voxelHeight)); - } - if (y <= up) { - entryDest->setHermiteZ(glm::normalize(glm::vec3(deltaX * 0.5f, 1.0f, voxelHeight - up)), - (up == voxelHeight) ? 0.5f : (y - voxelHeight) / (up - voxelHeight)); - } - } else { - if (right != 0.0f && y > right) { - entryDest->setHermiteX(glm::normalize(glm::vec3(voxelHeight - right, 1.0f, deltaZ * 0.5f)), - (right == voxelHeight) ? 0.5f : (y - voxelHeight) / (right - voxelHeight)); - } - if (up != 0.0f && y > up) { - entryDest->setHermiteZ(glm::normalize(glm::vec3(deltaX * 0.5f, 1.0f, voxelHeight - up)), - (up == voxelHeight) ? 0.5f : (y - voxelHeight) / (up - voxelHeight)); - } - if (colorDest) { - entryDest->color = qRgb(colorDest[0], colorDest[1], colorDest[2]); - } - if (materialDest) { - int index = *materialDest; - if (index != 0) { - int& mapping = materialMappings[index]; - if (mapping == 0) { - mapping = getMaterialIndex(newMaterialMaterials.at(index - 1), - newStackMaterials, newStackContents); - } - index = mapping; - } - entryDest->material = index; - } - if (y + 1 > voxelHeight) { - *heightLineDest = 0; - entryDest->setHermiteY(glm::normalize(glm::vec3(deltaX, 2.0f, deltaZ)), voxelHeight - y); - } - } - } - } - StackArray::Entry* entryDest = stackDest->getEntryData() + (newTop - stackDest->getPosition()); - glm::vec3 pos = worldPos; - float voxelHeight = *heightLineDest * voxelScale; - float nextVoxelHeightX = heightLineDest[1] * voxelScale; - float nextVoxelHeightZ = heightLineDest[heightWidth] * voxelScale; - float oldVoxelHeight = *oldHeightLineDest * voxelScale; - float oldNextVoxelHeightX = oldHeightLineDest[1] * voxelScale; - float oldNextVoxelHeightZ = oldHeightLineDest[heightWidth] * voxelScale; - // skip the actual set if voxelizing - for (int y = voxelize ? newBottom - 1 : newTop; y >= newBottom; y--, entryDest--, pos -= worldStepY) { - int oldCurrentAlpha = stackDest->getEntryAlpha(y, oldVoxelHeight); - if (spanner->contains(pos)) { - if (hasOwnColors && !erase) { - entryDest->color = spanner->getColorAt(pos); - - } else { - entryDest->color = rgba; - } - if (hasOwnMaterials && !erase) { - int index = spanner->getMaterialAt(pos); - if (index != 0) { - int& mapping = materialMappings[index]; - if (mapping == 0) { - mapping = getMaterialIndex(spanner->getMaterials().at(index - 1), - newStackMaterials, newStackContents); - } - index = mapping; - } - entryDest->material = index; - - } else { - entryDest->material = stackMaterialIndex; - } - } - - int currentAlpha = stackDest->getEntryAlpha(y, voxelHeight); - bool flipped = (color.alpha() == currentAlpha); - int nextStackX = (int)stackX + 1; - if (nextStackX <= innerStackWidth) { - int nextAlphaX = newStackContents.at((int)stackZ * stackWidth + nextStackX).getEntryAlpha( - y, nextVoxelHeightX); - if (nextAlphaX == currentAlpha) { - entryDest->hermiteX = 0; - - } else { - float oldDistance = flipped ? 0.0f : 1.0f; - if (currentAlpha == oldCurrentAlpha && nextAlphaX == oldStackContents.at((int)stackZ * stackWidth + - nextStackX).getEntryAlpha(y, oldNextVoxelHeightX) && entryDest->hermiteX != 0) { - oldDistance = qAlpha(entryDest->hermiteX) / (float)numeric_limits::max(); - } - if (flipped ? (spanner->intersects(pos + worldStepX, pos, distance, normal) && - (distance = 1.0f - distance) >= oldDistance) : - (spanner->intersects(pos, pos + worldStepX, distance, normal) && - distance <= oldDistance)) { - entryDest->setHermiteX(erase ? -normal : normal, distance); - } - } - } - int nextAlphaY = stackDest->getEntryAlpha(y + 1, voxelHeight); - if (nextAlphaY == currentAlpha) { - entryDest->hermiteY = 0; - - } else { - float oldDistance = flipped ? 0.0f : 1.0f; - if (currentAlpha == oldCurrentAlpha && nextAlphaY == oldStackContents.at((int)stackZ * stackWidth + - (int)stackX).getEntryAlpha(y + 1, oldVoxelHeight) && entryDest->hermiteY != 0) { - oldDistance = qAlpha(entryDest->hermiteY) / (float)numeric_limits::max(); - } - if (flipped ? (spanner->intersects(pos + worldStepY, pos, distance, normal) && - (distance = 1.0f - distance) >= oldDistance) : - (spanner->intersects(pos, pos + worldStepY, distance, normal) && - distance <= oldDistance)) { - entryDest->setHermiteY(erase ? -normal : normal, distance); - } - } - int nextStackZ = (int)stackZ + 1; - if (nextStackZ <= innerStackHeight) { - int nextAlphaZ = newStackContents.at(nextStackZ * stackWidth + (int)stackX).getEntryAlpha( - y, nextVoxelHeightZ); - if (nextAlphaZ == currentAlpha) { - entryDest->hermiteZ = 0; - - } else { - float oldDistance = flipped ? 0.0f : 1.0f; - if (currentAlpha == oldCurrentAlpha && nextAlphaZ == oldStackContents.at(nextStackZ * stackWidth + - (int)stackX).getEntryAlpha(y, oldNextVoxelHeightZ) && entryDest->hermiteZ != 0) { - oldDistance = qAlpha(entryDest->hermiteZ) / (float)numeric_limits::max(); - } - if (flipped ? (spanner->intersects(pos + worldStepZ, pos, distance, normal) && - (distance = 1.0f - distance) >= oldDistance) : - (spanner->intersects(pos, pos + worldStepZ, distance, normal) && - distance <= oldDistance)) { - entryDest->setHermiteZ(erase ? -normal : normal, distance); - } - } - } - } - - // prune zero entries from end, repeated entries from beginning - int endPruneCount = 0; - for (int i = stackDest->getEntryCount() - 1; i >= 0 && stackDest->getEntryData()[i].isZero(); i--) { - endPruneCount++; - } - if (endPruneCount == stackDest->getEntryCount()) { - stackDest->clear(); - - } else { - stackDest->removeEntries(stackDest->getEntryCount() - endPruneCount, endPruneCount); - int beginningPruneCount = 0; - for (int i = 0; i < stackDest->getEntryCount() - 1 && stackDest->getEntryData()[i].isMergeable( - stackDest->getEntryData()[i + 1]); i++) { - beginningPruneCount++; - } - stackDest->removeEntries(0, beginningPruneCount); - stackDest->getPositionRef() += beginningPruneCount; - } - } - } - } - clearUnusedMaterials(newMaterialMaterials, newMaterialContents); - clearUnusedMaterials(newStackMaterials, newStackContents); - - return new HeightfieldNode(paint ? _height : HeightfieldHeightPointer( - new HeightfieldHeight(heightWidth, newHeightContents)), - HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)), - HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, newMaterialContents, newMaterialMaterials)), - HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); -} - -void HeightfieldNode::read(HeightfieldStreamState& state) { - clearChildren(); - - if (!state.shouldSubdivide()) { - state.base.stream >> _height >> _color >> _material >> _stack; - return; - } - bool leaf; - state.base.stream >> leaf; - if (leaf) { - state.base.stream >> _height >> _color >> _material >> _stack; - - } else { - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i] = new HeightfieldNode(); - _children[i]->read(nextState); - } - mergeChildren(); - } -} - -void HeightfieldNode::write(HeightfieldStreamState& state) const { - if (!state.shouldSubdivide()) { - state.base.stream << _height << _color << _material << _stack; - return; - } - bool leaf = isLeaf(); - state.base.stream << leaf; - if (leaf) { - state.base.stream << _height << _color << _material << _stack; - - } else { - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->write(nextState); - } - } -} - -void HeightfieldNode::readDelta(const HeightfieldNodePointer& reference, HeightfieldStreamState& state) { - clearChildren(); - - if (!state.shouldSubdivide()) { - state.base.stream.readDelta(_height, reference->getHeight()); - state.base.stream.readDelta(_color, reference->getColor()); - state.base.stream.readDelta(_material, reference->getMaterial()); - state.base.stream.readDelta(_stack, reference->getStack()); - return; - } - bool leaf; - state.base.stream >> leaf; - if (leaf) { - state.base.stream.readDelta(_height, reference->getHeight()); - state.base.stream.readDelta(_color, reference->getColor()); - state.base.stream.readDelta(_material, reference->getMaterial()); - state.base.stream.readDelta(_stack, reference->getStack()); - - } else { - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - if (reference->isLeaf() || !state.shouldSubdivideReference()) { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i] = new HeightfieldNode(); - _children[i]->read(nextState); - } - } else { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - bool changed; - state.base.stream >> changed; - if (changed) { - _children[i] = new HeightfieldNode(); - _children[i]->readDelta(reference->getChild(i), nextState); - } else { - if (nextState.becameSubdividedOrCollapsed()) { - _children[i] = reference->getChild(i)->readSubdivision(nextState); - - } else { - _children[i] = reference->getChild(i); - } - } - } - } - mergeChildren(); - } -} - -void HeightfieldNode::writeDelta(const HeightfieldNodePointer& reference, HeightfieldStreamState& state) const { - if (!state.shouldSubdivide()) { - state.base.stream.writeDelta(_height, reference->getHeight()); - state.base.stream.writeDelta(_color, reference->getColor()); - state.base.stream.writeDelta(_material, reference->getMaterial()); - state.base.stream.writeDelta(_stack, reference->getStack()); - return; - } - bool leaf = isLeaf(); - state.base.stream << leaf; - if (leaf) { - state.base.stream.writeDelta(_height, reference->getHeight()); - state.base.stream.writeDelta(_color, reference->getColor()); - state.base.stream.writeDelta(_material, reference->getMaterial()); - state.base.stream.writeDelta(_stack, reference->getStack()); - - } else { - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - if (reference->isLeaf() || !state.shouldSubdivideReference()) { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->write(nextState); - } - } else { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (_children[i] == reference->getChild(i)) { - state.base.stream << false; - if (nextState.becameSubdivided()) { - _children[i]->writeSubdivision(nextState); - } - } else { - state.base.stream << true; - _children[i]->writeDelta(reference->getChild(i), nextState); - } - } - } - } -} - -HeightfieldNode* HeightfieldNode::readSubdivision(HeightfieldStreamState& state) { - if (state.shouldSubdivide()) { - if (!state.shouldSubdivideReference()) { - bool leaf; - state.base.stream >> leaf; - if (leaf) { - return isLeaf() ? this : new HeightfieldNode(_height, _color, _material, _stack); - - } else { - HeightfieldNode* newNode = new HeightfieldNode(_height, _color, _material, _stack); - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - newNode->_children[i] = new HeightfieldNode(); - newNode->_children[i]->readSubdivided(nextState, state, this); - } - return newNode; - } - } else if (!isLeaf()) { - HeightfieldNode* node = this; - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (nextState.becameSubdividedOrCollapsed()) { - HeightfieldNode* child = _children[i]->readSubdivision(nextState); - if (_children[i] != child) { - if (node == this) { - node = new HeightfieldNode(*this); - } - node->_children[i] = child; - } - } - } - if (node != this) { - node->mergeChildren(); - } - return node; - } - } else if (!isLeaf()) { - return new HeightfieldNode(_height, _color, _material, _stack); - } - return this; -} - -void HeightfieldNode::writeSubdivision(HeightfieldStreamState& state) const { - if (!state.shouldSubdivide()) { - return; - } - bool leaf = isLeaf(); - if (!state.shouldSubdivideReference()) { - state.base.stream << leaf; - if (!leaf) { - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->writeSubdivided(nextState, state, this); - } - } - } else if (!leaf) { - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (nextState.becameSubdivided()) { - _children[i]->writeSubdivision(nextState); - } - } - } -} - -void HeightfieldNode::readSubdivided(HeightfieldStreamState& state, const HeightfieldStreamState& ancestorState, - const HeightfieldNode* ancestor) { - clearChildren(); - - if (!state.shouldSubdivide()) { - // TODO: subdivision encoding - state.base.stream >> _height >> _color >> _material >> _stack; - return; - } - bool leaf; - state.base.stream >> leaf; - if (leaf) { - state.base.stream >> _height >> _color >> _material >> _stack; - - } else { - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i] = new HeightfieldNode(); - _children[i]->readSubdivided(nextState, ancestorState, ancestor); - } - mergeChildren(); - } -} - -void HeightfieldNode::writeSubdivided(HeightfieldStreamState& state, const HeightfieldStreamState& ancestorState, - const HeightfieldNode* ancestor) const { - if (!state.shouldSubdivide()) { - // TODO: subdivision encoding - state.base.stream << _height << _color << _material << _stack; - return; - } - bool leaf = isLeaf(); - state.base.stream << leaf; - if (leaf) { - state.base.stream << _height << _color << _material << _stack; - - } else { - HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->writeSubdivided(nextState, ancestorState, ancestor); - } - } -} - -void HeightfieldNode::clearChildren() { - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i].reset(); - } -} - -void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { - if (isLeaf()) { - return; - } - int heightWidth = 0; - int heightHeight = 0; - int colorWidth = 0; - int colorHeight = 0; - int materialWidth = 0; - int materialHeight = 0; - int stackWidth = 0; - int stackHeight = 0; - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldHeightPointer childHeight = _children[i]->getHeight(); - if (childHeight) { - int childHeightWidth = childHeight->getWidth(); - int childHeightHeight = childHeight->getContents().size() / childHeightWidth; - heightWidth = qMax(heightWidth, childHeightWidth); - heightHeight = qMax(heightHeight, childHeightHeight); - } - HeightfieldColorPointer childColor = _children[i]->getColor(); - if (childColor) { - int childColorWidth = childColor->getWidth(); - int childColorHeight = childColor->getContents().size() / (childColorWidth * DataBlock::COLOR_BYTES); - colorWidth = qMax(colorWidth, childColorWidth); - colorHeight = qMax(colorHeight, childColorHeight); - } - HeightfieldMaterialPointer childMaterial = _children[i]->getMaterial(); - if (childMaterial) { - int childMaterialWidth = childMaterial->getWidth(); - int childMaterialHeight = childMaterial->getContents().size() / childMaterialWidth; - materialWidth = qMax(materialWidth, childMaterialWidth); - materialHeight = qMax(materialHeight, childMaterialHeight); - } - HeightfieldStackPointer childStack = _children[i]->getStack(); - if (childStack) { - int childStackWidth = childStack->getWidth(); - int childStackHeight = childStack->getContents().size() / childStackWidth; - stackWidth = qMax(stackWidth, childStackWidth); - stackHeight = qMax(stackHeight, childStackHeight); - } - } - if (heightWidth > 0 && height) { - QVector heightContents(heightWidth * heightHeight); - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldHeightPointer childHeight = _children[i]->getHeight(); - if (!childHeight) { - continue; - } - int childHeightWidth = childHeight->getWidth(); - int childHeightHeight = childHeight->getContents().size() / childHeightWidth; - if (childHeightWidth != heightWidth || childHeightHeight != heightHeight) { - qWarning() << "Height dimension mismatch [heightWidth=" << heightWidth << ", heightHeight=" << heightHeight << - ", childHeightWidth=" << childHeightWidth << ", childHeightHeight=" << childHeightHeight << "]"; - continue; - } - int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - int innerQuadrantHeightWidth = innerHeightWidth / 2; - int innerQuadrantHeightHeight = innerHeightHeight / 2; - int quadrantHeightWidth = innerQuadrantHeightWidth + HeightfieldHeight::HEIGHT_EXTENSION - 1; - int quadrantHeightHeight = innerQuadrantHeightHeight + HeightfieldHeight::HEIGHT_EXTENSION - 1; - quint16* dest = heightContents.data() + (i & Y_MAXIMUM_FLAG ? (innerQuadrantHeightHeight + 1) * heightWidth : 0) + - (i & X_MAXIMUM_FLAG ? innerQuadrantHeightWidth + 1 : 0); - const quint16* src = childHeight->getContents().constData(); - for (int z = 0; z < quadrantHeightHeight; z++, dest += heightWidth, src += heightWidth * 2) { - const quint16* lineSrc = src; - for (quint16* lineDest = dest, *end = dest + quadrantHeightWidth; lineDest != end; lineDest++, lineSrc += 2) { - *lineDest = *lineSrc; - } - } - } - _height = new HeightfieldHeight(heightWidth, heightContents); - - } else if (height) { - _height.reset(); - } - if (colorWidth > 0 && colorMaterial) { - QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu); - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldColorPointer childColor = _children[i]->getColor(); - if (!childColor) { - continue; - } - int childColorWidth = childColor->getWidth(); - int childColorHeight = childColor->getContents().size() / (childColorWidth * DataBlock::COLOR_BYTES); - if (childColorWidth != colorWidth || childColorHeight != colorHeight) { - qWarning() << "Color dimension mismatch [colorWidth=" << colorWidth << ", colorHeight=" << colorHeight << - ", childColorWidth=" << childColorWidth << ", childColorHeight=" << childColorHeight << "]"; - continue; - } - int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; - int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; - int innerQuadrantColorWidth = innerColorWidth / 2; - int innerQuadrantColorHeight = innerColorHeight / 2; - int quadrantColorWidth = innerQuadrantColorWidth + HeightfieldData::SHARED_EDGE; - int quadrantColorHeight = innerQuadrantColorHeight + HeightfieldData::SHARED_EDGE; - char* dest = colorContents.data() + ((i & Y_MAXIMUM_FLAG ? innerQuadrantColorHeight * colorWidth : 0) + - (i & X_MAXIMUM_FLAG ? innerQuadrantColorWidth : 0)) * DataBlock::COLOR_BYTES; - const uchar* src = (const uchar*)childColor->getContents().constData(); - for (int z = 0; z < quadrantColorHeight; z++, dest += colorWidth * DataBlock::COLOR_BYTES, - src += colorWidth * DataBlock::COLOR_BYTES * 2) { - const uchar* lineSrc = src; - for (char* lineDest = dest, *end = dest + quadrantColorWidth * DataBlock::COLOR_BYTES; - lineDest != end; lineDest += DataBlock::COLOR_BYTES, lineSrc += DataBlock::COLOR_BYTES * 2) { - lineDest[0] = lineSrc[0]; - lineDest[1] = lineSrc[1]; - lineDest[2] = lineSrc[2]; - } - } - } - _color = new HeightfieldColor(colorWidth, colorContents); - - } else { - _color.reset(); - } - if (materialWidth > 0 && colorMaterial) { - QByteArray materialContents(materialWidth * materialHeight, 0); - QVector materials; - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldMaterialPointer childMaterial = _children[i]->getMaterial(); - if (!childMaterial) { - continue; - } - int childMaterialWidth = childMaterial->getWidth(); - int childMaterialHeight = childMaterial->getContents().size() / childMaterialWidth; - if (childMaterialWidth != materialWidth || childMaterialHeight != materialHeight) { - qWarning() << "Material dimension mismatch [materialWidth=" << materialWidth << ", materialHeight=" << - materialHeight << ", childMaterialWidth=" << childMaterialWidth << ", childMaterialHeight=" << - childMaterialHeight << "]"; - continue; - } - int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; - int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; - int innerQuadrantMaterialWidth = innerMaterialWidth / 2; - int innerQuadrantMaterialHeight = innerMaterialHeight / 2; - int quadrantMaterialWidth = innerQuadrantMaterialWidth + HeightfieldData::SHARED_EDGE; - int quadrantMaterialHeight = innerQuadrantMaterialHeight + HeightfieldData::SHARED_EDGE; - uchar* dest = (uchar*)materialContents.data() + - (i & Y_MAXIMUM_FLAG ? innerQuadrantMaterialHeight * materialWidth : 0) + - (i & X_MAXIMUM_FLAG ? innerQuadrantMaterialWidth : 0); - const uchar* src = (const uchar*)childMaterial->getContents().constData(); - QHash materialMap; - for (int z = 0; z < quadrantMaterialHeight; z++, dest += materialWidth, src += materialWidth * 2) { - const uchar* lineSrc = src; - for (uchar* lineDest = dest, *end = dest + quadrantMaterialWidth; lineDest != end; lineDest++, lineSrc += 2) { - int value = *lineSrc; - if (value != 0) { - int& mapping = materialMap[value]; - if (mapping == 0) { - mapping = getMaterialIndex(childMaterial->getMaterials().at(value - 1), - materials, materialContents); - } - value = mapping; - } - *lineDest = value; - } - } - } - _material = new HeightfieldMaterial(materialWidth, materialContents, materials); - - } else { - _material.reset(); - } - if (stackWidth > 0) { - QVector stackContents(stackWidth * stackHeight); - QVector stackMaterials; - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldStackPointer childStack = _children[i]->getStack(); - if (!childStack) { - continue; - } - int childStackWidth = childStack->getWidth(); - int childStackHeight = childStack->getContents().size() / childStackWidth; - if (childStackWidth != stackWidth || childStackHeight != stackHeight) { - qWarning() << "Stack dimension mismatch [stackWidth=" << stackWidth << ", stackHeight=" << stackHeight << - ", childStackWidth=" << childStackWidth << ", childStackHeight=" << childStackHeight << "]"; - continue; - } - int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; - int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; - int innerQuadrantStackWidth = innerStackWidth / 2; - int innerQuadrantStackHeight = innerStackHeight / 2; - int quadrantStackWidth = innerQuadrantStackWidth + HeightfieldData::SHARED_EDGE; - int quadrantStackHeight = innerQuadrantStackHeight + HeightfieldData::SHARED_EDGE; - StackArray* dest = stackContents.data() + (i & Y_MAXIMUM_FLAG ? innerQuadrantStackHeight * stackWidth : 0) + - (i & X_MAXIMUM_FLAG ? innerQuadrantStackWidth : 0); - const StackArray* src = childStack->getContents().constData(); - QHash materialMap; - for (int z = 0; z < quadrantStackHeight; z++, dest += stackWidth, src += stackWidth * 2) { - const StackArray* lineSrc = src; - StackArray* lineDest = dest; - for (int x = 0; x < quadrantStackWidth; x++, lineDest++, lineSrc += 2) { - if (lineSrc->isEmpty()) { - continue; - } - int minimumY = lineSrc->getPosition(); - int maximumY = lineSrc->getPosition() + lineSrc->getEntryCount() - 1; - int newMinimumY = minimumY / 2; - int newMaximumY = maximumY / 2; - *lineDest = StackArray(newMaximumY - newMinimumY + 1); - lineDest->setPosition(newMinimumY); - int y = newMinimumY; - for (StackArray::Entry* destEntry = lineDest->getEntryData(), *end = destEntry + lineDest->getEntryCount(); - destEntry != end; destEntry++, y++) { - int srcY = y * 2; - const StackArray::Entry& srcEntry = lineSrc->getEntry(srcY); - destEntry->color = srcEntry.color; - destEntry->material = srcEntry.material; - if (destEntry->material != 0) { - int& mapping = materialMap[destEntry->material]; - if (mapping == 0) { - mapping = getMaterialIndex(childStack->getMaterials().at(destEntry->material - 1), - stackMaterials, stackContents); - } - destEntry->material = mapping; - } - int srcAlpha = qAlpha(srcEntry.color); - glm::vec3 normal; - if (srcAlpha != lineSrc->getEntryAlpha(srcY + 2)) { - const StackArray::Entry& nextSrcEntry = lineSrc->getEntry(srcY + 1); - if (qAlpha(nextSrcEntry.color) == srcAlpha) { - float distance = nextSrcEntry.getHermiteY(normal); - destEntry->setHermiteY(normal, distance * 0.5f + 0.5f); - } else { - float distance = srcEntry.getHermiteY(normal); - destEntry->setHermiteY(normal, distance * 0.5f); - } - } - if (x != quadrantStackWidth - 1 && srcAlpha != lineSrc[2].getEntryAlpha(srcY)) { - const StackArray::Entry& nextSrcEntry = lineSrc[1].getEntry(srcY); - if (qAlpha(nextSrcEntry.color) == srcAlpha) { - float distance = nextSrcEntry.getHermiteX(normal); - destEntry->setHermiteX(normal, distance * 0.5f + 0.5f); - } else { - float distance = srcEntry.getHermiteX(normal); - destEntry->setHermiteX(normal, distance * 0.5f); - } - } - if (z != quadrantStackHeight - 1 && srcAlpha != lineSrc[2 * stackWidth].getEntryAlpha(srcY)) { - const StackArray::Entry& nextSrcEntry = lineSrc[stackWidth].getEntry(srcY); - if (qAlpha(nextSrcEntry.color) == srcAlpha) { - float distance = nextSrcEntry.getHermiteZ(normal); - destEntry->setHermiteZ(normal, distance * 0.5f + 0.5f); - } else { - float distance = srcEntry.getHermiteZ(normal); - destEntry->setHermiteZ(normal, distance * 0.5f); - } - } - } - } - } - } - _stack = new HeightfieldStack(stackWidth, stackContents, stackMaterials); - - } else { - _stack.reset(); - } -} - -QRgb HeightfieldNode::getColorAt(const glm::vec3& location) const { - if (location.x < 0.0f || location.z < 0.0f || location.x > 1.0f || location.z > 1.0f) { - return 0; - } - int width = _color->getWidth(); - const QByteArray& contents = _color->getContents(); - const uchar* src = (const uchar*)contents.constData(); - int height = contents.size() / (width * DataBlock::COLOR_BYTES); - int innerWidth = width - HeightfieldData::SHARED_EDGE; - int innerHeight = height - HeightfieldData::SHARED_EDGE; - - glm::vec3 relative = location * glm::vec3((float)innerWidth, 1.0f, (float)innerHeight); - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = (int)floors.x; - int floorZ = (int)floors.z; - int ceilX = (int)ceils.x; - int ceilZ = (int)ceils.z; - const uchar* upperLeft = src + (floorZ * width + floorX) * DataBlock::COLOR_BYTES; - const uchar* lowerRight = src + (ceilZ * width + ceilX) * DataBlock::COLOR_BYTES; - glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), - glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - const uchar* upperRight = src + (floorZ * width + ceilX) * DataBlock::COLOR_BYTES; - interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]), - glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - const uchar* lowerLeft = src + (ceilZ * width + floorX) * DataBlock::COLOR_BYTES; - interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), - glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z); - } - return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b); -} - -int HeightfieldNode::getMaterialAt(const glm::vec3& location) const { - if (location.x < 0.0f || location.z < 0.0f || location.x > 1.0f || location.z > 1.0f) { - return -1; - } - int width = _material->getWidth(); - const QByteArray& contents = _material->getContents(); - const uchar* src = (const uchar*)contents.constData(); - int height = contents.size() / width; - int innerWidth = width - HeightfieldData::SHARED_EDGE; - int innerHeight = height - HeightfieldData::SHARED_EDGE; - - glm::vec3 relative = location * glm::vec3((float)innerWidth, 1.0f, (float)innerHeight); - return src[(int)glm::round(relative.z) * width + (int)glm::round(relative.x)]; -} - -void HeightfieldNode::maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, - int innerStackWidth, QVector& heightContents, QVector& stackContents) { - if (normalizeScale == 1.0f && normalizeOffset == 0.0f) { - return; - } - for (quint16* dest = heightContents.data(), *end = dest + heightContents.size(); dest != end; dest++) { - int value = *dest; - if (value != 0) { - *dest = (value + normalizeOffset) * normalizeScale; - } - } - if (stackContents.isEmpty()) { - return; - } - int stackOffset = glm::round(scale.y * normalizeOffset * normalizeScale * innerStackWidth / - (numeric_limits::max() * scale.x)); - for (StackArray* dest = stackContents.data(), *end = dest + stackContents.size(); dest != end; dest++) { - if (!dest->isEmpty()) { - dest->getPositionRef() += stackOffset; - } - } -} - -bool HeightfieldNode::findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float boundsDistance, float& distance) const { - if (!_height) { - return false; - } - int width = _height->getWidth(); - const QVector& contents = _height->getContents(); - const quint16* src = contents.constData(); - int height = contents.size() / width; - int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; - int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; - int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; - - glm::vec3 heightScale((float)innerWidth, (float)numeric_limits::max(), (float)innerHeight); - glm::vec3 dir = direction * heightScale; - glm::vec3 entry = origin * heightScale + dir * boundsDistance; - - entry.x += HeightfieldHeight::HEIGHT_BORDER; - entry.z += HeightfieldHeight::HEIGHT_BORDER; - glm::vec3 floors = glm::floor(entry); - glm::vec3 ceils = glm::ceil(entry); - if (floors.x == ceils.x) { - if (dir.x > 0.0f) { - ceils.x += 1.0f; - } else { - floors.x -= 1.0f; - } - } - if (floors.z == ceils.z) { - if (dir.z > 0.0f) { - ceils.z += 1.0f; - } else { - floors.z -= 1.0f; - } - } - - bool withinBounds = true; - float accumulatedDistance = 0.0f; - while (withinBounds) { - // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - float upperLeft = src[floorZ * width + floorX]; - float upperRight = src[floorZ * width + ceilX]; - float lowerLeft = src[ceilZ * width + floorX]; - float lowerRight = src[ceilZ * width + ceilX]; - - // find the distance to the next x coordinate - float xDistance = FLT_MAX; - if (dir.x > 0.0f) { - xDistance = (ceils.x - entry.x) / dir.x; - } else if (dir.x < 0.0f) { - xDistance = (floors.x - entry.x) / dir.x; - } - - // and the distance to the next z coordinate - float zDistance = FLT_MAX; - if (dir.z > 0.0f) { - zDistance = (ceils.z - entry.z) / dir.z; - } else if (dir.z < 0.0f) { - zDistance = (floors.z - entry.z) / dir.z; - } - - // the exit distance is the lower of those two - float exitDistance = qMin(xDistance, zDistance); - glm::vec3 exit, nextFloors = floors, nextCeils = ceils; - if (exitDistance == FLT_MAX) { - if (dir.y > 0.0f) { - return false; // line points upwards; no collisions possible - } - withinBounds = false; // line points downwards; check this cell only - - } else { - // find the exit point and the next cell, and determine whether it's still within the bounds - exit = entry + exitDistance * dir; - withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits::max()); - if (exitDistance == xDistance) { - if (dir.x > 0.0f) { - nextFloors.x += 1.0f; - withinBounds &= (nextCeils.x += 1.0f) <= highestX; - } else { - withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; - nextCeils.x -= 1.0f; - } - } - if (exitDistance == zDistance) { - if (dir.z > 0.0f) { - nextFloors.z += 1.0f; - withinBounds &= (nextCeils.z += 1.0f) <= highestZ; - } else { - withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; - nextCeils.z -= 1.0f; - } - } - // check the vertical range of the ray against the ranges of the cell heights - if (upperLeft == 0 || upperRight == 0 || lowerLeft == 0 || lowerRight == 0 || - qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || - qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - continue; - } - } - // having passed the bounds check, we must check against the planes - glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); - - // first check the triangle including the Z+ segment - glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); - float lowerProduct = glm::dot(lowerNormal, dir); - if (lowerProduct < 0.0f) { - float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; - glm::vec3 intersection = relativeEntry + planeDistance * dir; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.z >= intersection.x) { - distance = boundsDistance + accumulatedDistance + planeDistance; - return true; - } - } - - // then the one with the X+ segment - glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); - float upperProduct = glm::dot(upperNormal, dir); - if (upperProduct < 0.0f) { - float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; - glm::vec3 intersection = relativeEntry + planeDistance * dir; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.x >= intersection.z) { - distance = boundsDistance + accumulatedDistance + planeDistance; - return true; - } - } - - // no joy; continue on our way - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - } - - return false; -} - -static inline float mixHeights(float firstHeight, float secondHeight, float t) { - return (firstHeight == 0.0f) ? secondHeight : (secondHeight == 0.0f ? firstHeight : - glm::mix(firstHeight, secondHeight, t)); -} - -HeightfieldNode* HeightfieldNode::subdivide(const QVector& heightContents, - const QVector& stackContents) const { - HeightfieldNode* newNode = new HeightfieldNode(*this); - int heightWidth = _height->getWidth(); - int heightHeight = heightContents.size() / heightWidth; - newNode->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, heightContents))); - int stackWidth = 0, stackHeight = 0; - QVector stackMaterials; - if (_stack) { - stackWidth = _stack->getWidth(); - stackHeight = stackContents.size() / stackWidth; - stackMaterials = _stack->getMaterials(); - newNode->setStack(HeightfieldStackPointer(new HeightfieldStack(stackWidth, stackContents, stackMaterials))); - } - int colorWidth = 0, colorHeight = 0; - if (_color) { - colorWidth = _color->getWidth(); - colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - } - int materialWidth = 0, materialHeight = 0; - QVector materialMaterials; - if (_material) { - materialWidth = _material->getWidth(); - materialHeight = _material->getContents().size() / materialWidth; - materialMaterials = _material->getMaterials(); - } - for (int i = 0; i < CHILD_COUNT; i++) { - QVector childHeightContents(heightWidth * heightHeight); - QByteArray childColorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu); - QByteArray childMaterialContents(materialWidth * materialHeight, 0); - QVector childStackContents(stackWidth * stackHeight); - - quint16* heightDest = childHeightContents.data(); - const quint16* heightSrc = heightContents.constData() + (i & Y_MAXIMUM_FLAG ? (heightHeight / 2) * heightWidth : 0) + - (i & X_MAXIMUM_FLAG ? heightWidth / 2 : 0); - for (int z = 0; z < heightHeight; z++) { - float srcZ = z * 0.5f + 0.5f; - float fractZ = glm::fract(srcZ); - const quint16* heightSrcZ = heightSrc + (int)srcZ * heightWidth; - for (int x = 0; x < heightWidth; x++) { - float srcX = x * 0.5f + 0.5f; - float fractX = glm::fract(srcX); - const quint16* heightSrcX = heightSrcZ + (int)srcX; - if (fractZ == 0.0f) { - if (fractX == 0.0f) { - *heightDest++ = heightSrcX[0]; - } else { - *heightDest++ = mixHeights(heightSrcX[0], heightSrcX[1], fractX); - } - } else { - if (fractX == 0.0f) { - *heightDest++ = mixHeights(heightSrcX[0], heightSrcX[heightWidth], fractZ); - } else { - *heightDest++ = mixHeights(mixHeights(heightSrcX[0], heightSrcX[1], fractX), - mixHeights(heightSrcX[heightWidth], heightSrcX[heightWidth + 1], fractX), fractZ); - } - } - } - } - - if (colorWidth != 0) { - char* colorDest = childColorContents.data(); - const uchar* colorSrc = (const uchar*)_color->getContents().constData() + - ((i & Y_MAXIMUM_FLAG ? (colorHeight / 2) * colorWidth : 0) + - (i & X_MAXIMUM_FLAG ? colorWidth / 2 : 0)) * DataBlock::COLOR_BYTES; - for (int z = 0; z < colorHeight; z++) { - float srcZ = z * 0.5f; - float fractZ = glm::fract(srcZ); - const uchar* colorSrcZ = colorSrc + (int)srcZ * colorWidth * DataBlock::COLOR_BYTES; - for (int x = 0; x < colorWidth; x++) { - float srcX = x * 0.5f; - float fractX = glm::fract(srcX); - const uchar* colorSrcX = colorSrcZ + (int)srcX * DataBlock::COLOR_BYTES; - const uchar* nextColorSrcX = colorSrcX + colorWidth * DataBlock::COLOR_BYTES; - if (fractZ == 0.0f) { - if (fractX == 0.0f) { - *colorDest++ = colorSrcX[0]; - *colorDest++ = colorSrcX[1]; - *colorDest++ = colorSrcX[2]; - } else { - *colorDest++ = glm::mix(colorSrcX[0], colorSrcX[3], fractX); - *colorDest++ = glm::mix(colorSrcX[1], colorSrcX[4], fractX); - *colorDest++ = glm::mix(colorSrcX[2], colorSrcX[5], fractX); - } - } else { - if (fractX == 0.0f) { - *colorDest++ = glm::mix(colorSrcX[0], nextColorSrcX[0], fractZ); - *colorDest++ = glm::mix(colorSrcX[1], nextColorSrcX[1], fractZ); - *colorDest++ = glm::mix(colorSrcX[2], nextColorSrcX[2], fractZ); - } else { - *colorDest++ = glm::mix(glm::mix(colorSrcX[0], colorSrcX[3], fractX), - glm::mix(nextColorSrcX[0], nextColorSrcX[3], fractX), fractZ); - *colorDest++ = glm::mix(glm::mix(colorSrcX[1], colorSrcX[4], fractX), - glm::mix(nextColorSrcX[1], nextColorSrcX[4], fractX), fractZ); - *colorDest++ = glm::mix(glm::mix(colorSrcX[2], colorSrcX[5], fractX), - glm::mix(nextColorSrcX[2], nextColorSrcX[5], fractX), fractZ); - } - } - } - } - } - - if (materialWidth != 0) { - char* materialDest = childMaterialContents.data(); - const char* materialSrc = _material->getContents().constData() + - (i & Y_MAXIMUM_FLAG ? (materialHeight / 2) * materialWidth : 0) + - (i & X_MAXIMUM_FLAG ? materialWidth / 2 : 0); - for (int z = 0; z < materialHeight; z++) { - float srcZ = z * 0.5f; - const char* materialSrcZ = materialSrc + (int)srcZ * materialWidth; - for (int x = 0; x < materialWidth; x++) { - float srcX = x * 0.5f; - const char* materialSrcX = materialSrcZ + (int)srcX; - *materialDest++ = *materialSrcX; - } - } - } - - if (stackWidth != 0) { - StackArray* stackDest = childStackContents.data(); - const StackArray* stackSrc = _stack->getContents().constData() + - (i & Y_MAXIMUM_FLAG ? (stackHeight / 2) * stackWidth : 0) + - (i & X_MAXIMUM_FLAG ? stackWidth / 2 : 0); - for (int z = 0; z < stackHeight; z++) { - float srcZ = z * 0.5f; - float fractZ = glm::fract(srcZ); - const StackArray* stackSrcZ = stackSrc + (int)srcZ * stackWidth; - for (int x = 0; x < stackWidth; x++) { - float srcX = x * 0.5f; - float fractX = glm::fract(srcX); - const StackArray* stackSrcX = stackSrcZ + (int)srcX; - if (stackSrcX->isEmpty()) { - stackDest++; - continue; - } - int minimumY = stackSrcX->getPosition() * 2; - int maximumY = (stackSrcX->getPosition() + stackSrcX->getEntryCount() - 1) * 2; - *stackDest = StackArray(maximumY - minimumY + 1); - stackDest->setPosition(minimumY); - for (int y = minimumY; y <= maximumY; y++) { - float srcY = y * 0.5f; - float fractY = glm::fract(srcY); - const StackArray::Entry& srcEntry = stackSrcX->getEntry((int)srcY); - StackArray::Entry& destEntry = stackDest->getEntry(y); - destEntry.color = srcEntry.color; - destEntry.material = srcEntry.material; - if (srcEntry.hermiteX != 0) { - glm::vec3 normal; - float distance = srcEntry.getHermiteX(normal); - if (distance < fractX) { - const StackArray::Entry& nextSrcEntryX = stackSrcX[1].getEntry((int)srcY); - destEntry.color = nextSrcEntryX.color; - destEntry.material = nextSrcEntryX.material; - - } else { - destEntry.setHermiteX(normal, (distance - fractX) / 0.5f); - } - } - if (srcEntry.hermiteY != 0) { - glm::vec3 normal; - float distance = srcEntry.getHermiteY(normal); - if (distance < fractY) { - const StackArray::Entry& nextSrcEntryY = stackSrcX->getEntry((int)srcY + 1); - destEntry.color = nextSrcEntryY.color; - destEntry.material = nextSrcEntryY.material; - - } else { - destEntry.setHermiteY(normal, (distance - fractY) / 0.5f); - } - } - if (srcEntry.hermiteZ != 0) { - glm::vec3 normal; - float distance = srcEntry.getHermiteZ(normal); - if (distance < fractZ) { - const StackArray::Entry& nextSrcEntryZ = stackSrcX[stackWidth].getEntry((int)srcY); - destEntry.color = nextSrcEntryZ.color; - destEntry.material = nextSrcEntryZ.material; - - } else { - destEntry.setHermiteZ(normal, (distance - fractZ) / 0.5f); - } - } - } - stackDest++; - } - } - } - - newNode->setChild(i, HeightfieldNodePointer(new HeightfieldNode( - HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, childHeightContents)), - HeightfieldColorPointer(colorWidth == 0 ? NULL : new HeightfieldColor(colorWidth, childColorContents)), - HeightfieldMaterialPointer(materialWidth == 0 ? NULL : - new HeightfieldMaterial(materialWidth, childMaterialContents, materialMaterials)), - HeightfieldStackPointer(stackWidth == 0 ? NULL : - new HeightfieldStack(stackWidth, childStackContents, stackMaterials))))); - } - return newNode; -} - -AbstractHeightfieldNodeRenderer::~AbstractHeightfieldNodeRenderer() { -} - -bool AbstractHeightfieldNodeRenderer::findRayIntersection(const glm::vec3& translation, - const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction, - float boundsDistance, float& distance) const { - return false; -} - -Heightfield::Heightfield() : - _aspectY(1.0f), - _aspectZ(1.0f) { - - connect(this, &Heightfield::translationChanged, this, &Heightfield::updateBounds); - connect(this, &Heightfield::rotationChanged, this, &Heightfield::updateBounds); - connect(this, &Heightfield::scaleChanged, this, &Heightfield::updateBounds); - connect(this, &Heightfield::aspectYChanged, this, &Heightfield::updateBounds); - connect(this, &Heightfield::aspectZChanged, this, &Heightfield::updateBounds); - updateBounds(); - - connect(this, &Heightfield::heightChanged, this, &Heightfield::updateRoot); - connect(this, &Heightfield::colorChanged, this, &Heightfield::updateRoot); - connect(this, &Heightfield::materialChanged, this, &Heightfield::updateRoot); - connect(this, &Heightfield::stackChanged, this, &Heightfield::updateRoot); - updateRoot(); -} - -void Heightfield::setAspectY(float aspectY) { - if (_aspectY != aspectY) { - emit aspectYChanged(_aspectY = aspectY); - } -} - -void Heightfield::setAspectZ(float aspectZ) { - if (_aspectZ != aspectZ) { - emit aspectZChanged(_aspectZ = aspectZ); - } -} - -void Heightfield::setHeight(const HeightfieldHeightPointer& height) { - if (_height != height) { - emit heightChanged(_height = height); - } -} - -void Heightfield::setColor(const HeightfieldColorPointer& color) { - if (_color != color) { - emit colorChanged(_color = color); - } -} - -void Heightfield::setMaterial(const HeightfieldMaterialPointer& material) { - if (_material != material) { - emit materialChanged(_material = material); - } -} - -void Heightfield::setStack(const HeightfieldStackPointer& stack) { - if (_stack != stack) { - emit stackChanged(_stack = stack); - } -} - -MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { - // after transforming into unit space, we scale the threshold in proportion to vertical distance - glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); - glm::vec3 position = glm::inverse(getRotation()) * (lod.position - getTranslation()) * inverseScale; - const float THRESHOLD_MULTIPLIER = 256.0f; - return MetavoxelLOD(glm::vec3(position.x, position.z, 0.0f), lod.threshold * - qMax(0.5f, glm::abs(position.y * _aspectY - 0.5f)) * THRESHOLD_MULTIPLIER); -} - -SharedObject* Heightfield::clone(bool withID, SharedObject* target) const { - Heightfield* newHeightfield = static_cast(Spanner::clone(withID, target)); - newHeightfield->setHeight(_height); - newHeightfield->setColor(_color); - newHeightfield->setMaterial(_material); - newHeightfield->setRoot(_root); - return newHeightfield; -} - -bool Heightfield::isHeightfield() const { - return true; -} - -float Heightfield::getHeight(const glm::vec3& location) const { - float distance; - glm::vec3 down = getRotation() * glm::vec3(0.0f, -1.0f, 0.0f); - glm::vec3 origin = location - down * (glm::dot(down, location) + getScale() * _aspectY - glm::dot(down, getTranslation())); - if (findRayIntersection(origin, down, distance)) { - return origin.y + distance * down.y; - } - return -FLT_MAX; -} - -bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - return _root->findRayIntersection(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, - getScale() * _aspectZ), origin, direction, distance); -} - -Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height, - bool set, bool erase, float granularity) { - // first see if we're going to exceed the range limits - float minimumValue = 1.0f, maximumValue = numeric_limits::max(); - if (set) { - float heightValue = height * numeric_limits::max() / (getScale() * _aspectY); - minimumValue = qMin(minimumValue, heightValue); - maximumValue = qMax(maximumValue, heightValue); - - } else if (!erase) { - _root->getRangeAfterHeightPaint(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, - getScale() * _aspectZ), position, radius, height, minimumValue, maximumValue); - } - - // normalize if necessary - float normalizeScale, normalizeOffset; - Heightfield* newHeightfield = prepareEdit(minimumValue, maximumValue, normalizeScale, normalizeOffset); - newHeightfield->setRoot(HeightfieldNodePointer(_root->paintHeight(newHeightfield->getTranslation(), getRotation(), - glm::vec3(getScale(), getScale() * newHeightfield->getAspectY(), getScale() * _aspectZ), position, radius, height, - set, erase, normalizeScale, normalizeOffset, granularity))); - return newHeightfield; -} - -Spanner* Heightfield::fillHeight(const glm::vec3& position, float radius, float granularity) { - Heightfield* newHeightfield = static_cast(clone(true)); - newHeightfield->setRoot(HeightfieldNodePointer(_root->fillHeight(getTranslation(), getRotation(), - glm::vec3(getScale(), getScale() * _aspectY, getScale() * _aspectZ), position, radius, granularity))); - return newHeightfield; -} - -Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint, bool voxelize, float granularity) { - // first see if we're going to exceed the range limits, normalizing if necessary - Spanner* spannerData = static_cast(spanner.data()); - float normalizeScale = 1.0f, normalizeOffset = 0.0f; - Heightfield* newHeightfield; - if (paint) { - newHeightfield = static_cast(clone(true)); - } else { - float minimumValue = 1.0f, maximumValue = numeric_limits::max(); - _root->getRangeAfterEdit(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, - getScale() * _aspectZ), spannerData->getBounds(), minimumValue, maximumValue); - newHeightfield = prepareEdit(minimumValue, maximumValue, normalizeScale, normalizeOffset); - } - newHeightfield->setRoot(HeightfieldNodePointer(_root->setMaterial(newHeightfield->getTranslation(), getRotation(), - glm::vec3(getScale(), getScale() * newHeightfield->getAspectY(), getScale() * _aspectZ), spannerData, - material, color, paint, voxelize, normalizeScale, normalizeOffset, granularity))); - return newHeightfield; -} - -bool Heightfield::hasOwnColors() const { - return _color; -} - -bool Heightfield::hasOwnMaterials() const { - return _material; -} - -QRgb Heightfield::getColorAt(const glm::vec3& point) { - int width = _color->getWidth(); - const QByteArray& contents = _color->getContents(); - const uchar* src = (const uchar*)contents.constData(); - int height = contents.size() / (width * DataBlock::COLOR_BYTES); - int innerWidth = width - HeightfieldData::SHARED_EDGE; - int innerHeight = height - HeightfieldData::SHARED_EDGE; - - glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), - 1.0f, innerHeight / (getScale() * _aspectZ)); - if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { - return 0; - } - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = (int)floors.x; - int floorZ = (int)floors.z; - int ceilX = (int)ceils.x; - int ceilZ = (int)ceils.z; - const uchar* upperLeft = src + (floorZ * width + floorX) * DataBlock::COLOR_BYTES; - const uchar* lowerRight = src + (ceilZ * width + ceilX) * DataBlock::COLOR_BYTES; - glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), - glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - const uchar* upperRight = src + (floorZ * width + ceilX) * DataBlock::COLOR_BYTES; - interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]), - glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - const uchar* lowerLeft = src + (ceilZ * width + floorX) * DataBlock::COLOR_BYTES; - interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), - glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z); - } - return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b); -} - -int Heightfield::getMaterialAt(const glm::vec3& point) { - int width = _material->getWidth(); - const QByteArray& contents = _material->getContents(); - const uchar* src = (const uchar*)contents.constData(); - int height = contents.size() / width; - int innerWidth = width - HeightfieldData::SHARED_EDGE; - int innerHeight = height - HeightfieldData::SHARED_EDGE; - - glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), - 1.0f, innerHeight / (getScale() * _aspectZ)); - if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { - return -1; - } - return src[(int)glm::round(relative.z) * width + (int)glm::round(relative.x)]; -} - -QVector& Heightfield::getMaterials() { - return _material->getMaterials(); -} - -bool Heightfield::contains(const glm::vec3& point) { - if (!_height) { - return false; - } - int width = _height->getWidth(); - const QVector& contents = _height->getContents(); - const quint16* src = contents.constData(); - int height = contents.size() / width; - int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; - int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; - int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; - - glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), - numeric_limits::max() / (getScale() * _aspectY), innerHeight / (getScale() * _aspectZ)); - if (relative.x < 0.0f || relative.y < 0.0f || relative.z < 0.0f || relative.x > innerWidth || - relative.y > numeric_limits::max() || relative.z > innerHeight) { - return false; - } - relative.x += HeightfieldHeight::HEIGHT_BORDER; - relative.z += HeightfieldHeight::HEIGHT_BORDER; - - // find the bounds of the cell containing the point and the shared vertex heights - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - float upperLeft = src[floorZ * width + floorX]; - float lowerRight = src[ceilZ * width + ceilX]; - float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - float upperRight = src[floorZ * width + ceilX]; - interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), - (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - float lowerLeft = src[ceilZ * width + floorX]; - interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); - } - if (interpolatedHeight == 0.0f) { - return false; // ignore zero values - } - - // compare - return relative.y <= interpolatedHeight; -} - -bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { - int width = _height->getWidth(); - const QVector& contents = _height->getContents(); - const quint16* src = contents.constData(); - int height = contents.size() / width; - int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; - int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; - int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; - - glm::quat inverseRotation = glm::inverse(getRotation()); - glm::vec3 inverseScale(innerWidth / getScale(), numeric_limits::max() / (getScale() * _aspectY), - innerHeight / (getScale() * _aspectZ)); - glm::vec3 direction = end - start; - glm::vec3 dir = inverseRotation * direction * inverseScale; - glm::vec3 entry = inverseRotation * (start - getTranslation()) * inverseScale; - - float boundsDistance; - if (!Box(glm::vec3(), glm::vec3((float)innerWidth, (float)numeric_limits::max(), - (float)innerHeight)).findRayIntersection(entry, dir, boundsDistance) || boundsDistance > 1.0f) { - return false; - } - entry += dir * boundsDistance; - - const float DISTANCE_THRESHOLD = 0.001f; - if (glm::abs(entry.x - 0.0f) < DISTANCE_THRESHOLD) { - normal = getRotation() * glm::vec3(-1.0f, 0.0f, 0.0f); - distance = boundsDistance; - return true; - - } else if (glm::abs(entry.x - innerWidth) < DISTANCE_THRESHOLD) { - normal = getRotation() * glm::vec3(1.0f, 0.0f, 0.0f); - distance = boundsDistance; - return true; - - } else if (glm::abs(entry.y - 0.0f) < DISTANCE_THRESHOLD) { - normal = getRotation() * glm::vec3(0.0f, -1.0f, 0.0f); - distance = boundsDistance; - return true; - - } else if (glm::abs(entry.y - numeric_limits::max()) < DISTANCE_THRESHOLD) { - normal = getRotation() * glm::vec3(0.0f, 1.0f, 0.0f); - distance = boundsDistance; - return true; - - } else if (glm::abs(entry.z - 0.0f) < DISTANCE_THRESHOLD) { - normal = getRotation() * glm::vec3(0.0f, 0.0f, -1.0f); - distance = boundsDistance; - return true; - - } else if (glm::abs(entry.z - innerHeight) < DISTANCE_THRESHOLD) { - normal = getRotation() * glm::vec3(0.0f, 0.0f, 1.0f); - distance = boundsDistance; - return true; - } - - entry.x += HeightfieldHeight::HEIGHT_BORDER; - entry.z += HeightfieldHeight::HEIGHT_BORDER; - glm::vec3 floors = glm::floor(entry); - glm::vec3 ceils = glm::ceil(entry); - if (floors.x == ceils.x) { - if (dir.x > 0.0f) { - ceils.x += 1.0f; - } else { - floors.x -= 1.0f; - } - } - if (floors.z == ceils.z) { - if (dir.z > 0.0f) { - ceils.z += 1.0f; - } else { - floors.z -= 1.0f; - } - } - - glm::vec3 normalScale(1.0f / (inverseScale.y * inverseScale.z), 1.0f / (inverseScale.x * inverseScale.z), - 1.0f / (inverseScale.x * inverseScale.y)); - - bool withinBounds = true; - float accumulatedDistance = boundsDistance; - while (withinBounds && accumulatedDistance <= 1.0f) { - // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - float upperLeft = src[floorZ * width + floorX]; - float upperRight = src[floorZ * width + ceilX]; - float lowerLeft = src[ceilZ * width + floorX]; - float lowerRight = src[ceilZ * width + ceilX]; - - // find the distance to the next x coordinate - float xDistance = FLT_MAX; - if (dir.x > 0.0f) { - xDistance = (ceils.x - entry.x) / dir.x; - } else if (dir.x < 0.0f) { - xDistance = (floors.x - entry.x) / dir.x; - } - - // and the distance to the next z coordinate - float zDistance = FLT_MAX; - if (dir.z > 0.0f) { - zDistance = (ceils.z - entry.z) / dir.z; - } else if (dir.z < 0.0f) { - zDistance = (floors.z - entry.z) / dir.z; - } - - // the exit distance is the lower of those two - float exitDistance = qMin(xDistance, zDistance); - glm::vec3 exit, nextFloors = floors, nextCeils = ceils; - if (exitDistance == FLT_MAX) { - withinBounds = false; // line points upwards/downwards; check this cell only - - } else { - // find the exit point and the next cell, and determine whether it's still within the bounds - exit = entry + exitDistance * dir; - withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits::max()); - if (exitDistance == xDistance) { - if (dir.x > 0.0f) { - nextFloors.x += 1.0f; - withinBounds &= (nextCeils.x += 1.0f) <= highestX; - } else { - withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; - nextCeils.x -= 1.0f; - } - } - if (exitDistance == zDistance) { - if (dir.z > 0.0f) { - nextFloors.z += 1.0f; - withinBounds &= (nextCeils.z += 1.0f) <= highestZ; - } else { - withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; - nextCeils.z -= 1.0f; - } - } - // check the vertical range of the ray against the ranges of the cell heights - if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || - qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - continue; - } - } - // having passed the bounds check, we must check against the planes - glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); - - // first check the triangle including the Z+ segment - glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); - float lowerProduct = glm::dot(lowerNormal, dir); - if (lowerProduct != 0.0f) { - float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; - glm::vec3 intersection = relativeEntry + planeDistance * dir; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.z >= intersection.x) { - distance = accumulatedDistance + planeDistance; - normal = glm::normalize(getRotation() * (lowerNormal * normalScale)); - return true; - } - } - - // then the one with the X+ segment - glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); - float upperProduct = glm::dot(upperNormal, dir); - if (upperProduct != 0.0f) { - float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; - glm::vec3 intersection = relativeEntry + planeDistance * dir; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.x >= intersection.z) { - distance = accumulatedDistance + planeDistance; - normal = glm::normalize(getRotation() * (upperNormal * normalScale)); - return true; - } - } - - // no joy; continue on our way - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - } - - return false; -} - -void Heightfield::writeExtra(Bitstream& out) const { - if (getWillBeVoxelized()) { - out << _height << _color << _material << _stack; - return; - } - MetavoxelLOD lod; - if (out.getContext()) { - lod = transformLOD(static_cast(out.getContext())->lod); - } - HeightfieldStreamBase base = { out, lod, lod }; - HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - _root->write(state); -} - -void Heightfield::readExtra(Bitstream& in, bool reread) { - if (getWillBeVoxelized()) { - if (reread) { - HeightfieldHeightPointer height; - HeightfieldColorPointer color; - HeightfieldMaterialPointer material; - HeightfieldStackPointer stack; - in >> height >> color >> material >> stack; - - } else { - in >> _height >> _color >> _material >> _stack; - } - return; - } - MetavoxelLOD lod; - if (in.getContext()) { - lod = transformLOD(static_cast(in.getContext())->lod); - } - HeightfieldStreamBase base = { in, lod, lod }; - HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - - HeightfieldNodePointer root(new HeightfieldNode()); - root->read(state); - if (!reread) { - setRoot(root); - } -} - -void Heightfield::writeExtraDelta(Bitstream& out, const SharedObject* reference) const { - MetavoxelLOD lod, referenceLOD; - if (out.getContext()) { - MetavoxelStreamBase* base = static_cast(out.getContext()); - lod = transformLOD(base->lod); - referenceLOD = transformLOD(base->referenceLOD); - } - HeightfieldStreamBase base = { out, lod, referenceLOD }; - HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - const HeightfieldNodePointer& referenceRoot = static_cast(reference)->getRoot(); - if (_root == referenceRoot) { - out << false; - if (state.becameSubdivided()) { - _root->writeSubdivision(state); - } - } else { - out << true; - _root->writeDelta(referenceRoot, state); - } -} - -void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) { - MetavoxelLOD lod, referenceLOD; - if (in.getContext()) { - MetavoxelStreamBase* base = static_cast(in.getContext()); - lod = transformLOD(base->lod); - referenceLOD = transformLOD(base->referenceLOD); - } - HeightfieldStreamBase base = { in, lod, referenceLOD }; - HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - - bool changed; - in >> changed; - if (changed) { - HeightfieldNodePointer root(new HeightfieldNode()); - root->readDelta(static_cast(reference)->getRoot(), state); - if (!reread) { - setRoot(root); - } - } else if (state.becameSubdividedOrCollapsed()) { - HeightfieldNodePointer root(_root->readSubdivision(state)); - if (!reread) { - setRoot(root); - } - } else if (!reread) { - setRoot(static_cast(reference)->getRoot()); - } -} - -void Heightfield::maybeWriteSubdivision(Bitstream& out) { - MetavoxelLOD lod, referenceLOD; - if (out.getContext()) { - MetavoxelStreamBase* base = static_cast(out.getContext()); - lod = transformLOD(base->lod); - referenceLOD = transformLOD(base->referenceLOD); - } - HeightfieldStreamBase base = { out, lod, referenceLOD }; - HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - - if (state.becameSubdividedOrCollapsed()) { - out << SharedObjectPointer(this); - _root->writeSubdivision(state); - } -} - -SharedObject* Heightfield::readSubdivision(Bitstream& in) { - MetavoxelLOD lod, referenceLOD; - if (in.getContext()) { - MetavoxelStreamBase* base = static_cast(in.getContext()); - lod = transformLOD(base->lod); - referenceLOD = transformLOD(base->referenceLOD); - } - HeightfieldStreamBase base = { in, lod, referenceLOD }; - HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - - if (state.becameSubdividedOrCollapsed()) { - HeightfieldNodePointer root(_root->readSubdivision(state)); - if (_root != root) { - Heightfield* newHeightfield = static_cast(clone(true)); - newHeightfield->setRemoteID(getRemoteID()); - newHeightfield->setRemoteOriginID(getRemoteOriginID()); - newHeightfield->setRoot(root); - return newHeightfield; - } - } - return this; -} - -QByteArray Heightfield::getRendererClassName() const { - return "HeightfieldRenderer"; -} - -void Heightfield::updateBounds() { - glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ); - glm::mat4 rotationMatrix = glm::mat4_cast(getRotation()); - setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(glm::vec3(), extent)); -} - -void Heightfield::updateRoot() { - HeightfieldNodePointer root(new HeightfieldNode()); - if (_height) { - root->setContents(_height, _color, _material, _stack); - } - setRoot(root); -} - -Heightfield* Heightfield::prepareEdit(float minimumValue, float maximumValue, float& normalizeScale, float& normalizeOffset) { - // renormalize if necessary - Heightfield* newHeightfield = static_cast(clone(true)); - if (minimumValue < 1.0f || maximumValue > numeric_limits::max()) { - normalizeScale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); - normalizeOffset = 1.0f - minimumValue; - newHeightfield->setAspectY(_aspectY / normalizeScale); - newHeightfield->setTranslation(getTranslation() - getRotation() * - glm::vec3(0.0f, normalizeOffset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); - } else { - normalizeScale = 1.0f; - normalizeOffset = 0.0f; - } - return newHeightfield; -} - diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h deleted file mode 100644 index b60a104d68..0000000000 --- a/libraries/metavoxels/src/Spanner.h +++ /dev/null @@ -1,866 +0,0 @@ -// -// Spanner.h -// libraries/metavoxels/src -// -// Created by Andrzej Kapolka on 11/10/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Spanner_h -#define hifi_Spanner_h - -#include - -#include "AttributeRegistry.h" -#include "MetavoxelUtil.h" - -class AbstractHeightfieldNodeRenderer; -class DataBlock; -class Heightfield; -class HeightfieldColor; -class HeightfieldHeight; -class HeightfieldMaterial; -class HeightfieldNode; -class HeightfieldStack; -class SpannerRenderer; - -/// An object that spans multiple octree cells. -class Spanner : public SharedObject { - Q_OBJECT - Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) - Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false) - Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false) - Q_PROPERTY(bool willBeVoxelized MEMBER _willBeVoxelized DESIGNABLE false) - -public: - - /// Returns the value of the global visit counter and increments it. - static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); } - - Spanner(); - - void setBounds(const Box& bounds); - const Box& getBounds() const { return _bounds; } - - void setPlacementGranularity(float granularity) { _placementGranularity = granularity; } - float getPlacementGranularity() const { return _placementGranularity; } - - void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; } - float getVoxelizationGranularity() const { return _voxelizationGranularity; } - - void setMerged(bool merged) { _merged = merged; } - bool isMerged() const { return _merged; } - - void setWillBeVoxelized(bool willBeVoxelized) { _willBeVoxelized = willBeVoxelized; } - bool getWillBeVoxelized() const { return _willBeVoxelized; } - - /// 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); - - /// Returns a pointer to the renderer, creating it if necessary. - SpannerRenderer* getRenderer(); - - /// Checks whether this is a heightfield. - virtual bool isHeightfield() const; - - /// Finds the height at the specified location, or returns -FLT_MAX for none. - virtual float getHeight(const glm::vec3& location) const; - - /// Finds the intersection between the described ray and this spanner. - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - - /// Attempts to modify the spanner's height. - /// \param set whether to set the height as opposed to raising/lowering it - /// \param erase whether to erase height values - /// \return the modified spanner, or this if no modification was performed - virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, - bool set, bool erase, float granularity); - - /// Attempts to fill the spanner's height (adding removing volumetric information). - /// \return the modified spanner, or this if no modification was performed - virtual Spanner* fillHeight(const glm::vec3& position, float radius, float granularity); - - /// Attempts to "sculpt" or "paint," etc., with the supplied spanner. - /// \return the modified spanner, or this if no modification was performed - virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint, bool voxelize, float granularity); - - /// Checks whether this spanner has its own colors. - virtual bool hasOwnColors() const; - - /// Checks whether this spanner has its own materials. - virtual bool hasOwnMaterials() const; - - /// Checks whether the spanner contains the specified point. - virtual bool contains(const glm::vec3& point); - - /// Retrieves the color at the specified point. - virtual QRgb getColorAt(const glm::vec3& point); - - /// Retrieves the material at the specified point. - virtual int getMaterialAt(const glm::vec3& point); - - /// Retrieves a reference to the list of materials. - virtual QVector& getMaterials(); - - /// Finds the intersection, if any, between the specified line segment and the spanner. - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - -signals: - - void boundsWillChange(); - void boundsChanged(const Box& bounds); - -protected: - - SpannerRenderer* _renderer; - - /// Returns the name of the class to instantiate in order to render this spanner. - virtual QByteArray getRendererClassName() const; - -private: - - Box _bounds; - float _placementGranularity; - float _voxelizationGranularity; - bool _merged; - bool _willBeVoxelized; - QHash _lastVisits; ///< last visit identifiers for each thread - QMutex _lastVisitsMutex; - - static QAtomicInt _nextVisit; ///< the global visit counter -}; - -/// 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(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - -protected: - - Spanner* _spanner; -}; - -/// 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::quat 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::quat& rotation); - const glm::quat& getRotation() const { return _rotation; } - - void setScale(float scale); - float getScale() const { return _scale; } - -signals: - - void translationChanged(const glm::vec3& translation); - void rotationChanged(const glm::quat& rotation); - void scaleChanged(float scale); - -private: - - glm::vec3 _translation; - glm::quat _rotation; - float _scale; -}; - -/// A transformable object with a color. -class ColorTransformable : public Transformable { - Q_OBJECT - Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged DESIGNABLE false) - -public: - - ColorTransformable(); - - void setColor(const QColor& color); - const QColor& getColor() const { return _color; } - -signals: - - void colorChanged(const QColor& color); - -protected: - - QColor _color; -}; - -/// A sphere. -class Sphere : public ColorTransformable { - Q_OBJECT - -public: - - Q_INVOKABLE Sphere(); - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - virtual bool contains(const glm::vec3& point); - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - -protected: - - virtual QByteArray getRendererClassName() const; - -private slots: - - void updateBounds(); -}; - -/// A cuboid. -class Cuboid : public ColorTransformable { - Q_OBJECT - Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged) - Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged) - -public: - - Q_INVOKABLE Cuboid(); - - void setAspectY(float aspectY); - float getAspectY() const { return _aspectY; } - - void setAspectZ(float aspectZ); - float getAspectZ() const { return _aspectZ; } - - virtual bool contains(const glm::vec3& point); - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - -signals: - - void aspectYChanged(float aspectY); - void aspectZChanged(float aspectZ); - -protected: - - virtual QByteArray getRendererClassName() const; - -private slots: - - void updateBoundsAndPlanes(); - -private: - - float _aspectY; - float _aspectZ; - - static const int PLANE_COUNT = 6; - glm::vec4 _planes[PLANE_COUNT]; -}; - -/// 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; } - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - -signals: - - void urlChanged(const QUrl& url); - -protected: - - virtual QByteArray getRendererClassName() const; - -private: - - QUrl _url; -}; - -typedef QExplicitlySharedDataPointer DataBlockPointer; - -/// Base class for blocks of data. -class DataBlock : public QSharedData { -public: - - static const int COLOR_BYTES = 3; - - virtual ~DataBlock(); - - void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; } - const DataBlockPointer& getDeltaData() const { return _deltaData; } - - void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; } - const QByteArray& getEncodedDelta() const { return _encodedDelta; } - - QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; } - -protected: - - QByteArray _encoded; - QMutex _encodedMutex; - - DataBlockPointer _deltaData; - QByteArray _encodedDelta; - QMutex _encodedDeltaMutex; - - class EncodedSubdivision { - public: - DataBlockPointer ancestor; - QByteArray data; - }; - QVector _encodedSubdivisions; - QMutex _encodedSubdivisionsMutex; -}; - -/// Base class for heightfield data blocks. -class HeightfieldData : public DataBlock { -public: - - static const int SHARED_EDGE; - - HeightfieldData(int width = 0); - - int getWidth() const { return _width; } - -protected: - - int _width; -}; - -typedef QExplicitlySharedDataPointer HeightfieldHeightPointer; - -/// A block of height data associated with a heightfield. -class HeightfieldHeight : public HeightfieldData { -public: - - static const int HEIGHT_BORDER; - static const int HEIGHT_EXTENSION; - - HeightfieldHeight(int width, const QVector& contents); - HeightfieldHeight(Bitstream& in, int bytes); - HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference); - - QVector& getContents() { return _contents; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QVector _contents; -}; - -Q_DECLARE_METATYPE(HeightfieldHeightPointer) - -Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value); -Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value); - -template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference); -template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference); - -/// Allows editing heightfield height blocks. -class HeightfieldHeightEditor : public QWidget { - Q_OBJECT - Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged USER true) - -public: - - HeightfieldHeightEditor(QWidget* parent = NULL); - - const HeightfieldHeightPointer& getHeight() const { return _height; } - -signals: - - void heightChanged(const HeightfieldHeightPointer& height); - -public slots: - - void setHeight(const HeightfieldHeightPointer& height); - -private slots: - - void select(); - void clear(); - -private: - - HeightfieldHeightPointer _height; - - QPushButton* _select; - QPushButton* _clear; -}; - -typedef QExplicitlySharedDataPointer HeightfieldColorPointer; - -/// A block of color data associated with a heightfield. -class HeightfieldColor : public HeightfieldData { -public: - - HeightfieldColor(int width, const QByteArray& contents); - HeightfieldColor(Bitstream& in, int bytes); - HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference); - - QByteArray& getContents() { return _contents; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldColorPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QByteArray _contents; -}; - -Q_DECLARE_METATYPE(HeightfieldColorPointer) - -Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value); -Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value); - -template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference); -template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference); - -/// Allows editing heightfield color blocks. -class HeightfieldColorEditor : public QWidget { - Q_OBJECT - Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged USER true) - -public: - - HeightfieldColorEditor(QWidget* parent = NULL); - - const HeightfieldColorPointer& getColor() const { return _color; } - -signals: - - void colorChanged(const HeightfieldColorPointer& color); - -public slots: - - void setColor(const HeightfieldColorPointer& color); - -private slots: - - void select(); - void clear(); - -private: - - HeightfieldColorPointer _color; - - QPushButton* _select; - QPushButton* _clear; -}; - -typedef QExplicitlySharedDataPointer HeightfieldMaterialPointer; - -/// A block of material data associated with a heightfield. -class HeightfieldMaterial : public HeightfieldData { -public: - - HeightfieldMaterial(int width, const QByteArray& contents, const QVector& materials); - HeightfieldMaterial(Bitstream& in, int bytes); - HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference); - - QByteArray& getContents() { return _contents; } - QVector& getMaterials() { return _materials; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QByteArray _contents; - QVector _materials; -}; - -Q_DECLARE_METATYPE(HeightfieldMaterialPointer) - -Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value); -Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value); - -template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference); -template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference); - -/// Contains the description of a material. -class MaterialObject : public SharedObject { - Q_OBJECT - Q_PROPERTY(QUrl diffuse MEMBER _diffuse) - Q_PROPERTY(float scaleS MEMBER _scaleS) - Q_PROPERTY(float scaleT MEMBER _scaleT) - -public: - - Q_INVOKABLE MaterialObject(); - - const QUrl& getDiffuse() const { return _diffuse; } - - float getScaleS() const { return _scaleS; } - float getScaleT() const { return _scaleT; } - -private: - - QUrl _diffuse; - float _scaleS; - float _scaleT; -}; - -/// Finds a material index for the supplied material in the provided list, adding an entry if necessary. Returns -1 -/// on failure (no room to add new material). -int getMaterialIndex(const SharedObjectPointer& material, QVector& materials); - -typedef QExplicitlySharedDataPointer HeightfieldStackPointer; - -/// A single column within a stack block. -class StackArray : public QByteArray { -public: - -#pragma pack(push, 1) - /// A single entry within the array. - class Entry { - public: - quint32 color; - uchar material; - quint32 hermiteX; - quint32 hermiteY; - quint32 hermiteZ; - - Entry(); - - bool isSet() const { return qAlpha(color) != 0; } - - bool isZero() const; - bool isMergeable(const Entry& other) const; - - void setHermiteX(const glm::vec3& normal, float position); - float getHermiteX(glm::vec3& normal) const; - - void setHermiteY(const glm::vec3& normal, float position); - float getHermiteY(glm::vec3& normal) const; - - void setHermiteZ(const glm::vec3& normal, float position); - float getHermiteZ(glm::vec3& normal) const; - }; -#pragma pack(pop) - - static int getSize(int entries) { return (entries == 0) ? 0 : sizeof(quint16) + sizeof(Entry) * entries; } - - StackArray() : QByteArray() { } - StackArray(int entries) : QByteArray(getSize(entries), 0) { } - StackArray(const QByteArray& other) : QByteArray(other) { } - StackArray(const char* src, int bytes) : QByteArray(src, bytes) { } - - int getPosition() const { return *(const quint16*)constData(); } - void setPosition(int position) { *(quint16*)data() = position; } - - quint16& getPositionRef() { return *(quint16*)data(); } - - int getEntryCount() const { return isEmpty() ? 0 : (size() - sizeof(quint16)) / sizeof(Entry); } - - Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); } - const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); } - - int getEntryAlpha(int y, float heightfieldHeight = 0.0f) const; - - Entry& getEntry(int y, float heightfieldHeight = 0.0f); - const Entry& getEntry(int y, float heightfieldHeight = 0.0f) const; - - void getExtents(int& minimumY, int& maximumY) const; - - bool hasSetEntries() const; - - void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); } -}; - -/// A block of stack data associated with a heightfield. -class HeightfieldStack : public HeightfieldData { -public: - - HeightfieldStack(int width, const QVector& contents, const QVector& materials); - HeightfieldStack(Bitstream& in, int bytes); - HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference); - - QVector& getContents() { return _contents; } - QVector& getMaterials() { return _materials; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldStackPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QVector _contents; - QVector _materials; -}; - -Q_DECLARE_METATYPE(HeightfieldStackPointer) - -Bitstream& operator<<(Bitstream& out, const HeightfieldStackPointer& value); -Bitstream& operator>>(Bitstream& in, HeightfieldStackPointer& value); - -template<> void Bitstream::writeRawDelta(const HeightfieldStackPointer& value, const HeightfieldStackPointer& reference); -template<> void Bitstream::readRawDelta(HeightfieldStackPointer& value, const HeightfieldStackPointer& reference); - -typedef QExplicitlySharedDataPointer HeightfieldNodePointer; - -/// Holds the base state used in streaming heightfield data. -class HeightfieldStreamBase { -public: - Bitstream& stream; - const MetavoxelLOD& lod; - const MetavoxelLOD& referenceLOD; -}; - -/// Holds the state used in streaming a heightfield node. -class HeightfieldStreamState { -public: - HeightfieldStreamBase& base; - glm::vec2 minimum; - float size; - - bool shouldSubdivide() const; - bool shouldSubdivideReference() const; - bool becameSubdivided() const; - bool becameSubdividedOrCollapsed() const; - - void setMinimum(const glm::vec2& lastMinimum, int index); -}; - -/// A node in a heightfield quadtree. -class HeightfieldNode : public QSharedData { -public: - - static const int CHILD_COUNT = 4; - - HeightfieldNode(const HeightfieldHeightPointer& height = HeightfieldHeightPointer(), - const HeightfieldColorPointer& color = HeightfieldColorPointer(), - const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer(), - const HeightfieldStackPointer& stack = HeightfieldStackPointer()); - - HeightfieldNode(const HeightfieldNode& other); - - ~HeightfieldNode(); - - void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, - const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack); - - void setHeight(const HeightfieldHeightPointer& height) { _height = height; } - const HeightfieldHeightPointer& getHeight() const { return _height; } - - void setColor(const HeightfieldColorPointer& color) { _color = color; } - const HeightfieldColorPointer& getColor() const { return _color; } - - void setMaterial(const HeightfieldMaterialPointer& material) { _material = material; } - const HeightfieldMaterialPointer& getMaterial() const { return _material; } - - void setStack(const HeightfieldStackPointer& stack) { _stack = stack; } - const HeightfieldStackPointer& getStack() const { return _stack; } - - void setRenderer(AbstractHeightfieldNodeRenderer* renderer) { _renderer = renderer; } - AbstractHeightfieldNodeRenderer* getRenderer() const { return _renderer; } - - bool isLeaf() const; - - void setChild(int index, const HeightfieldNodePointer& child) { _children[index] = child; } - const HeightfieldNodePointer& getChild(int index) const { return _children[index]; } - - bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - - void getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const; - - HeightfieldNode* paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius, float height, bool set, bool erase, - float normalizeScale, float normalizeOffset, float granularity); - - HeightfieldNode* fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius, float granularity); - - void getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const Box& editBounds, float& minimum, float& maximum) const; - - HeightfieldNode* setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize, - float normalizeScale, float normalizeOffset, float granularity); - - void read(HeightfieldStreamState& state); - void write(HeightfieldStreamState& state) const; - - void readDelta(const HeightfieldNodePointer& reference, HeightfieldStreamState& state); - void writeDelta(const HeightfieldNodePointer& reference, HeightfieldStreamState& state) const; - - HeightfieldNode* readSubdivision(HeightfieldStreamState& state); - void writeSubdivision(HeightfieldStreamState& state) const; - - void readSubdivided(HeightfieldStreamState& state, const HeightfieldStreamState& ancestorState, - const HeightfieldNode* ancestor); - void writeSubdivided(HeightfieldStreamState& state, const HeightfieldStreamState& ancestorState, - const HeightfieldNode* ancestor) const; - -private: - - void clearChildren(); - void mergeChildren(bool height = true, bool colorMaterial = true); - - QRgb getColorAt(const glm::vec3& location) const; - int getMaterialAt(const glm::vec3& location) const; - - void maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, int innerStackWidth, - QVector& heightContents, QVector& stackContents); - - bool findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float boundsDistance, float& distance) const; - - HeightfieldNode* subdivide(const QVector& heightContents, const QVector& stackContents) const; - - HeightfieldHeightPointer _height; - HeightfieldColorPointer _color; - HeightfieldMaterialPointer _material; - HeightfieldStackPointer _stack; - - HeightfieldNodePointer _children[CHILD_COUNT]; - - AbstractHeightfieldNodeRenderer* _renderer; -}; - -/// Base class for heightfield node rendering. -class AbstractHeightfieldNodeRenderer { -public: - - virtual ~AbstractHeightfieldNodeRenderer(); - - virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const; -}; - -/// A heightfield represented as a spanner. -class Heightfield : public Transformable { - Q_OBJECT - Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged) - Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged) - Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged STORED false) - Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged STORED false) - Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged STORED false - DESIGNABLE false) - Q_PROPERTY(HeightfieldStackPointer stack MEMBER _stack WRITE setStack NOTIFY stackChanged STORED false - DESIGNABLE false) - -public: - - Q_INVOKABLE Heightfield(); - - void setAspectY(float aspectY); - float getAspectY() const { return _aspectY; } - - void setAspectZ(float aspectZ); - float getAspectZ() const { return _aspectZ; } - - void setHeight(const HeightfieldHeightPointer& height); - const HeightfieldHeightPointer& getHeight() const { return _height; } - - void setColor(const HeightfieldColorPointer& color); - const HeightfieldColorPointer& getColor() const { return _color; } - - void setMaterial(const HeightfieldMaterialPointer& material); - const HeightfieldMaterialPointer& getMaterial() const { return _material; } - - void setStack(const HeightfieldStackPointer& stack); - const HeightfieldStackPointer& getStack() const { return _stack; } - - void setRoot(const HeightfieldNodePointer& root) { _root = root; } - const HeightfieldNodePointer& getRoot() const { return _root; } - - MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const; - - virtual SharedObject* clone(bool withID = false, SharedObject* target = NULL) const; - - virtual bool isHeightfield() const; - - virtual float getHeight(const glm::vec3& location) const; - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - - virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, - bool set, bool erase, float granularity); - - virtual Spanner* fillHeight(const glm::vec3& position, float radius, float granularity); - - virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint, bool voxelize, float granularity); - - virtual bool hasOwnColors() const; - virtual bool hasOwnMaterials() const; - virtual QRgb getColorAt(const glm::vec3& point); - virtual int getMaterialAt(const glm::vec3& point); - virtual QVector& getMaterials(); - - virtual bool contains(const glm::vec3& point); - virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); - - virtual void writeExtra(Bitstream& out) const; - virtual void readExtra(Bitstream& in, bool reread); - virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; - virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread); - virtual void maybeWriteSubdivision(Bitstream& out); - virtual SharedObject* readSubdivision(Bitstream& in); - -signals: - - void aspectYChanged(float aspectY); - void aspectZChanged(float aspectZ); - void heightChanged(const HeightfieldHeightPointer& height); - void colorChanged(const HeightfieldColorPointer& color); - void materialChanged(const HeightfieldMaterialPointer& material); - void stackChanged(const HeightfieldStackPointer& stack); - -protected: - - virtual QByteArray getRendererClassName() const; - -private slots: - - void updateBounds(); - void updateRoot(); - -private: - - Heightfield* prepareEdit(float minimumValue, float maximumValue, float& normalizeScale, float& normalizeOffset); - - float _aspectY; - float _aspectZ; - - HeightfieldHeightPointer _height; - HeightfieldColorPointer _color; - HeightfieldMaterialPointer _material; - HeightfieldStackPointer _stack; - - HeightfieldNodePointer _root; -}; - -#endif // hifi_Spanner_h diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index e01edcd003..944041730e 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -27,8 +27,6 @@ Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) { return Assignment::AgentType; case NodeType::EntityServer: return Assignment::EntityServerType; - case NodeType::MetavoxelServer: - return Assignment::MetavoxelServerType; default: return Assignment::AllTypes; } @@ -133,8 +131,6 @@ const char* Assignment::getTypeName() const { return "agent"; case Assignment::EntityServerType: return "entity-server"; - case Assignment::MetavoxelServerType: - return "metavoxel-server"; default: return "unknown"; } diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index 1e620ed32b..a3b810c4ac 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -31,7 +31,7 @@ public: AgentType, UNUSED_0, UNUSED_1, - MetavoxelServerType, + UNUSED_2, EntityServerType, AllTypes }; diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 2a38799707..0304defb55 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -29,7 +29,6 @@ namespace NodeType { void NodeType::init() { TypeNameHash.insert(NodeType::DomainServer, "Domain Server"); TypeNameHash.insert(NodeType::EntityServer, "Entity Server"); - TypeNameHash.insert(NodeType::MetavoxelServer, "Metavoxel Server"); TypeNameHash.insert(NodeType::Agent, "Agent"); TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer"); TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer"); diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index ddda947ff4..fa77540c96 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -31,7 +31,6 @@ typedef quint8 NodeType_t; namespace NodeType { const NodeType_t DomainServer = 'D'; const NodeType_t EntityServer = 'o'; // was ModelServer - const NodeType_t MetavoxelServer = 'm'; const NodeType_t EnvironmentServer = 'E'; const NodeType_t Agent = 'I'; const NodeType_t AudioMixer = 'M'; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index c8f6b6e25c..c9add10e5f 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -79,8 +79,6 @@ PacketVersion versionForPacketType(PacketType type) { return 2; case PacketTypeAudioStreamStats: return 1; - case PacketTypeMetavoxelData: - return 13; default: return 0; } @@ -114,7 +112,6 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeStats); PACKET_TYPE_NAME_LOOKUP(PacketTypeJurisdiction); PACKET_TYPE_NAME_LOOKUP(PacketTypeJurisdictionRequest); - PACKET_TYPE_NAME_LOOKUP(PacketTypeMetavoxelData); PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarIdentity); PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarBillboard); PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectRequest); diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 44a464201b..c89127058f 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -42,20 +42,20 @@ enum PacketType { PacketTypeMuteEnvironment, PacketTypeAudioStreamStats, PacketTypeDataServerConfirm, // 20 - UNUSED_5, - UNUSED_6, - UNUSED_7, - UNUSED_8, - UNUSED_9, // 25 + UNUSED_1, + UNUSED_2, + UNUSED_3, + UNUSED_4, + UNUSED_5, // 25 PacketTypeOctreeStats, PacketTypeJurisdiction, PacketTypeJurisdictionRequest, - UNUSED_1, - UNUSED_2, // 30 - UNUSED_3, - UNUSED_4, + UNUSED_6, + UNUSED_7, // 30 + UNUSED_8, + UNUSED_9, PacketTypeNoisyMute, - PacketTypeMetavoxelData, + UNUSED_10, PacketTypeAvatarIdentity, // 35 PacketTypeAvatarBillboard, PacketTypeDomainConnectRequest, diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index c375f3eb52..951c36f038 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -24,7 +24,7 @@ class AbstractViewStateInterface; class PostLightingRenderable; -/// Handles deferred lighting for the bits that require it (voxels, metavoxels...) +/// Handles deferred lighting for the bits that require it (voxels...) class DeferredLightingEffect : public Dependency { SINGLETON_DEPENDENCY diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 013f43530c..99d9149c3a 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -7,4 +7,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared octree gpu model fbx entities animation audio physics metavoxels) +link_hifi_libraries(shared octree gpu model fbx entities animation audio physics) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 3bdf9a9fa9..195d3033b9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -324,7 +323,6 @@ void ScriptEngine::init() { registerAnimationTypes(this); registerAvatarTypes(this); registerAudioMetaTypes(this); - Bitstream::registerTypes(this); qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValue); qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); diff --git a/tests/metavoxels/CMakeLists.txt b/tests/metavoxels/CMakeLists.txt deleted file mode 100644 index e6a62dc55c..0000000000 --- a/tests/metavoxels/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(TARGET_NAME metavoxel-tests) - -auto_mtc() - -setup_hifi_project(Network Script Widgets) - -# link in the shared libraries -link_hifi_libraries(metavoxels networking shared) - -copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp deleted file mode 100644 index 938b5470e8..0000000000 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ /dev/null @@ -1,1230 +0,0 @@ -// -// MetavoxelTests.cpp -// tests/metavoxels/src -// -// Created by Andrzej Kapolka on 2/7/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include - -#include - -#include - -#include "MetavoxelTests.h" - -REGISTER_META_OBJECT(TestSharedObjectA) -REGISTER_META_OBJECT(TestSharedObjectB) - -IMPLEMENT_ENUM_METATYPE(TestSharedObjectA, TestEnum) - -MetavoxelTests::MetavoxelTests(int& argc, char** argv) : - QCoreApplication(argc, argv) { -} - -static bool testSpanList() { - SpanList list; - - if (list.getTotalSet() != 0 || !list.getSpans().isEmpty()) { - qDebug() << "Failed empty state test."; - return true; - } - - if (list.set(-5, 15) != 10 || list.getTotalSet() != 0 || !list.getSpans().isEmpty()) { - qDebug() << "Failed initial front set."; - return true; - } - - if (list.set(5, 15) != 0 || list.getTotalSet() != 15 || list.getSpans().size() != 1 || - list.getSpans().at(0).unset != 5 || list.getSpans().at(0).set != 15) { - qDebug() << "Failed initial middle set."; - return true; - } - - if (list.set(25, 5) != 0 || list.getTotalSet() != 20 || list.getSpans().size() != 2 || - list.getSpans().at(0).unset != 5 || list.getSpans().at(0).set != 15 || - list.getSpans().at(1).unset != 5 || list.getSpans().at(1).set != 5) { - qDebug() << "Failed initial end set."; - return true; - } - - if (list.set(1, 3) != 0 || list.getTotalSet() != 23 || list.getSpans().size() != 3 || - list.getSpans().at(0).unset != 1 || list.getSpans().at(0).set != 3 || - list.getSpans().at(1).unset != 1 || list.getSpans().at(1).set != 15 || - list.getSpans().at(2).unset != 5 || list.getSpans().at(2).set != 5) { - qDebug() << "Failed second front set."; - return true; - } - SpanList threeSet = list; - - if (list.set(20, 5) != 0 || list.getTotalSet() != 28 || list.getSpans().size() != 2 || - list.getSpans().at(0).unset != 1 || list.getSpans().at(0).set != 3 || - list.getSpans().at(1).unset != 1 || list.getSpans().at(1).set != 25) { - qDebug() << "Failed minimal join last two."; - return true; - } - - list = threeSet; - if (list.set(5, 25) != 0 || list.getTotalSet() != 28 || list.getSpans().size() != 2 || - list.getSpans().at(0).unset != 1 || list.getSpans().at(0).set != 3 || - list.getSpans().at(1).unset != 1 || list.getSpans().at(1).set != 25) { - qDebug() << "Failed maximal join last two."; - return true; - } - - list = threeSet; - if (list.set(10, 18) != 0 || list.getTotalSet() != 28 || list.getSpans().size() != 2 || - list.getSpans().at(0).unset != 1 || list.getSpans().at(0).set != 3 || - list.getSpans().at(1).unset != 1 || list.getSpans().at(1).set != 25) { - qDebug() << "Failed middle join last two."; - return true; - } - - list = threeSet; - if (list.set(10, 18) != 0 || list.getTotalSet() != 28 || list.getSpans().size() != 2 || - list.getSpans().at(0).unset != 1 || list.getSpans().at(0).set != 3 || - list.getSpans().at(1).unset != 1 || list.getSpans().at(1).set != 25) { - qDebug() << "Failed middle join last two."; - return true; - } - - list = threeSet; - if (list.set(2, 26) != 0 || list.getTotalSet() != 29 || list.getSpans().size() != 1 || - list.getSpans().at(0).unset != 1 || list.getSpans().at(0).set != 29) { - qDebug() << "Failed middle join three."; - return true; - } - - list = threeSet; - if (list.set(0, 2) != 4 || list.getTotalSet() != 20 || list.getSpans().size() != 2 || - list.getSpans().at(0).unset != 1 || list.getSpans().at(0).set != 15 || - list.getSpans().at(1).unset != 5 || list.getSpans().at(1).set != 5) { - qDebug() << "Failed front advance."; - return true; - } - - list = threeSet; - if (list.set(-10, 15) != 20 || list.getTotalSet() != 5 || list.getSpans().size() != 1 || - list.getSpans().at(0).unset != 5 || list.getSpans().at(0).set != 5) { - qDebug() << "Failed middle advance."; - return true; - } - - list = threeSet; - if (list.set(-10, 38) != 30 || list.getTotalSet() != 0 || list.getSpans().size() != 0) { - qDebug() << "Failed end advance."; - return true; - } - - list = threeSet; - if (list.set(-10, 100) != 90 || list.getTotalSet() != 0 || list.getSpans().size() != 0) { - qDebug() << "Failed clobber advance."; - return true; - } - - list = threeSet; - if (list.set(21, 3) != 0 || list.getTotalSet() != 26 || list.getSpans().size() != 4 || - list.getSpans().at(0).unset != 1 || list.getSpans().at(0).set != 3 || - list.getSpans().at(1).unset != 1 || list.getSpans().at(1).set != 15 || - list.getSpans().at(2).unset != 1 || list.getSpans().at(2).set != 3 || - list.getSpans().at(3).unset != 1 || list.getSpans().at(3).set != 5) { - qDebug() << "Failed adding fourth."; - return true; - } - - return false; -} - -static int datagramsSent = 0; -static int datagramsReceived = 0; -static int bytesSent = 0; -static int bytesReceived = 0; -static int maxDatagramsPerPacket = 0; -static int maxBytesPerPacket = 0; -static int groupsSent = 0; -static int maxPacketsPerGroup = 0; -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 sharedObjectsCreated = 0; -static int sharedObjectsDestroyed = 0; -static int objectMutationsPerformed = 0; -static int scriptObjectsCreated = 0; -static int scriptMutationsPerformed = 0; -static int metavoxelMutationsPerformed = 0; -static int spannerMutationsPerformed = 0; - -static QByteArray createRandomBytes(int minimumSize, int maximumSize) { - QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0); - for (int i = 0; i < bytes.size(); i++) { - bytes[i] = rand(); - } - return bytes; -} - -static QByteArray createRandomBytes() { - const int MIN_BYTES = 4; - const int MAX_BYTES = 16; - return createRandomBytes(MIN_BYTES, MAX_BYTES); -} - -static TestSharedObjectA::TestEnum getRandomTestEnum() { - switch (randIntInRange(0, 2)) { - case 0: return TestSharedObjectA::FIRST_TEST_ENUM; - case 1: return TestSharedObjectA::SECOND_TEST_ENUM; - case 2: - default: return TestSharedObjectA::THIRD_TEST_ENUM; - } -} - -static TestSharedObjectA::TestFlags getRandomTestFlags() { - TestSharedObjectA::TestFlags flags = 0; - if (randomBoolean()) { - flags |= TestSharedObjectA::FIRST_TEST_FLAG; - } - if (randomBoolean()) { - flags |= TestSharedObjectA::SECOND_TEST_FLAG; - } - if (randomBoolean()) { - flags |= TestSharedObjectA::THIRD_TEST_FLAG; - } - return flags; -} - -static QScriptValue createRandomScriptValue(bool complex = false, bool ensureHashOrder = false) { - scriptObjectsCreated++; - switch (randIntInRange(0, complex ? 5 : 3)) { - case 0: - return QScriptValue(QScriptValue::NullValue); - - case 1: - return QScriptValue(randomBoolean()); - - case 2: - return QScriptValue(randFloat()); - - case 3: - return QScriptValue(QString(createRandomBytes())); - - case 4: { - int length = randIntInRange(2, 6); - QScriptValue value = DependencyManager::get()->getEngine()->newArray(length); - for (int i = 0; i < length; i++) { - value.setProperty(i, createRandomScriptValue()); - } - return value; - } - default: { - QScriptValue value = DependencyManager::get()->getEngine()->newObject(); - if (ensureHashOrder) { - // we can't depend on the iteration order, so if we need it to be the same (as when comparing bytes), we - // can only have one property - value.setProperty("foo", createRandomScriptValue()); - } else { - if (randomBoolean()) { - value.setProperty("foo", createRandomScriptValue()); - } - if (randomBoolean()) { - value.setProperty("bar", createRandomScriptValue()); - } - if (randomBoolean()) { - value.setProperty("baz", createRandomScriptValue()); - } - if (randomBoolean()) { - value.setProperty("bong", createRandomScriptValue()); - } - } - return value; - } - } -} - -static TestMessageC createRandomMessageC(bool ensureHashOrder = false) { - TestMessageC message; - message.foo = randomBoolean(); - message.bar = rand(); - message.baz = randFloat(); - message.bong.foo = createRandomBytes(); - message.bong.baz = getRandomTestEnum(); - message.bizzle = createRandomScriptValue(true, ensureHashOrder); - return message; -} - -static bool testSerialization(Bitstream::MetadataType metadataType) { - QByteArray array; - QDataStream outStream(&array, QIODevice::WriteOnly); - Bitstream out(outStream, metadataType); - SharedObjectPointer testObjectWrittenA = new TestSharedObjectA(randFloat(), TestSharedObjectA::SECOND_TEST_ENUM, - TestSharedObjectA::TestFlags(TestSharedObjectA::FIRST_TEST_FLAG | TestSharedObjectA::THIRD_TEST_FLAG)); - out << testObjectWrittenA; - SharedObjectPointer testObjectWrittenB = new TestSharedObjectB(randFloat(), createRandomBytes(), - TestSharedObjectB::THIRD_TEST_ENUM, TestSharedObjectB::SECOND_TEST_FLAG); - out << testObjectWrittenB; - TestMessageC messageWritten = createRandomMessageC(true); - out << QVariant::fromValue(messageWritten); - QByteArray endWritten = "end"; - out << endWritten; - out.flush(); - - QDataStream inStream(array); - Bitstream in(inStream, metadataType); - in.addMetaObjectSubstitution("TestSharedObjectA", &TestSharedObjectB::staticMetaObject); - in.addMetaObjectSubstitution("TestSharedObjectB", &TestSharedObjectA::staticMetaObject); - in.addTypeSubstitution("TestMessageC", TestMessageA::Type); - in.addTypeSubstitution("TestSharedObjectA::TestEnum", "TestSharedObjectB::TestEnum"); - in.addTypeSubstitution("TestSharedObjectB::TestEnum", "TestSharedObjectA::TestEnum"); - in.addTypeSubstitution("TestSharedObjectA::TestFlags", "TestSharedObjectB::TestFlags"); - in.addTypeSubstitution("TestSharedObjectB::TestFlags", "TestSharedObjectA::TestFlags"); - SharedObjectPointer testObjectReadA; - in >> testObjectReadA; - - if (!testObjectReadA || testObjectReadA->metaObject() != &TestSharedObjectB::staticMetaObject) { - qDebug() << "Wrong class for A" << testObjectReadA << metadataType; - return true; - } - if (metadataType == Bitstream::FULL_METADATA && (static_cast(testObjectWrittenA.data())->getFoo() != - static_cast(testObjectReadA.data())->getFoo() || - static_cast(testObjectReadA.data())->getBaz() != TestSharedObjectB::SECOND_TEST_ENUM || - static_cast(testObjectReadA.data())->getBong() != - TestSharedObjectB::TestFlags(TestSharedObjectB::FIRST_TEST_FLAG | TestSharedObjectB::THIRD_TEST_FLAG))) { - QDebug debug = qDebug() << "Failed to transfer shared field from A to B"; - testObjectWrittenA->dump(debug); - testObjectReadA->dump(debug); - return true; - } - - SharedObjectPointer testObjectReadB; - in >> testObjectReadB; - if (!testObjectReadB || testObjectReadB->metaObject() != &TestSharedObjectA::staticMetaObject) { - qDebug() << "Wrong class for B" << testObjectReadB << metadataType; - return true; - } - if (metadataType == Bitstream::FULL_METADATA && (static_cast(testObjectWrittenB.data())->getFoo() != - static_cast(testObjectReadB.data())->getFoo() || - static_cast(testObjectReadB.data())->getBaz() != TestSharedObjectA::THIRD_TEST_ENUM || - static_cast(testObjectReadB.data())->getBong() != TestSharedObjectA::SECOND_TEST_FLAG)) { - QDebug debug = qDebug() << "Failed to transfer shared field from B to A"; - testObjectWrittenB->dump(debug); - testObjectReadB->dump(debug); - return true; - } - - QVariant messageRead; - in >> messageRead; - if (!messageRead.isValid() || messageRead.userType() != TestMessageA::Type) { - qDebug() << "Wrong type for message" << messageRead; - return true; - } - if (metadataType == Bitstream::FULL_METADATA && messageWritten.foo != messageRead.value().foo) { - QDebug debug = qDebug() << "Failed to transfer shared field between messages" << - messageWritten.foo << messageRead.value().foo; - return true; - } - - QByteArray endRead; - in >> endRead; - if (endWritten != endRead) { - qDebug() << "End tag mismatch." << endRead; - return true; - } - - // go back to the beginning and read everything as generics - inStream.device()->seek(0); - Bitstream genericIn(inStream, metadataType, Bitstream::ALL_GENERICS); - genericIn >> testObjectReadA; - genericIn >> testObjectReadB; - genericIn >> messageRead; - genericIn >> endRead; - - // reassign the ids - testObjectReadA->setID(testObjectWrittenA->getID()); - testObjectReadA->setOriginID(testObjectWrittenA->getOriginID()); - testObjectReadB->setID(testObjectWrittenB->getID()); - testObjectReadB->setOriginID(testObjectWrittenB->getOriginID()); - - // write it back out and compare - QByteArray compareArray; - QDataStream compareOutStream(&compareArray, QIODevice::WriteOnly); - Bitstream compareOut(compareOutStream, metadataType); - compareOut << testObjectReadA; - compareOut << testObjectReadB; - compareOut << messageRead; - compareOut << endRead; - compareOut.flush(); - - if (array != compareArray) { - qDebug() << "Mismatch between written/generic written streams."; - return true; - } - - if (metadataType != Bitstream::FULL_METADATA) { - return false; - } - - // now write to JSON - JSONWriter jsonWriter; - jsonWriter << testObjectReadA; - jsonWriter << testObjectReadB; - jsonWriter << messageRead; - jsonWriter << endRead; - QByteArray encodedJson = jsonWriter.getDocument().toJson(); - - // and read from JSON - JSONReader jsonReader(QJsonDocument::fromJson(encodedJson), Bitstream::ALL_GENERICS); - jsonReader >> testObjectReadA; - jsonReader >> testObjectReadB; - jsonReader >> messageRead; - jsonReader >> endRead; - - // reassign the ids - testObjectReadA->setID(testObjectWrittenA->getID()); - testObjectReadA->setOriginID(testObjectWrittenA->getOriginID()); - testObjectReadB->setID(testObjectWrittenB->getID()); - testObjectReadB->setOriginID(testObjectWrittenB->getOriginID()); - - // and back to binary - QByteArray secondCompareArray; - QDataStream secondCompareOutStream(&secondCompareArray, QIODevice::WriteOnly); - Bitstream secondCompareOut(secondCompareOutStream, Bitstream::FULL_METADATA); - secondCompareOut << testObjectReadA; - secondCompareOut << testObjectReadB; - secondCompareOut << messageRead; - secondCompareOut << endRead; - secondCompareOut.flush(); - - if (compareArray != secondCompareArray) { - qDebug() << "Mismatch between written/JSON streams (generics)."; - return true; - } - - // once more, with mapping! - JSONReader secondJSONReader(QJsonDocument::fromJson(encodedJson)); - secondJSONReader >> testObjectReadA; - secondJSONReader >> testObjectReadB; - secondJSONReader >> messageRead; - secondJSONReader >> endRead; - - // reassign the ids - testObjectReadA->setID(testObjectWrittenA->getID()); - testObjectReadA->setOriginID(testObjectWrittenA->getOriginID()); - testObjectReadB->setID(testObjectWrittenB->getID()); - testObjectReadB->setOriginID(testObjectWrittenB->getOriginID()); - - // and back to binary - QByteArray thirdCompareArray; - QDataStream thirdCompareOutStream(&thirdCompareArray, QIODevice::WriteOnly); - Bitstream thirdCompareOut(thirdCompareOutStream, Bitstream::FULL_METADATA); - thirdCompareOut << testObjectReadA; - thirdCompareOut << testObjectReadB; - thirdCompareOut << messageRead; - thirdCompareOut << endRead; - thirdCompareOut.flush(); - - if (compareArray != thirdCompareArray) { - qDebug() << "Mismatch between written/JSON streams (mapped)."; - return true; - } - - return false; -} - -bool MetavoxelTests::run() { - DependencyManager::set(); - - // seed the random number generator so that our tests are reproducible - srand(0xBAAAAABE); - - // register our test attribute - AttributePointer testAttribute = AttributeRegistry::getInstance()->registerAttribute(new FloatAttribute("testAttribute")); - - // check for an optional command line argument specifying a single test - QStringList arguments = this->arguments(); - int test = (arguments.size() > 1) ? arguments.at(1).toInt() : 0; - - if (test == 0 || test == 1) { - qDebug() << "Running SpanList test..."; - qDebug(); - - if (testSpanList()) { - return true; - } - } - - const int SIMULATION_ITERATIONS = 10000; - if (test == 0 || test == 2) { - qDebug() << "Running transmission test..."; - qDebug(); - - // create two endpoints with the same header - TestEndpoint alice, bob; - - alice.setOther(&bob); - bob.setOther(&alice); - - // perform a large number of simulation iterations - for (int i = 0; i < SIMULATION_ITERATIONS; i++) { - if (alice.simulate(i) || bob.simulate(i)) { - return true; - } - } - - 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" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" << - datagramsReceived << "with" << bytesReceived << "bytes"; - qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet"; - qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; - qDebug() << "Performed" << objectMutationsPerformed << "object mutations"; - qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed; - qDebug(); - } - - if (test == 0 || test == 3) { - qDebug() << "Running congestion control test..."; - qDebug(); - - // clear the stats - streamedBytesSent = streamedBytesReceived = datagramsSent = bytesSent = 0; - datagramsReceived = bytesReceived = maxDatagramsPerPacket = maxBytesPerPacket = 0; - - // create two endpoints with the same header - TestEndpoint alice(TestEndpoint::CONGESTION_MODE), bob(TestEndpoint::CONGESTION_MODE); - - alice.setOther(&bob); - bob.setOther(&alice); - - // perform a large number of simulation iterations - for (int i = 0; i < SIMULATION_ITERATIONS; i++) { - if (alice.simulate(i) || bob.simulate(i)) { - return true; - } - } - - qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived; - qDebug() << "Sent" << datagramsSent << "datagrams in" << groupsSent << "groups with" << bytesSent << - "bytes, received" << datagramsReceived << "with" << bytesReceived << "bytes"; - qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet"; - qDebug() << "Max" << maxPacketsPerGroup << "packets per group"; - qDebug() << "Average" << (bytesReceived / datagramsReceived) << "bytes per datagram," << - (datagramsSent / groupsSent) << "datagrams per group"; - qDebug() << "Speed:" << (bytesReceived / SIMULATION_ITERATIONS) << "bytes per iteration"; - qDebug() << "Efficiency:" << ((float)streamedBytesReceived / bytesReceived); - } - - if (test == 0 || test == 4) { - qDebug() << "Running serialization test..."; - qDebug(); - - if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) { - return true; - } - } - - if (test == 0 || test == 5) { - qDebug() << "Running metavoxel data test..."; - qDebug(); - - // clear the stats - datagramsSent = bytesSent = datagramsReceived = bytesReceived = maxDatagramsPerPacket = maxBytesPerPacket = 0; - - // create client and server endpoints - TestEndpoint client(TestEndpoint::METAVOXEL_CLIENT_MODE); - TestEndpoint server(TestEndpoint::METAVOXEL_SERVER_MODE); - - client.setOther(&server); - server.setOther(&client); - - // simulate - for (int i = 0; i < SIMULATION_ITERATIONS; i++) { - if (client.simulate(i) || server.simulate(i)) { - return true; - } - } - - qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" << - datagramsReceived << "with" << bytesReceived << "bytes"; - qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet"; - qDebug() << "Performed" << metavoxelMutationsPerformed << "metavoxel mutations," << spannerMutationsPerformed << - "spanner mutations"; - } - - qDebug() << "All tests passed!"; - - return false; -} - -static SharedObjectPointer createRandomSharedObject() { - switch (randIntInRange(0, 2)) { - case 0: return new TestSharedObjectA(randFloat(), getRandomTestEnum(), getRandomTestFlags()); - case 1: return new TestSharedObjectB(); - case 2: - default: return SharedObjectPointer(); - } -} - -class RandomVisitor : public MetavoxelVisitor { -public: - - int leafCount; - - RandomVisitor(); - virtual int visit(MetavoxelInfo& info); -}; - -RandomVisitor::RandomVisitor() : - MetavoxelVisitor(QVector(), QVector() << - AttributeRegistry::getInstance()->getAttribute("testAttribute")), - leafCount(0) { -} - -const float MAXIMUM_LEAF_SIZE = 0.5f; -const float MINIMUM_LEAF_SIZE = 0.25f; - -int RandomVisitor::visit(MetavoxelInfo& info) { - if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { - return DEFAULT_ORDER; - } - info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(randFloat())); - leafCount++; - return STOP_RECURSION; -} - -class TestSendRecord : public PacketRecord { -public: - - TestSendRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), - const SharedObjectPointer& localState = SharedObjectPointer()); - - const SharedObjectPointer& getLocalState() const { return _localState; } - -private: - - SharedObjectPointer _localState; -}; - -TestSendRecord::TestSendRecord(int packetNumber, const MetavoxelLOD& lod, const MetavoxelData& data, - const SharedObjectPointer& localState) : - PacketRecord(packetNumber, lod, data), - _localState(localState) { -} - -class TestReceiveRecord : public PacketRecord { -public: - - TestReceiveRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), - const MetavoxelData& data = MetavoxelData(), const SharedObjectPointer& remoteState = SharedObjectPointer()); - - const SharedObjectPointer& getRemoteState() const { return _remoteState; } - -private: - - SharedObjectPointer _remoteState; -}; - -TestReceiveRecord::TestReceiveRecord(int packetNumber, const MetavoxelLOD& lod, - const MetavoxelData& data, const SharedObjectPointer& remoteState) : - PacketRecord(packetNumber, lod, data), - _remoteState(remoteState) { -} - -TestEndpoint::TestEndpoint(Mode mode) : - Endpoint(SharedNodePointer(), new TestSendRecord(), new TestReceiveRecord()), - _mode(mode), - _highPriorityMessagesToSend(0.0f), - _reliableMessagesToSend(0.0f), - _reliableDeltaChannel(NULL), - _reliableDeltaID(0) { - - connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), - SLOT(handleHighPriorityMessage(const QVariant&))); - connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&, Bitstream&)), - SLOT(handleReliableMessage(const QVariant&, Bitstream&))); - - if (mode == METAVOXEL_CLIENT_MODE) { - _lod = MetavoxelLOD(glm::vec3(), 0.01f); - connect(_sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX), - SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleReliableMessage(const QVariant&, Bitstream&))); - return; - } - if (mode == METAVOXEL_SERVER_MODE) { - connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(checkReliableDeltaReceived())); - - _data.expand(); - _data.expand(); - - RandomVisitor visitor; - _data.guide(visitor); - qDebug() << "Created" << visitor.leafCount << "base leaves"; - - _data.insert(AttributeRegistry::getInstance()->getSpannersAttribute(), new Sphere()); - - _sphere = new Sphere(); - static_cast(_sphere.data())->setScale(0.01f); - _data.insert(AttributeRegistry::getInstance()->getSpannersAttribute(), _sphere); - return; - } - // create the object that represents out delta-encoded state - _localState = new TestSharedObjectA(); - - ReliableChannel* secondInput = _sequencer.getReliableInputChannel(1); - 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); - output->setMessagesEnabled(false); - QByteArray bytes; - if (mode == CONGESTION_MODE) { - const int HUGE_STREAM_BYTES = 60 * 1024 * 1024; - bytes = createRandomBytes(HUGE_STREAM_BYTES, HUGE_STREAM_BYTES); - - // initialize the pipeline - for (int i = 0; i < 10; i++) { - _pipeline.append(ByteArrayVector()); - } - _remainingPipelineCapacity = 100 * 1024; - - } else { - const int MIN_STREAM_BYTES = 100000; - const int MAX_STREAM_BYTES = 200000; - bytes = createRandomBytes(MIN_STREAM_BYTES, MAX_STREAM_BYTES); - } - _dataStreamed.append(bytes); - output->getBuffer().write(bytes); - streamedBytesSent += bytes.size(); -} - -static QVariant createRandomMessage() { - switch (randIntInRange(0, 2)) { - case 0: { - TestMessageA message = { randomBoolean(), rand(), randFloat() }; - return QVariant::fromValue(message); - } - case 1: { - TestMessageB message = { createRandomBytes(), createRandomSharedObject(), getRandomTestEnum() }; - return QVariant::fromValue(message); - } - default: { - return QVariant::fromValue(createRandomMessageC()); - } - } -} - -static SharedObjectPointer mutate(const SharedObjectPointer& state) { - switch (randIntInRange(0, 4)) { - case 0: { - SharedObjectPointer newState = state->clone(true); - static_cast(newState.data())->setFoo(randFloat()); - objectMutationsPerformed++; - return newState; - } - case 1: { - SharedObjectPointer newState = state->clone(true); - static_cast(newState.data())->setBaz(getRandomTestEnum()); - objectMutationsPerformed++; - return newState; - } - case 2: { - SharedObjectPointer newState = state->clone(true); - static_cast(newState.data())->setBong(getRandomTestFlags()); - objectMutationsPerformed++; - return newState; - } - case 3: { - SharedObjectPointer newState = state->clone(true); - QScriptValue oldValue = static_cast(newState.data())->getBizzle(); - QScriptValue newValue = DependencyManager::get()->getEngine()->newObject(); - for (QScriptValueIterator it(oldValue); it.hasNext(); ) { - it.next(); - newValue.setProperty(it.scriptName(), it.value()); - } - switch (randIntInRange(0, 2)) { - case 0: { - QScriptValue oldArray = oldValue.property("foo"); - int oldLength = oldArray.property(DependencyManager::get()->getLengthString()).toInt32(); - QScriptValue newArray = DependencyManager::get()->getEngine()->newArray(oldLength); - for (int i = 0; i < oldLength; i++) { - newArray.setProperty(i, oldArray.property(i)); - } - newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue(true)); - break; - } - case 1: - newValue.setProperty("bar", QScriptValue(randFloat())); - break; - - default: - newValue.setProperty("baz", createRandomScriptValue(true)); - break; - } - static_cast(newState.data())->setBizzle(newValue); - scriptMutationsPerformed++; - objectMutationsPerformed++; - return newState; - } - default: - return state; - } -} - -static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMessage) { - int type = firstMessage.userType(); - if (secondMessage.userType() != type) { - return false; - } - if (type == TestMessageA::Type) { - return firstMessage.value() == secondMessage.value(); - - } else if (type == TestMessageB::Type) { - TestMessageB first = firstMessage.value(); - TestMessageB second = secondMessage.value(); - return first.foo == second.foo && equals(first.bar, second.bar) && first.baz == second.baz; - - } else if (type == TestMessageC::Type) { - return firstMessage.value() == secondMessage.value(); - - } else { - return firstMessage == secondMessage; - } -} - -class MutateVisitor : public MetavoxelVisitor { -public: - - MutateVisitor(); - virtual int visit(MetavoxelInfo& info); - -private: - - int _mutationsRemaining; -}; - -MutateVisitor::MutateVisitor() : - MetavoxelVisitor(QVector(), QVector() << - AttributeRegistry::getInstance()->getAttribute("testAttribute")), - _mutationsRemaining(randIntInRange(2, 4)) { -} - -int MutateVisitor::visit(MetavoxelInfo& info) { - if (_mutationsRemaining <= 0) { - return STOP_RECURSION; - } - if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { - return encodeRandomOrder(); - } - info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(randFloat())); - _mutationsRemaining--; - metavoxelMutationsPerformed++; - return STOP_RECURSION; -} - -bool TestEndpoint::simulate(int iterationNumber) { - // update/send our delayed datagrams - for (QList::iterator it = _delayedDatagrams.begin(); it != _delayedDatagrams.end(); ) { - if (it->second-- == 1) { - _other->parseData(it->first); - it = _delayedDatagrams.erase(it); - - } else { - it++; - } - } - - int oldDatagramsSent = datagramsSent; - int oldBytesSent = bytesSent; - if (_mode == CONGESTION_MODE) { - // cycle our pipeline - ByteArrayVector datagrams = _pipeline.takeLast(); - _pipeline.prepend(ByteArrayVector()); - foreach (const QByteArray& datagram, datagrams) { - _sequencer.receivedDatagram(datagram); - datagramsReceived++; - bytesReceived += datagram.size(); - _remainingPipelineCapacity += datagram.size(); - } - int packetCount = _sequencer.notePacketGroup(); - groupsSent++; - maxPacketsPerGroup = qMax(maxPacketsPerGroup, packetCount); - for (int i = 0; i < packetCount; i++) { - oldDatagramsSent = datagramsSent; - oldBytesSent = bytesSent; - - Bitstream& out = _sequencer.startPacket(); - out << QVariant(); - _sequencer.endPacket(); - - maxDatagramsPerPacket = qMax(maxDatagramsPerPacket, datagramsSent - oldDatagramsSent); - maxBytesPerPacket = qMax(maxBytesPerPacket, bytesSent - oldBytesSent); - } - return false; - - } else if (_mode == METAVOXEL_CLIENT_MODE) { - Bitstream& out = _sequencer.startPacket(); - - ClientStateMessage state = { _lod }; - out << QVariant::fromValue(state); - _sequencer.endPacket(); - - } else if (_mode == METAVOXEL_SERVER_MODE) { - // make a random change - MutateVisitor visitor; - _data.guide(visitor); - - // perhaps mutate the spanner - if (randomBoolean()) { - SharedObjectPointer oldSphere = _sphere; - _sphere = _sphere->clone(true); - Sphere* newSphere = static_cast(_sphere.data()); - if (randomBoolean()) { - newSphere->setColor(QColor(randomColorValue(), randomColorValue(), randomColorValue())); - } else { - newSphere->setTranslation(newSphere->getTranslation() + glm::vec3(randFloatInRange(-0.01f, 0.01f), - randFloatInRange(-0.01f, 0.01f), randFloatInRange(-0.01f, 0.01f))); - } - _data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), oldSphere, _sphere); - spannerMutationsPerformed++; - } - - // wait until we have a valid lod before sending - if (!_lod.isValid()) { - return false; - } - // if we're sending a reliable delta, wait until it's acknowledged - if (_reliableDeltaChannel) { - Bitstream& out = _sequencer.startPacket(); - MetavoxelDeltaPendingMessage msg = { _reliableDeltaID }; - out << QVariant::fromValue(msg); - _sequencer.endPacket(); - return false; - } - Bitstream& out = _sequencer.startPacket(); - int start = _sequencer.getOutputStream().getUnderlying().device()->pos(); - out << QVariant::fromValue(MetavoxelDeltaMessage()); - PacketRecord* sendRecord = getLastAcknowledgedSendRecord(); - _data.writeDelta(sendRecord->getData(), sendRecord->getLOD(), out, _lod); - out.flush(); - int end = _sequencer.getOutputStream().getUnderlying().device()->pos(); - if (end > _sequencer.getMaxPacketSize()) { - // we need to send the delta on the reliable channel - _reliableDeltaChannel = _sequencer.getReliableOutputChannel(RELIABLE_DELTA_CHANNEL_INDEX); - _reliableDeltaChannel->startMessage(); - _reliableDeltaChannel->getBuffer().write(_sequencer.getOutgoingPacketData().constData() + start, end - start); - _reliableDeltaChannel->endMessage(); - - _reliableDeltaWriteMappings = out.getAndResetWriteMappings(); - _reliableDeltaReceivedOffset = _reliableDeltaChannel->getBytesWritten(); - _reliableDeltaData = _data; - _reliableDeltaLOD = _lod; - - _sequencer.getOutputStream().getUnderlying().device()->seek(start); - MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID }; - out << QVariant::fromValue(msg); - _sequencer.endPacket(); - - } else { - _sequencer.endPacket(); - } - } else { - // enqueue some number of high priority messages - const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f; - const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f; - _highPriorityMessagesToSend += randFloatInRange(MIN_HIGH_PRIORITY_MESSAGES, MAX_HIGH_PRIORITY_MESSAGES); - while (_highPriorityMessagesToSend >= 1.0f) { - QVariant message = createRandomMessage(); - _highPriorityMessagesSent.append(message); - _sequencer.sendHighPriorityMessage(message); - highPriorityMessagesSent++; - _highPriorityMessagesToSend -= 1.0f; - } - - // 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; - } - - // tweak the local state - _localState = mutate(_localState); - - // send a packet - try { - Bitstream& out = _sequencer.startPacket(); - SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState }; - _unreliableMessagesSent.append(message); - unreliableMessagesSent++; - out << message; - _sequencer.endPacket(); - - } catch (const QString& message) { - qDebug() << message; - return true; - } - } - maxDatagramsPerPacket = qMax(maxDatagramsPerPacket, datagramsSent - oldDatagramsSent); - maxBytesPerPacket = qMax(maxBytesPerPacket, bytesSent - oldBytesSent); - return false; -} - -int TestEndpoint::parseData(const QByteArray& packet) { - if (_mode == CONGESTION_MODE) { - if (packet.size() <= _remainingPipelineCapacity) { - // have to copy the datagram; the one we're passed is a reference to a shared buffer - _pipeline[0].append(QByteArray(packet.constData(), packet.size())); - _remainingPipelineCapacity -= packet.size(); - } - } else { - _sequencer.receivedDatagram(packet); - datagramsReceived++; - bytesReceived += packet.size(); - } - return packet.size(); -} - -void TestEndpoint::sendDatagram(const QByteArray& datagram) { - datagramsSent++; - bytesSent += datagram.size(); - - // some datagrams are dropped - const float DROP_PROBABILITY = 0.1f; - float probabilityMultiplier = (_mode == CONGESTION_MODE) ? 0.01f : 1.0f; - if (randFloat() < DROP_PROBABILITY * probabilityMultiplier) { - return; - } - - // some are received out of order - const float REORDER_PROBABILITY = 0.1f; - if (randFloat() < REORDER_PROBABILITY * probabilityMultiplier) { - const int MIN_DELAY = 2; - const int MAX_DELAY = 5; - // have to copy the datagram; the one we're passed is a reference to a shared buffer - _delayedDatagrams.append(ByteArrayIntPair(QByteArray(datagram.constData(), datagram.size()), - randIntInRange(MIN_DELAY, MAX_DELAY))); - - // and some are duplicated - const float DUPLICATE_PROBABILITY = 0.01f; - if (randFloat() > DUPLICATE_PROBABILITY * probabilityMultiplier) { - return; - } - } - - _delayedDatagrams.append(ByteArrayIntPair(QByteArray(datagram.constData(), datagram.size()), 1)); -} - -void TestEndpoint::readMessage(Bitstream& in) { - if (_mode == CONGESTION_MODE) { - QVariant message; - in >> message; - return; - } - if (_mode == METAVOXEL_CLIENT_MODE) { - QVariant message; - in >> message; - handleMessage(message, in); - return; - } - if (_mode == METAVOXEL_SERVER_MODE) { - QVariant message; - in >> message; - handleMessage(message, in); - return; - } - SequencedTestMessage message; - in >> message; - - _remoteState = message.state; - - for (QList::iterator it = _other->_unreliableMessagesSent.begin(); - it != _other->_unreliableMessagesSent.end(); it++) { - if (it->sequenceNumber == message.sequenceNumber) { - if (!messagesEqual(it->submessage, message.submessage)) { - qDebug() << "Sent/received unreliable message mismatch."; - exit(true); - } - if (!it->state->equals(message.state)) { - qDebug() << "Delta-encoded object mismatch."; - exit(true); - } - _other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1); - unreliableMessagesReceived++; - return; - } - } - qDebug() << "Received unsent/already sent unreliable message."; - exit(true); -} - -void TestEndpoint::handleMessage(const QVariant& message, Bitstream& in) { - int userType = message.userType(); - if (userType == ClientStateMessage::Type) { - ClientStateMessage state = message.value(); - _lod = state.lod; - - } else if (userType == MetavoxelDeltaMessage::Type) { - PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); - _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, - _remoteDataLOD = getLastAcknowledgedSendRecord()->getLOD()); - in.reset(); - _data = _remoteData; - compareMetavoxelData(); - - } else if (userType == MetavoxelDeltaPendingMessage::Type) { - int id = message.value().id; - if (id > _reliableDeltaID) { - _reliableDeltaID = id; - _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); - _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); - _reliableDeltaLOD = getLastAcknowledgedSendRecord()->getLOD(); - PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); - _remoteDataLOD = receiveRecord->getLOD(); - _remoteData = receiveRecord->getData(); - } - } else if (userType == QMetaType::QVariantList) { - foreach (const QVariant& element, message.toList()) { - handleMessage(element, in); - } - } -} - -PacketRecord* TestEndpoint::maybeCreateSendRecord() const { - if (_reliableDeltaChannel) { - return new TestSendRecord(_sequencer.getOutgoingPacketNumber(), _reliableDeltaLOD, _reliableDeltaData, _localState); - } - return new TestSendRecord(_sequencer.getOutgoingPacketNumber(), _lod, - (_mode == METAVOXEL_CLIENT_MODE) ? MetavoxelData() : _data, _localState); -} - -PacketRecord* TestEndpoint::maybeCreateReceiveRecord() const { - return new TestReceiveRecord(_sequencer.getIncomingPacketNumber(), _remoteDataLOD, _remoteData, _remoteState); -} - -void TestEndpoint::handleHighPriorityMessage(const QVariant& message) { - if (message.userType() == ClearSharedObjectMessage::Type) { - return; - } - if (_other->_highPriorityMessagesSent.isEmpty()) { - throw QString("Received unsent/already sent high priority message."); - } - QVariant sentMessage = _other->_highPriorityMessagesSent.takeFirst(); - if (!messagesEqual(message, sentMessage)) { - throw QString("Sent/received high priority message mismatch."); - } - highPriorityMessagesReceived++; -} - -void TestEndpoint::handleReliableMessage(const QVariant& message, Bitstream& in) { - if (message.userType() == MetavoxelDeltaMessage::Type) { - PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); - _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, _remoteDataLOD = _reliableDeltaLOD); - _sequencer.getInputStream().persistReadMappings(in.getAndResetReadMappings()); - in.clearPersistentMappings(); - _data = _remoteData; - compareMetavoxelData(); - _reliableDeltaChannel = NULL; - return; - } - if (message.userType() == ClearSharedObjectMessage::Type || - message.userType() == ClearMainChannelSharedObjectMessage::Type) { - return; - } - 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 TestEndpoint::readReliableChannel() { - 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."); - } - QByteArray compare = _other->_dataStreamed.readBytes(0, bytes.size()); - _other->_dataStreamed.remove(bytes.size()); - if (compare != bytes) { - throw QString("Sent/received streamed data mismatch."); - } - streamedBytesReceived += bytes.size(); -} - -void TestEndpoint::checkReliableDeltaReceived() { - if (!_reliableDeltaChannel || _reliableDeltaChannel->getOffset() < _reliableDeltaReceivedOffset) { - return; - } - _sequencer.getOutputStream().persistWriteMappings(_reliableDeltaWriteMappings); - _reliableDeltaWriteMappings = Bitstream::WriteMappings(); - _reliableDeltaData = MetavoxelData(); - _reliableDeltaChannel = NULL; -} - -void TestEndpoint::compareMetavoxelData() { - // deep-compare data to sent version - int packetNumber = _sequencer.getIncomingPacketNumber(); - foreach (PacketRecord* record, _other->_sendRecords) { - TestSendRecord* sendRecord = static_cast(record); - if (sendRecord->getPacketNumber() == packetNumber) { - if (!sendRecord->getData().deepEquals(_data, getLastAcknowledgedSendRecord()->getLOD())) { - qDebug() << "Sent/received metavoxel data mismatch."; - exit(true); - } - return; - } - } - qDebug() << "Received metavoxel data with no corresponding send." << packetNumber; - exit(true); -} - -TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : - _foo(foo), - _baz(baz), - _bong(bong) { - sharedObjectsCreated++; - - _bizzle = DependencyManager::get()->getEngine()->newObject(); - _bizzle.setProperty("foo", DependencyManager::get()->getEngine()->newArray(4)); -} - -TestSharedObjectA::~TestSharedObjectA() { - sharedObjectsDestroyed++; -} - -void TestSharedObjectA::setFoo(float foo) { - if (_foo != foo) { - emit fooChanged(_foo = foo); - } -} - -TestSharedObjectB::TestSharedObjectB(float foo, const QByteArray& bar, TestEnum baz, TestFlags bong) : - _foo(foo), - _bar(bar), - _baz(baz), - _bong(bong) { - sharedObjectsCreated++; -} - -TestSharedObjectB::~TestSharedObjectB() { - sharedObjectsDestroyed++; -} diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h deleted file mode 100644 index ce357fbb90..0000000000 --- a/tests/metavoxels/src/MetavoxelTests.h +++ /dev/null @@ -1,251 +0,0 @@ -// -// MetavoxelTests.h -// tests/metavoxels/src -// -// Created by Andrzej Kapolka on 2/7/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MetavoxelTests_h -#define hifi_MetavoxelTests_h - -#include -#include - -#include -#include - -class SequencedTestMessage; - -/// Tests various aspects of the metavoxel library. -class MetavoxelTests : public QCoreApplication { - Q_OBJECT - -public: - - MetavoxelTests(int& argc, char** argv); - - /// Performs our various tests. - /// \return true if any of the tests failed. - bool run(); -}; - -/// Represents a simulated endpoint. -class TestEndpoint : public Endpoint { - Q_OBJECT - -public: - - enum Mode { BASIC_PEER_MODE, CONGESTION_MODE, METAVOXEL_SERVER_MODE, METAVOXEL_CLIENT_MODE }; - - TestEndpoint(Mode mode = BASIC_PEER_MODE); - - void setOther(TestEndpoint* other) { _other = other; } - - /// Perform a simulation step. - /// \return true if failure was detected - bool simulate(int iterationNumber); - - virtual int parseData(const QByteArray& packet); - -protected: - - virtual void sendDatagram(const QByteArray& data); - virtual void readMessage(Bitstream& in); - - virtual void handleMessage(const QVariant& message, Bitstream& in); - - virtual PacketRecord* maybeCreateSendRecord() const; - virtual PacketRecord* maybeCreateReceiveRecord() const; - -private slots: - - void handleHighPriorityMessage(const QVariant& message); - void handleReliableMessage(const QVariant& message, Bitstream& in); - void readReliableChannel(); - void checkReliableDeltaReceived(); - -private: - - void compareMetavoxelData(); - - Mode _mode; - - SharedObjectPointer _localState; - SharedObjectPointer _remoteState; - - MetavoxelData _data; - MetavoxelLOD _dataLOD; - MetavoxelData _remoteData; - MetavoxelLOD _remoteDataLOD; - MetavoxelLOD _lod; - - SharedObjectPointer _sphere; - - TestEndpoint* _other; - - typedef QPair ByteArrayIntPair; - QList _delayedDatagrams; - - typedef QVector ByteArrayVector; - QList _pipeline; - int _remainingPipelineCapacity; - - float _highPriorityMessagesToSend; - QVariantList _highPriorityMessagesSent; - QList _unreliableMessagesSent; - float _reliableMessagesToSend; - QVariantList _reliableMessagesSent; - CircularBuffer _dataStreamed; - - ReliableChannel* _reliableDeltaChannel; - int _reliableDeltaReceivedOffset; - MetavoxelData _reliableDeltaData; - MetavoxelLOD _reliableDeltaLOD; - Bitstream::WriteMappings _reliableDeltaWriteMappings; - int _reliableDeltaID; -}; - -/// A simple shared object. -class TestSharedObjectA : public SharedObject { - Q_OBJECT - Q_ENUMS(TestEnum) - Q_FLAGS(TestFlag TestFlags) - Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged) - Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz) - Q_PROPERTY(TestFlags bong READ getBong WRITE setBong) - Q_PROPERTY(QScriptValue bizzle READ getBizzle WRITE setBizzle) - -public: - - enum TestEnum { FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM }; - - enum TestFlag { NO_TEST_FLAGS = 0x0, FIRST_TEST_FLAG = 0x01, SECOND_TEST_FLAG = 0x02, THIRD_TEST_FLAG = 0x04 }; - Q_DECLARE_FLAGS(TestFlags, TestFlag) - - Q_INVOKABLE TestSharedObjectA(float foo = 0.0f, TestEnum baz = FIRST_TEST_ENUM, TestFlags bong = 0); - virtual ~TestSharedObjectA(); - - void setFoo(float foo); - float getFoo() const { return _foo; } - - void setBaz(TestEnum baz) { _baz = baz; } - TestEnum getBaz() const { return _baz; } - - void setBong(TestFlags bong) { _bong = bong; } - TestFlags getBong() const { return _bong; } - - void setBizzle(const QScriptValue& bizzle) { _bizzle = bizzle; } - const QScriptValue& getBizzle() const { return _bizzle; } - -signals: - - void fooChanged(float foo); - -private: - - float _foo; - TestEnum _baz; - TestFlags _bong; - QScriptValue _bizzle; -}; - -DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum) - -/// Another simple shared object. -class TestSharedObjectB : public SharedObject { - Q_OBJECT - Q_ENUMS(TestEnum) - Q_FLAGS(TestFlag TestFlags) - Q_PROPERTY(float foo READ getFoo WRITE setFoo) - Q_PROPERTY(QByteArray bar READ getBar WRITE setBar) - Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz) - Q_PROPERTY(TestFlags bong READ getBong WRITE setBong) - -public: - - enum TestEnum { ZEROTH_TEST_ENUM, FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM, FOURTH_TEST_ENUM }; - - enum TestFlag { NO_TEST_FLAGS = 0x0, ZEROTH_TEST_FLAG = 0x01, FIRST_TEST_FLAG = 0x02, - SECOND_TEST_FLAG = 0x04, THIRD_TEST_FLAG = 0x08, FOURTH_TEST_FLAG = 0x10 }; - Q_DECLARE_FLAGS(TestFlags, TestFlag) - - Q_INVOKABLE TestSharedObjectB(float foo = 0.0f, const QByteArray& bar = QByteArray(), - TestEnum baz = FIRST_TEST_ENUM, TestFlags bong = 0); - virtual ~TestSharedObjectB(); - - void setFoo(float foo) { _foo = foo; } - float getFoo() const { return _foo; } - - void setBar(const QByteArray& bar) { _bar = bar; } - const QByteArray& getBar() const { return _bar; } - - void setBaz(TestEnum baz) { _baz = baz; } - TestEnum getBaz() const { return _baz; } - - void setBong(TestFlags bong) { _bong = bong; } - TestFlags getBong() const { return _bong; } - -private: - - float _foo; - QByteArray _bar; - TestEnum _baz; - TestFlags _bong; -}; - -/// A simple test message. -class TestMessageA { - STREAMABLE - -public: - - STREAM bool foo; - STREAM int bar; - STREAM float baz; -}; - -DECLARE_STREAMABLE_METATYPE(TestMessageA) - -// Another simple test message. -class TestMessageB { - STREAMABLE - -public: - - STREAM QByteArray foo; - STREAM SharedObjectPointer bar; - STREAM TestSharedObjectA::TestEnum baz; -}; - -DECLARE_STREAMABLE_METATYPE(TestMessageB) - -// A test message that demonstrates inheritance and composition. -class TestMessageC : STREAM public TestMessageA { - STREAMABLE - -public: - - STREAM TestMessageB bong; - STREAM QScriptValue bizzle; -}; - -DECLARE_STREAMABLE_METATYPE(TestMessageC) - -/// Combines a sequence number with a submessage; used for testing unreliable transport. -class SequencedTestMessage { - STREAMABLE - -public: - - STREAM int sequenceNumber; - STREAM QVariant submessage; - STREAM SharedObjectPointer state; -}; - -DECLARE_STREAMABLE_METATYPE(SequencedTestMessage) - -#endif // hifi_MetavoxelTests_h diff --git a/tests/metavoxels/src/main.cpp b/tests/metavoxels/src/main.cpp deleted file mode 100644 index 51d4e565b2..0000000000 --- a/tests/metavoxels/src/main.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// -// main.cpp -// tests/metavoxels/src -// -// Created by Andrzej Kapolka on 2/7/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include "MetavoxelTests.h" - -int main(int argc, char** argv) { - return MetavoxelTests(argc, argv).run(); -} diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index 70457e220a..ffd908741d 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -3,6 +3,6 @@ set(TARGET_NAME octree-tests) setup_hifi_project(Script Network) # link in the shared libraries -link_hifi_libraries(shared octree gpu model fbx metavoxels networking entities avatars audio animation script-engine physics) +link_hifi_libraries(shared octree gpu model fbx networking entities avatars audio animation script-engine physics) copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 003920a442..08fe8fd7f3 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,6 +1,4 @@ # add the tool directories -add_subdirectory(bitstream2json) -add_subdirectory(json2bitstream) add_subdirectory(mtc) add_subdirectory(scribe) diff --git a/tools/bitstream2json/CMakeLists.txt b/tools/bitstream2json/CMakeLists.txt deleted file mode 100644 index 32a5a639ab..0000000000 --- a/tools/bitstream2json/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(TARGET_NAME bitstream2json) -setup_hifi_project(Widgets Script) - -link_hifi_libraries(metavoxels) - -copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/tools/bitstream2json/src/main.cpp b/tools/bitstream2json/src/main.cpp deleted file mode 100644 index 0f299527b0..0000000000 --- a/tools/bitstream2json/src/main.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// main.cpp -// tools/bitstream2json/src -// -// Created by Andrzej Kapolka on 6/17/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -#include - -#include -#include -#include - -#include - -using namespace std; - -int main (int argc, char** argv) { - // need the core application for the script engine - QCoreApplication app(argc, argv); - - if (argc < 3) { - cerr << "Usage: bitstream2json inputfile outputfile [types...]" << endl; - return 0; - } - QFile inputFile(argv[1]); - if (!inputFile.open(QIODevice::ReadOnly)) { - cerr << "Failed to open input file: " << inputFile.errorString().toLatin1().constData() << endl; - return 1; - } - QDataStream inputData(&inputFile); - Bitstream input(inputData, Bitstream::FULL_METADATA, Bitstream::ALL_GENERICS); - - QFile outputFile(argv[2]); - if (!outputFile.open(QIODevice::WriteOnly)) { - cerr << "Failed to open output file: " << outputFile.errorString().toLatin1().constData() << endl; - return 1; - } - JSONWriter output; - - if (argc < 4) { - // default type is a single QVariant - QVariant value; - input >> value; - output << value; - - } else { - for (int i = 3; i < argc; i++) { - int type = QMetaType::type(argv[i]); - if (type == QMetaType::UnknownType) { - cerr << "Unknown type: " << argv[i] << endl; - return 1; - } - const TypeStreamer* streamer = Bitstream::getTypeStreamer(type); - if (!streamer) { - cerr << "Non-streamable type: " << argv[i] << endl; - return 1; - } - QVariant value = streamer->read(input); - output.appendToContents(streamer->getJSONData(output, value)); - } - } - - outputFile.write(output.getDocument().toJson()); - - return 0; -} diff --git a/tools/json2bitstream/CMakeLists.txt b/tools/json2bitstream/CMakeLists.txt deleted file mode 100644 index abda40667c..0000000000 --- a/tools/json2bitstream/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(TARGET_NAME json2bitstream) -setup_hifi_project(Widgets Script) - -link_hifi_libraries(metavoxels) - -copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/tools/json2bitstream/src/main.cpp b/tools/json2bitstream/src/main.cpp deleted file mode 100644 index ff09bcfdc6..0000000000 --- a/tools/json2bitstream/src/main.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// -// main.cpp -// tools/json2bitstream/src -// -// Created by Andrzej Kapolka on 6/17/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -#include - -#include -#include -#include - -#include - -using namespace std; - -int main (int argc, char** argv) { - // need the core application for the script engine - QCoreApplication app(argc, argv); - - if (argc < 3) { - cerr << "Usage: bitstream2json inputfile outputfile [types...]" << endl; - return 0; - } - QFile inputFile(argv[1]); - if (!inputFile.open(QIODevice::ReadOnly)) { - cerr << "Failed to open input file: " << inputFile.errorString().toLatin1().constData() << endl; - return 1; - } - QJsonParseError error; - QJsonDocument document = QJsonDocument::fromJson(inputFile.readAll(), &error); - if (error.error != QJsonParseError::NoError) { - cerr << "Failed to read input file: " << error.errorString().toLatin1().constData() << endl; - return 1; - } - JSONReader input(document, Bitstream::ALL_GENERICS); - - QFile outputFile(argv[2]); - if (!outputFile.open(QIODevice::WriteOnly)) { - cerr << "Failed to open output file: " << outputFile.errorString().toLatin1().constData() << endl; - return 1; - } - QDataStream outputData(&outputFile); - Bitstream output(outputData, Bitstream::FULL_METADATA); - - if (argc < 4) { - // default type is a single QVariant - QVariant value; - input >> value; - output << value; - - } else { - for (int i = 3; i < argc; i++) { - int type = QMetaType::type(argv[i]); - if (type == QMetaType::UnknownType) { - cerr << "Unknown type: " << argv[i] << endl; - return 1; - } - const TypeStreamer* streamer = Bitstream::getTypeStreamer(type); - if (!streamer) { - cerr << "Non-streamable type: " << argv[i] << endl; - return 1; - } - QVariant value; - streamer->putJSONData(input, input.retrieveNextFromContents(), value); - streamer->write(output, value); - } - } - output.flush(); - - return 0; -}