Merge branch 'master' of https://github.com/highfidelity/hifi into domain-server-auth

This commit is contained in:
Stephen Birarda 2014-07-29 10:50:36 -07:00
commit 133e8570a3
27 changed files with 1606 additions and 494 deletions

View file

@ -98,6 +98,9 @@ Currently building on Windows has been tested using the following compilers:
Whichever version of Visual Studio you use, first install [Microsoft Windows SDK for Windows 7 and .NET Framework 4](http://www.microsoft.com/en-us/download/details.aspx?id=8279). Whichever version of Visual Studio you use, first install [Microsoft Windows SDK for Windows 7 and .NET Framework 4](http://www.microsoft.com/en-us/download/details.aspx?id=8279).
######Windows 8.1
You may have already downloaded the Windows 8 SDK (e.g. if you have previously installed Visual Studio 2013). If so, change CMAKE_PREFIX_PATH in %HIFI_DIR%\CMakeLists.txt to point to the Windows 8 SDK binaries. The default path is `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86`
#####Visual Studio C++ 2010 Express #####Visual Studio C++ 2010 Express
Visual Studio C++ 2010 Express can be downloaded [here](http://www.visualstudio.com/en-us/downloads#d-2010-express). Visual Studio C++ 2010 Express can be downloaded [here](http://www.visualstudio.com/en-us/downloads#d-2010-express).
@ -116,6 +119,8 @@ Visual Studio 2013 doesn't have a shortcut to start a Visual Studio Command Prom
"%VS120COMNTOOLS%\vsvars32.bat" "%VS120COMNTOOLS%\vsvars32.bat"
If you experience issues building interface on Visual Studio 2013, try generating the build files with Visual Studio 2010 instead. To do so, download Visual Studio 2010 and run `cmake .. -G "Visual Studio 10"` (Assuming running from %HIFI_DIR%\build).
####Qt ####Qt
You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version. You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version.

View file

@ -9,6 +9,10 @@ add_definitions(-DGLM_FORCE_RADIANS)
if (WIN32) if (WIN32)
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
# set path for Microsoft SDKs
# if get build error about missing 'glu32' this path is likely wrong
# Uncomment the line with 8.1 if running Windows 8.1
#set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ") set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)

View file

@ -29,6 +29,8 @@
#include <ParticlesScriptingInterface.h> // TODO: consider moving to scriptengine.h #include <ParticlesScriptingInterface.h> // TODO: consider moving to scriptengine.h
#include <ModelsScriptingInterface.h> // TODO: consider moving to scriptengine.h #include <ModelsScriptingInterface.h> // TODO: consider moving to scriptengine.h
#include "avatars/ScriptableAvatar.h"
#include "Agent.h" #include "Agent.h"
Agent::Agent(const QByteArray& packet) : Agent::Agent(const QByteArray& packet) :
@ -228,7 +230,7 @@ void Agent::run() {
qDebug() << "Downloaded script:" << scriptContents; qDebug() << "Downloaded script:" << scriptContents;
// setup an Avatar for the script to use // setup an Avatar for the script to use
AvatarData scriptedAvatar; ScriptableAvatar scriptedAvatar(&_scriptEngine);
scriptedAvatar.setForceFaceshiftConnected(true); scriptedAvatar.setForceFaceshiftConnected(true);
// call model URL setters with empty URLs so our avatar, if user, will have the default models // call model URL setters with empty URLs so our avatar, if user, will have the default models

View file

@ -0,0 +1,88 @@
//
// ScriptableAvatar.cpp
//
//
// Created by Clement on 7/22/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 <QThread>
#include "ScriptableAvatar.h"
ScriptableAvatar::ScriptableAvatar(ScriptEngine* scriptEngine) : _scriptEngine(scriptEngine), _animation(NULL) {
connect(_scriptEngine, SIGNAL(update(float)), this, SLOT(update(float)));
}
// hold and priority unused but kept so that client side JS can run.
void ScriptableAvatar::startAnimation(const QString& url, float fps, float priority,
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps),
Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame),
Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
return;
}
_animation = _scriptEngine->getAnimationCache()->getAnimation(url);
_animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame);
_maskedJoints = maskedJoints;
}
void ScriptableAvatar::stopAnimation() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopAnimation");
return;
}
_animation.clear();
}
AnimationDetails ScriptableAvatar::getAnimationDetails() {
if (QThread::currentThread() != thread()) {
AnimationDetails result;
QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(AnimationDetails, result));
return result;
}
return _animationDetails;
}
void ScriptableAvatar::update(float deltatime) {
// Run animation
if (_animation != NULL && _animation->isValid() && _animation->getFrames().size() > 0) {
QStringList modelJoints = getJointNames();
QStringList animationJoints = _animation->getJointNames();
if (_jointData.size() != modelJoints.size()) {
_jointData.resize(modelJoints.size());
}
float frameIndex = _animationDetails.frameIndex + deltatime * _animationDetails.fps;
if (_animationDetails.loop || frameIndex < _animationDetails.lastFrame) {
while (frameIndex >= _animationDetails.lastFrame) {
frameIndex -= (_animationDetails.lastFrame - _animationDetails.firstFrame);
}
_animationDetails.frameIndex = frameIndex;
const int frameCount = _animation->getFrames().size();
const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(frameIndex) % frameCount);
const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(frameIndex) % frameCount);
const float frameFraction = glm::fract(frameIndex);
for (int i = 0; i < modelJoints.size(); i++) {
int mapping = animationJoints.indexOf(modelJoints[i]);
if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) {
JointData& data = _jointData[i];
data.valid = true;
data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
} else {
_jointData[i].valid = false;
}
}
} else {
_animation.clear();
}
}
}

View file

@ -0,0 +1,40 @@
//
// ScriptableAvatar.h
//
//
// Created by Clement on 7/22/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_ScriptableAvatar_h
#define hifi_ScriptableAvatar_h
#include <AnimationCache.h>
#include <AvatarData.h>
#include <ScriptEngine.h>
class ScriptableAvatar : public AvatarData {
Q_OBJECT
public:
ScriptableAvatar(ScriptEngine* scriptEngine);
/// Allows scripts to run animations.
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
Q_INVOKABLE void stopAnimation();
Q_INVOKABLE AnimationDetails getAnimationDetails();
private slots:
void update(float deltatime);
private:
ScriptEngine* _scriptEngine;
AnimationPointer _animation;
AnimationDetails _animationDetails;
QStringList _maskedJoints;
};
#endif // hifi_ScriptableAvatar_h

View file

