mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-11 20:38:40 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into domain-server-auth
This commit is contained in:
commit
133e8570a3
27 changed files with 1606 additions and 494 deletions
5
BUILD.md
5
BUILD.md
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
88
assignment-client/src/avatars/ScriptableAvatar.cpp
Normal file
88
assignment-client/src/avatars/ScriptableAvatar.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
assignment-client/src/avatars/ScriptableAvatar.h
Normal file
40
assignment-client/src/avatars/ScriptableAvatar.h
Normal 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
|
|
@ -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
|
if (node->getType() == NodeType::Agent) {
|
||||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
// we assume the node is already locked
|
||||||
if (node->getType() == NodeType::Agent) {
|
MetavoxelSession* session = static_cast<MetavoxelSession*>(node->getLinkedData());
|
||||||
static_cast<MetavoxelSession*>(node->getLinkedData())->update();
|
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()) {
|
||||||
|
|
|
@ -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,27 +32,64 @@ 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();
|
||||||
|
|
||||||
virtual void readPendingDatagrams();
|
virtual void readPendingDatagrams();
|
||||||
|
|
||||||
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,8 +102,8 @@ class MetavoxelSession : public Endpoint {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
MetavoxelSession(const SharedNodePointer& node, MetavoxelServer* server);
|
MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender);
|
||||||
|
|
||||||
virtual void update();
|
virtual void update();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -83,7 +121,7 @@ private:
|
||||||
|
|
||||||
void sendPacketGroup(int alreadySent = 0);
|
void sendPacketGroup(int alreadySent = 0);
|
||||||
|
|
||||||
MetavoxelServer* _server;
|
MetavoxelSender* _sender;
|
||||||
|
|
||||||
MetavoxelLOD _lod;
|
MetavoxelLOD _lod;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,183 +25,139 @@
|
||||||
#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();
|
||||||
if (!_program.isLinked()) {
|
_pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new PointBufferAttribute());
|
||||||
_program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
|
|
||||||
_program.link();
|
|
||||||
|
|
||||||
_pointScaleLocation = _program.uniformLocation("pointScale");
|
|
||||||
}
|
|
||||||
_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
|
|
||||||
_buffer.create();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MetavoxelLOD MetavoxelSystem::getLOD() const {
|
MetavoxelLOD MetavoxelSystem::getLOD() {
|
||||||
// the LOD threshold is temporarily tied to the avatar LOD parameter
|
QReadLocker locker(&_lodLock);
|
||||||
const float BASE_LOD_THRESHOLD = 0.01f;
|
return _lod;
|
||||||
return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(),
|
}
|
||||||
BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier());
|
|
||||||
|
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) {
|
void MetavoxelSystem::simulate(float deltaTime) {
|
||||||
// update the clients
|
// update the lod
|
||||||
_points.clear();
|
{
|
||||||
_simulateVisitor.setDeltaTime(deltaTime);
|
// the LOD threshold is temporarily tied to the avatar LOD parameter
|
||||||
_simulateVisitor.setOrder(-Application::getInstance()->getViewFrustum()->getDirection());
|
QWriteLocker locker(&_lodLock);
|
||||||
update();
|
const float BASE_LOD_THRESHOLD = 0.01f;
|
||||||
|
_lod = MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(),
|
||||||
_buffer.bind();
|
BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier());
|
||||||
int bytes = _points.size() * sizeof(Point);
|
|
||||||
if (_buffer.size() < bytes) {
|
|
||||||
_buffer.allocate(_points.constData(), bytes);
|
|
||||||
} else {
|
|
||||||
_buffer.write(0, _points.constData(), bytes);
|
|
||||||
}
|
}
|
||||||
_buffer.release();
|
|
||||||
|
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() {
|
void MetavoxelSystem::render() {
|
||||||
int viewport[4];
|
RenderVisitor renderVisitor(getLOD());
|
||||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
guideToAugmented(renderVisitor);
|
||||||
const int VIEWPORT_WIDTH_INDEX = 2;
|
|
||||||
const int VIEWPORT_HEIGHT_INDEX = 3;
|
|
||||||
float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX];
|
|
||||||
float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX];
|
|
||||||
float viewportDiagonal = sqrtf(viewportWidth*viewportWidth + viewportHeight*viewportHeight);
|
|
||||||
float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(),
|
|
||||||
Application::getInstance()->getViewFrustum()->getNearTopRight());
|
|
||||||
|
|
||||||
_program.bind();
|
|
||||||
_program.setUniformValue(_pointScaleLocation, viewportDiagonal *
|
|
||||||
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_COLOR_ARRAY);
|
|
||||||
glEnableClientState(GL_NORMAL_ARRAY);
|
|
||||||
|
|
||||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
|
||||||
|
|
||||||
glDrawArrays(GL_POINTS, 0, _points.size());
|
|
||||||
|
|
||||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
|
||||||
|
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
|
||||||
glDisableClientState(GL_COLOR_ARRAY);
|
|
||||||
glDisableClientState(GL_NORMAL_ARRAY);
|
|
||||||
|
|
||||||
_buffer.release();
|
|
||||||
|
|
||||||
_program.release();
|
|
||||||
|
|
||||||
|
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()) {
|
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||||
if (node->getType() == NodeType::MetavoxelServer) {
|
if (node->getType() == NodeType::MetavoxelServer) {
|
||||||
QMutexLocker locker(&node->getMutex());
|
QMutexLocker locker(&node->getMutex());
|
||||||
MetavoxelSystemClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData());
|
MetavoxelSystemClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData());
|
||||||
if (client) {
|
if (client) {
|
||||||
client->guide(_renderVisitor);
|
client->getAugmentedData().guide(visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) {
|
MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) :
|
||||||
return new MetavoxelSystemClient(node, this);
|
MetavoxelClient(node, updater) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetavoxelSystem::updateClient(MetavoxelClient* client) {
|
void MetavoxelSystemClient::setAugmentedData(const MetavoxelData& data) {
|
||||||
MetavoxelClientManager::updateClient(client);
|
QWriteLocker locker(&_augmentedDataLock);
|
||||||
client->guide(_simulateVisitor);
|
_augmentedData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector<Point>& points) :
|
MetavoxelData MetavoxelSystemClient::getAugmentedData() {
|
||||||
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
|
QReadLocker locker(&_augmentedDataLock);
|
||||||
QVector<AttributePointer>(), QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute() <<
|
return _augmentedData;
|
||||||
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) {
|
int MetavoxelSystemClient::parseData(const QByteArray& packet) {
|
||||||
|
@ -209,11 +167,284 @@ int MetavoxelSystemClient::parseData(const QByteArray& packet) {
|
||||||
return 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) {
|
void MetavoxelSystemClient::sendDatagram(const QByteArray& data) {
|
||||||
NodeList::getInstance()->writeDatagram(data, _node);
|
NodeList::getInstance()->writeDatagram(data, _node);
|
||||||
Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size());
|
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()) {
|
||||||
|
_program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
|
||||||
|
_program.link();
|
||||||
|
|
||||||
|
_program.bind();
|
||||||
|
_pointScaleLocation = _program.uniformLocation("pointScale");
|
||||||
|
_program.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PointMetavoxelRendererImplementation::PointMetavoxelRendererImplementation() {
|
||||||
|
}
|
||||||
|
|
||||||
|
class PointAugmentVisitor : public MetavoxelVisitor {
|
||||||
|
public:
|
||||||
|
|
||||||
|
PointAugmentVisitor(const MetavoxelLOD& lod);
|
||||||
|
|
||||||
|
virtual void prepare(MetavoxelData* data);
|
||||||
|
virtual int visit(MetavoxelInfo& info);
|
||||||
|
virtual bool postVisit(MetavoxelInfo& info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
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) {
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||||
|
const int VIEWPORT_WIDTH_INDEX = 2;
|
||||||
|
const int VIEWPORT_HEIGHT_INDEX = 3;
|
||||||
|
float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX];
|
||||||
|
float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX];
|
||||||
|
float viewportDiagonal = sqrtf(viewportWidth * viewportWidth + viewportHeight * viewportHeight);
|
||||||
|
float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(),
|
||||||
|
Application::getInstance()->getViewFrustum()->getNearTopRight());
|
||||||
|
|
||||||
|
_program.bind();
|
||||||
|
_program.setUniformValue(_pointScaleLocation, viewportDiagonal *
|
||||||
|
Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal);
|
||||||
|
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glEnableClientState(GL_COLOR_ARRAY);
|
||||||
|
glEnableClientState(GL_NORMAL_ARRAY);
|
||||||
|
|
||||||
|
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
||||||
|
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
PointRenderVisitor visitor(lod);
|
||||||
|
data.guide(visitor);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
|
||||||
|
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
||||||
|
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glDisableClientState(GL_COLOR_ARRAY);
|
||||||
|
glDisableClientState(GL_NORMAL_ARRAY);
|
||||||
|
|
||||||
|
_program.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramObject PointMetavoxelRendererImplementation::_program;
|
||||||
|
int PointMetavoxelRendererImplementation::_pointScaleLocation;
|
||||||
|
|
||||||
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 };
|
||||||
glClipPlane(plane, coefficients);
|
glClipPlane(plane, coefficients);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,59 +43,100 @@ 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);
|
||||||
public:
|
|
||||||
glm::vec4 vertex;
|
|
||||||
quint8 color[4];
|
|
||||||
quint8 normal[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
class SimulateVisitor : public SpannerVisitor {
|
AttributePointer _pointBufferAttribute;
|
||||||
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:
|
MetavoxelLOD _lod;
|
||||||
QVector<Point>& _points;
|
QReadWriteLock _lodLock;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Describes contents of a point in a point buffer.
|
||||||
|
class BufferPoint {
|
||||||
|
public:
|
||||||
|
glm::vec4 vertex;
|
||||||
|
quint8 color[3];
|
||||||
|
quint8 normal[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QVector<BufferPoint> BufferPointVector;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(BufferPointVector)
|
||||||
|
|
||||||
/// A client session associated with a single server.
|
/// A client session associated with a single server.
|
||||||
class MetavoxelSystemClient : public MetavoxelClient {
|
class MetavoxelSystemClient : public MetavoxelClient {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
void MetavoxelClientManager::init() {
|
||||||
updateClient(client);
|
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) {
|
||||||
client->update();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager) :
|
MetavoxelUpdater::MetavoxelUpdater(MetavoxelClientManager* clientManager) :
|
||||||
|
_clientManager(clientManager),
|
||||||
|
_sendTimer(this) {
|
||||||
|
|
||||||
|
_sendTimer.setSingleShot(true);
|
||||||
|
connect(&_sendTimer, &QTimer::timeout, this, &MetavoxelUpdater::sendUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int SEND_INTERVAL = 33;
|
||||||
|
|
||||||
|
void MetavoxelUpdater::start() {
|
||||||
|
_lastSend = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
_sendTimer.start(SEND_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetavoxelUpdater::addClient(QObject* client) {
|
||||||
|
_clients.insert(static_cast<MetavoxelClient*>(client));
|
||||||
|
connect(client, &QObject::destroyed, this, &MetavoxelUpdater::removeClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetavoxelUpdater::applyEdit(const MetavoxelEditMessage& edit, bool reliable) {
|
||||||
|
// apply to all clients
|
||||||
|
foreach (MetavoxelClient* client, _clients) {
|
||||||
|
client->applyEdit(edit, reliable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetavoxelUpdater::getStats(QObject* receiver, const QByteArray& method) {
|
||||||
|
int internal = 0, leaves = 0;
|
||||||
|
int sendProgress = 0, sendTotal = 0;
|
||||||
|
int receiveProgress = 0, receiveTotal = 0;
|
||||||
|
foreach (MetavoxelClient* client, _clients) {
|
||||||
|
client->getData().countNodes(internal, leaves, _lod);
|
||||||
|
client->getSequencer().addReliableChannelStats(sendProgress, sendTotal, receiveProgress, receiveTotal);
|
||||||
|
}
|
||||||
|
QMetaObject::invokeMethod(receiver, method.constData(), Q_ARG(int, internal), Q_ARG(int, leaves), Q_ARG(int, sendProgress),
|
||||||
|
Q_ARG(int, sendTotal), Q_ARG(int, receiveProgress), Q_ARG(int, receiveTotal));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetavoxelUpdater::sendUpdates() {
|
||||||
|
// get the latest LOD from the client manager
|
||||||
|
_lod = _clientManager->getLOD();
|
||||||
|
|
||||||
|
// send updates for all clients
|
||||||
|
foreach (MetavoxelClient* client, _clients) {
|
||||||
|
client->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
// restart the send timer
|
||||||
|
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
int elapsed = now - _lastSend;
|
||||||
|
_lastSend = now;
|
||||||
|
|
||||||
|
_sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetavoxelUpdater::removeClient(QObject* client) {
|
||||||
|
_clients.remove(static_cast<MetavoxelClient*>(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelUpdater* updater) :
|
||||||
Endpoint(node, new PacketRecord(), new PacketRecord()),
|
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 {
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
/// Returns a copy of the most recent data. This function *is* thread-safe.
|
||||||
|
MetavoxelData getDataCopy();
|
||||||
|
|
||||||
void guide(MetavoxelVisitor& visitor);
|
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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(""));
|
||||||
|
|
Loading…
Reference in a new issue