remove metavoxels

This commit is contained in:
ZappoMan 2015-03-02 17:21:43 -08:00
parent 6d8ccfbc2a
commit 5c3c94a618
66 changed files with 30 additions and 25269 deletions

View file

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

View file

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

View file

@ -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 <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QSaveFile>
#include <QThread>
#include <PacketHeaders.h>
#include <MetavoxelMessages.h>
#include <MetavoxelUtil.h>
#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>();
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<NodeList>();
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<MetavoxelSession*>(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<MetavoxelSession*>(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<MetavoxelSession*>(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<ClientStateMessage>();
_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<MetavoxelEditMessage>()));
}
} 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.";
}

View file

@ -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 <QList>
#include <QTimer>
#include <ThreadedAssignment.h>
#include <Endpoint.h>
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<MetavoxelSender*> _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<MetavoxelSession*> _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

View file

@ -559,8 +559,7 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
}
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
<< NodeType::AvatarMixer << NodeType::EntityServer
<< NodeType::MetavoxelServer;
<< NodeType::AvatarMixer << NodeType::EntityServer;
void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {

View file

@ -115,7 +115,7 @@ target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
# link required hifi libraries
link_hifi_libraries(shared octree environment gpu model fbx metavoxels networking entities avatars
link_hifi_libraries(shared octree environment gpu model fbx networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer)

View file

@ -52,6 +52,7 @@
#include <QMediaPlayer>
#include <QMimeData>
#include <QMessageBox>
#include <QJsonDocument>
#include <AddressManager.h>
#include <AccountManager.h>
@ -75,7 +76,7 @@
#include <PhysicsEngine.h>
#include <ProgramObject.h>
#include <ResourceCache.h>
#include <ScriptCache.h>
//#include <ScriptCache.h>
#include <SettingHandle.h>
#include <SoundCache.h>
#include <TextRenderer.h>
@ -220,7 +221,7 @@ bool setupEssentials(int& argc, char** argv) {
auto addressManager = DependencyManager::set<AddressManager>();
auto nodeList = DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
auto geometryCache = DependencyManager::set<GeometryCache>();
auto scriptCache = DependencyManager::set<ScriptCache>();
//auto scriptCache = DependencyManager::set<ScriptCache>();
auto soundCache = DependencyManager::set<SoundCache>();
auto glowEffect = DependencyManager::set<GlowEffect>();
auto faceshift = DependencyManager::set<Faceshift>();
@ -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<AnimationCache>();
DependencyManager::destroy<TextureCache>();
DependencyManager::destroy<GeometryCache>();
DependencyManager::destroy<ScriptCache>();
//DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<SoundCache>();
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<NodeList>()->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<AvatarManager>()->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<GeometryCache>()->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<AnimationCache>().data());
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels);
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);

View file

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

View file

@ -100,9 +100,6 @@ void DatagramProcessor::processDatagrams() {
}
break;
}
case PacketTypeMetavoxelData:
nodeList->findNodeAndUpdateWithDataFromPacket(incomingPacket);
break;
case PacketTypeBulkAvatarData:
case PacketTypeKillAvatar:
case PacketTypeAvatarIdentity:

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -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 <QList>
#include <QOpenGLBuffer>
#include <QReadWriteLock>
#include <QVector>
#include <glm/glm.hpp>
#include <MetavoxelClientManager.h>
#include <ProgramObject.h>
#include <TextureCache.h>
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<HeightfieldBaseLayerBatch> _heightfieldBaseBatches;
QVector<HeightfieldSplatBatch> _heightfieldSplatBatches;
QVector<MetavoxelBatch> _voxelBaseBatches;
QVector<VoxelSplatBatch> _voxelSplatBatches;
QVector<HermiteBatch> _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<qint64, int> Bucket;
QList<Bucket> _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<BufferData> 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<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
const QMultiHash<VoxelCoord, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
QVector<SharedObjectPointer>());
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<VoxelPoint> _vertices;
QVector<int> _indices;
QVector<glm::vec3> _hermite;
bool _hermiteEnabled;
QMultiHash<VoxelCoord, int> _quadIndices;
int _size;
int _vertexCount;
int _indexCount;
int _hermiteCount;
GLuint _vertexBufferID;
GLuint _indexBufferID;
GLuint _hermiteBufferID;
QVector<SharedObjectPointer> _materials;
QVector<NetworkTexturePointer> _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<NetworkTexturePointer> _networkTextures;
BufferDataPointer _voxels;
typedef QPair<int, int> IntPair;
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
static QHash<IntPair, BufferPair> _bufferPairs;
};
#endif // hifi_MetavoxelSystem_h