@ -21,18 +21,21 @@
#include "MetavoxelServer.h" #include "MetavoxelServer.h"
const int SEND_INTERVAL = 50;
MetavoxelServer::MetavoxelServer(const QByteArray& packet) : MetavoxelServer::MetavoxelServer(const QByteArray& packet) :
ThreadedAssignment(packet), ThreadedAssignment(packet),
_sendTimer(this) { _nextSender(0) {
_sendTimer.setSingleShot(true);
connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas()));
} }
void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) { void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) {
edit.apply(_data, SharedObject::getWeakHash()); MetavoxelData data = _data;
edit.apply(data, SharedObject::getWeakHash());
setData(data);
}
void MetavoxelServer::setData(const MetavoxelData& data) {
if (_data != data) {
emit dataChanged(_data = data);
}
} }
const QString METAVOXEL_SERVER_LOGGING_NAME = "metavoxel-server"; const QString METAVOXEL_SERVER_LOGGING_NAME = "metavoxel-server";
@ -43,19 +46,34 @@ void MetavoxelServer::run() {
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();
nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->addNodeTypeToInterestSet(NodeType::Agent);
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachSession(const SharedNodePointer&))); connect(nodeList, &NodeList::nodeAdded, this, &MetavoxelServer::maybeAttachSession);
connect(nodeList, &NodeList::nodeKilled, this, &MetavoxelServer::maybeDeleteSession);
_lastSend = QDateTime::currentMSecsSinceEpoch();
_sendTimer.start(SEND_INTERVAL);
// initialize Bitstream before using it in multiple threads // initialize Bitstream before using it in multiple threads
Bitstream::preThreadingInit(); 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 // create the persister and start it in its own thread
_persister = new MetavoxelPersister(this); _persister = new MetavoxelPersister(this);
QThread* persistenceThread = new QThread(this); QThread* persistenceThread = new QThread(this);
_persister->moveToThread(persistenceThread); _persister->moveToThread(persistenceThread);
_persister->connect(persistenceThread, SIGNAL(finished()), SLOT(deleteLater())); connect(persistenceThread, &QThread::finished, _persister, &QObject::deleteLater);
persistenceThread->start(); persistenceThread->start();
// queue up the load // queue up the load
@ -85,6 +103,11 @@ void MetavoxelServer::readPendingDatagrams() {
void MetavoxelServer::aboutToFinish() { void MetavoxelServer::aboutToFinish() {
QMetaObject::invokeMethod(_persister, "save", Q_ARG(const MetavoxelData&, _data)); QMetaObject::invokeMethod(_persister, "save", Q_ARG(const MetavoxelData&, _data));
foreach (MetavoxelSender* sender, _senders) {
sender->thread()->quit();
sender->thread()->wait();
}
_persister->thread()->quit(); _persister->thread()->quit();
_persister->thread()->wait(); _persister->thread()->wait();
} }
@ -92,17 +115,53 @@ void MetavoxelServer::aboutToFinish() {
void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) { void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) {
if (node->getType() == NodeType::Agent) { if (node->getType() == NodeType::Agent) {
QMutexLocker locker(&node->getMutex()); QMutexLocker locker(&node->getMutex());
node->setLinkedData(new MetavoxelSession(node, this)); 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::sendDeltas() { void MetavoxelServer::maybeDeleteSession(const SharedNodePointer& node) {
// send deltas for all sessions
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NodeType::Agent) { if (node->getType() == NodeType::Agent) {
static_cast<MetavoxelSession*>(node->getLinkedData())->update(); // we assume the node is already locked
MetavoxelSession* session = static_cast<MetavoxelSession*>(node->getLinkedData());
if (session) {
node->setLinkedData(NULL);
session->deleteLater();
} }
} }
}
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 // restart the send timer
qint64 now = QDateTime::currentMSecsSinceEpoch(); qint64 now = QDateTime::currentMSecsSinceEpoch();
@ -112,9 +171,13 @@ void MetavoxelServer::sendDeltas() {
_sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL))); _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL)));
} }
MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelServer* server) : void MetavoxelSender::removeSession(QObject* session) {
_sessions.remove(static_cast<MetavoxelSession*>(session));
}
MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender) :
Endpoint(node, new PacketRecord(), NULL), Endpoint(node, new PacketRecord(), NULL),
_server(server), _sender(sender),
_reliableDeltaChannel(NULL), _reliableDeltaChannel(NULL),
_reliableDeltaID(0) { _reliableDeltaID(0) {
@ -138,7 +201,7 @@ void MetavoxelSession::update() {
int start = _sequencer.getOutputStream().getUnderlying().device()->pos(); int start = _sequencer.getOutputStream().getUnderlying().device()->pos();
out << QVariant::fromValue(MetavoxelDeltaMessage()); out << QVariant::fromValue(MetavoxelDeltaMessage());
PacketRecord* sendRecord = getLastAcknowledgedSendRecord(); PacketRecord* sendRecord = getLastAcknowledgedSendRecord();
_server->getData().writeDelta(sendRecord->getData(), sendRecord->getLOD(), out, _lod); _sender->getData().writeDelta(sendRecord->getData(), sendRecord->getLOD(), out, _lod);
out.flush(); out.flush();
int end = _sequencer.getOutputStream().getUnderlying().device()->pos(); int end = _sequencer.getOutputStream().getUnderlying().device()->pos();
if (end > _sequencer.getMaxPacketSize()) { if (end > _sequencer.getMaxPacketSize()) {
@ -150,7 +213,7 @@ void MetavoxelSession::update() {
_reliableDeltaWriteMappings = out.getAndResetWriteMappings(); _reliableDeltaWriteMappings = out.getAndResetWriteMappings();
_reliableDeltaReceivedOffset = _reliableDeltaChannel->getBytesWritten(); _reliableDeltaReceivedOffset = _reliableDeltaChannel->getBytesWritten();
_reliableDeltaData = _server->getData(); _reliableDeltaData = _sender->getData();
_reliableDeltaLOD = _lod; _reliableDeltaLOD = _lod;
// go back to the beginning with the current packet and note that there's a delta pending // go back to the beginning with the current packet and note that there's a delta pending
@ -173,7 +236,7 @@ void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) {
PacketRecord* MetavoxelSession::maybeCreateSendRecord() const { PacketRecord* MetavoxelSession::maybeCreateSendRecord() const {
return _reliableDeltaChannel ? new PacketRecord(_reliableDeltaLOD, _reliableDeltaData) : return _reliableDeltaChannel ? new PacketRecord(_reliableDeltaLOD, _reliableDeltaData) :
new PacketRecord(_lod, _server->getData()); new PacketRecord(_lod, _sender->getData());
} }
void MetavoxelSession::handleMessage(const QVariant& message) { void MetavoxelSession::handleMessage(const QVariant& message) {
@ -183,7 +246,8 @@ void MetavoxelSession::handleMessage(const QVariant& message) {
_lod = state.lod; _lod = state.lod;
} else if (userType == MetavoxelEditMessage::Type) { } else if (userType == MetavoxelEditMessage::Type) {
_server->applyEdit(message.value<MetavoxelEditMessage>()); QMetaObject::invokeMethod(_sender->getServer(), "applyEdit", Q_ARG(const MetavoxelEditMessage&,
message.value<MetavoxelEditMessage>()));
} else if (userType == QMetaType::QVariantList) { } else if (userType == QMetaType::QVariantList) {
foreach (const QVariant& element, message.toList()) { foreach (const QVariant& element, message.toList()) {

View file

@ -21,6 +21,7 @@
class MetavoxelEditMessage; class MetavoxelEditMessage;
class MetavoxelPersister; class MetavoxelPersister;
class MetavoxelSender;
class MetavoxelSession; class MetavoxelSession;
/// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. /// Maintains a shared metavoxel system, accepting change requests and broadcasting updates.
@ -31,11 +32,11 @@ public:
MetavoxelServer(const QByteArray& packet); MetavoxelServer(const QByteArray& packet);
void applyEdit(const MetavoxelEditMessage& edit); Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit);
const MetavoxelData& getData() const { return _data; } const MetavoxelData& getData() const { return _data; }
Q_INVOKABLE void setData(const MetavoxelData& data) { _data = data; } Q_INVOKABLE void setData(const MetavoxelData& data);
virtual void run(); virtual void run();
@ -43,15 +44,52 @@ public:
virtual void aboutToFinish(); virtual void aboutToFinish();
signals:
void dataChanged(const MetavoxelData& data);
private slots: private slots:
void maybeAttachSession(const SharedNodePointer& node); void maybeAttachSession(const SharedNodePointer& node);
void sendDeltas(); void maybeDeleteSession(const SharedNodePointer& node);
private: private:
QVector<MetavoxelSender*> _senders;
int _nextSender;
MetavoxelPersister* _persister; MetavoxelPersister* _persister;
MetavoxelData _data;
};
/// 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; QTimer _sendTimer;
qint64 _lastSend; qint64 _lastSend;
@ -64,7 +102,7 @@ class MetavoxelSession : public Endpoint {
public: public:
MetavoxelSession(const SharedNodePointer& node, MetavoxelServer* server); MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender);
virtual void update(); virtual void update();
@ -83,7 +121,7 @@ private:
void sendPacketGroup(int alreadySent = 0); void sendPacketGroup(int alreadySent = 0);
MetavoxelServer* _server; MetavoxelSender* _sender;
MetavoxelLOD _lod; MetavoxelLOD _lod;

View file

@ -748,18 +748,19 @@ function Tooltip() {
this.updateText = function(properties) { this.updateText = function(properties) {
var angles = Quat.safeEulerAngles(properties.modelRotation); var angles = Quat.safeEulerAngles(properties.modelRotation);
var text = "Model Properties:\n" var text = "Model Properties:\n"
text += "x: " + properties.position.x.toFixed(this.decimals) + "\n" text += "X: " + properties.position.x.toFixed(this.decimals) + "\n"
text += "y: " + properties.position.y.toFixed(this.decimals) + "\n" text += "Y: " + properties.position.y.toFixed(this.decimals) + "\n"
text += "z: " + properties.position.z.toFixed(this.decimals) + "\n" text += "Z: " + properties.position.z.toFixed(this.decimals) + "\n"
text += "pitch: " + angles.x.toFixed(this.decimals) + "\n" text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n"
text += "yaw: " + angles.y.toFixed(this.decimals) + "\n" text += "Yaw: " + angles.y.toFixed(this.decimals) + "\n"
text += "roll: " + angles.z.toFixed(this.decimals) + "\n" text += "Roll: " + angles.z.toFixed(this.decimals) + "\n"
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n" text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n"
text += "ID: " + properties.id + "\n" text += "ID: " + properties.id + "\n"
text += "model url: " + properties.modelURL + "\n" text += "Model URL: " + properties.modelURL + "\n"
text += "animation url: " + properties.animationURL + "\n" text += "Animation URL: " + properties.animationURL + "\n"
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
if (properties.sittingPoints.length > 0) { if (properties.sittingPoints.length > 0) {
text += properties.sittingPoints.length + " sitting points: " text += properties.sittingPoints.length + " Sitting points: "
for (var i = 0; i < properties.sittingPoints.length; ++i) { for (var i = 0; i < properties.sittingPoints.length; ++i) {
text += properties.sittingPoints[i].name + " " text += properties.sittingPoints[i].name + " "
} }
@ -1146,6 +1147,7 @@ function handeMenuEvent(menuItem){
var decimals = 3; var decimals = 3;
array.push({ label: "Model URL:", value: selectedModelProperties.modelURL }); array.push({ label: "Model URL:", value: selectedModelProperties.modelURL });
array.push({ label: "Animation URL:", value: selectedModelProperties.animationURL }); array.push({ label: "Animation URL:", value: selectedModelProperties.animationURL });
array.push({ label: "Animation is playing:", value: selectedModelProperties.animationIsPlaying });
array.push({ label: "X:", value: selectedModelProperties.position.x.toFixed(decimals) }); array.push({ label: "X:", value: selectedModelProperties.position.x.toFixed(decimals) });
array.push({ label: "Y:", value: selectedModelProperties.position.y.toFixed(decimals) }); array.push({ label: "Y:", value: selectedModelProperties.position.y.toFixed(decimals) });
array.push({ label: "Z:", value: selectedModelProperties.position.z.toFixed(decimals) }); array.push({ label: "Z:", value: selectedModelProperties.position.z.toFixed(decimals) });
@ -1158,16 +1160,18 @@ function handeMenuEvent(menuItem){
var propertyName = Window.form("Edit Properties", array); var propertyName = Window.form("Edit Properties", array);
modelSelected = false; modelSelected = false;
selectedModelProperties.modelURL = array[0].value; var index = 0;
selectedModelProperties.animationURL = array[1].value; selectedModelProperties.modelURL = array[index++].value;
selectedModelProperties.position.x = array[2].value; selectedModelProperties.animationURL = array[index++].value;
selectedModelProperties.position.y = array[3].value; selectedModelProperties.animationIsPlaying = array[index++].value;
selectedModelProperties.position.z = array[4].value; selectedModelProperties.position.x = array[index++].value;
angles.x = array[5].value; selectedModelProperties.position.y = array[index++].value;
angles.y = array[6].value; selectedModelProperties.position.z = array[index++].value;
angles.z = array[7].value; angles.x = array[index++].value;
angles.y = array[index++].value;
angles.z = array[index++].value;
selectedModelProperties.modelRotation = Quat.fromVec3Degrees(angles); selectedModelProperties.modelRotation = Quat.fromVec3Degrees(angles);
selectedModelProperties.radius = array[8].value / 2; selectedModelProperties.radius = array[index++].value / 2;
Models.editModel(selectedModelID, selectedModelProperties); Models.editModel(selectedModelID, selectedModelProperties);
} }
@ -1200,6 +1204,7 @@ Controller.keyPressEvent.connect(function(event) {
somethingChanged = true; somethingChanged = true;
} }
}); });
Controller.keyReleaseEvent.connect(function(event) { Controller.keyReleaseEvent.connect(function(event) {
if (event.text == "z" || event.text == "Z") { if (event.text == "z" || event.text == "Z") {
zIsPressed = false; zIsPressed = false;

View file

@ -18,7 +18,7 @@ void main(void) {
// standard diffuse lighting // standard diffuse lighting
gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb + gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb +
gl_LightSource[0].diffuse.rgb * max(0.0, dot(gl_NormalMatrix * gl_Normal, gl_LightSource[0].position.xyz))), gl_LightSource[0].diffuse.rgb * max(0.0, dot(gl_NormalMatrix * gl_Normal, gl_LightSource[0].position.xyz))),
gl_Color.a); 0.0);
// extract the first three components of the vertex for position // extract the first three components of the vertex for position
gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0); gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0);

View file

@ -10,6 +10,8 @@
// //
#include <QMutexLocker> #include <QMutexLocker>
#include <QReadLocker>
#include <QWriteLocker>
#include <QtDebug> #include <QtDebug>
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
@ -23,55 +25,387 @@
#include "MetavoxelSystem.h" #include "MetavoxelSystem.h"
#include "renderer/Model.h" #include "renderer/Model.h"
REGISTER_META_OBJECT(PointMetavoxelRendererImplementation)
REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(SphereRenderer)
REGISTER_META_OBJECT(StaticModelRenderer) REGISTER_META_OBJECT(StaticModelRenderer)
ProgramObject MetavoxelSystem::_program; static int bufferPointVectorMetaTypeId = qRegisterMetaType<BufferPointVector>();
int MetavoxelSystem::_pointScaleLocation;
MetavoxelSystem::MetavoxelSystem() :
_simulateVisitor(_points),
_buffer(QOpenGLBuffer::VertexBuffer) {
}
void MetavoxelSystem::init() { void MetavoxelSystem::init() {
MetavoxelClientManager::init(); MetavoxelClientManager::init();
PointMetavoxelRendererImplementation::init();
_pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new PointBufferAttribute());
}
MetavoxelLOD MetavoxelSystem::getLOD() {
QReadLocker locker(&_lodLock);
return _lod;
}
class SpannerSimulateVisitor : public SpannerVisitor {
public:
SpannerSimulateVisitor(float deltaTime);
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
private:
float _deltaTime;
};
SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(),
Application::getInstance()->getMetavoxels()->getLOD()),
_deltaTime(deltaTime) {
}
bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
spanner->getRenderer()->simulate(_deltaTime);
return true;
}
void MetavoxelSystem::simulate(float deltaTime) {
// update the lod
{
// the LOD threshold is temporarily tied to the avatar LOD parameter
QWriteLocker locker(&_lodLock);
const float BASE_LOD_THRESHOLD = 0.01f;
_lod = MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(),
BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier());
}
SpannerSimulateVisitor spannerSimulateVisitor(deltaTime);
guide(spannerSimulateVisitor);
}
class SpannerRenderVisitor : public SpannerVisitor {
public:
SpannerRenderVisitor();
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
};
SpannerRenderVisitor::SpannerRenderVisitor() :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(),
Application::getInstance()->getMetavoxels()->getLOD(),
encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) {
}
bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
spanner->getRenderer()->render(1.0f, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize);
return true;
}
class RenderVisitor : public MetavoxelVisitor {
public:
RenderVisitor(const MetavoxelLOD& lod);
virtual int visit(MetavoxelInfo& info);
};
RenderVisitor::RenderVisitor(const MetavoxelLOD& lod) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getRendererAttribute(),
QVector<AttributePointer>(), lod) {
}
int RenderVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
static_cast<MetavoxelRenderer*>(info.inputValues.at(0).getInlineValue<
SharedObjectPointer>().data())->getImplementation()->render(*_data, info, _lod);
return STOP_RECURSION;
}
void MetavoxelSystem::render() {
RenderVisitor renderVisitor(getLOD());
guideToAugmented(renderVisitor);
SpannerRenderVisitor spannerRenderVisitor;
guide(spannerRenderVisitor);
}
MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) {
return new MetavoxelSystemClient(node, _updater);
}
void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor) {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex());
MetavoxelSystemClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData());
if (client) {
client->getAugmentedData().guide(visitor);
}
}
}
}
MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) :
MetavoxelClient(node, updater) {
}
void MetavoxelSystemClient::setAugmentedData(const MetavoxelData& data) {
QWriteLocker locker(&_augmentedDataLock);
_augmentedData = data;
}
MetavoxelData MetavoxelSystemClient::getAugmentedData() {
QReadLocker locker(&_augmentedDataLock);
return _augmentedData;
}
int MetavoxelSystemClient::parseData(const QByteArray& packet) {
// process through sequencer
QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet));
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size());
return packet.size();
}
class AugmentVisitor : public MetavoxelVisitor {
public:
AugmentVisitor(const MetavoxelLOD& lod, const MetavoxelData& previousData);
virtual int visit(MetavoxelInfo& info);
private:
const MetavoxelData& _previousData;
};
AugmentVisitor::AugmentVisitor(const MetavoxelLOD& lod, const MetavoxelData& previousData) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getRendererAttribute(),
QVector<AttributePointer>(), lod),
_previousData(previousData) {
}
int AugmentVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
static_cast<MetavoxelRenderer*>(info.inputValues.at(0).getInlineValue<
SharedObjectPointer>().data())->getImplementation()->augment(*_data, _previousData, info, _lod);
return STOP_RECURSION;
}
class Augmenter : public QRunnable {
public:
Augmenter(const SharedNodePointer& node, const MetavoxelData& data,
const MetavoxelData& previousData, const MetavoxelLOD& lod);
virtual void run();
private:
QWeakPointer<Node> _node;
MetavoxelData _data;
MetavoxelData _previousData;
MetavoxelLOD _lod;
};
Augmenter::Augmenter(const SharedNodePointer& node, const MetavoxelData& data,
const MetavoxelData& previousData, const MetavoxelLOD& lod) :
_node(node),
_data(data),
_previousData(previousData),
_lod(lod) {
}
void Augmenter::run() {
SharedNodePointer node = _node;
if (!node) {
return;
}
AugmentVisitor visitor(_lod, _previousData);
_data.guide(visitor);
QMutexLocker locker(&node->getMutex());
QMetaObject::invokeMethod(node->getLinkedData(), "setAugmentedData", Q_ARG(const MetavoxelData&, _data));
}
void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) {
MetavoxelClient::dataChanged(oldData);
QThreadPool::globalInstance()->start(new Augmenter(_node, _data, getAugmentedData(), _remoteDataLOD));
}
void MetavoxelSystemClient::sendDatagram(const QByteArray& data) {
NodeList::getInstance()->writeDatagram(data, _node);
Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size());
}
PointBuffer::PointBuffer(const BufferPointVector& points) :
_points(points) {
}
void PointBuffer::render() {
// initialize buffer, etc. on first render
if (!_buffer.isCreated()) {
_buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
_buffer.create();
_buffer.bind();
_pointCount = _points.size();
_buffer.allocate(_points.constData(), _pointCount * sizeof(BufferPoint));
_points.clear();
_buffer.release();
}
if (_pointCount == 0) {
return;
}
_buffer.bind();
BufferPoint* point = 0;
glVertexPointer(4, GL_FLOAT, sizeof(BufferPoint), &point->vertex);
glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(BufferPoint), &point->color);
glNormalPointer(GL_BYTE, sizeof(BufferPoint), &point->normal);
glDrawArrays(GL_POINTS, 0, _pointCount);
_buffer.release();
}
PointBufferAttribute::PointBufferAttribute() :
InlineAttribute<PointBufferPointer>("pointBuffer") {
}
bool PointBufferAttribute::merge(void*& parent, void* children[], bool postRead) const {
PointBufferPointer firstChild = decodeInline<PointBufferPointer>(children[0]);
for (int i = 1; i < MERGE_COUNT; i++) {
if (firstChild != decodeInline<PointBufferPointer>(children[i])) {
*(PointBufferPointer*)&parent = _defaultValue;
return false;
}
}
*(PointBufferPointer*)&parent = firstChild;
return true;
}
void PointMetavoxelRendererImplementation::init() {
if (!_program.isLinked()) { if (!_program.isLinked()) {
_program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert"); _program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
_program.link(); _program.link();
_program.bind();
_pointScaleLocation = _program.uniformLocation("pointScale"); _pointScaleLocation = _program.uniformLocation("pointScale");
_program.release();
} }
_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
_buffer.create();
} }
MetavoxelLOD MetavoxelSystem::getLOD() const { PointMetavoxelRendererImplementation::PointMetavoxelRendererImplementation() {
// the LOD threshold is temporarily tied to the avatar LOD parameter
const float BASE_LOD_THRESHOLD = 0.01f;
return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(),
BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier());
} }
void MetavoxelSystem::simulate(float deltaTime) { class PointAugmentVisitor : public MetavoxelVisitor {
// update the clients public:
_points.clear();
_simulateVisitor.setDeltaTime(deltaTime);
_simulateVisitor.setOrder(-Application::getInstance()->getViewFrustum()->getDirection());
update();
_buffer.bind(); PointAugmentVisitor(const MetavoxelLOD& lod);
int bytes = _points.size() * sizeof(Point);
if (_buffer.size() < bytes) { virtual void prepare(MetavoxelData* data);
_buffer.allocate(_points.constData(), bytes); virtual int visit(MetavoxelInfo& info);
} else { virtual bool postVisit(MetavoxelInfo& info);
_buffer.write(0, _points.constData(), bytes);
} private:
_buffer.release();
BufferPointVector _points;
float _pointLeafSize;
};
PointAugmentVisitor::PointAugmentVisitor(const MetavoxelLOD& lod) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute() <<
AttributeRegistry::getInstance()->getNormalAttribute(), QVector<AttributePointer>() <<
Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod) {
} }
void MetavoxelSystem::render() { const int ALPHA_RENDER_THRESHOLD = 0;
void PointAugmentVisitor::prepare(MetavoxelData* data) {
MetavoxelVisitor::prepare(data);
const float MAX_POINT_LEAF_SIZE = 64.0f;
_pointLeafSize = qMin(data->getSize(), MAX_POINT_LEAF_SIZE);
}
int PointAugmentVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) {
return (info.size > _pointLeafSize) ? DEFAULT_ORDER : (DEFAULT_ORDER | ALL_NODES_REST);
}
QRgb color = info.inputValues.at(0).getInlineValue<QRgb>();
quint8 alpha = qAlpha(color);
if (alpha > ALPHA_RENDER_THRESHOLD) {
QRgb normal = info.inputValues.at(1).getInlineValue<QRgb>();
BufferPoint point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)) },
{ quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } };
_points.append(point);
}
if (info.size >= _pointLeafSize) {
BufferPointVector swapPoints;
_points.swap(swapPoints);
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(PointBufferPointer(
new PointBuffer(swapPoints))));
}
return STOP_RECURSION;
}
bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) {
if (info.size != _pointLeafSize) {
return false;
}
BufferPointVector swapPoints;
_points.swap(swapPoints);
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(PointBufferPointer(
new PointBuffer(swapPoints))));
return true;
}
void PointMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
MetavoxelInfo& info, const MetavoxelLOD& lod) {
// copy the previous buffers
MetavoxelData expandedPrevious = previous;
while (expandedPrevious.getSize() < data.getSize()) {
expandedPrevious.expand();
}
const AttributePointer& pointBufferAttribute = Application::getInstance()->getMetavoxels()->getPointBufferAttribute();
MetavoxelNode* root = expandedPrevious.getRoot(pointBufferAttribute);
if (root) {
data.setRoot(pointBufferAttribute, root);
root->incrementReferenceCount();
}
PointAugmentVisitor visitor(lod);
data.guideToDifferent(expandedPrevious, visitor);
}
class PointRenderVisitor : public MetavoxelVisitor {
public:
PointRenderVisitor(const MetavoxelLOD& lod);
virtual int visit(MetavoxelInfo& info);
private:
int _order;
};
PointRenderVisitor::PointRenderVisitor(const MetavoxelLOD& lod) :
MetavoxelVisitor(QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getPointBufferAttribute(),
QVector<AttributePointer>(), lod),
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) {
}
int PointRenderVisitor::visit(MetavoxelInfo& info) {
PointBufferPointer buffer = info.inputValues.at(0).getInlineValue<PointBufferPointer>();
if (buffer) {
buffer->render();
}
return info.isLeaf ? STOP_RECURSION : _order;
}
void PointMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
int viewport[4]; int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport); glGetIntegerv(GL_VIEWPORT, viewport);
const int VIEWPORT_WIDTH_INDEX = 2; const int VIEWPORT_WIDTH_INDEX = 2;
@ -86,20 +420,18 @@ void MetavoxelSystem::render() {
_program.setUniformValue(_pointScaleLocation, viewportDiagonal * _program.setUniformValue(_pointScaleLocation, viewportDiagonal *
Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal); Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal);
_buffer.bind();
Point* pt = 0;
glVertexPointer(4, GL_FLOAT, sizeof(Point), &pt->vertex);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Point), &pt->color);
glNormalPointer(GL_BYTE, sizeof(Point), &pt->normal);
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
glDrawArrays(GL_POINTS, 0, _points.size()); glDisable(GL_BLEND);
PointRenderVisitor visitor(lod);
data.guide(visitor);
glEnable(GL_BLEND);
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
@ -107,112 +439,11 @@ void MetavoxelSystem::render() {
glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_NORMAL_ARRAY);
_buffer.release();
_program.release(); _program.release();
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex());
MetavoxelSystemClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData());
if (client) {
client->guide(_renderVisitor);
}
}
}
} }
MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { ProgramObject PointMetavoxelRendererImplementation::_program;
return new MetavoxelSystemClient(node, this); int PointMetavoxelRendererImplementation::_pointScaleLocation;
}
void MetavoxelSystem::updateClient(MetavoxelClient* client) {
MetavoxelClientManager::updateClient(client);
client->guide(_simulateVisitor);
}
MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector<Point>& points) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute() <<
AttributeRegistry::getInstance()->getNormalAttribute() <<
AttributeRegistry::getInstance()->getSpannerColorAttribute() <<
AttributeRegistry::getInstance()->getSpannerNormalAttribute()),
_points(points) {
}
bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
spanner->getRenderer()->simulate(_deltaTime);
return true;
}
int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) {
SpannerVisitor::visit(info);
if (!info.isLeaf) {
return _order;
}
QRgb color = info.inputValues.at(0).getInlineValue<QRgb>();
QRgb normal = info.inputValues.at(1).getInlineValue<QRgb>();
quint8 alpha = qAlpha(color);
if (!info.isLODLeaf) {
if (alpha > 0) {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha },
{ quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } };
_points.append(point);
}
} else {
QRgb spannerColor = info.inputValues.at(2).getInlineValue<QRgb>();
QRgb spannerNormal = info.inputValues.at(3).getInlineValue<QRgb>();
quint8 spannerAlpha = qAlpha(spannerColor);
if (spannerAlpha > 0) {
if (alpha > 0) {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(spannerColor)), quint8(qGreen(spannerColor)), quint8(qBlue(spannerColor)), spannerAlpha },
{ quint8(qRed(spannerNormal)), quint8(qGreen(spannerNormal)), quint8(qBlue(spannerNormal)) } };
_points.append(point);
} else {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(spannerColor)), quint8(qGreen(spannerColor)), quint8(qBlue(spannerColor)), spannerAlpha },
{ quint8(qRed(spannerNormal)), quint8(qGreen(spannerNormal)), quint8(qBlue(spannerNormal)) } };
_points.append(point);
}
} else if (alpha > 0) {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha },
{ quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } };
_points.append(point);
}
}
return STOP_RECURSION;
}
MetavoxelSystem::RenderVisitor::RenderVisitor() :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannerMaskAttribute()) {
}
bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
spanner->getRenderer()->render(1.0f, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize);
return true;
}
MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system) :
MetavoxelClient(node, system) {
}
int MetavoxelSystemClient::parseData(const QByteArray& packet) {
// process through sequencer
QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet));
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size());
return packet.size();
}
void MetavoxelSystemClient::sendDatagram(const QByteArray& data) {
NodeList::getInstance()->writeDatagram(data, _node);
Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size());
}
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
GLdouble coefficients[] = { x, y, z, w }; GLdouble coefficients[] = { x, y, z, w };
@ -251,7 +482,7 @@ void SphereRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum
return; return;
} }
// slight performance optimization: don't render if clip bounds are entirely within sphere // slight performance optimization: don't render if clip bounds are entirely within sphere
Sphere* sphere = static_cast<Sphere*>(parent()); Sphere* sphere = static_cast<Sphere*>(_spanner);
Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize)); Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize));
for (int i = 0; i < Box::VERTEX_COUNT; i++) { for (int i = 0; i < Box::VERTEX_COUNT; i++) {
const float CLIP_PROPORTION = 0.95f; const float CLIP_PROPORTION = 0.95f;
@ -263,7 +494,7 @@ void SphereRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum
} }
void SphereRenderer::renderUnclipped(float alpha, Mode mode) { void SphereRenderer::renderUnclipped(float alpha, Mode mode) {
Sphere* sphere = static_cast<Sphere*>(parent()); Sphere* sphere = static_cast<Sphere*>(_spanner);
const QColor& color = sphere->getColor(); const QColor& color = sphere->getColor();
glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha);
@ -284,6 +515,8 @@ StaticModelRenderer::StaticModelRenderer() :
} }
void StaticModelRenderer::init(Spanner* spanner) { void StaticModelRenderer::init(Spanner* spanner) {
SpannerRenderer::init(spanner);
_model->init(); _model->init();
StaticModel* staticModel = static_cast<StaticModel*>(spanner); StaticModel* staticModel = static_cast<StaticModel*>(spanner);
@ -305,7 +538,7 @@ void StaticModelRenderer::simulate(float deltaTime) {
const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents; const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents;
bounds = Box(extents.minimum, extents.maximum); bounds = Box(extents.minimum, extents.maximum);
} }
static_cast<StaticModel*>(parent())->setBounds(glm::translate(_model->getTranslation()) * static_cast<StaticModel*>(_spanner)->setBounds(glm::translate(_model->getTranslation()) *
glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds); glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds);
_model->simulate(deltaTime); _model->simulate(deltaTime);
} }

View file

@ -14,6 +14,7 @@
#include <QList> #include <QList>
#include <QOpenGLBuffer> #include <QOpenGLBuffer>
#include <QReadWriteLock>
#include <QVector> #include <QVector>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -30,11 +31,11 @@ class MetavoxelSystem : public MetavoxelClientManager {
public: public:
MetavoxelSystem();
virtual void init(); virtual void init();
virtual MetavoxelLOD getLOD() const; virtual MetavoxelLOD getLOD();
const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; }
void simulate(float deltaTime); void simulate(float deltaTime);
void render(); void render();
@ -42,45 +43,28 @@ public:
protected: protected:
virtual MetavoxelClient* createClient(const SharedNodePointer& node); virtual MetavoxelClient* createClient(const SharedNodePointer& node);
virtual void updateClient(MetavoxelClient* client);
private: private:
class Point { void guideToAugmented(MetavoxelVisitor& visitor);
AttributePointer _pointBufferAttribute;
MetavoxelLOD _lod;
QReadWriteLock _lodLock;
};
/// Describes contents of a point in a point buffer.
class BufferPoint {
public: public:
glm::vec4 vertex; glm::vec4 vertex;
quint8 color[4]; quint8 color[3];
quint8 normal[3]; quint8 normal[3];
}; };
class SimulateVisitor : public SpannerVisitor { typedef QVector<BufferPoint> BufferPointVector;
public:
SimulateVisitor(QVector<Point>& points);
void setDeltaTime(float deltaTime) { _deltaTime = deltaTime; }
void setOrder(const glm::vec3& direction) { _order = encodeOrder(direction); }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
virtual int visit(MetavoxelInfo& info);
private: Q_DECLARE_METATYPE(BufferPointVector)
QVector<Point>& _points;
float _deltaTime;
int _order;
};
class RenderVisitor : public SpannerVisitor {
public:
RenderVisitor();
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
};
static ProgramObject _program;
static int _pointScaleLocation;
QVector<Point> _points;
SimulateVisitor _simulateVisitor;
RenderVisitor _renderVisitor;
QOpenGLBuffer _buffer;
};
/// A client session associated with a single server. /// A client session associated with a single server.
class MetavoxelSystemClient : public MetavoxelClient { class MetavoxelSystemClient : public MetavoxelClient {
@ -88,13 +72,71 @@ class MetavoxelSystemClient : public MetavoxelClient {
public: public:
MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system); 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();
virtual int parseData(const QByteArray& packet); virtual int parseData(const QByteArray& packet);
protected: protected:
virtual void dataChanged(const MetavoxelData& oldData);
virtual void sendDatagram(const QByteArray& data); virtual void sendDatagram(const QByteArray& data);
private:
MetavoxelData _augmentedData;
QReadWriteLock _augmentedDataLock;
};
/// Contains the information necessary to render a group of points.
class PointBuffer : public QSharedData {
public:
PointBuffer(const BufferPointVector& points);
void render();
private:
BufferPointVector _points;
QOpenGLBuffer _buffer;
int _pointCount;
};
typedef QExplicitlySharedDataPointer<PointBuffer> PointBufferPointer;
/// A client-side attribute that stores point buffers.
class PointBufferAttribute : public InlineAttribute<PointBufferPointer> {
Q_OBJECT
public:
Q_INVOKABLE PointBufferAttribute();
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
/// Renders metavoxels as points.
class PointMetavoxelRendererImplementation : public MetavoxelRendererImplementation {
Q_OBJECT
public:
static void init();
Q_INVOKABLE PointMetavoxelRendererImplementation();
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod);
private:
static ProgramObject _program;
static int _pointScaleLocation;
}; };
/// Base class for spanner renderers; provides clipping. /// Base class for spanner renderers; provides clipping.

View file

@ -46,12 +46,12 @@ void XmppClient::xmppConnected() {
#endif #endif
} }
void XmppClient::xmppError(QXmppClient::Error error) {
#ifdef HAVE_QXMPP #ifdef HAVE_QXMPP
void XmppClient::xmppError(QXmppClient::Error error) {
qDebug() << "Error connnecting to XMPP for user " qDebug() << "Error connnecting to XMPP for user "
<< AccountManager::getInstance().getAccountInfo().getUsername() << ": " << error; << AccountManager::getInstance().getAccountInfo().getUsername() << ": " << error;
#endif
} }
#endif
void XmppClient::connectToServer() { void XmppClient::connectToServer() {
#ifdef HAVE_QXMPP #ifdef HAVE_QXMPP

View file

@ -35,7 +35,9 @@ signals:
private slots: private slots:
void xmppConnected(); void xmppConnected();
#ifdef HAVE_QXMPP
void xmppError(QXmppClient::Error error); void xmppError(QXmppClient::Error error);
#endif
void connectToServer(); void connectToServer();
void disconnectFromServer(); void disconnectFromServer();

View file

@ -1878,8 +1878,6 @@ void MyAvatar::renderLaserPointers() {
//Gets the tip position for the laser pointer //Gets the tip position for the laser pointer
glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
const float PALM_TIP_ROD_LENGTH_MULT = 40.0f;
glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition());
glm::vec3 position = palm->getPosition(); glm::vec3 position = palm->getPosition();

View file

@ -48,7 +48,13 @@ Stats::Stats():
_pingStatsWidth(STATS_PING_MIN_WIDTH), _pingStatsWidth(STATS_PING_MIN_WIDTH),
_geoStatsWidth(STATS_GEO_MIN_WIDTH), _geoStatsWidth(STATS_GEO_MIN_WIDTH),
_voxelStatsWidth(STATS_VOXEL_MIN_WIDTH), _voxelStatsWidth(STATS_VOXEL_MIN_WIDTH),
_lastHorizontalOffset(0) _lastHorizontalOffset(0),
_metavoxelInternal(0),
_metavoxelLeaves(0),
_metavoxelSendProgress(0),
_metavoxelSendTotal(0),
_metavoxelReceiveProgress(0),
_metavoxelReceiveTotal(0)
{ {
QGLWidget* glWidget = Application::getInstance()->getGLWidget(); QGLWidget* glWidget = Application::getInstance()->getGLWidget();
resetWidth(glWidget->width(), 0); resetWidth(glWidget->width(), 0);
@ -364,36 +370,26 @@ void Stats::display(
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color);
int internal = 0, leaves = 0; QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels()->getUpdater(), "getStats",
int sendProgress = 0, sendTotal = 0; Q_ARG(QObject*, this), Q_ARG(const QByteArray&, "setMetavoxelStats"));
int receiveProgress = 0, receiveTotal = 0;
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData());
if (client) {
client->getData().countNodes(internal, leaves, Application::getInstance()->getMetavoxels()->getLOD());
client->getSequencer().addReliableChannelStats(sendProgress, sendTotal, receiveProgress, receiveTotal);
}
}
}
stringstream nodes; stringstream nodes;
nodes << "Metavoxels: " << (internal + leaves); nodes << "Metavoxels: " << (_metavoxelInternal + _metavoxelLeaves);
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodes.str().c_str(), color); drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodes.str().c_str(), color);
stringstream nodeTypes; stringstream nodeTypes;
nodeTypes << "Internal: " << internal << " Leaves: " << leaves; nodeTypes << "Internal: " << _metavoxelInternal << " Leaves: " << _metavoxelLeaves;
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color); drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color);
if (sendTotal > 0 || receiveTotal > 0) { if (_metavoxelSendTotal > 0 || _metavoxelReceiveTotal > 0) {
stringstream reliableStats; stringstream reliableStats;
if (sendTotal > 0) { if (_metavoxelSendTotal > 0) {
reliableStats << "Upload: " << (sendProgress * 100 / sendTotal) << "% "; reliableStats << "Upload: " << (_metavoxelSendProgress * 100 / _metavoxelSendTotal) << "% ";
} }
if (receiveTotal > 0) { if (_metavoxelReceiveTotal > 0) {
reliableStats << "Download: " << (receiveProgress * 100 / receiveTotal) << "%"; reliableStats << "Download: " << (_metavoxelReceiveProgress * 100 / _metavoxelReceiveTotal) << "%";
} }
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableStats.str().c_str(), color); drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableStats.str().c_str(), color);
@ -726,3 +722,13 @@ 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