View file

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

View file

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

View file

@ -16,7 +16,6 @@
#include <AnimationCache.h>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <ScriptCache.h>
#include <SoundCache.h>
#include <TextureCache.h>
@ -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<AnimationCache>()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES);
DependencyManager::get<GeometryCache>()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES);
DependencyManager::get<ScriptCache>()->setUnusedResourceCacheSize(_scripts->value() * BYTES_PER_MEGABYTES);
DependencyManager::get<SoundCache>()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES);
DependencyManager::get<TextureCache>()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES);
@ -69,7 +66,6 @@ void CachesSizeDialog::confirmClicked(bool checked) {
void CachesSizeDialog::resetClicked(bool checked) {
_animations->setValue(DependencyManager::get<AnimationCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
_geometries->setValue(DependencyManager::get<GeometryCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
_scripts->setValue(DependencyManager::get<ScriptCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
_sounds->setValue(DependencyManager::get<SoundCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
_textures->setValue(DependencyManager::get<TextureCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
}

View file

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

View file

@ -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> _hmdToolsDialog;
QPointer<LodToolsDialog> _lodToolsDialog;
QPointer<LoginDialog> _loginDialog;
QPointer<MetavoxelEditor> _metavoxelEditor;
QPointer<MetavoxelNetworkSimulator> _metavoxelNetworkSimulator;
QPointer<OctreeStatsDialog> _octreeStatsDialog;
QPointer<PreferencesDialog> _preferencesDialog;
QPointer<ScriptEditorWindow> _scriptEditor;

File diff suppressed because it is too large Load diff

View file

@ -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 <QFormLayout>
#include <QList>
#include <QWidget>
#include <ProgramObject.h>
#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<MetavoxelTool*> _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<NetworkTexture> _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

View file

@ -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 <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QSpinBox>
#include <QVBoxLayout>
#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<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
&MetavoxelNetworkSimulator::updateMetavoxelSystem);
form->addRow("Repeat Rate:", _repeatRate = new QDoubleSpinBox());
_repeatRate->setSuffix("%");
_repeatRate->setValue(simulation.repeatRate * 100.0);
connect(_repeatRate, static_cast<void (QDoubleSpinBox::*)(double)>(&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<void (QSpinBox::*)(int)>(&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<void (QSpinBox::*)(int)>(&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<void (QSpinBox::*)(int)>(&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));
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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 <QBuffer>
#include <QMutexLocker>
#include <QReadLocker>
#include <QScriptEngine>
#include <QWriteLocker>
#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<AttributePointer>();
static int ownedAttributeValueMetaTypeId = qRegisterMetaType<OwnedAttributeValue>();
AttributeRegistry* AttributeRegistry::getInstance() {
static AttributeRegistry registry;
return &registry;
}
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<SharedObjectPointer>(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<SharedObjectPointer>(value);
}
}
bool SharedObjectAttribute::deepEqual(void* first, void* second) const {
SharedObjectPointer firstObject = decodeInline<SharedObjectPointer>(first);
SharedObjectPointer secondObject = decodeInline<SharedObjectPointer>(second);
return firstObject ? firstObject->equals(secondObject) : !secondObject;
}
bool SharedObjectAttribute::merge(void*& parent, void* children[], bool postRead) const {
SharedObjectPointer firstChild = decodeInline<SharedObjectPointer>(children[0]);
for (int i = 1; i < MERGE_COUNT; i++) {
if (firstChild != decodeInline<SharedObjectPointer>(children[i])) {
*(SharedObjectPointer*)&parent = _defaultValue;
return false;
}
}
*(SharedObjectPointer*)&parent = firstChild;
return true;
}
void* SharedObjectAttribute::createFromVariant(const QVariant& value) const {
return create(encodeInline(value.value<SharedObjectPointer>()));
}
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<SharedObjectSet>(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<SharedObjectSet>(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<SharedObjectSet>(first), decodeInline<SharedObjectSet>(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<SharedObjectSet>(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);
}

View file

@ -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 <QHash>
#include <QMutex>
#include <QObject>
#include <QReadWriteLock>
#include <QSharedPointer>
#include <QString>
#include <QUrl>
#include <QWidget>
#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<Attribute> 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<QString, AttributePointer>& 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<QString, AttributePointer> _attributes;
QReadWriteLock _attributesLock;
AttributePointer _guideAttribute;
AttributePointer _rendererAttribute;
AttributePointer _spannersAttribute;
};
/// Converts a value to a void pointer.
template<class T> inline void* encodeInline(T value) {
return *(void**)&value;
}
/// Extracts a value from a void pointer.
template<class T> 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<class T> void setInlineValue(T value) { _value = encodeInline(value); }
template<class T> T getInlineValue() const { return decodeInline<T>(_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 T, int bits = 32> 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*)&copy); 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<T>(first) == decodeInline<T>(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<class T, int bits> inline void InlineAttribute<T, bits>::read(Bitstream& in, void*& value, bool isLeaf) const {
if (isLeaf) {
value = getDefaultValue();
in.read(&value, bits);
}
}
template<class T, int bits> inline void InlineAttribute<T, bits>::write(Bitstream& out, void* value, bool isLeaf) const {
if (isLeaf) {
out.write(&value, bits);
}
}
/// Provides averaging using the +=, ==, and / operators.
template<class T, int bits = 32> class SimpleInlineAttribute : public InlineAttribute<T, bits> {
public:
SimpleInlineAttribute(const QString& name, const T& defaultValue = T()) : InlineAttribute<T, bits>(name, defaultValue) { }
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
template<class T, int bits> inline bool SimpleInlineAttribute<T, bits>::merge(
void*& parent, void* children[], bool postRead) const {
T firstValue = decodeInline<T>(children[0]);
T totalValue = firstValue;
bool allChildrenEqual = true;
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
T value = decodeInline<T>(children[i]);
totalValue += value;
allChildrenEqual &= (firstValue == value);
}
parent = encodeInline(totalValue / Attribute::MERGE_COUNT);
return allChildrenEqual;
}
/// A simple float attribute.
class FloatAttribute : public SimpleInlineAttribute<float> {
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<SharedObjectPointer> {
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<SharedObjectSet> {
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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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 <cstring>
#include <QtDebug>
#include <LimitedNodeList.h>
#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<ChannelSpan> 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<SendRecord>::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<ClearSharedObjectMessage>().id);
}
}
void DatagramSequencer::clearReliableChannel(QObject* object) {
ReliableChannel* channel = static_cast<ReliableChannel*>(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<ChannelSpan>& 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<ChannelSpan>& 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<Span>::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<Span>::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<ClearSharedObjectMessage>().id);
} else if (message.userType() == ClearMainChannelSharedObjectMessage::Type) {
static_cast<DatagramSequencer*>(parent())->_inputStream.clearSharedObject(
message.value<ClearMainChannelSharedObjectMessage>().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<DatagramSequencer::ChannelSpan>& 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<DatagramSequencer::ChannelSpan>& 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<DatagramSequencer::ChannelSpan>& 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();
}
}

View file

@ -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 <QBuffer>
#include <QDataStream>
#include <QByteArray>
#include <QList>
#include <QSet>
#include <QVector>
#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<HighPriorityMessage>& 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<ChannelSpan> 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<ChannelSpan>& 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<ChannelSpan>& spans);
QList<SendRecord> _sendRecords;
QList<ReceiveRecord> _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<int> _offsetsReceived;
int _remainingBytes;
QList<HighPriorityMessage> _highPriorityMessages;
int _receivedHighPriorityMessages;
int _maxPacketSize;
float _packetsPerGroup;
float _packetsToWrite;
float _slowStartThreshold;
int _packetRateIncreasePacketNumber;
int _packetRateDecreasePacketNumber;
int _packetDropCount;
int _lastPacketDropped;
QHash<int, ReliableChannel*> _reliableOutputChannels;
QHash<int, ReliableChannel*> _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<Span>& 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<Span>::iterator it, int length);
QList<Span> _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<DatagramSequencer::ChannelSpan>& spans);
void writeFullSpans(QDataStream& out, int bytes, int startingIndex, int position,
QVector<DatagramSequencer::ChannelSpan>& spans);
int writeSpan(QDataStream& out, int position, int length, QVector<DatagramSequencer::ChannelSpan>& 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<int, int> OffsetSizePair;
QVector<OffsetSizePair> _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

View file

@ -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 <PacketHeaders.h>
#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<NodeList>()->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<PacketRecord*>::iterator end = _sendRecords.begin() + index + 1;
for (QList<PacketRecord*>::const_iterator it = _sendRecords.begin(); it != end; it++) {
delete *it;
}
_sendRecords.erase(_sendRecords.begin(), end);
}
void Endpoint::clearReceiveRecordsBefore(int index) {
QList<PacketRecord*>::iterator end = _receiveRecords.begin() + index + 1;
for (QList<PacketRecord*>::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() {
}

View file

@ -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 <NodeList.h>
#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<PacketRecord*> _sendRecords;
QList<PacketRecord*> _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

View file

@ -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 <QDateTime>
#include <QReadLocker>
#include <QThread>
#include <QWriteLocker>
#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<NodeList>().data(), &NodeList::nodeAdded,
this, &MetavoxelClientManager::maybeAttachClient);
connect(DependencyManager::get<NodeList>().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<NodeList>()->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelClient*>(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<AttributePointer>() <<
AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), 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<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), 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<MetavoxelClient*>(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<NodeList>()->eachNode([&visitor](const SharedNodePointer& node){
if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelClient*>(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<MetavoxelClient*>(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<MetavoxelClient*>(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<PacketRecord*>::iterator end = _sendRecords.begin() + index + 1;
for (QList<PacketRecord*>::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<PacketRecord*>::iterator end = _receiveRecords.begin() + index + 1;
for (QList<PacketRecord*>::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<MetavoxelEditMessage>().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<MetavoxelDeltaPendingMessage>();
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);
}

View file

@ -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 <QReadWriteLock>
#include <QTimer>
#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<MetavoxelClient*> _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<PacketRecord*> _clearedSendRecords;
typedef QPair<PacketRecord*, Bitstream::ReadMappings> ClearedReceiveRecord;
QList<ClearedReceiveRecord> _clearedReceiveRecords;
};
#endif // hifi_MetavoxelClientManager_h

File diff suppressed because it is too large Load diff

View file

@ -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 <QHash>
#include <QMutex>
#include <QSharedData>
#include <QSharedPointer>
#include <QVector>
#include <glm/glm.hpp>
#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<SharedObjectPointer>& 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<AttributePointer, MetavoxelNode*> _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<AttributeValue> inputValues;
QVector<OwnedAttributeValue> 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<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD());
virtual ~MetavoxelVisitor();
/// Returns a reference to the list of input attributes desired.
const QVector<AttributePointer>& getInputs() const { return _inputs; }
/// Returns a reference to the list of output attributes provided.
const QVector<AttributePointer>& 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<AttributePointer> _inputs;
QVector<AttributePointer> _outputs;
MetavoxelLOD _lod;
float _minimumLODThresholdMultiplier;
MetavoxelData* _data;
QList<MetavoxelVisitation> _visitations;
int _depth;
};
/// Base class for visitors to spanners.
class SpannerVisitor : public MetavoxelVisitor {
public:
SpannerVisitor(const QVector<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
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<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
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<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
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<MetavoxelNode*> inputNodes;
QVector<MetavoxelNode*> outputNodes;
QVector<MetavoxelNode*> 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

View file

@ -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 <glm/gtx/transform.hpp>
#include "MetavoxelMessages.h"
#include "Spanner.h"
void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
static_cast<const MetavoxelEdit*>(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<AttributePointer>(), QVector<AttributePointer>() << 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<AttributePointer>(), QVector<AttributePointer>() << 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<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(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<SharedObjectPointer>& results);
virtual bool visit(Spanner* spanner);
private:
const Box& _bounds;
QVector<SharedObjectPointer>& _results;
float _closestDistance;
};
SpannerProjectionFetchVisitor::SpannerProjectionFetchVisitor(const Box& bounds, QVector<SharedObjectPointer>& results) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute()),
_bounds(bounds),
_results(results),
_closestDistance(FLT_MAX) {
}
bool SpannerProjectionFetchVisitor::visit(Spanner* spanner) {
Heightfield* heightfield = qobject_cast<Heightfield*>(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<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
static_cast<Spanner*>(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*>(spanner.data())->getBounds(), results);
data.guide(visitor);
}
foreach (const SharedObjectPointer& result, results) {
Spanner* newResult = static_cast<Spanner*>(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<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->fillHeight(position, radius, granularity);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
}
}

View file

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

View file

@ -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 <QByteArray>
#include <QColorDialog>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QItemEditorFactory>
#include <QLineEdit>
#include <QMetaType>
#include <QPushButton>
#include <QScriptEngine>
#include <QVBoxLayout>
#include <QtDebug>
#include <GLMHelpers.h>
#include <SettingHandle.h>
#include "MetavoxelUtil.h"
#include "ScriptCache.h"
#include "StreamUtils.h"
static int scriptHashType = qRegisterMetaType<ScriptHash>();
static int parameterizedURLType = qRegisterMetaType<ParameterizedURL>();
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<DoubleEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<double>(), creator);
getItemEditorFactory()->registerEditor(qMetaTypeId<float>(), creator);
return creator;
}
static QItemEditorCreatorBase* createQMetaObjectEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<QMetaObjectEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<const QMetaObject*>(), creator);
return creator;
}
static QItemEditorCreatorBase* createQColorEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<QColorEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<QColor>(), creator);
return creator;
}
static QItemEditorCreatorBase* createQUrlEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<QUrlEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<QUrl>(), creator);
return creator;
}
static QItemEditorCreatorBase* createVec3EditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<Vec3Editor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<glm::vec3>(), creator);
return creator;
}
static QItemEditorCreatorBase* createQuatEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<QuatEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<glm::quat>(), creator);
return creator;
}
static QItemEditorCreatorBase* createParameterizedURLEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<ParameterizedURLEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<ParameterizedURL>(), 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<const QMetaObject*>());
}
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<QStringList> 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<QFormLayout*>(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<ScriptCache>()->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<ScriptCache>()->getProgram(_url.getURL());
if (_program->isLoaded()) {
continueUpdatingParameters();
} else {
connect(_program.data(), SIGNAL(loaded()), SLOT(continueUpdatingParameters()));
}
}
void ParameterizedURLEditor::continueUpdatingParameters() {
QVBoxLayout* layout = static_cast<QVBoxLayout*>(this->layout());
if (layout->count() > 1) {
QFormLayout* form = static_cast<QFormLayout*>(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<NetworkValue> value = DependencyManager::get<ScriptCache>()->getValue(_url.getURL());
const QList<ParameterInfo>& parameters = static_cast<RootNetworkValue*>(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()));
}
}
}
}

View file

@ -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 <QColor>
#include <QComboBox>
#include <QItemEditorCreatorBase>
#include <QSharedPointer>
#include <QUrl>
#include <QWidget>
#include <RegisteredMetaTypes.h>
#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 T> class LazyItemEditorCreator : public QItemEditorCreatorBase {
public:
virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); }
virtual QByteArray valuePropertyName() const;
protected:
QByteArray _valuePropertyName;
};
template<class T> QByteArray LazyItemEditorCreator<T>::valuePropertyName() const {
if (_valuePropertyName.isNull()) {
const_cast<LazyItemEditorCreator<T>*>(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<QScriptString, QVariant> 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<NetworkProgram> _program;
QUrlEditor _urlEditor;
};
#endif // hifi_MetavoxelUtil_h

View file

@ -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 <cmath>
#include <QNetworkReply>
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QTextStream>
#include "AttributeRegistry.h"
#include "ScriptCache.h"
static int scriptValueMetaTypeId = qRegisterMetaType<QScriptValue>();
static bool scriptValueComparators = QMetaType::registerComparators<QScriptValue>();
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<ScriptCache>()->getLengthString()).toInt32();
if (second.property(DependencyManager::get<ScriptCache>()->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<NetworkValue> ScriptCache::getValue(const ParameterizedURL& url) {
QSharedPointer<NetworkValue> value = _networkValues.value(url);
if (value.isNull()) {
value = QSharedPointer<NetworkValue>(url.getParameters().isEmpty() ?
(NetworkValue*)new RootNetworkValue(getProgram(url.getURL())) :
(NetworkValue*)new DerivedNetworkValue(getValue(url.getURL()), url.getParameters()));
_networkValues.insert(url, value);
}
return value;
}
QSharedPointer<Resource> ScriptCache::createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(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<NetworkProgram>& program) :
_program(program) {
}
QScriptValue& RootNetworkValue::getValue() {
if (!_value.isValid() && _program->isLoaded()) {
_value = _program->getCache()->getEngine()->evaluate(_program->getProgram());
}
return _value;
}
const QList<ParameterInfo>& 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<NetworkValue>& baseValue, const ScriptHash& parameters) :
_baseValue(baseValue),
_parameters(parameters) {
}
QScriptValue& DerivedNetworkValue::getValue() {
if (!_value.isValid() && _baseValue->isLoaded()) {
RootNetworkValue* root = static_cast<RootNetworkValue*>(_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;
}

View file

@ -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 <QScriptProgram>
#include <QScriptValue>
#include <ResourceCache.h>
#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<NetworkProgram> getProgram(const QUrl& url) { return getResource(url).staticCast<NetworkProgram>(); }
/// Loads a script value from the specified URL.
QSharedPointer<NetworkValue> 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<Resource> createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
private:
QScriptEngine* _engine;
QHash<ParameterizedURL, QWeakPointer<NetworkValue> > _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<NetworkProgram>& program);
const QSharedPointer<NetworkProgram>& getProgram() const { return _program; }
virtual QScriptValue& getValue();
const QList<ParameterInfo>& getParameterInfo();
private:
QSharedPointer<NetworkProgram> _program;
QList<ParameterInfo> _parameterInfo;
};
/// The result of running a program's generator using a set of arguments.
class DerivedNetworkValue : public NetworkValue {
public:
DerivedNetworkValue(const QSharedPointer<NetworkValue>& baseValue, const ScriptHash& parameters);
virtual QScriptValue& getValue();
private:
QSharedPointer<NetworkValue> _baseValue;
ScriptHash _parameters;
};
#endif // hifi_ScriptCache_h

View file

@ -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 <QComboBox>
#include <QFormLayout>
#include <QItemEditorFactory>
#include <QMetaProperty>
#include <QVBoxLayout>
#include <QWriteLocker>
#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<SharedObject*>(metaObject->newInstance());
}
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (property.isStored()) {
if (property.userType() == qMetaTypeId<SharedObjectPointer>()) {
SharedObject* value = property.read(this).value<SharedObjectPointer>().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<QByteArray> 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<QByteArray> 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<QFormLayout*>(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<QFormLayout*>(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<SharedObject*>(_object.data());
const QMetaObject* oldMetaObject = NULL;
if (oldObject) {
oldMetaObject = oldObject->metaObject();
oldObject->disconnect(this);
}
const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value<const QMetaObject*>();
if (!metaObject) {
_object.reset();
emit objectChanged(_object);
return;
}
QObject* newObject = metaObject->newInstance();
QFormLayout* form = new QFormLayout();
static_cast<QVBoxLayout*>(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<SharedObject*>(newObject));
}
void SharedObjectEditor::propertyChanged() {
QFormLayout* form = static_cast<QFormLayout*>(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<QFormLayout*>(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()));
}
}

View file

@ -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 <QAtomicInt>
#include <QHash>
#include <QMetaType>
#include <QObject>
#include <QPointer>
#include <QReadWriteLock>
#include <QSet>
#include <QWidget>
#include <QtDebug>
class QComboBox;
class Bitstream;
class SharedObject;
typedef QHash<int, QPointer<SharedObject> > 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 T> class SharedObjectPointerTemplate {
public:
SharedObjectPointerTemplate(T* data = NULL);
SharedObjectPointerTemplate(const SharedObjectPointerTemplate<T>& 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<T>& 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<class X> SharedObjectPointerTemplate<X> staticCast() const;
SharedObjectPointerTemplate<T>& operator=(T* data);
SharedObjectPointerTemplate<T>& operator=(const SharedObjectPointerTemplate<T>& other);
bool operator==(const SharedObjectPointerTemplate<T>& other) const { return _data == other._data; }
bool operator!=(const SharedObjectPointerTemplate<T>& other) const { return _data != other._data; }
private:
T* _data;
};
template<class T> inline SharedObjectPointerTemplate<T>::SharedObjectPointerTemplate(T* data) : _data(data) {
if (_data) {
_data->incrementReferenceCount();
}
}
template<class T> inline SharedObjectPointerTemplate<T>::SharedObjectPointerTemplate(const SharedObjectPointerTemplate<T>& other) :
_data(other._data) {
if (_data) {
_data->incrementReferenceCount();
}
}
template<class T> inline SharedObjectPointerTemplate<T>::~SharedObjectPointerTemplate() {
if (_data) {
_data->decrementReferenceCount();
}
}
template<class T> inline bool SharedObjectPointerTemplate<T>::detach() {
if (_data && _data->getReferenceCount() > 1) {
_data->decrementReferenceCount();
(_data = _data->clone())->incrementReferenceCount();
return true;
}
return false;
}
template<class T> inline void SharedObjectPointerTemplate<T>::reset() {
if (_data) {
_data->decrementReferenceCount();
}
_data = NULL;
}
template<class T> template<class X> inline SharedObjectPointerTemplate<X> SharedObjectPointerTemplate<T>::staticCast() const {
return SharedObjectPointerTemplate<X>(static_cast<X*>(_data));
}
template<class T> inline SharedObjectPointerTemplate<T>& SharedObjectPointerTemplate<T>::operator=(T* data) {
if (_data) {
_data->decrementReferenceCount();
}
if ((_data = data)) {
_data->incrementReferenceCount();
}
return *this;
}
template<class T> inline SharedObjectPointerTemplate<T>& SharedObjectPointerTemplate<T>::operator=(
const SharedObjectPointerTemplate<T>& other) {
if (_data) {
_data->decrementReferenceCount();
}
if ((_data = other._data)) {
_data->incrementReferenceCount();
}
return *this;
}
template<class T> uint qHash(const SharedObjectPointerTemplate<T>& pointer, uint seed = 0) {
return qHash(pointer.data(), seed);
}
template<class T, class X> bool equals(const SharedObjectPointerTemplate<T>& first,
const SharedObjectPointerTemplate<X>& second) {
return first ? first->equals(second) : !second;
}
typedef SharedObjectPointerTemplate<SharedObject> SharedObjectPointer;
Q_DECLARE_METATYPE(SharedObjectPointer)
typedef QSet<SharedObjectPointer> 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

File diff suppressed because it is too large Load diff

View file

@ -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 <glm/glm.hpp>
#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<SharedObjectPointer>& 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<QThread*, int> _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<DataBlock> 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<EncodedSubdivision> _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<HeightfieldHeight> 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<quint16>& contents);
HeightfieldHeight(Bitstream& in, int bytes);
HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference);
QVector<quint16>& getContents() { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<quint16> _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<HeightfieldColor> 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<HeightfieldMaterial> HeightfieldMaterialPointer;
/// A block of material data associated with a heightfield.
class HeightfieldMaterial : public HeightfieldData {
public:
HeightfieldMaterial(int width, const QByteArray& contents, const QVector<SharedObjectPointer>& materials);
HeightfieldMaterial(Bitstream& in, int bytes);
HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference);
QByteArray& getContents() { return _contents; }
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
QVector<SharedObjectPointer> _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<SharedObjectPointer>& materials);
typedef QExplicitlySharedDataPointer<HeightfieldStack> 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<StackArray>& contents, const QVector<SharedObjectPointer>& materials);
HeightfieldStack(Bitstream& in, int bytes);
HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference);
QVector<StackArray>& getContents() { return _contents; }
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldStackPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<StackArray> _contents;
QVector<SharedObjectPointer> _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<HeightfieldNode> 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<quint16>& heightContents, QVector<StackArray>& stackContents);
bool findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float boundsDistance, float& distance) const;
HeightfieldNode* subdivide(const QVector<quint16>& heightContents, const QVector<StackArray>& 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<SharedObjectPointer>& 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

View file

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

View file

@ -31,7 +31,7 @@ public:
AgentType,
UNUSED_0,
UNUSED_1,
MetavoxelServerType,
UNUSED_2,
EntityServerType,
AllTypes
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -20,7 +20,6 @@
#include <AudioConstants.h>
#include <AudioEffectOptions.h>
#include <AvatarData.h>
#include <Bitstream.h>
#include <CollisionInfo.h>
#include <EntityScriptingInterface.h>
#include <NetworkAccessManager.h>
@ -324,7 +323,6 @@ void ScriptEngine::init() {
registerAnimationTypes(this);
registerAvatarTypes(this);
registerAudioMetaTypes(this);
Bitstream::registerTypes(this);
qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValue);
qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);