@ -31,6 +31,10 @@ public:
void resetWidth(int width, int horizontalOffset); void resetWidth(int width, int horizontalOffset);
void display(const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess); void display(const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess);
bool includeTimingRecord(const QString& name); bool includeTimingRecord(const QString& name);
Q_INVOKABLE void setMetavoxelStats(int internal, int leaves, int sendProgress,
int sendTotal, int receiveProgress, int receiveTotal);
private: private:
static Stats* _sharedInstance; static Stats* _sharedInstance;
@ -45,6 +49,13 @@ private:
int _voxelStatsWidth; int _voxelStatsWidth;
int _lastHorizontalOffset; int _lastHorizontalOffset;
int _metavoxelInternal;
int _metavoxelLeaves;
int _metavoxelSendProgress;
int _metavoxelSendTotal;
int _metavoxelReceiveProgress;
int _metavoxelReceiveTotal;
}; };
#endif // hifi_Stats_h #endif // hifi_Stats_h

View file

@ -35,7 +35,9 @@ AttributeRegistry* AttributeRegistry::getInstance() {
AttributeRegistry::AttributeRegistry() : AttributeRegistry::AttributeRegistry() :
_guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject, _guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject,
SharedObjectPointer(new DefaultMetavoxelGuide())))), new DefaultMetavoxelGuide()))),
_rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject,
new PointMetavoxelRenderer()))),
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))),
_colorAttribute(registerAttribute(new QRgbAttribute("color"))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))),
_normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))), _normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))),
@ -197,7 +199,7 @@ MetavoxelNode* Attribute::createMetavoxelNode(const AttributeValue& value, const
} }
void Attribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { void Attribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) {
data.createRoot(state.attribute)->read(state); data.createRoot(state.base.attribute)->read(state);
} }
void Attribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { void Attribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) {
@ -205,7 +207,7 @@ void Attribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamSta
} }
void Attribute::readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { void Attribute::readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) {
data.createRoot(state.attribute)->readDelta(reference, state); data.createRoot(state.base.attribute)->readDelta(reference, state);
} }
void Attribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { void Attribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) {
@ -214,10 +216,10 @@ void Attribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNo
void Attribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { void Attribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) {
// copy if changed // copy if changed
MetavoxelNode* oldRoot = data.getRoot(state.attribute); MetavoxelNode* oldRoot = data.getRoot(state.base.attribute);
MetavoxelNode* newRoot = oldRoot->readSubdivision(state); MetavoxelNode* newRoot = oldRoot->readSubdivision(state);
if (newRoot != oldRoot) { if (newRoot != oldRoot) {
data.setRoot(state.attribute, newRoot); data.setRoot(state.base.attribute, newRoot);
} }
} }
@ -567,62 +569,62 @@ SpannerSetAttribute::SpannerSetAttribute(const QString& name, const QMetaObject*
void SpannerSetAttribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { void SpannerSetAttribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) {
forever { forever {
SharedObjectPointer object; SharedObjectPointer object;
state.stream >> object; state.base.stream >> object;
if (!object) { if (!object) {
break; break;
} }
data.insert(state.attribute, object); data.insert(state.base.attribute, object);
} }
// even if the root is empty, it should still exist // even if the root is empty, it should still exist
if (!data.getRoot(state.attribute)) { if (!data.getRoot(state.base.attribute)) {
data.createRoot(state.attribute); data.createRoot(state.base.attribute);
} }
} }
void SpannerSetAttribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { void SpannerSetAttribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) {
Spanner::incrementVisit(); state.base.visit = Spanner::getAndIncrementNextVisit();
root.writeSpanners(state); root.writeSpanners(state);
state.stream << SharedObjectPointer(); state.base.stream << SharedObjectPointer();
} }
void SpannerSetAttribute::readMetavoxelDelta(MetavoxelData& data, void SpannerSetAttribute::readMetavoxelDelta(MetavoxelData& data,
const MetavoxelNode& reference, MetavoxelStreamState& state) { const MetavoxelNode& reference, MetavoxelStreamState& state) {
forever { forever {
SharedObjectPointer object; SharedObjectPointer object;
state.stream >> object; state.base.stream >> object;
if (!object) { if (!object) {
break; break;
} }
data.toggle(state.attribute, object); data.toggle(state.base.attribute, object);
} }
// even if the root is empty, it should still exist // even if the root is empty, it should still exist
if (!data.getRoot(state.attribute)) { if (!data.getRoot(state.base.attribute)) {
data.createRoot(state.attribute); data.createRoot(state.base.attribute);
} }
} }
void SpannerSetAttribute::writeMetavoxelDelta(const MetavoxelNode& root, void SpannerSetAttribute::writeMetavoxelDelta(const MetavoxelNode& root,
const MetavoxelNode& reference, MetavoxelStreamState& state) { const MetavoxelNode& reference, MetavoxelStreamState& state) {
Spanner::incrementVisit(); state.base.visit = Spanner::getAndIncrementNextVisit();
root.writeSpannerDelta(reference, state); root.writeSpannerDelta(reference, state);
state.stream << SharedObjectPointer(); state.base.stream << SharedObjectPointer();
} }
void SpannerSetAttribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { void SpannerSetAttribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) {
forever { forever {
SharedObjectPointer object; SharedObjectPointer object;
state.stream >> object; state.base.stream >> object;
if (!object) { if (!object) {
break; break;
} }
data.insert(state.attribute, object); data.insert(state.base.attribute, object);
} }
} }
void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) {
Spanner::incrementVisit(); state.base.visit = Spanner::getAndIncrementNextVisit();
root.writeSpannerSubdivision(state); root.writeSpannerSubdivision(state);
state.stream << SharedObjectPointer(); state.base.stream << SharedObjectPointer();
} }
bool SpannerSetAttribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, bool SpannerSetAttribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,

View file

@ -73,6 +73,9 @@ public:
/// Returns a reference to the standard SharedObjectPointer "guide" attribute. /// Returns a reference to the standard SharedObjectPointer "guide" attribute.
const AttributePointer& getGuideAttribute() const { return _guideAttribute; } 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. /// Returns a reference to the standard SharedObjectSet "spanners" attribute.
const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; } const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; }
@ -99,6 +102,7 @@ private:
QReadWriteLock _attributesLock; QReadWriteLock _attributesLock;
AttributePointer _guideAttribute; AttributePointer _guideAttribute;
AttributePointer _rendererAttribute;
AttributePointer _spannersAttribute; AttributePointer _spannersAttribute;
AttributePointer _colorAttribute; AttributePointer _colorAttribute;
AttributePointer _normalAttribute; AttributePointer _normalAttribute;

View file

@ -29,7 +29,7 @@ const float DEFAULT_SLOW_START_THRESHOLD = 1000.0f;
DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader, QObject* parent) : DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader, QObject* parent) :
QObject(parent), QObject(parent),
_outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly), _outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly),
_outputStream(_outgoingPacketStream), _outputStream(_outgoingPacketStream, Bitstream::NO_METADATA, Bitstream::NO_GENERICS, this),
_incomingDatagramStream(&_incomingDatagramBuffer), _incomingDatagramStream(&_incomingDatagramBuffer),
_datagramHeaderSize(datagramHeader.size()), _datagramHeaderSize(datagramHeader.size()),
_outgoingPacketNumber(0), _outgoingPacketNumber(0),
@ -38,7 +38,7 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader, QObject*
_outgoingDatagramStream(&_outgoingDatagramBuffer), _outgoingDatagramStream(&_outgoingDatagramBuffer),
_incomingPacketNumber(0), _incomingPacketNumber(0),
_incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly),
_inputStream(_incomingPacketStream), _inputStream(_incomingPacketStream, Bitstream::NO_METADATA, Bitstream::NO_GENERICS, this),
_receivedHighPriorityMessages(0), _receivedHighPriorityMessages(0),
_maxPacketSize(DEFAULT_MAX_PACKET_SIZE), _maxPacketSize(DEFAULT_MAX_PACKET_SIZE),
_packetsPerGroup(1.0f), _packetsPerGroup(1.0f),
@ -752,7 +752,7 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o
_index(index), _index(index),
_output(output), _output(output),
_dataStream(&_buffer), _dataStream(&_buffer),
_bitstream(_dataStream), _bitstream(_dataStream, Bitstream::NO_METADATA, Bitstream::NO_GENERICS, this),
_priority(1.0f), _priority(1.0f),
_offset(0), _offset(0),
_writePosition(0), _writePosition(0),

View file

@ -15,7 +15,7 @@
Endpoint::Endpoint(const SharedNodePointer& node, PacketRecord* baselineSendRecord, PacketRecord* baselineReceiveRecord) : Endpoint::Endpoint(const SharedNodePointer& node, PacketRecord* baselineSendRecord, PacketRecord* baselineReceiveRecord) :
_node(node), _node(node),
_sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData)) { _sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData), this) {
connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&)));
connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&)));
@ -49,7 +49,7 @@ void Endpoint::update() {
int Endpoint::parseData(const QByteArray& packet) { int Endpoint::parseData(const QByteArray& packet) {
// process through sequencer // process through sequencer
_sequencer.receivedDatagram(packet); QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet));
return packet.size(); return packet.size();
} }

View file

@ -9,25 +9,31 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QDateTime>
#include <QReadLocker>
#include <QThread> #include <QThread>
#include <QWriteLocker>
#include "MetavoxelClientManager.h" #include "MetavoxelClientManager.h"
#include "MetavoxelMessages.h" #include "MetavoxelMessages.h"
void MetavoxelClientManager::init() { MetavoxelClientManager::MetavoxelClientManager() :
connect(NodeList::getInstance(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachClient(const SharedNodePointer&))); _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");
} }
void MetavoxelClientManager::update() { MetavoxelClientManager::~MetavoxelClientManager() {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { _updater->thread()->quit();
if (node->getType() == NodeType::MetavoxelServer) { _updater->thread()->wait();
QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) {
updateClient(client);
}
}
} }
void MetavoxelClientManager::init() {
connect(NodeList::getInstance(), &NodeList::nodeAdded, this, &MetavoxelClientManager::maybeAttachClient);
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &MetavoxelClientManager::maybeDeleteClient);
} }
SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(const glm::vec3& origin, SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(const glm::vec3& origin,
@ -40,7 +46,7 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData()); MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) { if (client) {
float clientDistance; float clientDistance;
SharedObjectPointer clientSpanner = client->getData().findFirstRaySpannerIntersection( SharedObjectPointer clientSpanner = client->getDataCopy().findFirstRaySpannerIntersection(
origin, direction, attribute, clientDistance); origin, direction, attribute, clientDistance);
if (clientSpanner && clientDistance < closestDistance) { if (clientSpanner && clientDistance < closestDistance) {
closestSpanner = clientSpanner; closestSpanner = clientSpanner;
@ -69,43 +75,113 @@ void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool
} }
void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) {
if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable));
QMetaObject::invokeMethod(this, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable));
return;
}
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) {
client->applyEdit(edit, reliable);
}
}
}
} }
MetavoxelLOD MetavoxelClientManager::getLOD() const { MetavoxelLOD MetavoxelClientManager::getLOD() {
return MetavoxelLOD(); return MetavoxelLOD();
} }
void MetavoxelClientManager::maybeAttachClient(const SharedNodePointer& node) { void MetavoxelClientManager::maybeAttachClient(const SharedNodePointer& node) {
if (node->getType() == NodeType::MetavoxelServer) { if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex()); QMutexLocker locker(&node->getMutex());
node->setLinkedData(createClient(node)); 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) { MetavoxelClient* MetavoxelClientManager::createClient(const SharedNodePointer& node) {
return new MetavoxelClient(node, this); return new MetavoxelClient(node, _updater);
} }
void MetavoxelClientManager::updateClient(MetavoxelClient* client) { void MetavoxelClientManager::guide(MetavoxelVisitor& visitor) {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
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(); client->update();
} }
MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager) : // 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()), Endpoint(node, new PacketRecord(), new PacketRecord()),
_manager(manager), _updater(updater),
_reliableDeltaChannel(NULL), _reliableDeltaChannel(NULL),
_reliableDeltaID(0) { _reliableDeltaID(0) {
@ -113,9 +189,9 @@ MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientM
SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&)));
} }
void MetavoxelClient::guide(MetavoxelVisitor& visitor) { MetavoxelData MetavoxelClient::getDataCopy() {
visitor.setLOD(_manager->getLOD()); QReadLocker locker(&_dataCopyLock);
_data.guide(visitor); return _dataCopy;
} }
void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) {
@ -124,15 +200,25 @@ void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable)
} else { } else {
// apply immediately to local tree // apply immediately to local tree
MetavoxelData oldData = _data;
edit.apply(_data, _sequencer.getWeakSharedObjectHash()); edit.apply(_data, _sequencer.getWeakSharedObjectHash());
if (_data != oldData) {
dataChanged(oldData);
}
// start sending it out // start sending it out
_sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit));
} }
} }
void MetavoxelClient::dataChanged(const MetavoxelData& oldData) {
// make thread-safe copy
QWriteLocker locker(&_dataCopyLock);
_dataCopy = _data;
}
void MetavoxelClient::writeUpdateMessage(Bitstream& out) { void MetavoxelClient::writeUpdateMessage(Bitstream& out) {
ClientStateMessage state = { _manager->getLOD() }; ClientStateMessage state = { _updater->getLOD() };
out << QVariant::fromValue(state); out << QVariant::fromValue(state);
} }
@ -152,12 +238,16 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) {
in.reset(); in.reset();
} }
// copy to local and reapply local edits // copy to local and reapply local edits
MetavoxelData oldData = _data;
_data = _remoteData; _data = _remoteData;
foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) { foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) {
if (message.data.userType() == MetavoxelEditMessage::Type) { if (message.data.userType() == MetavoxelEditMessage::Type) {
message.data.value<MetavoxelEditMessage>().apply(_data, _sequencer.getWeakSharedObjectHash()); message.data.value<MetavoxelEditMessage>().apply(_data, _sequencer.getWeakSharedObjectHash());
} }
} }
if (_data != oldData) {
dataChanged(oldData);
}
} else if (userType == MetavoxelDeltaPendingMessage::Type) { } else if (userType == MetavoxelDeltaPendingMessage::Type) {
// check the id to make sure this is not a delta we've already processed // check the id to make sure this is not a delta we've already processed
int id = message.value<MetavoxelDeltaPendingMessage>().id; int id = message.value<MetavoxelDeltaPendingMessage>().id;
@ -176,7 +266,7 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) {
} }
PacketRecord* MetavoxelClient::maybeCreateSendRecord() const { PacketRecord* MetavoxelClient::maybeCreateSendRecord() const {
return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _manager->getLOD()); return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _updater->getLOD());
} }
PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const { PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const {

View file

@ -12,10 +12,14 @@
#ifndef hifi_MetavoxelClientManager_h #ifndef hifi_MetavoxelClientManager_h
#define hifi_MetavoxelClientManager_h #define hifi_MetavoxelClientManager_h
#include <QReadWriteLock>
#include <QTimer>
#include "Endpoint.h" #include "Endpoint.h"
class MetavoxelClient; class MetavoxelClient;
class MetavoxelEditMessage; class MetavoxelEditMessage;
class MetavoxelUpdater;
/// Manages the set of connected metavoxel clients. /// Manages the set of connected metavoxel clients.
class MetavoxelClientManager : public QObject { class MetavoxelClientManager : public QObject {
@ -23,8 +27,12 @@ class MetavoxelClientManager : public QObject {
public: public:
MetavoxelClientManager();
virtual ~MetavoxelClientManager();
virtual void init(); virtual void init();
void update();
MetavoxelUpdater* getUpdater() const { return _updater; }
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
const AttributePointer& attribute, float& distance); const AttributePointer& attribute, float& distance);
@ -35,16 +43,57 @@ public:
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);
virtual MetavoxelLOD getLOD() const; /// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread.
virtual MetavoxelLOD getLOD();
private slots: private slots:
void maybeAttachClient(const SharedNodePointer& node); void maybeAttachClient(const SharedNodePointer& node);
void maybeDeleteClient(const SharedNodePointer& node);
protected: protected:
virtual MetavoxelClient* createClient(const SharedNodePointer& node); virtual MetavoxelClient* createClient(const SharedNodePointer& node);
virtual void updateClient(MetavoxelClient* client);
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. /// Base class for metavoxel clients.
@ -53,25 +102,27 @@ class MetavoxelClient : public Endpoint {
public: public:
MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager); MetavoxelClient(const SharedNodePointer& node, MetavoxelUpdater* updater);
MetavoxelData& getData() { return _data; } /// Returns a reference to the most recent data. This function is *not* thread-safe.
const MetavoxelData& getData() const { return _data; }
void guide(MetavoxelVisitor& visitor); /// Returns a copy of the most recent data. This function *is* thread-safe.
MetavoxelData getDataCopy();
void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);
protected: protected:
virtual void dataChanged(const MetavoxelData& oldData);
virtual void writeUpdateMessage(Bitstream& out); virtual void writeUpdateMessage(Bitstream& out);
virtual void handleMessage(const QVariant& message, Bitstream& in); virtual void handleMessage(const QVariant& message, Bitstream& in);
virtual PacketRecord* maybeCreateSendRecord() const; virtual PacketRecord* maybeCreateSendRecord() const;
virtual PacketRecord* maybeCreateReceiveRecord() const; virtual PacketRecord* maybeCreateReceiveRecord() const;
private: MetavoxelUpdater* _updater;
MetavoxelClientManager* _manager;
MetavoxelData _data; MetavoxelData _data;
MetavoxelData _remoteData; MetavoxelData _remoteData;
MetavoxelLOD _remoteDataLOD; MetavoxelLOD _remoteDataLOD;
@ -79,6 +130,9 @@ private:
ReliableChannel* _reliableDeltaChannel; ReliableChannel* _reliableDeltaChannel;
MetavoxelLOD _reliableDeltaLOD; MetavoxelLOD _reliableDeltaLOD;
int _reliableDeltaID; int _reliableDeltaID;
MetavoxelData _dataCopy;
QReadWriteLock _dataCopyLock;
}; };
#endif // hifi_MetavoxelClientManager_h #endif // hifi_MetavoxelClientManager_h

View file