View file

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

File diff suppressed because it is too large Load diff

View file

@ -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 <QCoreApplication>
#include <QVariantList>
#include <Endpoint.h>
#include <ScriptCache.h>
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<QByteArray, int> ByteArrayIntPair;
QList<ByteArrayIntPair> _delayedDatagrams;
typedef QVector<QByteArray> ByteArrayVector;
QList<ByteArrayVector> _pipeline;
int _remainingPipelineCapacity;
float _highPriorityMessagesToSend;
QVariantList _highPriorityMessagesSent;
QList<SequencedTestMessage> _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

View file

@ -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 <QDebug>
#include "MetavoxelTests.h"
int main(int argc, char** argv) {
return MetavoxelTests(argc, argv).run();
}

View file

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

View file

@ -1,6 +1,4 @@
# add the tool directories
add_subdirectory(bitstream2json)
add_subdirectory(json2bitstream)
add_subdirectory(mtc)
add_subdirectory(scribe)

View file

@ -1,6 +0,0 @@
set(TARGET_NAME bitstream2json)
setup_hifi_project(Widgets Script)
link_hifi_libraries(metavoxels)
copy_dlls_beside_windows_executable()

View file

@ -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 <iostream>
#include <QCoreApplication>
#include <QDataStream>
#include <QFile>
#include <AttributeRegistry.h>
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;
}

View file

@ -1,6 +0,0 @@
set(TARGET_NAME json2bitstream)
setup_hifi_project(Widgets Script)
link_hifi_libraries(metavoxels)
copy_dlls_beside_windows_executable()

View file

@ -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 <iostream>
#include <QCoreApplication>
#include <QDataStream>
#include <QFile>
#include <AttributeRegistry.h>
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;
}