@ -12,6 +12,7 @@
#include <QDateTime> #include <QDateTime>
#include <QDebugStateSaver> #include <QDebugStateSaver>
#include <QScriptEngine> #include <QScriptEngine>
#include <QThread>
#include <QtDebug> #include <QtDebug>
#include <GeometryUtil.h> #include <GeometryUtil.h>
@ -24,6 +25,8 @@ REGISTER_META_OBJECT(MetavoxelGuide)
REGISTER_META_OBJECT(DefaultMetavoxelGuide) REGISTER_META_OBJECT(DefaultMetavoxelGuide)
REGISTER_META_OBJECT(ScriptedMetavoxelGuide) REGISTER_META_OBJECT(ScriptedMetavoxelGuide)
REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) REGISTER_META_OBJECT(ThrobbingMetavoxelGuide)
REGISTER_META_OBJECT(MetavoxelRenderer)
REGISTER_META_OBJECT(PointMetavoxelRenderer)
REGISTER_META_OBJECT(Spanner) REGISTER_META_OBJECT(Spanner)
REGISTER_META_OBJECT(Sphere) REGISTER_META_OBJECT(Sphere)
REGISTER_META_OBJECT(StaticModel) REGISTER_META_OBJECT(StaticModel)
@ -80,18 +83,19 @@ Box MetavoxelData::getBounds() const {
void MetavoxelData::guide(MetavoxelVisitor& visitor) { void MetavoxelData::guide(MetavoxelVisitor& visitor) {
// let the visitor know we're about to begin a tour // let the visitor know we're about to begin a tour
visitor.prepare(); visitor.prepare(this);
// start with the root values/defaults (plus the guide attribute) // start with the root values/defaults (plus the guide attribute)
const QVector<AttributePointer>& inputs = visitor.getInputs(); const QVector<AttributePointer>& inputs = visitor.getInputs();
const QVector<AttributePointer>& outputs = visitor.getOutputs(); const QVector<AttributePointer>& outputs = visitor.getOutputs();
MetavoxelVisitation firstVisitation = { NULL, visitor, QVector<MetavoxelNode*>(inputs.size() + 1), MetavoxelVisitation& firstVisitation = visitor.acquireVisitation();
QVector<MetavoxelNode*>(outputs.size()), { NULL, getMinimum(), _size, firstVisitation.info.minimum = getMinimum();
QVector<AttributeValue>(inputs.size() + 1), QVector<OwnedAttributeValue>(outputs.size()) } }; firstVisitation.info.size = _size;
for (int i = 0; i < inputs.size(); i++) { for (int i = 0; i < inputs.size(); i++) {
MetavoxelNode* node = _roots.value(inputs.at(i)); const AttributePointer& input = inputs.at(i);
MetavoxelNode* node = _roots.value(input);
firstVisitation.inputNodes[i] = node; firstVisitation.inputNodes[i] = node;
firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(inputs[i]) : inputs[i]; firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(input) : input;
} }
AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute(); AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute();
MetavoxelNode* node = _roots.value(guideAttribute); MetavoxelNode* node = _roots.value(guideAttribute);
@ -119,6 +123,80 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) {
node->decrementReferenceCount(value.getAttribute()); node->decrementReferenceCount(value.getAttribute());
_roots.remove(value.getAttribute()); _roots.remove(value.getAttribute());
} }
value = AttributeValue();
}
visitor.releaseVisitation();
}
void MetavoxelData::guideToDifferent(const MetavoxelData& other, MetavoxelVisitor& visitor) {
// if the other data is smaller, we need to expand it to compare
const MetavoxelData* expandedOther = &other;
if (_size > other._size) {
MetavoxelData* expanded = new MetavoxelData(other);
while (expanded->_size < _size) {
expanded->expand();
}
expandedOther = expanded;
}
// let the visitor know we're about to begin a tour
visitor.prepare(this);
// start with the root values/defaults (plus the guide attribute)
const QVector<AttributePointer>& inputs = visitor.getInputs();
const QVector<AttributePointer>& outputs = visitor.getOutputs();
MetavoxelVisitation& firstVisitation = visitor.acquireVisitation();
firstVisitation.compareNodes.resize(inputs.size() + 1);
firstVisitation.info.minimum = getMinimum();
firstVisitation.info.size = _size;
bool allNodesSame = true;
for (int i = 0; i < inputs.size(); i++) {
const AttributePointer& input = inputs.at(i);
MetavoxelNode* node = _roots.value(input);
firstVisitation.inputNodes[i] = node;
firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(input) : input;
MetavoxelNode* compareNode = expandedOther->_roots.value(input);
firstVisitation.compareNodes[i] = compareNode;
allNodesSame &= (node == compareNode);
}
AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute();
MetavoxelNode* node = _roots.value(guideAttribute);
firstVisitation.inputNodes.last() = node;
firstVisitation.info.inputValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute;
MetavoxelNode* compareNode = expandedOther->_roots.value(guideAttribute);
firstVisitation.compareNodes.last() = compareNode;
allNodesSame &= (node == compareNode);
if (!allNodesSame) {
for (int i = 0; i < outputs.size(); i++) {
MetavoxelNode* node = _roots.value(outputs.at(i));
firstVisitation.outputNodes[i] = node;
}
static_cast<MetavoxelGuide*>(firstVisitation.info.inputValues.last().getInlineValue<
SharedObjectPointer>().data())->guideToDifferent(firstVisitation);
for (int i = 0; i < outputs.size(); i++) {
OwnedAttributeValue& value = firstVisitation.info.outputValues[i];
if (!value.getAttribute()) {
continue;
}
// replace the old node with the new
MetavoxelNode*& node = _roots[value.getAttribute()];
if (node) {
node->decrementReferenceCount(value.getAttribute());
}
node = firstVisitation.outputNodes.at(i);
if (node->isLeaf() && value.isDefault()) {
// immediately remove the new node if redundant
node->decrementReferenceCount(value.getAttribute());
_roots.remove(value.getAttribute());
}
value = AttributeValue();
}
}
visitor.releaseVisitation();
// delete the expanded other if we had to expand
if (expandedOther != &other) {
delete expandedOther;
} }
} }
@ -467,7 +545,8 @@ void MetavoxelData::read(Bitstream& in, const MetavoxelLOD& lod) {
if (!attribute) { if (!attribute) {
break; break;
} }
MetavoxelStreamState state = { getMinimum(), _size, attribute, in, lod, lod }; MetavoxelStreamBase base = { attribute, in, lod, lod };
MetavoxelStreamState state = { base, getMinimum(), _size };
attribute->readMetavoxelRoot(*this, state); attribute->readMetavoxelRoot(*this, state);
} }
} }
@ -476,7 +555,8 @@ void MetavoxelData::write(Bitstream& out, const MetavoxelLOD& lod) const {
out << _size; out << _size;
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
out << it.key(); out << it.key();
MetavoxelStreamState state = { getMinimum(), _size, it.key(), out, lod, lod }; MetavoxelStreamBase base = { it.key(), out, lod, lod };
MetavoxelStreamState state = { base, getMinimum(), _size };
it.key()->writeMetavoxelRoot(*it.value(), state); it.key()->writeMetavoxelRoot(*it.value(), state);
} }
out << AttributePointer(); out << AttributePointer();
@ -509,7 +589,8 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD
if (!attribute) { if (!attribute) {
break; break;
} }
MetavoxelStreamState state = { getMinimum(), _size, attribute, in, lod, referenceLOD }; MetavoxelStreamBase base = { attribute, in, lod, referenceLOD };
MetavoxelStreamState state = { base, getMinimum(), _size };
MetavoxelNode* oldRoot = _roots.value(attribute); MetavoxelNode* oldRoot = _roots.value(attribute);
if (oldRoot) { if (oldRoot) {
bool changed; bool changed;
@ -565,7 +646,8 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO
// write the added/changed/subdivided roots // write the added/changed/subdivided roots
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key());
MetavoxelStreamState state = { minimum, _size, it.key(), out, lod, referenceLOD }; MetavoxelStreamBase base = { it.key(), out, lod, referenceLOD };
MetavoxelStreamState state = { base, minimum, _size };
if (it.value() != referenceRoot || becameSubdivided) { if (it.value() != referenceRoot || becameSubdivided) {
out << it.key(); out << it.key();
if (referenceRoot) { if (referenceRoot) {
@ -695,15 +777,15 @@ template<> void Bitstream::readDelta(MetavoxelData& value, const MetavoxelData&
} }
bool MetavoxelStreamState::shouldSubdivide() const { bool MetavoxelStreamState::shouldSubdivide() const {
return lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier()); return base.lod.shouldSubdivide(minimum, size, base.attribute->getLODThresholdMultiplier());
} }
bool MetavoxelStreamState::shouldSubdivideReference() const { bool MetavoxelStreamState::shouldSubdivideReference() const {
return referenceLOD.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier()); return base.referenceLOD.shouldSubdivide(minimum, size, base.attribute->getLODThresholdMultiplier());
} }
bool MetavoxelStreamState::becameSubdivided() const { bool MetavoxelStreamState::becameSubdivided() const {
return lod.becameSubdivided(minimum, size, referenceLOD, attribute->getLODThresholdMultiplier()); return base.lod.becameSubdivided(minimum, size, base.referenceLOD, base.attribute->getLODThresholdMultiplier());
} }
void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) { void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) {
@ -777,38 +859,36 @@ bool MetavoxelNode::isLeaf() const {
} }
void MetavoxelNode::read(MetavoxelStreamState& state) { void MetavoxelNode::read(MetavoxelStreamState& state) {
clearChildren(state.attribute); clearChildren(state.base.attribute);
if (!state.shouldSubdivide()) { if (!state.shouldSubdivide()) {
state.attribute->read(state.stream, _attributeValue, true); state.base.attribute->read(state.base.stream, _attributeValue, true);
return; return;
} }
bool leaf; bool leaf;
state.stream >> leaf; state.base.stream >> leaf;
state.attribute->read(state.stream, _attributeValue, leaf); state.base.attribute->read(state.base.stream, _attributeValue, leaf);
if (!leaf) { if (!leaf) {
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
_children[i] = new MetavoxelNode(state.attribute); _children[i] = new MetavoxelNode(state.base.attribute);
_children[i]->read(nextState); _children[i]->read(nextState);
} }
mergeChildren(state.attribute, true); mergeChildren(state.base.attribute, true);
} }
} }
void MetavoxelNode::write(MetavoxelStreamState& state) const { void MetavoxelNode::write(MetavoxelStreamState& state) const {
if (!state.shouldSubdivide()) { if (!state.shouldSubdivide()) {
state.attribute->write(state.stream, _attributeValue, true); state.base.attribute->write(state.base.stream, _attributeValue, true);
return; return;
} }
bool leaf = isLeaf(); bool leaf = isLeaf();
state.stream << leaf; state.base.stream << leaf;
state.attribute->write(state.stream, _attributeValue, leaf); state.base.attribute->write(state.base.stream, _attributeValue, leaf);
if (!leaf) { if (!leaf) {
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
_children[i]->write(nextState); _children[i]->write(nextState);
@ -817,31 +897,30 @@ void MetavoxelNode::write(MetavoxelStreamState& state) const {
} }
void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) { void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) {
clearChildren(state.attribute); clearChildren(state.base.attribute);
if (!state.shouldSubdivide()) { if (!state.shouldSubdivide()) {
state.attribute->readDelta(state.stream, _attributeValue, reference._attributeValue, true); state.base.attribute->readDelta(state.base.stream, _attributeValue, reference._attributeValue, true);
return; return;
} }
bool leaf; bool leaf;
state.stream >> leaf; state.base.stream >> leaf;
state.attribute->readDelta(state.stream, _attributeValue, reference._attributeValue, leaf); state.base.attribute->readDelta(state.base.stream, _attributeValue, reference._attributeValue, leaf);
if (!leaf) { if (!leaf) {
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
if (reference.isLeaf() || !state.shouldSubdivideReference()) { if (reference.isLeaf() || !state.shouldSubdivideReference()) {
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
_children[i] = new MetavoxelNode(state.attribute); _children[i] = new MetavoxelNode(state.base.attribute);
_children[i]->read(nextState); _children[i]->read(nextState);
} }
} else { } else {
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
bool changed; bool changed;
state.stream >> changed; state.base.stream >> changed;
if (changed) { if (changed) {
_children[i] = new MetavoxelNode(state.attribute); _children[i] = new MetavoxelNode(state.base.attribute);
_children[i]->readDelta(*reference._children[i], nextState); _children[i]->readDelta(*reference._children[i], nextState);
} else { } else {
if (nextState.becameSubdivided()) { if (nextState.becameSubdivided()) {
@ -856,21 +935,20 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta
} }
} }
} }
mergeChildren(state.attribute, true); mergeChildren(state.base.attribute, true);
} }
} }
void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const { void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const {
if (!state.shouldSubdivide()) { if (!state.shouldSubdivide()) {
state.attribute->writeDelta(state.stream, _attributeValue, reference._attributeValue, true); state.base.attribute->writeDelta(state.base.stream, _attributeValue, reference._attributeValue, true);
return; return;
} }
bool leaf = isLeaf(); bool leaf = isLeaf();
state.stream << leaf; state.base.stream << leaf;
state.attribute->writeDelta(state.stream, _attributeValue, reference._attributeValue, leaf); state.base.attribute->writeDelta(state.base.stream, _attributeValue, reference._attributeValue, leaf);
if (!leaf) { if (!leaf) {
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
if (reference.isLeaf() || !state.shouldSubdivideReference()) { if (reference.isLeaf() || !state.shouldSubdivideReference()) {
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
@ -880,12 +958,12 @@ void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamSt
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
if (_children[i] == reference._children[i]) { if (_children[i] == reference._children[i]) {
state.stream << false; state.base.stream << false;
if (nextState.becameSubdivided()) { if (nextState.becameSubdivided()) {
_children[i]->writeSubdivision(nextState); _children[i]->writeSubdivision(nextState);
} }
} else { } else {
state.stream << true; state.base.stream << true;
_children[i]->writeDelta(*reference._children[i], nextState); _children[i]->writeDelta(*reference._children[i], nextState);
} }
} }
@ -896,40 +974,38 @@ void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamSt
MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) {
if (!state.shouldSubdivideReference()) { if (!state.shouldSubdivideReference()) {
bool leaf; bool leaf;
state.stream >> leaf; state.base.stream >> leaf;
if (leaf) { if (leaf) {
return isLeaf() ? this : new MetavoxelNode(getAttributeValue(state.attribute)); return isLeaf() ? this : new MetavoxelNode(getAttributeValue(state.base.attribute));
} else { } else {
MetavoxelNode* newNode = new MetavoxelNode(getAttributeValue(state.attribute)); MetavoxelNode* newNode = new MetavoxelNode(getAttributeValue(state.base.attribute));
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
newNode->_children[i] = new MetavoxelNode(state.attribute); newNode->_children[i] = new MetavoxelNode(state.base.attribute);
newNode->_children[i]->read(nextState); newNode->_children[i]->read(nextState);
} }
return newNode; return newNode;
} }
} else if (!isLeaf()) { } else if (!isLeaf()) {
MetavoxelNode* node = this; MetavoxelNode* node = this;
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
if (nextState.becameSubdivided()) { if (nextState.becameSubdivided()) {
MetavoxelNode* child = _children[i]->readSubdivision(nextState); MetavoxelNode* child = _children[i]->readSubdivision(nextState);
if (child != _children[i]) { if (child != _children[i]) {
if (node == this) { if (node == this) {
node = new MetavoxelNode(state.attribute, this); node = new MetavoxelNode(state.base.attribute, this);
} }
node->_children[i] = child; node->_children[i] = child;
_children[i]->decrementReferenceCount(state.attribute); _children[i]->decrementReferenceCount(state.base.attribute);
} }
} }
} }
if (node != this) { if (node != this) {
node->mergeChildren(state.attribute, true); node->mergeChildren(state.base.attribute, true);
} }
return node; return node;
} }
@ -939,18 +1015,16 @@ MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) {
void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const { void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const {
bool leaf = isLeaf(); bool leaf = isLeaf();
if (!state.shouldSubdivideReference()) { if (!state.shouldSubdivideReference()) {
state.stream << leaf; state.base.stream << leaf;
if (!leaf) { if (!leaf) {
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
_children[i]->write(nextState); _children[i]->write(nextState);
} }
} }
} else if (!leaf) { } else if (!leaf) {
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
if (nextState.becameSubdivided()) { if (nextState.becameSubdivided()) {
@ -962,15 +1036,14 @@ void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const {
void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const { void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const {
foreach (const SharedObjectPointer& object, decodeInline<SharedObjectSet>(_attributeValue)) { foreach (const SharedObjectPointer& object, decodeInline<SharedObjectSet>(_attributeValue)) {
if (static_cast<Spanner*>(object.data())->testAndSetVisited()) { if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit)) {
state.stream << object; state.base.stream << object;
} }
} }
if (!state.shouldSubdivide() || isLeaf()) { if (!state.shouldSubdivide() || isLeaf()) {
return; return;
} }
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
_children[i]->writeSpanners(nextState); _children[i]->writeSpanners(nextState);
@ -981,19 +1054,18 @@ void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelS
SharedObjectSet oldSet = decodeInline<SharedObjectSet>(reference.getAttributeValue()); SharedObjectSet oldSet = decodeInline<SharedObjectSet>(reference.getAttributeValue());
SharedObjectSet newSet = decodeInline<SharedObjectSet>(_attributeValue); SharedObjectSet newSet = decodeInline<SharedObjectSet>(_attributeValue);
foreach (const SharedObjectPointer& object, oldSet) { foreach (const SharedObjectPointer& object, oldSet) {
if (static_cast<Spanner*>(object.data())->testAndSetVisited() && !newSet.contains(object)) { if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit) && !newSet.contains(object)) {
state.stream << object; state.base.stream << object;
} }
} }
foreach (const SharedObjectPointer& object, newSet) { foreach (const SharedObjectPointer& object, newSet) {
if (static_cast<Spanner*>(object.data())->testAndSetVisited() && !oldSet.contains(object)) { if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit) && !oldSet.contains(object)) {
state.stream << object; state.base.stream << object;
} }
} }
if (isLeaf() || !state.shouldSubdivide()) { if (isLeaf() || !state.shouldSubdivide()) {
if (!reference.isLeaf() && state.shouldSubdivideReference()) { if (!reference.isLeaf() && state.shouldSubdivideReference()) {
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
reference._children[i]->writeSpanners(nextState); reference._children[i]->writeSpanners(nextState);
@ -1001,8 +1073,7 @@ void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelS
} }
return; return;
} }
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
if (reference.isLeaf() || !state.shouldSubdivideReference()) { if (reference.isLeaf() || !state.shouldSubdivideReference()) {
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
@ -1023,8 +1094,7 @@ void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelS
void MetavoxelNode::writeSpannerSubdivision(MetavoxelStreamState& state) const { void MetavoxelNode::writeSpannerSubdivision(MetavoxelStreamState& state) const {
if (!isLeaf()) { if (!isLeaf()) {
MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
state.stream, state.lod, state.referenceLOD };
if (!state.shouldSubdivideReference()) { if (!state.shouldSubdivideReference()) {
for (int i = 0; i < CHILD_COUNT; i++) { for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i); nextState.setMinimum(state.minimum, i);
@ -1042,7 +1112,7 @@ void MetavoxelNode::writeSpannerSubdivision(MetavoxelStreamState& state) const {
} }
void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) { void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) {
if (--_referenceCount == 0) { if (!_referenceCount.deref()) {
destroy(attribute); destroy(attribute);
delete this; delete this;
} }
@ -1119,6 +1189,15 @@ void MetavoxelNode::countNodes(const AttributePointer& attribute, const glm::vec
} }
} }
MetavoxelInfo::MetavoxelInfo(MetavoxelInfo* parentInfo, int inputValuesSize, int outputValuesSize) :
parentInfo(parentInfo),
inputValues(inputValuesSize),
outputValues(outputValuesSize) {
}
MetavoxelInfo::MetavoxelInfo() {
}
int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth, int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth,
int fifth, int sixth, int seventh, int eighth) { int fifth, int sixth, int seventh, int eighth) {
return first | (second << 3) | (third << 6) | (fourth << 9) | return first | (second << 3) | (third << 6) | (fourth << 9) |
@ -1169,13 +1248,16 @@ int MetavoxelVisitor::encodeRandomOrder() {
const int MetavoxelVisitor::DEFAULT_ORDER = encodeOrder(0, 1, 2, 3, 4, 5, 6, 7); const int MetavoxelVisitor::DEFAULT_ORDER = encodeOrder(0, 1, 2, 3, 4, 5, 6, 7);
const int MetavoxelVisitor::STOP_RECURSION = 0; const int MetavoxelVisitor::STOP_RECURSION = 0;
const int MetavoxelVisitor::SHORT_CIRCUIT = -1; const int MetavoxelVisitor::SHORT_CIRCUIT = -1;
const int MetavoxelVisitor::ALL_NODES = 1 << 24;
const int MetavoxelVisitor::ALL_NODES_REST = 1 << 25;
MetavoxelVisitor::MetavoxelVisitor(const QVector<AttributePointer>& inputs, MetavoxelVisitor::MetavoxelVisitor(const QVector<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs, const MetavoxelLOD& lod) : const QVector<AttributePointer>& outputs, const MetavoxelLOD& lod) :
_inputs(inputs), _inputs(inputs),
_outputs(outputs), _outputs(outputs),
_lod(lod), _lod(lod),
_minimumLODThresholdMultiplier(FLT_MAX) { _minimumLODThresholdMultiplier(FLT_MAX),
_depth(-1) {
// find the minimum LOD threshold multiplier over all attributes // find the minimum LOD threshold multiplier over all attributes
foreach (const AttributePointer& attribute, _inputs) { foreach (const AttributePointer& attribute, _inputs) {
@ -1189,33 +1271,48 @@ MetavoxelVisitor::MetavoxelVisitor(const QVector<AttributePointer>& inputs,
MetavoxelVisitor::~MetavoxelVisitor() { MetavoxelVisitor::~MetavoxelVisitor() {
} }
void MetavoxelVisitor::prepare() { void MetavoxelVisitor::prepare(MetavoxelData* data) {
// nothing by default _data = data;
}
bool MetavoxelVisitor::postVisit(MetavoxelInfo& info) {
return false;
}
MetavoxelVisitation& MetavoxelVisitor::acquireVisitation() {
if (++_depth >= _visitations.size()) {
_visitations.append(MetavoxelVisitation(_depth == 0 ? NULL : &_visitations[_depth - 1],
this, _inputs.size() + 1, _outputs.size()));
}
return _visitations[_depth];
} }
SpannerVisitor::SpannerVisitor(const QVector<AttributePointer>& spannerInputs, const QVector<AttributePointer>& spannerMasks, SpannerVisitor::SpannerVisitor(const QVector<AttributePointer>& spannerInputs, const QVector<AttributePointer>& spannerMasks,
const QVector<AttributePointer>& inputs, const QVector<AttributePointer>& outputs, const MetavoxelLOD& lod) : const QVector<AttributePointer>& inputs, const QVector<AttributePointer>& outputs,
const MetavoxelLOD& lod, int order) :
MetavoxelVisitor(inputs + spannerInputs + spannerMasks, outputs, lod), MetavoxelVisitor(inputs + spannerInputs + spannerMasks, outputs, lod),
_spannerInputCount(spannerInputs.size()), _spannerInputCount(spannerInputs.size()),
_spannerMaskCount(spannerMasks.size()) { _spannerMaskCount(spannerMasks.size()),
_order(order) {
} }
void SpannerVisitor::prepare() { void SpannerVisitor::prepare(MetavoxelData* data) {
Spanner::incrementVisit(); MetavoxelVisitor::prepare(data);
_visit = Spanner::getAndIncrementNextVisit();
} }
int SpannerVisitor::visit(MetavoxelInfo& info) { int SpannerVisitor::visit(MetavoxelInfo& info) {
for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) {
foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue<SharedObjectSet>()) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue<SharedObjectSet>()) {
Spanner* spanner = static_cast<Spanner*>(object.data()); Spanner* spanner = static_cast<Spanner*>(object.data());
if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited() && if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited(_visit) &&
!visit(spanner, glm::vec3(), 0.0f)) { !visit(spanner, glm::vec3(), 0.0f)) {
return SHORT_CIRCUIT; return SHORT_CIRCUIT;
} }
} }
} }
if (!info.isLeaf) { if (!info.isLeaf) {
return DEFAULT_ORDER; return _order;
} }
for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) {
float maskValue = info.inputValues.at(i).getInlineValue<float>(); float maskValue = info.inputValues.at(i).getInlineValue<float>();
@ -1259,8 +1356,9 @@ RaySpannerIntersectionVisitor::RaySpannerIntersectionVisitor(const glm::vec3& or
_spannerMaskCount(spannerMasks.size()) { _spannerMaskCount(spannerMasks.size()) {
} }
void RaySpannerIntersectionVisitor::prepare() { void RaySpannerIntersectionVisitor::prepare(MetavoxelData* data) {
Spanner::incrementVisit(); MetavoxelVisitor::prepare(data);
_visit = Spanner::getAndIncrementNextVisit();
} }
class SpannerDistance { class SpannerDistance {
@ -1278,7 +1376,7 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) {
foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue<SharedObjectSet>()) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue<SharedObjectSet>()) {
Spanner* spanner = static_cast<Spanner*>(object.data()); Spanner* spanner = static_cast<Spanner*>(object.data());
if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited()) { if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited(_visit)) {
SpannerDistance spannerDistance = { spanner }; SpannerDistance spannerDistance = { spanner };
if (spanner->findRayIntersection(_origin, _direction, glm::vec3(), 0.0f, spannerDistance.distance)) { if (spanner->findRayIntersection(_origin, _direction, glm::vec3(), 0.0f, spannerDistance.distance)) {
spannerDistances.append(spannerDistance); spannerDistances.append(spannerDistance);
@ -1324,39 +1422,16 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
return STOP_RECURSION; return STOP_RECURSION;
} }
bool MetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
return guide(visitation);
}
DefaultMetavoxelGuide::DefaultMetavoxelGuide() { DefaultMetavoxelGuide::DefaultMetavoxelGuide() {
} }
bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float lodBase, int encodedOrder) {
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation();
float lodBase = glm::distance(visitation.visitor.getLOD().position, visitation.info.getCenter()) * nextVisitation.info.size = visitation.info.size * 0.5f;
visitation.visitor.getLOD().threshold;
visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor.getMinimumLODThresholdMultiplier());
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
int encodedOrder = visitation.visitor.visit(visitation.info);
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
return false;
}
for (int i = 0; i < visitation.outputNodes.size(); i++) {
OwnedAttributeValue& value = visitation.info.outputValues[i];
if (!value.getAttribute()) {
continue;
}
MetavoxelNode*& node = visitation.outputNodes[i];
if (node && node->isLeaf() && value.getAttribute()->equal(value.getValue(), node->getAttributeValue())) {
// "set" to same value; disregard
value = AttributeValue();
} else {
node = value.getAttribute()->createMetavoxelNode(value, node);
}
}
if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) {
return true;
}
MetavoxelVisitation nextVisitation = { &visitation, visitation.visitor,
QVector<MetavoxelNode*>(visitation.inputNodes.size()), QVector<MetavoxelNode*>(visitation.outputNodes.size()),
{ &visitation.info, glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.inputNodes.size()),
QVector<OwnedAttributeValue>(visitation.outputNodes.size()) } };
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
// the encoded order tells us the child indices for each iteration // the encoded order tells us the child indices for each iteration
int index = encodedOrder & ORDER_ELEMENT_MASK; int index = encodedOrder & ORDER_ELEMENT_MASK;
@ -1372,12 +1447,13 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
for (int j = 0; j < visitation.outputNodes.size(); j++) { for (int j = 0; j < visitation.outputNodes.size(); j++) {
MetavoxelNode* node = visitation.outputNodes.at(j); MetavoxelNode* node = visitation.outputNodes.at(j);
MetavoxelNode* child = (node && (visitation.info.size >= lodBase * MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
visitation.visitor.getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
nextVisitation.outputNodes[j] = child; nextVisitation.outputNodes[j] = child;
} }
nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index); nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index);
if (!static_cast<MetavoxelGuide*>(nextVisitation.info.inputValues.last().getInlineValue< if (!static_cast<MetavoxelGuide*>(nextVisitation.info.inputValues.last().getInlineValue<
SharedObjectPointer>().data())->guide(nextVisitation)) { SharedObjectPointer>().data())->guide(nextVisitation)) {
visitation.visitor->releaseVisitation();
return false; return false;
} }
for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { for (int j = 0; j < nextVisitation.outputNodes.size(); j++) {
@ -1421,6 +1497,188 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
value = node->getAttributeValue(value.getAttribute()); value = node->getAttributeValue(value.getAttribute());
} }
} }
visitation.visitor->releaseVisitation();
visitation.info.outputValues.swap(nextVisitation.info.outputValues);
bool changed = visitation.visitor->postVisit(visitation.info);
visitation.info.outputValues.swap(nextVisitation.info.outputValues);
if (changed) {
for (int i = 0; i < visitation.outputNodes.size(); i++) {
OwnedAttributeValue& newValue = nextVisitation.info.outputValues[i];
if (!newValue.getAttribute()) {
continue;
}
OwnedAttributeValue& value = visitation.info.outputValues[i];
MetavoxelNode*& node = visitation.outputNodes[i];
if (value.getAttribute()) {
node->setAttributeValue(value = newValue);
} else if (!(node && node->isLeaf() && newValue.getAttribute()->equal(
newValue.getValue(), node->getAttributeValue()))) {
node = newValue.getAttribute()->createMetavoxelNode(value = newValue, node);
}
newValue = AttributeValue();
}
}
return true;
}
bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
visitation.visitor->getLOD().threshold;
visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier());
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
int encodedOrder = visitation.visitor->visit(visitation.info);
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
return false;
}
for (int i = 0; i < visitation.outputNodes.size(); i++) {
OwnedAttributeValue& value = visitation.info.outputValues[i];
if (!value.getAttribute()) {
continue;
}
MetavoxelNode*& node = visitation.outputNodes[i];
if (node && node->isLeaf() && value.getAttribute()->equal(value.getValue(), node->getAttributeValue())) {
// "set" to same value; disregard
value = AttributeValue();
} else {
node = value.getAttribute()->createMetavoxelNode(value, node);
}
}
if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) {
return true;
}
return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, lodBase, encodedOrder));
}
bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
visitation.visitor->getLOD().threshold;
visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier());
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
int encodedOrder = visitation.visitor->visit(visitation.info);
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
return false;
}
for (int i = 0; i < visitation.outputNodes.size(); i++) {
OwnedAttributeValue& value = visitation.info.outputValues[i];
if (!value.getAttribute()) {
continue;
}
MetavoxelNode*& node = visitation.outputNodes[i];
if (node && node->isLeaf() && value.getAttribute()->equal(value.getValue(), node->getAttributeValue())) {
// "set" to same value; disregard
value = AttributeValue();
} else {
node = value.getAttribute()->createMetavoxelNode(value, node);
}
}
if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) {
return true;
}
if (encodedOrder & MetavoxelVisitor::ALL_NODES_REST) {
return defaultGuideToChildren(visitation, lodBase, encodedOrder);
}
bool onlyVisitDifferent = !(encodedOrder & MetavoxelVisitor::ALL_NODES);
MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation();
nextVisitation.compareNodes.resize(visitation.compareNodes.size());
nextVisitation.info.size = visitation.info.size * 0.5f;
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
// the encoded order tells us the child indices for each iteration
int index = encodedOrder & ORDER_ELEMENT_MASK;
encodedOrder >>= ORDER_ELEMENT_BITS;
bool allNodesSame = onlyVisitDifferent;
for (int j = 0; j < visitation.inputNodes.size(); j++) {
MetavoxelNode* node = visitation.inputNodes.at(j);
const AttributeValue& parentValue = visitation.info.inputValues.at(j);
bool expand = (visitation.info.size >= lodBase * parentValue.getAttribute()->getLODThresholdMultiplier());
MetavoxelNode* child = (node && expand) ? node->getChild(index) : NULL;
nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue);
MetavoxelNode* compareNode = visitation.compareNodes.at(j);
MetavoxelNode* compareChild = (compareNode && expand) ? compareNode->getChild(index) : NULL;
nextVisitation.compareNodes[j] = compareChild;
allNodesSame &= (child == compareChild);
}
if (allNodesSame) {
continue;
}
for (int j = 0; j < visitation.outputNodes.size(); j++) {
MetavoxelNode* node = visitation.outputNodes.at(j);
MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
nextVisitation.outputNodes[j] = child;
}
nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index);
if (!static_cast<MetavoxelGuide*>(nextVisitation.info.inputValues.last().getInlineValue<
SharedObjectPointer>().data())->guideToDifferent(nextVisitation)) {
visitation.visitor->releaseVisitation();
return false;
}
for (int j = 0; j < nextVisitation.outputNodes.size(); j++) {
OwnedAttributeValue& value = nextVisitation.info.outputValues[j];
if (!value.getAttribute()) {
continue;
}
// replace the child
OwnedAttributeValue& parentValue = visitation.info.outputValues[j];
if (!parentValue.getAttribute()) {
// shallow-copy the parent node on first change
parentValue = value;
MetavoxelNode*& node = visitation.outputNodes[j];
if (node) {
node = new MetavoxelNode(value.getAttribute(), node);
} else {
// create leaf with inherited value
node = new MetavoxelNode(value.getAttribute()->inherit(visitation.getInheritedOutputValue(j)));
}
}
MetavoxelNode* node = visitation.outputNodes.at(j);
MetavoxelNode* child = node->getChild(index);
if (child) {
child->decrementReferenceCount(value.getAttribute());
} else {
// it's a leaf; we need to split it up
AttributeValue nodeValue = value.getAttribute()->inherit(node->getAttributeValue(value.getAttribute()));
for (int k = 1; k < MetavoxelNode::CHILD_COUNT; k++) {
node->setChild((index + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue));
}
}
node->setChild(index, nextVisitation.outputNodes.at(j));
value = AttributeValue();
}
}
for (int i = 0; i < visitation.outputNodes.size(); i++) {
OwnedAttributeValue& value = visitation.info.outputValues[i];
if (value.getAttribute()) {
MetavoxelNode* node = visitation.outputNodes.at(i);
node->mergeChildren(value.getAttribute());
value = node->getAttributeValue(value.getAttribute());
}
}
visitation.visitor->releaseVisitation();
visitation.info.outputValues.swap(nextVisitation.info.outputValues);
bool changed = visitation.visitor->postVisit(visitation.info);
visitation.info.outputValues.swap(nextVisitation.info.outputValues);
if (changed) {
for (int i = 0; i < visitation.outputNodes.size(); i++) {
OwnedAttributeValue& newValue = nextVisitation.info.outputValues[i];
if (!newValue.getAttribute()) {
continue;
}
OwnedAttributeValue& value = visitation.info.outputValues[i];
MetavoxelNode*& node = visitation.outputNodes[i];
if (value.getAttribute()) {
node->setAttributeValue(value = newValue);
} else if (!(node && node->isLeaf() && newValue.getAttribute()->equal(
newValue.getValue(), node->getAttributeValue()))) {
node = newValue.getAttribute()->createMetavoxelNode(value = newValue, node);
}
newValue = AttributeValue();
}
}
return true; return true;
} }
@ -1456,12 +1714,12 @@ static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide*
QScriptValue ScriptedMetavoxelGuide::getInputs(QScriptContext* context, QScriptEngine* engine) { QScriptValue ScriptedMetavoxelGuide::getInputs(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>()); ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
return getAttributes(engine, guide, guide->_visitation->visitor.getInputs()); return getAttributes(engine, guide, guide->_visitation->visitor->getInputs());
} }
QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) { QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>()); ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
return getAttributes(engine, guide, guide->_visitation->visitor.getOutputs()); return getAttributes(engine, guide, guide->_visitation->visitor->getOutputs());
} }
QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) { QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) {
@ -1470,14 +1728,16 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin
// start with the basics, including inherited attribute values // start with the basics, including inherited attribute values
QScriptValue infoValue = context->argument(0); QScriptValue infoValue = context->argument(0);
QScriptValue minimum = infoValue.property(guide->_minimumHandle); QScriptValue minimum = infoValue.property(guide->_minimumHandle);
MetavoxelInfo info = { MetavoxelInfo info(NULL, 0, 0);
NULL, glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), info.inputValues = guide->_visitation->info.inputValues;
(float)infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues, info.outputValues = guide->_visitation->info.outputValues;
guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() }; info.minimum = glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber());
info.size = (float)infoValue.property(guide->_sizeHandle).toNumber();
info.isLeaf = infoValue.property(guide->_isLeafHandle).toBool();
// extract and convert the values provided by the script // extract and convert the values provided by the script
QScriptValue inputValues = infoValue.property(guide->_inputValuesHandle); QScriptValue inputValues = infoValue.property(guide->_inputValuesHandle);
const QVector<AttributePointer>& inputs = guide->_visitation->visitor.getInputs(); const QVector<AttributePointer>& inputs = guide->_visitation->visitor->getInputs();
for (int i = 0; i < inputs.size(); i++) { for (int i = 0; i < inputs.size(); i++) {
QScriptValue attributeValue = inputValues.property(i); QScriptValue attributeValue = inputValues.property(i);
if (attributeValue.isValid()) { if (attributeValue.isValid()) {
@ -1486,7 +1746,7 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin
} }
} }
QScriptValue result = guide->_visitation->visitor.visit(info); QScriptValue result = guide->_visitation->visitor->visit(info);
// destroy any created values // destroy any created values
for (int i = 0; i < inputs.size(); i++) { for (int i = 0; i < inputs.size(); i++) {
@ -1559,6 +1819,18 @@ void ScriptedMetavoxelGuide::setURL(const ParameterizedURL& url) {
_minimumHandle = QScriptString(); _minimumHandle = QScriptString();
} }
MetavoxelVisitation::MetavoxelVisitation(MetavoxelVisitation* previous,
MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize) :
previous(previous),
visitor(visitor),
inputNodes(inputNodesSize),
outputNodes(outputNodesSize),
info(previous ? &previous->info : NULL, inputNodesSize, outputNodesSize) {
}
MetavoxelVisitation::MetavoxelVisitation() {
}
bool MetavoxelVisitation::allInputNodesLeaves() const { bool MetavoxelVisitation::allInputNodesLeaves() const {
foreach (MetavoxelNode* node, inputNodes) { foreach (MetavoxelNode* node, inputNodes) {
if (node && !node->isLeaf()) { if (node && !node->isLeaf()) {
@ -1572,10 +1844,55 @@ AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const {
for (const MetavoxelVisitation* visitation = previous; visitation; visitation = visitation->previous) { for (const MetavoxelVisitation* visitation = previous; visitation; visitation = visitation->previous) {
MetavoxelNode* node = visitation->outputNodes.at(index); MetavoxelNode* node = visitation->outputNodes.at(index);
if (node) { if (node) {
return node->getAttributeValue(visitor.getOutputs().at(index)); return node->getAttributeValue(visitor->getOutputs().at(index));
} }
} }
return AttributeValue(visitor.getOutputs().at(index)); return AttributeValue(visitor->getOutputs().at(index));
}
MetavoxelRenderer::MetavoxelRenderer() :
_implementation(NULL) {
}
MetavoxelRendererImplementation* MetavoxelRenderer::getImplementation() {
QMutexLocker locker(&_implementationMutex);
if (!_implementation) {
QByteArray className = getImplementationClassName();
const QMetaObject* metaObject = Bitstream::getMetaObject(className);
if (!metaObject) {
qDebug() << "Unknown class name:" << className;
metaObject = &MetavoxelRendererImplementation::staticMetaObject;
}
_implementation = static_cast<MetavoxelRendererImplementation*>(metaObject->newInstance());
connect(this, &QObject::destroyed, _implementation, &QObject::deleteLater);
_implementation->init(this);
}
return _implementation;
}
MetavoxelRendererImplementation::MetavoxelRendererImplementation() {
}
void MetavoxelRendererImplementation::init(MetavoxelRenderer* renderer) {
_renderer = renderer;
}
void MetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
MetavoxelInfo& info, const MetavoxelLOD& lod) {
}
void MetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
}
QByteArray MetavoxelRenderer::getImplementationClassName() const {
return "MetavoxelRendererImplementation";
}
PointMetavoxelRenderer::PointMetavoxelRenderer() {
}
QByteArray PointMetavoxelRenderer::getImplementationClassName() const {
return "PointMetavoxelRendererImplementation";
} }
const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f; const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f;
@ -1585,8 +1902,7 @@ Spanner::Spanner() :
_renderer(NULL), _renderer(NULL),
_placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY),
_voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY),
_masked(false), _masked(false) {
_lastVisit(0) {
} }
void Spanner::setBounds(const Box& bounds) { void Spanner::setBounds(const Box& bounds) {
@ -1615,11 +1931,13 @@ bool Spanner::blendAttributeValues(MetavoxelInfo& info, bool force) const {
return false; return false;
} }
bool Spanner::testAndSetVisited() { bool Spanner::testAndSetVisited(int visit) {
if (_lastVisit == _visit) { QMutexLocker locker(&_lastVisitsMutex);
int& lastVisit = _lastVisits[QThread::currentThread()];
if (lastVisit == visit) {
return false; return false;
} }
_lastVisit = _visit; lastVisit = visit;
return true; return true;
} }
@ -1632,7 +1950,7 @@ SpannerRenderer* Spanner::getRenderer() {
metaObject = &SpannerRenderer::staticMetaObject; metaObject = &SpannerRenderer::staticMetaObject;
} }
_renderer = static_cast<SpannerRenderer*>(metaObject->newInstance()); _renderer = static_cast<SpannerRenderer*>(metaObject->newInstance());
_renderer->setParent(this); connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater);
_renderer->init(this); _renderer->init(this);
} }
return _renderer; return _renderer;
@ -1647,13 +1965,13 @@ QByteArray Spanner::getRendererClassName() const {
return "SpannerRendererer"; return "SpannerRendererer";
} }
int Spanner::_visit = 0; QAtomicInt Spanner::_nextVisit(1);
SpannerRenderer::SpannerRenderer() { SpannerRenderer::SpannerRenderer() {
} }
void SpannerRenderer::init(Spanner* spanner) { void SpannerRenderer::init(Spanner* spanner) {
// nothing by default _spanner = spanner;
} }
void SpannerRenderer::simulate(float deltaTime) { void SpannerRenderer::simulate(float deltaTime) {

View file

@ -14,6 +14,7 @@
#include <QBitArray> #include <QBitArray>
#include <QHash> #include <QHash>
#include <QMutex>
#include <QSharedData> #include <QSharedData>
#include <QSharedPointer> #include <QSharedPointer>
#include <QScriptString> #include <QScriptString>
@ -27,7 +28,9 @@
class QScriptContext; class QScriptContext;
class MetavoxelInfo;
class MetavoxelNode; class MetavoxelNode;
class MetavoxelRendererImplementation;
class MetavoxelVisitation; class MetavoxelVisitation;
class MetavoxelVisitor; class MetavoxelVisitor;
class NetworkValue; class NetworkValue;
@ -80,6 +83,9 @@ public:
/// Applies the specified visitor to the contained voxels. /// Applies the specified visitor to the contained voxels.
void guide(MetavoxelVisitor& visitor); 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. /// Inserts a spanner into the specified attribute layer.
void insert(const AttributePointer& attribute, const SharedObjectPointer& object); void insert(const AttributePointer& attribute, const SharedObjectPointer& object);
void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
@ -155,15 +161,22 @@ template<> void Bitstream::readDelta(MetavoxelData& value, const MetavoxelData&
Q_DECLARE_METATYPE(MetavoxelData) Q_DECLARE_METATYPE(MetavoxelData)
/// Holds the state used in streaming metavoxel data. /// Holds the base state used in streaming metavoxel data.
class MetavoxelStreamState { class MetavoxelStreamBase {
public: public:
glm::vec3 minimum;
float size;
const AttributePointer& attribute; const AttributePointer& attribute;
Bitstream& stream; Bitstream& stream;
const MetavoxelLOD& lod; const MetavoxelLOD& lod;
const MetavoxelLOD& referenceLOD; 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 shouldSubdivide() const;
bool shouldSubdivideReference() const; bool shouldSubdivideReference() const;
@ -209,7 +222,7 @@ public:
void writeSpannerSubdivision(MetavoxelStreamState& state) const; void writeSpannerSubdivision(MetavoxelStreamState& state) const;
/// Increments the node's reference count. /// Increments the node's reference count.
void incrementReferenceCount() { _referenceCount++; } void incrementReferenceCount() { _referenceCount.ref(); }
/// Decrements the node's reference count. If the resulting reference count is zero, destroys the node /// Decrements the node's reference count. If the resulting reference count is zero, destroys the node
/// and calls delete this. /// and calls delete this.
@ -235,7 +248,7 @@ private:
friend class MetavoxelVisitation; friend class MetavoxelVisitation;
int _referenceCount; QAtomicInt _referenceCount;
void* _attributeValue; void* _attributeValue;
MetavoxelNode* _children[CHILD_COUNT]; MetavoxelNode* _children[CHILD_COUNT];
}; };
@ -252,6 +265,9 @@ public:
bool isLODLeaf; bool isLODLeaf;
bool isLeaf; bool isLeaf;
MetavoxelInfo(MetavoxelInfo* parentInfo, int inputValuesSize, int outputValuesSize);
MetavoxelInfo();
Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); } 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; } glm::vec3 getCenter() const { return minimum + glm::vec3(size, size, size) * 0.5f; }
}; };
@ -278,6 +294,14 @@ public:
/// A special "order" that short-circuits the tour. /// A special "order" that short-circuits the tour.
static const int SHORT_CIRCUIT; 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, MetavoxelVisitor(const QVector<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(), const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD()); const MetavoxelLOD& lod = MetavoxelLOD());
@ -297,19 +321,34 @@ public:
float getMinimumLODThresholdMultiplier() const { return _minimumLODThresholdMultiplier; } float getMinimumLODThresholdMultiplier() const { return _minimumLODThresholdMultiplier; }
/// Prepares for a new tour of the metavoxel data. /// Prepares for a new tour of the metavoxel data.
virtual void prepare(); virtual void prepare(MetavoxelData* data);
/// Visits a metavoxel. /// Visits a metavoxel.
/// \param info the metavoxel data /// \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 /// \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; 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: protected:
QVector<AttributePointer> _inputs; QVector<AttributePointer> _inputs;
QVector<AttributePointer> _outputs; QVector<AttributePointer> _outputs;
MetavoxelLOD _lod; MetavoxelLOD _lod;
float _minimumLODThresholdMultiplier; float _minimumLODThresholdMultiplier;
MetavoxelData* _data;
QList<MetavoxelVisitation> _visitations;
int _depth;
}; };
/// Base class for visitors to spanners. /// Base class for visitors to spanners.
@ -320,20 +359,23 @@ public:
const QVector<AttributePointer>& spannerMasks = QVector<AttributePointer>(), const QVector<AttributePointer>& spannerMasks = QVector<AttributePointer>(),
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(), const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(), const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD()); const MetavoxelLOD& lod = MetavoxelLOD(),
int order = DEFAULT_ORDER);
/// Visits a spanner (or part thereof). /// Visits a spanner (or part thereof).
/// \param clipSize the size of the clip volume, or zero if unclipped /// \param clipSize the size of the clip volume, or zero if unclipped
/// \return true to continue, false to short-circuit the tour /// \return true to continue, false to short-circuit the tour
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) = 0; virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) = 0;
virtual void prepare(); virtual void prepare(MetavoxelData* data);
virtual int visit(MetavoxelInfo& info); virtual int visit(MetavoxelInfo& info);
protected: protected:
int _spannerInputCount; int _spannerInputCount;
int _spannerMaskCount; int _spannerMaskCount;
int _order;
int _visit;
}; };
/// Base class for ray intersection visitors. /// Base class for ray intersection visitors.
@ -372,13 +414,14 @@ public:
/// \return true to continue, false to short-circuit the tour /// \return true to continue, false to short-circuit the tour
virtual bool visitSpanner(Spanner* spanner, float distance) = 0; virtual bool visitSpanner(Spanner* spanner, float distance) = 0;
virtual void prepare(); virtual void prepare(MetavoxelData* data);
virtual int visit(MetavoxelInfo& info, float distance); virtual int visit(MetavoxelInfo& info, float distance);
protected: protected:
int _spannerInputCount; int _spannerInputCount;
int _spannerMaskCount; int _spannerMaskCount;
int _visit;
}; };
/// Interface for objects that guide metavoxel visitors. /// Interface for objects that guide metavoxel visitors.
@ -390,6 +433,10 @@ public:
/// Guides the specified visitor to the contained voxels. /// Guides the specified visitor to the contained voxels.
/// \return true to keep going, false to short circuit the tour /// \return true to keep going, false to short circuit the tour
virtual bool guide(MetavoxelVisitation& visitation) = 0; 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. /// Guides visitors through the explicit content of the system.
@ -401,6 +448,7 @@ public:
Q_INVOKABLE DefaultMetavoxelGuide(); Q_INVOKABLE DefaultMetavoxelGuide();
virtual bool guide(MetavoxelVisitation& visitation); virtual bool guide(MetavoxelVisitation& visitation);
virtual bool guideToDifferent(MetavoxelVisitation& visitation);
}; };
/// A temporary test guide that just makes the existing voxels throb with delight. /// A temporary test guide that just makes the existing voxels throb with delight.
@ -464,15 +512,67 @@ class MetavoxelVisitation {
public: public:
MetavoxelVisitation* previous; MetavoxelVisitation* previous;
MetavoxelVisitor& visitor; MetavoxelVisitor* visitor;
QVector<MetavoxelNode*> inputNodes; QVector<MetavoxelNode*> inputNodes;
QVector<MetavoxelNode*> outputNodes; QVector<MetavoxelNode*> outputNodes;
QVector<MetavoxelNode*> compareNodes;
MetavoxelInfo info; MetavoxelInfo info;
MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize);
MetavoxelVisitation();
bool allInputNodesLeaves() const; bool allInputNodesLeaves() const;
AttributeValue getInheritedOutputValue(int index) 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 render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod);
protected:
MetavoxelRenderer* _renderer;
};
/// Renders metavoxels as points.
class PointMetavoxelRenderer : public MetavoxelRenderer {
Q_OBJECT
public:
Q_INVOKABLE PointMetavoxelRenderer();
virtual QByteArray getImplementationClassName() const;
};
/// An object that spans multiple octree cells. /// An object that spans multiple octree cells.
class Spanner : public SharedObject { class Spanner : public SharedObject {
Q_OBJECT Q_OBJECT
@ -483,8 +583,8 @@ class Spanner : public SharedObject {
public: public:
/// Increments the value of the global visit counter. /// Returns the value of the global visit counter and increments it.
static void incrementVisit() { _visit++; } static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); }
Spanner(); Spanner();
@ -517,7 +617,7 @@ public:
/// Checks whether we've visited this object on the current traversal. If we have, returns false. /// 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. /// If we haven't, sets the last visit identifier and returns true.
bool testAndSetVisited(); bool testAndSetVisited(int visit);
/// Returns a pointer to the renderer, creating it if necessary. /// Returns a pointer to the renderer, creating it if necessary.
SpannerRenderer* getRenderer(); SpannerRenderer* getRenderer();
@ -545,9 +645,10 @@ private:
float _placementGranularity; float _placementGranularity;
float _voxelizationGranularity; float _voxelizationGranularity;
bool _masked; bool _masked;
int _lastVisit; ///< the identifier of the last visit QHash<QThread*, int> _lastVisits; ///< last visit identifiers for each thread
QMutex _lastVisitsMutex;
static int _visit; ///< the global visit counter static QAtomicInt _nextVisit; ///< the global visit counter
}; };
/// Base class for objects that can render spanners. /// Base class for objects that can render spanners.
@ -565,6 +666,10 @@ public:
virtual void render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize); virtual void render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const; const glm::vec3& clipMinimum, float clipSize, float& distance) const;
protected:
Spanner* _spanner;
}; };
/// An object with a 3D transform. /// An object with a 3D transform.

View file

@ -23,7 +23,7 @@
REGISTER_META_OBJECT(SharedObject) REGISTER_META_OBJECT(SharedObject)
SharedObject::SharedObject() : SharedObject::SharedObject() :
_id(++_lastID), _id(_nextID.fetchAndAddOrdered(1)),
_originID(_id), _originID(_id),
_remoteID(0), _remoteID(0),
_remoteOriginID(0) { _remoteOriginID(0) {
@ -131,7 +131,7 @@ void SharedObject::dump(QDebug debug) const {
} }
} }
int SharedObject::_lastID = 0; QAtomicInt SharedObject::_nextID(1);
WeakSharedObjectHash SharedObject::_weakHash; WeakSharedObjectHash SharedObject::_weakHash;
QReadWriteLock SharedObject::_weakHashLock; QReadWriteLock SharedObject::_weakHashLock;

View file

@ -87,7 +87,7 @@ private:
int _remoteOriginID; int _remoteOriginID;
QAtomicInt _referenceCount; QAtomicInt _referenceCount;
static int _lastID; static QAtomicInt _nextID;
static WeakSharedObjectHash _weakHash; static WeakSharedObjectHash _weakHash;
static QReadWriteLock _weakHashLock; static QReadWriteLock _weakHashLock;
}; };

View file

@ -58,6 +58,7 @@ public:
static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; } static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; }
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
AnimationCache* getAnimationCache() { return &_animationCache; }
/// sets the script contents, will return false if failed, will fail if script is already running /// sets the script contents, will return false if failed, will fail if script is already running
bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString("")); bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString(""));