mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 16:55:07 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into authentication
This commit is contained in:
commit
6681754994
38 changed files with 1156 additions and 272 deletions
|
@ -95,6 +95,8 @@ MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const SharedNodePoin
|
|||
connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&)));
|
||||
connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int)));
|
||||
connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleMessage(const QVariant&)));
|
||||
connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)),
|
||||
SLOT(handleMessage(const QVariant&)));
|
||||
|
||||
// insert the baseline send record
|
||||
SendRecord record = { 0 };
|
||||
|
|
|
@ -921,7 +921,6 @@ function mousePressEvent(event) {
|
|||
}
|
||||
|
||||
voxelDetails = calculateVoxelFromIntersection(intersection,"add");
|
||||
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
|
||||
Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s,
|
||||
newColor.red, newColor.green, newColor.blue);
|
||||
lastVoxelPosition = { x: voxelDetails.x, y: voxelDetails.y, z: voxelDetails.z };
|
||||
|
|
|
@ -2234,8 +2234,7 @@ void Application::updateShadowMap() {
|
|||
glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
// store view matrix without translation, which we'll use for precision-sensitive objects
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix);
|
||||
_viewMatrixTranslation = glm::vec3();
|
||||
updateUntranslatedViewMatrix();
|
||||
|
||||
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
|
||||
_particles.render();
|
||||
|
@ -2320,8 +2319,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
glRotatef(-glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
// store view matrix without translation, which we'll use for precision-sensitive objects
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix);
|
||||
_viewMatrixTranslation = -whichCamera.getPosition();
|
||||
updateUntranslatedViewMatrix(-whichCamera.getPosition());
|
||||
|
||||
glTranslatef(_viewMatrixTranslation.x, _viewMatrixTranslation.y, _viewMatrixTranslation.z);
|
||||
|
||||
|
@ -2452,6 +2450,11 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) {
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix);
|
||||
_viewMatrixTranslation = viewMatrixTranslation;
|
||||
}
|
||||
|
||||
void Application::loadTranslatedViewMatrix(const glm::vec3& translation) {
|
||||
glLoadMatrixf((const GLfloat*)&_untranslatedViewMatrix);
|
||||
glTranslatef(translation.x + _viewMatrixTranslation.x, translation.y + _viewMatrixTranslation.y,
|
||||
|
@ -3249,9 +3252,12 @@ void Application::toggleRunningScriptsWidget()
|
|||
|
||||
void Application::uploadFST(bool isHead) {
|
||||
ModelUploader* uploader = new ModelUploader(isHead);
|
||||
if (uploader->zip()) {
|
||||
uploader->send();
|
||||
}
|
||||
QThread* thread = new QThread();
|
||||
thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit()));
|
||||
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
|
||||
uploader->connect(thread, SIGNAL(started()), SLOT(send()));
|
||||
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void Application::uploadHead() {
|
||||
|
@ -3278,6 +3284,7 @@ void Application::loadScript(const QString& scriptName) {
|
|||
// we can use the same ones from the application.
|
||||
scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
|
||||
scriptEngine->getVoxelsScriptingInterface()->setVoxelTree(_voxels.getTree());
|
||||
scriptEngine->getVoxelsScriptingInterface()->setUndoStack(&_undoStack);
|
||||
scriptEngine->getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
|
||||
scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());
|
||||
|
||||
|
|
|
@ -14,14 +14,15 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QAction>
|
||||
#include <QImage>
|
||||
#include <QSettings>
|
||||
#include <QTouchEvent>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <QPointer>
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
#include <QSet>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QTouchEvent>
|
||||
#include <QUndoStack>
|
||||
|
||||
#include <NetworkPacket.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -176,6 +177,7 @@ public:
|
|||
Visage* getVisage() { return &_visage; }
|
||||
SixenseManager* getSixenseManager() { return &_sixenseManager; }
|
||||
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
|
||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||
|
||||
/// if you need to access the application settings, use lockSettings()/unlockSettings()
|
||||
QSettings* lockSettings() { _settingsMutex.lock(); return _settings; }
|
||||
|
@ -203,6 +205,10 @@ public:
|
|||
|
||||
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false);
|
||||
|
||||
/// Stores the current modelview matrix as the untranslated view matrix to use for transforms and the supplied vector as
|
||||
/// the view matrix translation.
|
||||
void updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation = glm::vec3());
|
||||
|
||||
/// Loads a view matrix that incorporates the specified model translation without the precision issues that can
|
||||
/// result from matrix multiplication at high translation magnitudes.
|
||||
void loadTranslatedViewMatrix(const glm::vec3& translation);
|
||||
|
@ -362,6 +368,8 @@ private:
|
|||
QMutex _settingsMutex;
|
||||
QSettings* _settings;
|
||||
|
||||
QUndoStack _undoStack;
|
||||
|
||||
glm::vec3 _gravity;
|
||||
|
||||
// Frame Rate Measurement
|
||||
|
|
|
@ -161,6 +161,15 @@ Menu::Menu() :
|
|||
|
||||
|
||||
QMenu* editMenu = addMenu("Edit");
|
||||
|
||||
QUndoStack* undoStack = Application::getInstance()->getUndoStack();
|
||||
QAction* undoAction = undoStack->createUndoAction(editMenu);
|
||||
undoAction->setShortcut(Qt::CTRL | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, undoAction);
|
||||
|
||||
QAction* redoAction = undoStack->createRedoAction(editMenu);
|
||||
redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, redoAction);
|
||||
|
||||
addActionToQMenuAndActionHash(editMenu,
|
||||
MenuOption::Preferences,
|
||||
|
@ -183,7 +192,7 @@ Menu::Menu() :
|
|||
#ifdef HAVE_QXMPP
|
||||
_chatAction = addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::Chat,
|
||||
Qt::Key_Return,
|
||||
0,
|
||||
this,
|
||||
SLOT(showChat()));
|
||||
|
||||
|
@ -620,6 +629,41 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
|||
return action;
|
||||
}
|
||||
|
||||
QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
QAction::MenuRole role,
|
||||
int menuItemLocation) {
|
||||
QAction* actionBefore = NULL;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
}
|
||||
|
||||
if (!actionName.isEmpty()) {
|
||||
action->setText(actionName);
|
||||
}
|
||||
|
||||
if (shortcut != 0) {
|
||||
action->setShortcut(shortcut);
|
||||
}
|
||||
|
||||
if (role != QAction::NoRole) {
|
||||
action->setMenuRole(role);
|
||||
}
|
||||
|
||||
if (!actionBefore) {
|
||||
destinationMenu->addAction(action);
|
||||
} else {
|
||||
destinationMenu->insertAction(actionBefore, action);
|
||||
}
|
||||
|
||||
_actionHash.insert(action->text(), action);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
|
|
|
@ -102,7 +102,13 @@ public:
|
|||
const char* member = NULL,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName = QString(),
|
||||
const QKeySequence& shortcut = 0,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
void removeAction(QMenu* menu, const QString& actionName);
|
||||
|
||||
bool goToDestination(QString destination);
|
||||
|
@ -219,65 +225,65 @@ private:
|
|||
namespace MenuOption {
|
||||
const QString AboutApp = "About Interface";
|
||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||
const QString Avatars = "Avatars";
|
||||
const QString Atmosphere = "Atmosphere";
|
||||
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
|
||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||
const QString AudioToneInjection = "Inject Test Tone";
|
||||
const QString Avatars = "Avatars";
|
||||
const QString Bandwidth = "Bandwidth Display";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BuckyBalls = "Bucky Balls";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ChatCircling = "Chat Circling";
|
||||
const QString Collisions = "Collisions";
|
||||
const QString CollideWithAvatars = "Collide With Avatars";
|
||||
const QString CollideWithEnvironment = "Collide With World Boundaries";
|
||||
const QString CollideWithParticles = "Collide With Particles";
|
||||
const QString CollideWithVoxels = "Collide With Voxels";
|
||||
const QString CollideWithEnvironment = "Collide With World Boundaries";
|
||||
const QString Collisions = "Collisions";
|
||||
const QString CullSharedFaces = "Cull Shared Voxel Faces";
|
||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
||||
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
|
||||
const QString DisplayFrustum = "Display Frustum";
|
||||
const QString DisplayHands = "Display Hands";
|
||||
const QString DisplayHandTargets = "Display Hand Targets";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||
const QString AudioToneInjection = "Inject Test Tone";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
|
||||
const QString HeadMouse = "Head Mouse";
|
||||
const QString HandsCollideWithSelf = "Collide With Self";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString Faceshift = "Faceshift";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
const QString FirstPerson = "First Person";
|
||||
const QString FrameTimer = "Show Timer";
|
||||
const QString FrustumRenderMode = "Render Mode";
|
||||
const QString Fullscreen = "Fullscreen";
|
||||
const QString FullscreenMirror = "Fullscreen Mirror";
|
||||
const QString GlowMode = "Cycle Glow Mode";
|
||||
const QString GoHome = "Go Home";
|
||||
const QString GoTo = "Go To...";
|
||||
const QString GoToDomain = "Go To Domain...";
|
||||
const QString GoToLocation = "Go To Location...";
|
||||
const QString NameLocation = "Name this location";
|
||||
const QString GoTo = "Go To...";
|
||||
const QString Gravity = "Use Gravity";
|
||||
const QString HandsCollideWithSelf = "Collide With Self";
|
||||
const QString HeadMouse = "Head Mouse";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IncreaseVoxelSize = "Increase Voxel Size";
|
||||
const QString GoHome = "Go Home";
|
||||
const QString Gravity = "Use Gravity";
|
||||
const QString LoadScript = "Open and Run Script File...";
|
||||
const QString LoadScriptURL = "Open and Run Script from URL...";
|
||||
const QString LodTools = "LOD Tools";
|
||||
const QString Log = "Log";
|
||||
const QString Login = "Login";
|
||||
const QString Logout = "Logout";
|
||||
const QString LookAtVectors = "Look-at Vectors";
|
||||
const QString MetavoxelEditor = "Metavoxel Editor...";
|
||||
const QString Chat = "Chat...";
|
||||
const QString Metavoxels = "Metavoxels";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString MoveWithLean = "Move with Lean";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString NameLocation = "Name this location";
|
||||
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
|
||||
const QString OctreeStats = "Voxel and Particle Statistics";
|
||||
const QString OffAxisProjection = "Off-Axis Projection";
|
||||
const QString OldVoxelCullingMode = "Old Voxel Culling Mode";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString LoadScript = "Open and Run Script File...";
|
||||
const QString LoadScriptURL = "Open and Run Script from URL...";
|
||||
const QString Oscilloscope = "Audio Oscilloscope";
|
||||
const QString Pair = "Pair";
|
||||
const QString Particles = "Particles";
|
||||
|
@ -285,30 +291,30 @@ namespace MenuOption {
|
|||
const QString PipelineWarnings = "Show Render Pipeline Warnings";
|
||||
const QString PlaySlaps = "Play Slaps";
|
||||
const QString Preferences = "Preferences...";
|
||||
const QString Quit = "Quit";
|
||||
const QString ReloadAllScripts = "Reload All Scripts";
|
||||
const QString RenderSkeletonCollisionShapes = "Skeleton Collision Shapes";
|
||||
const QString RenderHeadCollisionShapes = "Head Collision Shapes";
|
||||
const QString RenderBoundingCollisionShapes = "Bounding Collision Shapes";
|
||||
const QString RenderHeadCollisionShapes = "Head Collision Shapes";
|
||||
const QString RenderSkeletonCollisionShapes = "Skeleton Collision Shapes";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString RunningScripts = "Running Scripts";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString SettingsExport = "Export Settings";
|
||||
const QString SettingsImport = "Import Settings";
|
||||
const QString Shadows = "Shadows";
|
||||
const QString SettingsExport = "Export Settings";
|
||||
const QString ShowCulledSharedFaces = "Show Culled Shared Voxel Faces";
|
||||
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||
const QString Stars = "Stars";
|
||||
const QString Stats = "Stats";
|
||||
const QString StopAllScripts = "Stop All Scripts";
|
||||
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||
const QString TestPing = "Test Ping";
|
||||
const QString TransmitterDrive = "Transmitter Drive";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString UploadHead = "Upload Head Model";
|
||||
const QString UploadSkeleton = "Upload Skeleton Model";
|
||||
const QString Visage = "Visage";
|
||||
const QString Quit = "Quit";
|
||||
const QString Voxels = "Voxels";
|
||||
const QString VoxelMode = "Cycle Voxel Mode";
|
||||
const QString OctreeStats = "Voxel and Particle Statistics";
|
||||
const QString Voxels = "Voxels";
|
||||
const QString VoxelTextures = "Voxel Textures";
|
||||
}
|
||||
|
||||
|
|
|
@ -69,13 +69,13 @@ SharedObjectPointer MetavoxelSystem::findFirstRaySpannerIntersection(
|
|||
return closestSpanner;
|
||||
}
|
||||
|
||||
void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit) {
|
||||
void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit, bool reliable) {
|
||||
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);
|
||||
client->applyEdit(edit, reliable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ MetavoxelSystem::RenderVisitor::RenderVisitor() :
|
|||
}
|
||||
|
||||
bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
|
||||
spanner->getRenderer()->render(1.0f, clipMinimum, clipSize);
|
||||
spanner->getRenderer()->render(1.0f, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -267,12 +267,17 @@ void MetavoxelClient::guide(MetavoxelVisitor& visitor) {
|
|||
_data.guide(visitor);
|
||||
}
|
||||
|
||||
void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) {
|
||||
// apply immediately to local tree
|
||||
edit.apply(_data, _sequencer.getWeakSharedObjectHash());
|
||||
void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) {
|
||||
if (reliable) {
|
||||
_sequencer.getReliableOutputChannel()->sendMessage(QVariant::fromValue(edit));
|
||||
|
||||
} else {
|
||||
// apply immediately to local tree
|
||||
edit.apply(_data, _sequencer.getWeakSharedObjectHash());
|
||||
|
||||
// start sending it out
|
||||
_sequencer.sendHighPriorityMessage(QVariant::fromValue(edit));
|
||||
// start sending it out
|
||||
_sequencer.sendHighPriorityMessage(QVariant::fromValue(edit));
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelClient::simulate(float deltaTime) {
|
||||
|
@ -290,11 +295,13 @@ void MetavoxelClient::simulate(float deltaTime) {
|
|||
int MetavoxelClient::parseData(const QByteArray& packet) {
|
||||
// process through sequencer
|
||||
QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet));
|
||||
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size());
|
||||
return packet.size();
|
||||
}
|
||||
|
||||
void MetavoxelClient::sendData(const QByteArray& data) {
|
||||
NodeList::getInstance()->writeDatagram(data, _node);
|
||||
Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size());
|
||||
}
|
||||
|
||||
void MetavoxelClient::readPacket(Bitstream& in) {
|
||||
|
@ -340,9 +347,9 @@ static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
|||
glEnable(plane);
|
||||
}
|
||||
|
||||
void ClippedRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) {
|
||||
void ClippedRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize) {
|
||||
if (clipSize == 0.0f) {
|
||||
renderUnclipped(alpha);
|
||||
renderUnclipped(alpha, mode);
|
||||
return;
|
||||
}
|
||||
enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipSize);
|
||||
|
@ -352,7 +359,7 @@ void ClippedRenderer::render(float alpha, const glm::vec3& clipMinimum, float cl
|
|||
enableClipPlane(GL_CLIP_PLANE4, 0.0f, 0.0f, -1.0f, clipMinimum.z + clipSize);
|
||||
enableClipPlane(GL_CLIP_PLANE5, 0.0f, 0.0f, 1.0f, -clipMinimum.z);
|
||||
|
||||
renderUnclipped(alpha);
|
||||
renderUnclipped(alpha, mode);
|
||||
|
||||
glDisable(GL_CLIP_PLANE0);
|
||||
glDisable(GL_CLIP_PLANE1);
|
||||
|
@ -365,9 +372,9 @@ void ClippedRenderer::render(float alpha, const glm::vec3& clipMinimum, float cl
|
|||
SphereRenderer::SphereRenderer() {
|
||||
}
|
||||
|
||||
void SphereRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) {
|
||||
void SphereRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize) {
|
||||
if (clipSize == 0.0f) {
|
||||
renderUnclipped(alpha);
|
||||
renderUnclipped(alpha, mode);
|
||||
return;
|
||||
}
|
||||
// slight performance optimization: don't render if clip bounds are entirely within sphere
|
||||
|
@ -376,13 +383,13 @@ void SphereRenderer::render(float alpha, const glm::vec3& clipMinimum, float cli
|
|||
for (int i = 0; i < Box::VERTEX_COUNT; i++) {
|
||||
const float CLIP_PROPORTION = 0.95f;
|
||||
if (glm::distance(sphere->getTranslation(), clipBox.getVertex(i)) >= sphere->getScale() * CLIP_PROPORTION) {
|
||||
ClippedRenderer::render(alpha, clipMinimum, clipSize);
|
||||
ClippedRenderer::render(alpha, mode, clipMinimum, clipSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SphereRenderer::renderUnclipped(float alpha) {
|
||||
void SphereRenderer::renderUnclipped(float alpha, Mode mode) {
|
||||
Sphere* sphere = static_cast<Sphere*>(parent());
|
||||
const QColor& color = sphere->getColor();
|
||||
glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha);
|
||||
|
@ -390,7 +397,7 @@ void SphereRenderer::renderUnclipped(float alpha) {
|
|||
glPushMatrix();
|
||||
const glm::vec3& translation = sphere->getTranslation();
|
||||
glTranslatef(translation.x, translation.y, translation.z);
|
||||
glm::quat rotation = glm::quat(glm::radians(sphere->getRotation()));
|
||||
glm::quat rotation = sphere->getRotation();
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
|
||||
|
||||
|
@ -413,7 +420,7 @@ void StaticModelRenderer::init(Spanner* spanner) {
|
|||
applyURL(staticModel->getURL());
|
||||
|
||||
connect(spanner, SIGNAL(translationChanged(const glm::vec3&)), SLOT(applyTranslation(const glm::vec3&)));
|
||||
connect(spanner, SIGNAL(rotationChanged(const glm::vec3&)), SLOT(applyRotation(const glm::vec3&)));
|
||||
connect(spanner, SIGNAL(rotationChanged(const glm::quat&)), SLOT(applyRotation(const glm::quat&)));
|
||||
connect(spanner, SIGNAL(scaleChanged(float)), SLOT(applyScale(float)));
|
||||
connect(spanner, SIGNAL(urlChanged(const QUrl&)), SLOT(applyURL(const QUrl&)));
|
||||
}
|
||||
|
@ -430,7 +437,20 @@ void StaticModelRenderer::simulate(float deltaTime) {
|
|||
_model->simulate(deltaTime);
|
||||
}
|
||||
|
||||
void StaticModelRenderer::renderUnclipped(float alpha) {
|
||||
void StaticModelRenderer::renderUnclipped(float alpha, Mode mode) {
|
||||
switch (mode) {
|
||||
case DIFFUSE_MODE:
|
||||
_model->render(alpha, Model::DIFFUSE_RENDER_MODE);
|
||||
break;
|
||||
|
||||
case NORMAL_MODE:
|
||||
_model->render(alpha, Model::NORMAL_RENDER_MODE);
|
||||
break;
|
||||
|
||||
default:
|
||||
_model->render(alpha);
|
||||
break;
|
||||
}
|
||||
_model->render(alpha);
|
||||
}
|
||||
|
||||
|
@ -443,8 +463,8 @@ void StaticModelRenderer::applyTranslation(const glm::vec3& translation) {
|
|||
_model->setTranslation(translation);
|
||||
}
|
||||
|
||||
void StaticModelRenderer::applyRotation(const glm::vec3& rotation) {
|
||||
_model->setRotation(glm::quat(glm::radians(rotation)));
|
||||
void StaticModelRenderer::applyRotation(const glm::quat& rotation) {
|
||||
_model->setRotation(rotation);
|
||||
}
|
||||
|
||||
void StaticModelRenderer::applyScale(float scale) {
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const AttributePointer& attribute, float& distance);
|
||||
|
||||
void applyEdit(const MetavoxelEditMessage& edit);
|
||||
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void render();
|
||||
|
@ -98,7 +98,7 @@ public:
|
|||
|
||||
void guide(MetavoxelVisitor& visitor);
|
||||
|
||||
void applyEdit(const MetavoxelEditMessage& edit);
|
||||
void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
|
||||
|
@ -147,11 +147,11 @@ class ClippedRenderer : public SpannerRenderer {
|
|||
|
||||
public:
|
||||
|
||||
virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize);
|
||||
virtual void render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void renderUnclipped(float alpha) = 0;
|
||||
virtual void renderUnclipped(float alpha, Mode mode) = 0;
|
||||
};
|
||||
|
||||
/// Renders spheres.
|
||||
|
@ -162,11 +162,11 @@ public:
|
|||
|
||||
Q_INVOKABLE SphereRenderer();
|
||||
|
||||
virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize);
|
||||
virtual void render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void renderUnclipped(float alpha);
|
||||
virtual void renderUnclipped(float alpha, Mode mode);
|
||||
};
|
||||
|
||||
/// Renders static models.
|
||||
|
@ -184,12 +184,12 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
virtual void renderUnclipped(float alpha);
|
||||
virtual void renderUnclipped(float alpha, Mode mode);
|
||||
|
||||
private slots:
|
||||
|
||||
void applyTranslation(const glm::vec3& translation);
|
||||
void applyRotation(const glm::vec3& eulerAngles); // eulerAngles are in degrees
|
||||
void applyRotation(const glm::quat& rotation);
|
||||
void applyScale(float scale);
|
||||
void applyURL(const QUrl& url);
|
||||
|
||||
|
|
|
@ -326,8 +326,10 @@ void Avatar::renderBody(RenderMode renderMode) {
|
|||
renderBillboard();
|
||||
return;
|
||||
}
|
||||
_skeletonModel.render(1.0f, renderMode == SHADOW_RENDER_MODE);
|
||||
getHead()->render(1.0f, renderMode == SHADOW_RENDER_MODE);
|
||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
_skeletonModel.render(1.0f, modelRenderMode);
|
||||
getHead()->render(1.0f, modelRenderMode);
|
||||
getHand()->render(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -180,8 +180,8 @@ void Head::relaxLean(float deltaTime) {
|
|||
_deltaLeanForward *= relaxationFactor;
|
||||
}
|
||||
|
||||
void Head::render(float alpha, bool forShadowMap) {
|
||||
if (_faceModel.render(alpha, forShadowMap) && _renderLookatVectors) {
|
||||
void Head::render(float alpha, Model::RenderMode mode) {
|
||||
if (_faceModel.render(alpha, mode) && _renderLookatVectors) {
|
||||
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, bool isMine, bool billboard = false);
|
||||
void render(float alpha, bool forShadowMap);
|
||||
void render(float alpha, Model::RenderMode mode);
|
||||
void setScale(float scale);
|
||||
void setPosition(glm::vec3 position) { _position = position; }
|
||||
void setGravity(glm::vec3 gravity) { _gravity = gravity; }
|
||||
|
|
|
@ -614,14 +614,16 @@ void MyAvatar::renderBody(RenderMode renderMode) {
|
|||
}
|
||||
|
||||
// Render the body's voxels and head
|
||||
_skeletonModel.render(1.0f, renderMode == SHADOW_RENDER_MODE);
|
||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
_skeletonModel.render(1.0f, modelRenderMode);
|
||||
|
||||
// Render head so long as the camera isn't inside it
|
||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.40f;
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
if (renderMode != NORMAL_RENDER_MODE || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) >
|
||||
RENDER_HEAD_CUTOFF_DISTANCE * _scale)) {
|
||||
getHead()->render(1.0f, renderMode == SHADOW_RENDER_MODE);
|
||||
getHead()->render(1.0f, modelRenderMode);
|
||||
}
|
||||
getHand()->render(true);
|
||||
}
|
||||
|
|
|
@ -58,7 +58,10 @@ Model::SkinLocations Model::_skinShadowLocations;
|
|||
|
||||
void Model::setScale(const glm::vec3& scale) {
|
||||
glm::vec3 deltaScale = _scale - scale;
|
||||
if (glm::length2(deltaScale) > EPSILON) {
|
||||
|
||||
// decreased epsilon because this wasn't handling scale changes of 0.01
|
||||
const float SMALLER_EPSILON = EPSILON * 0.0001f;
|
||||
if (glm::length2(deltaScale) > SMALLER_EPSILON) {
|
||||
_scale = scale;
|
||||
rebuildShapes();
|
||||
}
|
||||
|
@ -281,10 +284,10 @@ bool Model::updateGeometry() {
|
|||
return needFullUpdate;
|
||||
}
|
||||
|
||||
bool Model::render(float alpha, bool forShadowMap) {
|
||||
bool Model::render(float alpha, RenderMode mode) {
|
||||
// render the attachments
|
||||
foreach (Model* attachment, _attachments) {
|
||||
attachment->render(alpha);
|
||||
attachment->render(alpha, mode);
|
||||
}
|
||||
if (_meshStates.isEmpty()) {
|
||||
return false;
|
||||
|
@ -305,20 +308,24 @@ bool Model::render(float alpha, bool forShadowMap) {
|
|||
|
||||
glDisable(GL_COLOR_MATERIAL);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
if (mode == DIFFUSE_RENDER_MODE || mode == NORMAL_RENDER_MODE) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
// render opaque meshes with alpha testing
|
||||
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_GREATER, 0.5f * alpha);
|
||||
|
||||
renderMeshes(alpha, forShadowMap, false);
|
||||
renderMeshes(alpha, mode, false);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
|
||||
// render translucent meshes afterwards, with back face culling
|
||||
// render translucent meshes afterwards
|
||||
|
||||
renderMeshes(alpha, forShadowMap, true);
|
||||
renderMeshes(alpha, mode, true);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
|
@ -1112,7 +1119,7 @@ void Model::deleteGeometry() {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::renderMeshes(float alpha, bool forShadowMap, bool translucent) {
|
||||
void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
|
||||
|
||||
|
@ -1137,7 +1144,7 @@ void Model::renderMeshes(float alpha, bool forShadowMap, bool translucent) {
|
|||
ProgramObject* program = &_program;
|
||||
ProgramObject* skinProgram = &_skinProgram;
|
||||
SkinLocations* skinLocations = &_skinLocations;
|
||||
if (forShadowMap) {
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
program = &_shadowProgram;
|
||||
skinProgram = &_skinShadowProgram;
|
||||
skinLocations = &_skinShadowLocations;
|
||||
|
@ -1175,7 +1182,7 @@ void Model::renderMeshes(float alpha, bool forShadowMap, bool translucent) {
|
|||
}
|
||||
|
||||
if (mesh.blendshapes.isEmpty()) {
|
||||
if (!(mesh.tangents.isEmpty() || forShadowMap)) {
|
||||
if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) {
|
||||
activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3);
|
||||
activeProgram->enableAttributeArray(tangentLocation);
|
||||
}
|
||||
|
@ -1185,7 +1192,7 @@ void Model::renderMeshes(float alpha, bool forShadowMap, bool translucent) {
|
|||
(mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3)));
|
||||
|
||||
} else {
|
||||
if (!(mesh.tangents.isEmpty() || forShadowMap)) {
|
||||
if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) {
|
||||
activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, 0, 3);
|
||||
activeProgram->enableAttributeArray(tangentLocation);
|
||||
}
|
||||
|
@ -1214,7 +1221,7 @@ void Model::renderMeshes(float alpha, bool forShadowMap, bool translucent) {
|
|||
continue;
|
||||
}
|
||||
// apply material properties
|
||||
if (forShadowMap) {
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
} else {
|
||||
|
@ -1255,7 +1262,7 @@ void Model::renderMeshes(float alpha, bool forShadowMap, bool translucent) {
|
|||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
}
|
||||
|
||||
if (!(mesh.tangents.isEmpty() || forShadowMap)) {
|
||||
if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
|
|
@ -57,7 +57,10 @@ public:
|
|||
void init();
|
||||
void reset();
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
bool render(float alpha = 1.0f, bool forShadowMap = false);
|
||||
|
||||
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
|
||||
|
||||
bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE);
|
||||
|
||||
/// Sets the URL of the model to render.
|
||||
/// \param fallback the URL of a fallback model to render if the requested model fails to load
|
||||
|
@ -254,7 +257,7 @@ private:
|
|||
|
||||
void applyNextGeometry();
|
||||
void deleteGeometry();
|
||||
void renderMeshes(float alpha, bool forShadowMap, bool translucent);
|
||||
void renderMeshes(float alpha, RenderMode mode, bool translucent);
|
||||
QVector<JointState> createJointStates(const FBXGeometry& geometry);
|
||||
|
||||
QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base
|
||||
|
|
|
@ -42,7 +42,8 @@ namespace { // .cpp-local
|
|||
BandwidthMeter::ChannelInfo BandwidthMeter::_CHANNELS[] = {
|
||||
{ "Audio" , "Kbps", 8000.0 / 1024.0, 0x33cc99ff },
|
||||
{ "Avatars" , "Kbps", 8000.0 / 1024.0, 0xffef40c0 },
|
||||
{ "Voxels" , "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0 }
|
||||
{ "Voxels" , "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0 },
|
||||
{ "Metavoxels", "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0 }
|
||||
};
|
||||
|
||||
BandwidthMeter::BandwidthMeter() :
|
||||
|
|
|
@ -30,11 +30,11 @@ public:
|
|||
bool isWithinArea(int x, int y, int screenWidth, int screenHeight);
|
||||
|
||||
// Number of channels / streams.
|
||||
static size_t const N_CHANNELS = 3;
|
||||
static size_t const N_CHANNELS = 4;
|
||||
static size_t const N_STREAMS = N_CHANNELS * 2;
|
||||
|
||||
// Channel usage.
|
||||
enum ChannelIndex { AUDIO, AVATARS, VOXELS };
|
||||
enum ChannelIndex { AUDIO, AVATARS, VOXELS, METAVOXELS };
|
||||
|
||||
// Meta information held for a communication channel (bidirectional).
|
||||
struct ChannelInfo {
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
// Created by Andrzej Kapolka on 1/21/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
|
@ -13,8 +16,12 @@
|
|||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QMetaProperty>
|
||||
#include <QMetaProperty>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QPushButton>
|
||||
#include <QRunnable>
|
||||
#include <QScrollArea>
|
||||
#include <QThreadPool>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <AttributeRegistry.h>
|
||||
|
@ -565,7 +572,7 @@ void PlaceSpannerTool::render() {
|
|||
}
|
||||
Spanner* spanner = static_cast<Spanner*>(_editor->getValue().value<SharedObjectPointer>().data());
|
||||
const float SPANNER_ALPHA = 0.25f;
|
||||
spanner->getRenderer()->render(SPANNER_ALPHA, glm::vec3(), 0.0f);
|
||||
spanner->getRenderer()->render(SPANNER_ALPHA, SpannerRenderer::DEFAULT_MODE, glm::vec3(), 0.0f);
|
||||
}
|
||||
|
||||
bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const {
|
||||
|
@ -582,20 +589,18 @@ bool PlaceSpannerTool::eventFilter(QObject* watched, QEvent* event) {
|
|||
|
||||
void PlaceSpannerTool::place() {
|
||||
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
|
||||
if (!attribute) {
|
||||
return;
|
||||
if (attribute) {
|
||||
applyEdit(attribute, _editor->getValue().value<SharedObjectPointer>());
|
||||
}
|
||||
SharedObjectPointer spanner = _editor->getValue().value<SharedObjectPointer>();
|
||||
MetavoxelEditMessage message = { createEdit(attribute, spanner) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message);
|
||||
}
|
||||
|
||||
InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) :
|
||||
PlaceSpannerTool(editor, "Insert Spanner", "Insert") {
|
||||
}
|
||||
|
||||
QVariant InsertSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
|
||||
return QVariant::fromValue(InsertSpannerEdit(attribute, spanner));
|
||||
void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message);
|
||||
}
|
||||
|
||||
RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) :
|
||||
|
@ -654,6 +659,230 @@ bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const {
|
|||
return attribute == AttributeRegistry::getInstance()->getSpannersAttribute();
|
||||
}
|
||||
|
||||
QVariant SetSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
|
||||
return QVariant::fromValue(SetSpannerEdit(spanner));
|
||||
glm::quat DIRECTION_ROTATIONS[] = {
|
||||
rotationBetween(glm::vec3(-1.0f, 0.0f, 0.0f), IDENTITY_FRONT),
|
||||
rotationBetween(glm::vec3(1.0f, 0.0f, 0.0f), IDENTITY_FRONT),
|
||||
rotationBetween(glm::vec3(0.0f, -1.0f, 0.0f), IDENTITY_FRONT),
|
||||
rotationBetween(glm::vec3(0.0f, 1.0f, 0.0f), IDENTITY_FRONT),
|
||||
rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), IDENTITY_FRONT),
|
||||
rotationBetween(glm::vec3(0.0f, 0.0f, 1.0f), IDENTITY_FRONT) };
|
||||
|
||||
/// Represents a view from one direction of the spanner to be voxelized.
|
||||
class DirectionImages {
|
||||
public:
|
||||
QImage color;
|
||||
QVector<float> depth;
|
||||
glm::vec3 minima;
|
||||
glm::vec3 maxima;
|
||||
glm::vec3 scale;
|
||||
};
|
||||
|
||||
class Voxelizer : public QRunnable {
|
||||
public:
|
||||
|
||||
Voxelizer(float size, const Box& bounds, float granularity, const QVector<DirectionImages>& directionImages);
|
||||
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
|
||||
void voxelize(const glm::vec3& center);
|
||||
|
||||
float _size;
|
||||
Box _bounds;
|
||||
float _granularity;
|
||||
QVector<DirectionImages> _directionImages;
|
||||
};
|
||||
|
||||
Voxelizer::Voxelizer(float size, const Box& bounds, float granularity, const QVector<DirectionImages>& directionImages) :
|
||||
_size(size),
|
||||
_bounds(bounds),
|
||||
_granularity(granularity),
|
||||
_directionImages(directionImages) {
|
||||
}
|
||||
|
||||
void Voxelizer::run() {
|
||||
// voxelize separately each cell within the bounds
|
||||
float halfSize = _size * 0.5f;
|
||||
for (float x = _bounds.minimum.x + halfSize; x < _bounds.maximum.x; x += _size) {
|
||||
for (float y = _bounds.minimum.y + halfSize; y < _bounds.maximum.y; y += _size) {
|
||||
for (float z = _bounds.minimum.z + halfSize; z < _bounds.maximum.z; z += _size) {
|
||||
voxelize(glm::vec3(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VoxelizationVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
VoxelizationVisitor(const QVector<DirectionImages>& directionImages, const glm::vec3& center, float granularity);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
QVector<DirectionImages> _directionImages;
|
||||
glm::vec3 _center;
|
||||
float _granularity;
|
||||
};
|
||||
|
||||
VoxelizationVisitor::VoxelizationVisitor(const QVector<DirectionImages>& directionImages,
|
||||
const glm::vec3& center, float granularity) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() <<
|
||||
AttributeRegistry::getInstance()->getColorAttribute()),
|
||||
_directionImages(directionImages),
|
||||
_center(center),
|
||||
_granularity(granularity) {
|
||||
}
|
||||
|
||||
bool checkDisjoint(const DirectionImages& images, const glm::vec3& minimum, const glm::vec3& maximum, float extent) {
|
||||
for (int x = qMax(0, (int)minimum.x), xmax = qMin(images.color.width(), (int)maximum.x); x < xmax; x++) {
|
||||
for (int y = qMax(0, (int)minimum.y), ymax = qMin(images.color.height(), (int)maximum.y); y < ymax; y++) {
|
||||
float depth = 1.0f - images.depth.at(y * images.color.width() + x);
|
||||
if (depth - minimum.z >= -extent - EPSILON) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int VoxelizationVisitor::visit(MetavoxelInfo& info) {
|
||||
float halfSize = info.size * 0.5f;
|
||||
glm::vec3 center = info.minimum + _center + glm::vec3(halfSize, halfSize, halfSize);
|
||||
const float EXTENT_SCALE = 2.0f;
|
||||
if (info.size > _granularity) {
|
||||
for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) {
|
||||
glm::vec3 rotated = DIRECTION_ROTATIONS[i] * center;
|
||||
const DirectionImages& images = _directionImages.at(i);
|
||||
glm::vec3 relative = (rotated - images.minima) * images.scale;
|
||||
glm::vec3 extents = images.scale * halfSize;
|
||||
glm::vec3 minimum = relative - extents;
|
||||
glm::vec3 maximum = relative + extents;
|
||||
if (checkDisjoint(images, minimum, maximum, extents.z * EXTENT_SCALE)) {
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
}
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
QRgb closestColor;
|
||||
float closestDistance = FLT_MAX;
|
||||
for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) {
|
||||
glm::vec3 rotated = DIRECTION_ROTATIONS[i] * center;
|
||||
const DirectionImages& images = _directionImages.at(i);
|
||||
glm::vec3 relative = (rotated - images.minima) * images.scale;
|
||||
int x = qMax(qMin((int)glm::round(relative.x), images.color.width() - 1), 0);
|
||||
int y = qMax(qMin((int)glm::round(relative.y), images.color.height() - 1), 0);
|
||||
float depth = 1.0f - images.depth.at(y * images.color.width() + x);
|
||||
float distance = depth - relative.z;
|
||||
float extent = images.scale.z * halfSize * EXTENT_SCALE;
|
||||
if (distance < -extent - EPSILON) {
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
QRgb color = images.color.pixel(x, y);
|
||||
if (distance < extent + EPSILON) {
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<QRgb>(color));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (distance < closestDistance) {
|
||||
closestColor = color;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<QRgb>(closestColor));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
void Voxelizer::voxelize(const glm::vec3& center) {
|
||||
MetavoxelData data;
|
||||
data.setSize(_size);
|
||||
|
||||
VoxelizationVisitor visitor(_directionImages, center, _granularity);
|
||||
data.guide(visitor);
|
||||
|
||||
MetavoxelEditMessage edit = { QVariant::fromValue(SetDataEdit(
|
||||
center - glm::vec3(_size, _size, _size) * 0.5f, data, true)) };
|
||||
QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "applyEdit",
|
||||
Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, true));
|
||||
}
|
||||
|
||||
void SetSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
|
||||
Spanner* spannerData = static_cast<Spanner*>(spanner.data());
|
||||
Box bounds = spannerData->getBounds();
|
||||
float longestSide(qMax(bounds.getLongestSide(), spannerData->getPlacementGranularity()));
|
||||
float size = powf(2.0f, floorf(logf(longestSide) / logf(2.0f)));
|
||||
Box cellBounds(glm::floor(bounds.minimum / size) * size, glm::ceil(bounds.maximum / size) * size);
|
||||
|
||||
Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->bind();
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
|
||||
QVector<DirectionImages> directionImages;
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) {
|
||||
glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
glm::vec3 maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
for (int j = 0; j < Box::VERTEX_COUNT; j++) {
|
||||
glm::vec3 rotated = DIRECTION_ROTATIONS[i] * cellBounds.getVertex(j);
|
||||
minima = glm::min(minima, rotated);
|
||||
maxima = glm::max(maxima, rotated);
|
||||
}
|
||||
float renderGranularity = spannerData->getVoxelizationGranularity() / 4.0f;
|
||||
int width = glm::round((maxima.x - minima.x) / renderGranularity);
|
||||
int height = glm::round((maxima.y - minima.y) / renderGranularity);
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
glScissor(0, 0, width, height);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glLoadIdentity();
|
||||
glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glm::vec3 axis = glm::axis(DIRECTION_ROTATIONS[i]);
|
||||
glRotatef(glm::degrees(glm::angle(DIRECTION_ROTATIONS[i])), axis.x, axis.y, axis.z);
|
||||
|
||||
Application::getInstance()->setupWorldLight();
|
||||
|
||||
Application::getInstance()->updateUntranslatedViewMatrix();
|
||||
|
||||
spannerData->getRenderer()->render(1.0f, SpannerRenderer::DIFFUSE_MODE, glm::vec3(), 0.0f);
|
||||
|
||||
DirectionImages images = { QImage(width, height, QImage::Format_ARGB32),
|
||||
QVector<float>(width * height), minima, maxima, glm::vec3(width / (maxima.x - minima.x),
|
||||
height / (maxima.y - minima.y), 1.0f / (maxima.z - minima.z)) };
|
||||
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, images.color.bits());
|
||||
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, images.depth.data());
|
||||
directionImages.append(images);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->release();
|
||||
|
||||
glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height());
|
||||
|
||||
// send the images off to the lab for processing
|
||||
QThreadPool::globalInstance()->start(new Voxelizer(size, cellBounds,
|
||||
spannerData->getVoxelizationGranularity(), directionImages));
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) = 0;
|
||||
virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) = 0;
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -174,7 +174,7 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
|
||||
virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
|
||||
};
|
||||
|
||||
/// Allows removing a spanner from the scene.
|
||||
|
@ -217,7 +217,7 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
|
||||
virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__MetavoxelEditor__) */
|
||||
|
|
|
@ -144,6 +144,14 @@ void OwnedAttributeValue::mix(const AttributeValue& first, const AttributeValue&
|
|||
_value = _attribute->mix(first.getValue(), second.getValue(), alpha);
|
||||
}
|
||||
|
||||
void OwnedAttributeValue::blend(const AttributeValue& source, const AttributeValue& dest) {
|
||||
if (_attribute) {
|
||||
_attribute->destroy(_value);
|
||||
}
|
||||
_attribute = source.getAttribute();
|
||||
_value = _attribute->blend(source.getValue(), dest.getValue());
|
||||
}
|
||||
|
||||
OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) {
|
||||
if (_attribute) {
|
||||
_attribute->destroy(_value);
|
||||
|
@ -243,6 +251,19 @@ void* QRgbAttribute::mix(void* first, void* second, float alpha) const {
|
|||
glm::mix((float)qAlpha(firstValue), (float)qAlpha(secondValue), alpha)));
|
||||
}
|
||||
|
||||
const float EIGHT_BIT_MAXIMUM = 255.0f;
|
||||
|
||||
void* QRgbAttribute::blend(void* source, void* dest) const {
|
||||
QRgb sourceValue = decodeInline<QRgb>(source);
|
||||
QRgb destValue = decodeInline<QRgb>(dest);
|
||||
float alpha = qAlpha(sourceValue) / EIGHT_BIT_MAXIMUM;
|
||||
return encodeInline(qRgba(
|
||||
glm::mix((float)qRed(destValue), (float)qRed(sourceValue), alpha),
|
||||
glm::mix((float)qGreen(destValue), (float)qGreen(sourceValue), alpha),
|
||||
glm::mix((float)qBlue(destValue), (float)qBlue(sourceValue), alpha),
|
||||
glm::mix((float)qAlpha(destValue), (float)qAlpha(sourceValue), alpha)));
|
||||
}
|
||||
|
||||
void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const {
|
||||
return encodeInline((QRgb)value.toUInt32());
|
||||
}
|
||||
|
@ -287,6 +308,13 @@ void* PackedNormalAttribute::mix(void* first, void* second, float alpha) const {
|
|||
return encodeInline(packNormal(glm::normalize(glm::mix(firstNormal, secondNormal, alpha))));
|
||||
}
|
||||
|
||||
void* PackedNormalAttribute::blend(void* source, void* dest) const {
|
||||
QRgb sourceValue = decodeInline<QRgb>(source);
|
||||
QRgb destValue = decodeInline<QRgb>(dest);
|
||||
float alpha = qAlpha(sourceValue) / EIGHT_BIT_MAXIMUM;
|
||||
return encodeInline(packNormal(glm::normalize(glm::mix(unpackNormal(destValue), unpackNormal(sourceValue), alpha))));
|
||||
}
|
||||
|
||||
const float CHAR_SCALE = 127.0f;
|
||||
const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE;
|
||||
|
||||
|
|
|
@ -158,6 +158,9 @@ public:
|
|||
/// Sets this attribute to a mix of the first and second provided.
|
||||
void mix(const AttributeValue& first, const AttributeValue& second, float alpha);
|
||||
|
||||
/// Sets this attribute to a blend of the source and destination.
|
||||
void blend(const AttributeValue& source, const AttributeValue& dest);
|
||||
|
||||
/// Destroys the current value, if any, and copies the specified other value.
|
||||
OwnedAttributeValue& operator=(const AttributeValue& other);
|
||||
|
||||
|
@ -218,6 +221,9 @@ public:
|
|||
/// Mixes the first and the second, returning a new value with the result.
|
||||
virtual void* mix(void* first, void* second, float alpha) const = 0;
|
||||
|
||||
/// Blends the source with the destination, returning a new value with the result.
|
||||
virtual void* blend(void* source, void* dest) const = 0;
|
||||
|
||||
virtual void* getDefaultValue() const = 0;
|
||||
|
||||
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); }
|
||||
|
@ -249,6 +255,8 @@ public:
|
|||
|
||||
virtual void* mix(void* first, void* second, float alpha) const { return create(alpha < 0.5f ? first : second); }
|
||||
|
||||
virtual void* blend(void* source, void* dest) const { return create(source); }
|
||||
|
||||
virtual void* getDefaultValue() const { return encodeInline(_defaultValue); }
|
||||
|
||||
protected:
|
||||
|
@ -315,6 +323,8 @@ public:
|
|||
|
||||
virtual void* mix(void* first, void* second, float alpha) const;
|
||||
|
||||
virtual void* blend(void* source, void* dest) const;
|
||||
|
||||
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const;
|
||||
|
||||
virtual void* createFromVariant(const QVariant& value) const;
|
||||
|
@ -333,6 +343,8 @@ public:
|
|||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
|
||||
virtual void* mix(void* first, void* second, float alpha) const;
|
||||
|
||||
virtual void* blend(void* source, void* dest) const;
|
||||
};
|
||||
|
||||
/// Packs a normal into an RGB value.
|
||||
|
|
|
@ -35,6 +35,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(SharedObjectPointer)
|
|||
|
||||
// some types don't quite work with our macro
|
||||
static int vec3Streamer = Bitstream::registerTypeStreamer(qMetaTypeId<glm::vec3>(), new SimpleTypeStreamer<glm::vec3>());
|
||||
static int quatStreamer = Bitstream::registerTypeStreamer(qMetaTypeId<glm::quat>(), new SimpleTypeStreamer<glm::quat>());
|
||||
static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId<const QMetaObject*>(),
|
||||
new SimpleTypeStreamer<const QMetaObject*>());
|
||||
|
||||
|
@ -352,6 +353,14 @@ Bitstream& Bitstream::operator>>(glm::vec3& value) {
|
|||
return *this >> value.x >> value.y >> value.z;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const glm::quat& value) {
|
||||
return *this << value.w << value.x << value.y << value.z;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(glm::quat& value) {
|
||||
return *this >> value.w >> value.x >> value.y >> value.z;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QByteArray& string) {
|
||||
*this << string.size();
|
||||
return write(string.constData(), string.size() * BITS_IN_BYTE);
|
||||
|
@ -869,7 +878,11 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
|
|||
*this >> rawObject;
|
||||
}
|
||||
pointer = static_cast<SharedObject*>(rawObject);
|
||||
pointer->setRemoteID(id);
|
||||
if (pointer) {
|
||||
pointer->setRemoteID(id);
|
||||
} else {
|
||||
qDebug() << "Null object" << pointer << reference;
|
||||
}
|
||||
}
|
||||
object = static_cast<SharedObject*>(pointer.data());
|
||||
return *this;
|
||||
|
|
|
@ -312,6 +312,9 @@ public:
|
|||
Bitstream& operator<<(const glm::vec3& value);
|
||||
Bitstream& operator>>(glm::vec3& value);
|
||||
|
||||
Bitstream& operator<<(const glm::quat& value);
|
||||
Bitstream& operator>>(glm::quat& value);
|
||||
|
||||
Bitstream& operator<<(const QByteArray& string);
|
||||
Bitstream& operator>>(QByteArray& string);
|
||||
|
||||
|
@ -920,6 +923,13 @@ public:
|
|||
_Pragma(STRINGIFY(unused(_TypePtr##X)))
|
||||
#endif
|
||||
|
||||
/// Registers a simple type and its streamer.
|
||||
template<class T> int registerSimpleMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer<T>());
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Registers a streamable type and its streamer.
|
||||
template<class T> int registerStreamableMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
|
|
|
@ -24,6 +24,8 @@ REGISTER_META_OBJECT(Spanner)
|
|||
REGISTER_META_OBJECT(Sphere)
|
||||
REGISTER_META_OBJECT(StaticModel)
|
||||
|
||||
static int metavoxelDataTypeId = registerSimpleMetaType<MetavoxelData>();
|
||||
|
||||
MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) :
|
||||
position(position),
|
||||
threshold(threshold) {
|
||||
|
@ -327,6 +329,100 @@ const int Y_MAXIMUM_FLAG = 2;
|
|||
const int Z_MAXIMUM_FLAG = 4;
|
||||
const int MAXIMUM_FLAG_MASK = X_MAXIMUM_FLAG | Y_MAXIMUM_FLAG | Z_MAXIMUM_FLAG;
|
||||
|
||||
static glm::vec3 getNextMinimum(const glm::vec3& minimum, float nextSize, int index) {
|
||||
return minimum + glm::vec3(
|
||||
(index & X_MAXIMUM_FLAG) ? nextSize : 0.0f,
|
||||
(index & Y_MAXIMUM_FLAG) ? nextSize : 0.0f,
|
||||
(index & Z_MAXIMUM_FLAG) ? nextSize : 0.0f);
|
||||
}
|
||||
|
||||
static void setNode(const AttributeValue& value, MetavoxelNode*& node, MetavoxelNode* other, bool blend) {
|
||||
if (!blend) {
|
||||
// if we're not blending, we can just make a shallow copy
|
||||
if (node) {
|
||||
node->decrementReferenceCount(value.getAttribute());
|
||||
}
|
||||
(node = other)->incrementReferenceCount();
|
||||
return;
|
||||
}
|
||||
if (node) {
|
||||
MetavoxelNode* oldNode = node;
|
||||
node = new MetavoxelNode(value.getAttribute(), oldNode);
|
||||
oldNode->decrementReferenceCount(value.getAttribute());
|
||||
|
||||
} else {
|
||||
node = new MetavoxelNode(value);
|
||||
}
|
||||
OwnedAttributeValue oldValue = node->getAttributeValue(value.getAttribute());
|
||||
node->blendAttributeValues(other->getAttributeValue(value.getAttribute()), oldValue);
|
||||
if (!other->isLeaf()) {
|
||||
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
|
||||
MetavoxelNode* child = node->getChild(i);
|
||||
setNode(oldValue, child, other->getChild(i), true);
|
||||
node->setChild(i, child);
|
||||
}
|
||||
}
|
||||
node->mergeChildren(value.getAttribute());
|
||||
}
|
||||
|
||||
static void setNode(const AttributeValue& value, MetavoxelNode*& node, const glm::vec3& minimum, float size,
|
||||
MetavoxelNode* other, const glm::vec3& otherMinimum, float otherSize, bool blend) {
|
||||
if (otherSize >= size) {
|
||||
setNode(value, node, other, blend);
|
||||
return;
|
||||
}
|
||||
if (node) {
|
||||
MetavoxelNode* oldNode = node;
|
||||
node = new MetavoxelNode(value.getAttribute(), oldNode);
|
||||
oldNode->decrementReferenceCount(value.getAttribute());
|
||||
|
||||
} else {
|
||||
node = new MetavoxelNode(value);
|
||||
}
|
||||
int index = 0;
|
||||
float otherHalfSize = otherSize * 0.5f;
|
||||
float nextSize = size * 0.5f;
|
||||
if (otherMinimum.x + otherHalfSize >= minimum.x + nextSize) {
|
||||
index |= X_MAXIMUM_FLAG;
|
||||
}
|
||||
if (otherMinimum.y + otherHalfSize >= minimum.y + nextSize) {
|
||||
index |= Y_MAXIMUM_FLAG;
|
||||
}
|
||||
if (otherMinimum.z + otherHalfSize >= minimum.z + nextSize) {
|
||||
index |= Z_MAXIMUM_FLAG;
|
||||
}
|
||||
if (node->isLeaf()) {
|
||||
for (int i = 1; i < MetavoxelNode::CHILD_COUNT; i++) {
|
||||
node->setChild((index + i) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(
|
||||
node->getAttributeValue(value.getAttribute())));
|
||||
}
|
||||
}
|
||||
MetavoxelNode* nextNode = node->getChild(index);
|
||||
setNode(node->getAttributeValue(value.getAttribute()), nextNode, getNextMinimum(minimum, nextSize, index),
|
||||
nextSize, other, otherMinimum, otherSize, blend);
|
||||
node->setChild(index, nextNode);
|
||||
node->mergeChildren(value.getAttribute());
|
||||
}
|
||||
|
||||
void MetavoxelData::set(const glm::vec3& minimum, const MetavoxelData& data, bool blend) {
|
||||
// expand to fit the entire data
|
||||
Box bounds(minimum, minimum + glm::vec3(data.getSize(), data.getSize(), data.getSize()));
|
||||
while (!getBounds().contains(bounds)) {
|
||||
expand();
|
||||
}
|
||||
|
||||
// set/mix each attribute separately
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = data._roots.constBegin();
|
||||
it != data._roots.constEnd(); it++) {
|
||||
MetavoxelNode*& root = _roots[it.key()];
|
||||
setNode(it.key(), root, getMinimum(), getSize(), it.value(), minimum, data.getSize(), blend);
|
||||
if (root->isLeaf() && root->getAttributeValue(it.key()).isDefault()) {
|
||||
_roots.remove(it.key());
|
||||
root->decrementReferenceCount(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int getOppositeIndex(int index) {
|
||||
return index ^ MAXIMUM_FLAG_MASK;
|
||||
}
|
||||
|
@ -511,6 +607,14 @@ MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) {
|
|||
return root = new MetavoxelNode(attribute);
|
||||
}
|
||||
|
||||
bool MetavoxelData::operator==(const MetavoxelData& other) const {
|
||||
return _size == other._size && _roots == other._roots;
|
||||
}
|
||||
|
||||
bool MetavoxelData::operator!=(const MetavoxelData& other) const {
|
||||
return _size != other._size || _roots != other._roots;
|
||||
}
|
||||
|
||||
void MetavoxelData::incrementRootReferenceCounts() {
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
|
||||
it.value()->incrementReferenceCount();
|
||||
|
@ -523,11 +627,22 @@ void MetavoxelData::decrementRootReferenceCounts() {
|
|||
}
|
||||
}
|
||||
|
||||
static glm::vec3 getNextMinimum(const glm::vec3& minimum, float nextSize, int index) {
|
||||
return minimum + glm::vec3(
|
||||
(index & X_MAXIMUM_FLAG) ? nextSize : 0.0f,
|
||||
(index & Y_MAXIMUM_FLAG) ? nextSize : 0.0f,
|
||||
(index & Z_MAXIMUM_FLAG) ? nextSize : 0.0f);
|
||||
Bitstream& operator<<(Bitstream& out, const MetavoxelData& data) {
|
||||
data.write(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
Bitstream& operator>>(Bitstream& in, MetavoxelData& data) {
|
||||
data.read(in);
|
||||
return in;
|
||||
}
|
||||
|
||||
template<> void Bitstream::writeDelta(const MetavoxelData& value, const MetavoxelData& reference) {
|
||||
value.writeDelta(reference, MetavoxelLOD(), *this, MetavoxelLOD());
|
||||
}
|
||||
|
||||
template<> void Bitstream::readDelta(MetavoxelData& value, const MetavoxelData& reference) {
|
||||
value.readDelta(reference, MetavoxelLOD(), *this, MetavoxelLOD());
|
||||
}
|
||||
|
||||
bool MetavoxelStreamState::shouldSubdivide() const {
|
||||
|
@ -577,7 +692,11 @@ MetavoxelNode::MetavoxelNode(const AttributePointer& attribute, const MetavoxelN
|
|||
void MetavoxelNode::setAttributeValue(const AttributeValue& attributeValue) {
|
||||
attributeValue.getAttribute()->destroy(_attributeValue);
|
||||
_attributeValue = attributeValue.copy();
|
||||
clearChildren(attributeValue.getAttribute());
|
||||
}
|
||||
|
||||
void MetavoxelNode::blendAttributeValues(const AttributeValue& source, const AttributeValue& dest) {
|
||||
source.getAttribute()->destroy(_attributeValue);
|
||||
_attributeValue = source.getAttribute()->blend(source.getValue(), dest.getValue());
|
||||
}
|
||||
|
||||
AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribute) const {
|
||||
|
@ -1408,7 +1527,7 @@ void SpannerRenderer::simulate(float deltaTime) {
|
|||
// nothing by default
|
||||
}
|
||||
|
||||
void SpannerRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) {
|
||||
void SpannerRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
|
@ -1426,7 +1545,7 @@ void Transformable::setTranslation(const glm::vec3& translation) {
|
|||
}
|
||||
}
|
||||
|
||||
void Transformable::setRotation(const glm::vec3& rotation) {
|
||||
void Transformable::setRotation(const glm::quat& rotation) {
|
||||
if (_rotation != rotation) {
|
||||
emit rotationChanged(_rotation = rotation);
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
|
||||
MetavoxelData& operator=(const MetavoxelData& other);
|
||||
|
||||
void setSize(float size) { _size = size; }
|
||||
float getSize() const { return _size; }
|
||||
|
||||
glm::vec3 getMinimum() const { return glm::vec3(_size, _size, _size) * -0.5f; }
|
||||
|
@ -90,6 +91,9 @@ public:
|
|||
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const AttributePointer& attribute, float& distance, const MetavoxelLOD& lod = MetavoxelLOD());
|
||||
|
||||
/// Sets part of the data.
|
||||
void set(const glm::vec3& minimum, const MetavoxelData& data, bool blend = false);
|
||||
|
||||
/// Expands the tree, increasing its capacity in all dimensions.
|
||||
void expand();
|
||||
|
||||
|
@ -103,6 +107,9 @@ public:
|
|||
MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); }
|
||||
MetavoxelNode* createRoot(const AttributePointer& attribute);
|
||||
|
||||
bool operator==(const MetavoxelData& other) const;
|
||||
bool operator!=(const MetavoxelData& other) const;
|
||||
|
||||
private:
|
||||
|
||||
friend class MetavoxelVisitation;
|
||||
|
@ -114,6 +121,16 @@ private:
|
|||
QHash<AttributePointer, MetavoxelNode*> _roots;
|
||||
};
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const MetavoxelData& data);
|
||||
|
||||
Bitstream& operator>>(Bitstream& in, MetavoxelData& data);
|
||||
|
||||
template<> void Bitstream::writeDelta(const MetavoxelData& value, const MetavoxelData& reference);
|
||||
|
||||
template<> void Bitstream::readDelta(MetavoxelData& value, const MetavoxelData& reference);
|
||||
|
||||
Q_DECLARE_METATYPE(MetavoxelData)
|
||||
|
||||
/// Holds the state used in streaming metavoxel data.
|
||||
class MetavoxelStreamState {
|
||||
public:
|
||||
|
@ -142,6 +159,8 @@ public:
|
|||
|
||||
void setAttributeValue(const AttributeValue& attributeValue);
|
||||
|
||||
void blendAttributeValues(const AttributeValue& source, const AttributeValue& dest);
|
||||
|
||||
AttributeValue getAttributeValue(const AttributePointer& attribute) const;
|
||||
void* getAttributeValue() const { return _attributeValue; }
|
||||
|
||||
|
@ -174,13 +193,13 @@ public:
|
|||
|
||||
void destroy(const AttributePointer& attribute);
|
||||
|
||||
void clearChildren(const AttributePointer& attribute);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(MetavoxelNode)
|
||||
|
||||
friend class MetavoxelVisitation;
|
||||
|
||||
void clearChildren(const AttributePointer& attribute);
|
||||
|
||||
int _referenceCount;
|
||||
void* _attributeValue;
|
||||
MetavoxelNode* _children[CHILD_COUNT];
|
||||
|
@ -499,11 +518,13 @@ class SpannerRenderer : public QObject {
|
|||
|
||||
public:
|
||||
|
||||
enum Mode { DEFAULT_MODE, DIFFUSE_MODE, NORMAL_MODE };
|
||||
|
||||
Q_INVOKABLE SpannerRenderer();
|
||||
|
||||
virtual void init(Spanner* spanner);
|
||||
virtual void simulate(float deltaTime);
|
||||
virtual void render(float alpha, 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,
|
||||
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
|
||||
};
|
||||
|
@ -512,7 +533,7 @@ public:
|
|||
class Transformable : public Spanner {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::vec3 translation MEMBER _translation WRITE setTranslation NOTIFY translationChanged)
|
||||
Q_PROPERTY(glm::vec3 rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged)
|
||||
Q_PROPERTY(glm::quat rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged)
|
||||
Q_PROPERTY(float scale MEMBER _scale WRITE setScale NOTIFY scaleChanged)
|
||||
|
||||
public:
|
||||
|
@ -522,8 +543,8 @@ public:
|
|||
void setTranslation(const glm::vec3& translation);
|
||||
const glm::vec3& getTranslation() const { return _translation; }
|
||||
|
||||
void setRotation(const glm::vec3& rotation);
|
||||
const glm::vec3& getRotation() const { return _rotation; }
|
||||
void setRotation(const glm::quat& rotation);
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
|
||||
void setScale(float scale);
|
||||
float getScale() const { return _scale; }
|
||||
|
@ -531,13 +552,13 @@ public:
|
|||
signals:
|
||||
|
||||
void translationChanged(const glm::vec3& translation);
|
||||
void rotationChanged(const glm::vec3& rotation);
|
||||
void rotationChanged(const glm::quat& rotation);
|
||||
void scaleChanged(float scale);
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _translation;
|
||||
glm::vec3 _rotation; // Euler Angles in degrees
|
||||
glm::quat _rotation;
|
||||
float _scale;
|
||||
};
|
||||
|
||||
|
@ -592,7 +613,7 @@ public:
|
|||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
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;
|
||||
|
||||
signals:
|
||||
|
||||
|
|
|
@ -305,3 +305,13 @@ void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& obje
|
|||
|
||||
setIntersectingMasked(spanner->getBounds(), data);
|
||||
}
|
||||
|
||||
SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bool blend) :
|
||||
minimum(minimum),
|
||||
data(data),
|
||||
blend(blend) {
|
||||
}
|
||||
|
||||
void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
data.set(minimum, this->data, blend);
|
||||
}
|
||||
|
|
|
@ -176,4 +176,21 @@ public:
|
|||
|
||||
DECLARE_STREAMABLE_METATYPE(SetSpannerEdit)
|
||||
|
||||
/// An edit that directly sets part of the metavoxel data.
|
||||
class SetDataEdit : public MetavoxelEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM glm::vec3 minimum;
|
||||
STREAM MetavoxelData data;
|
||||
STREAM bool blend;
|
||||
|
||||
SetDataEdit(const glm::vec3& minimum = glm::vec3(), const MetavoxelData& data = MetavoxelData(), bool blend = false);
|
||||
|
||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(SetDataEdit)
|
||||
|
||||
#endif /* defined(__interface__MetavoxelMessages__) */
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
DoubleEditor::DoubleEditor(QWidget* parent) : QDoubleSpinBox(parent) {
|
||||
setMinimum(-FLT_MAX);
|
||||
setMaximum(FLT_MAX);
|
||||
setSingleStep(0.01);
|
||||
}
|
||||
|
||||
DelegatingItemEditorFactory::DelegatingItemEditorFactory() :
|
||||
|
@ -127,6 +128,12 @@ static QItemEditorCreatorBase* createVec3EditorCreator() {
|
|||
return creator;
|
||||
}
|
||||
|
||||
static QItemEditorCreatorBase* createQuatEditorCreator() {
|
||||
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<QuatEditor>();
|
||||
getItemEditorFactory()->registerEditor(qMetaTypeId<glm::quat>(), creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
static QItemEditorCreatorBase* createParameterizedURLEditorCreator() {
|
||||
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<ParameterizedURLEditor>();
|
||||
getItemEditorFactory()->registerEditor(qMetaTypeId<ParameterizedURL>(), creator);
|
||||
|
@ -138,6 +145,7 @@ static QItemEditorCreatorBase* qMetaObjectEditorCreator = createQMetaObjectEdito
|
|||
static QItemEditorCreatorBase* qColorEditorCreator = createQColorEditorCreator();
|
||||
static QItemEditorCreatorBase* qUrlEditorCreator = createQUrlEditorCreator();
|
||||
static QItemEditorCreatorBase* vec3EditorCreator = createVec3EditorCreator();
|
||||
static QItemEditorCreatorBase* quatEditorCreator = createQuatEditorCreator();
|
||||
static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator();
|
||||
|
||||
QByteArray signal(const char* signature) {
|
||||
|
@ -380,7 +388,7 @@ void QUrlEditor::updateSettings() {
|
|||
QSettings().setValue("editorURLs", urls);
|
||||
}
|
||||
|
||||
Vec3Editor::Vec3Editor(QWidget* parent) : QWidget(parent) {
|
||||
BaseVec3Editor::BaseVec3Editor(QWidget* parent) : QWidget(parent) {
|
||||
QHBoxLayout* layout = new QHBoxLayout();
|
||||
layout->setContentsMargins(QMargins());
|
||||
setLayout(layout);
|
||||
|
@ -390,26 +398,64 @@ Vec3Editor::Vec3Editor(QWidget* parent) : QWidget(parent) {
|
|||
layout->addWidget(_z = createComponentBox());
|
||||
}
|
||||
|
||||
void Vec3Editor::setVector(const glm::vec3& vector) {
|
||||
_vector = vector;
|
||||
_x->setValue(vector.x);
|
||||
_y->setValue(vector.y);
|
||||
_z->setValue(vector.z);
|
||||
}
|
||||
|
||||
void Vec3Editor::updateVector() {
|
||||
emit vectorChanged(_vector = glm::vec3(_x->value(), _y->value(), _z->value()));
|
||||
}
|
||||
|
||||
QDoubleSpinBox* Vec3Editor::createComponentBox() {
|
||||
QDoubleSpinBox* BaseVec3Editor::createComponentBox() {
|
||||
QDoubleSpinBox* box = new QDoubleSpinBox();
|
||||
box->setMinimum(-FLT_MAX);
|
||||
box->setMaximum(FLT_MAX);
|
||||
box->setMinimumWidth(50);
|
||||
connect(box, SIGNAL(valueChanged(double)), SLOT(updateVector()));
|
||||
connect(box, SIGNAL(valueChanged(double)), SLOT(updateValue()));
|
||||
return box;
|
||||
}
|
||||
|
||||
Vec3Editor::Vec3Editor(QWidget* parent) : BaseVec3Editor(parent) {
|
||||
_x->setSingleStep(0.01);
|
||||
_y->setSingleStep(0.01);
|
||||
_z->setSingleStep(0.01);
|
||||
}
|
||||
|
||||
static void setComponentValue(QDoubleSpinBox* box, double value) {
|
||||
box->blockSignals(true);
|
||||
box->setValue(value);
|
||||
box->blockSignals(false);
|
||||
}
|
||||
|
||||
void Vec3Editor::setValue(const glm::vec3& value) {
|
||||
_value = value;
|
||||
setComponentValue(_x, value.x);
|
||||
setComponentValue(_y, value.y);
|
||||
setComponentValue(_z, value.z);
|
||||
}
|
||||
|
||||
void Vec3Editor::updateValue() {
|
||||
emit valueChanged(_value = glm::vec3(_x->value(), _y->value(), _z->value()));
|
||||
}
|
||||
|
||||
QuatEditor::QuatEditor(QWidget* parent) : BaseVec3Editor(parent) {
|
||||
_x->setRange(-179.0, 180.0);
|
||||
_y->setRange(-179.0, 180.0);
|
||||
_z->setRange(-179.0, 180.0);
|
||||
|
||||
_x->setWrapping(true);
|
||||
_y->setWrapping(true);
|
||||
_z->setWrapping(true);
|
||||
}
|
||||
|
||||
void QuatEditor::setValue(const glm::quat& value) {
|
||||
if (_value != value) {
|
||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(_value = value));
|
||||
setComponentValue(_x, eulers.x);
|
||||
setComponentValue(_y, eulers.y);
|
||||
setComponentValue(_z, eulers.z);
|
||||
}
|
||||
}
|
||||
|
||||
void QuatEditor::updateValue() {
|
||||
glm::quat value(glm::radians(glm::vec3(_x->value(), _y->value(), _z->value())));
|
||||
if (_value != value) {
|
||||
emit valueChanged(_value = value);
|
||||
}
|
||||
}
|
||||
|
||||
ParameterizedURL::ParameterizedURL(const QUrl& url, const ScriptHash& parameters) :
|
||||
_url(url),
|
||||
_parameters(parameters) {
|
||||
|
|
|
@ -142,10 +142,31 @@ private:
|
|||
QUrl _url;
|
||||
};
|
||||
|
||||
/// Editor for vector values.
|
||||
class Vec3Editor : public QWidget {
|
||||
/// Base class for Vec3Editor and QuatEditor.
|
||||
class BaseVec3Editor : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::vec3 vector MEMBER _vector WRITE setVector NOTIFY vectorChanged USER true)
|
||||
|
||||
public:
|
||||
|
||||
BaseVec3Editor(QWidget* parent);
|
||||
|
||||
protected slots:
|
||||
|
||||
virtual void updateValue() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
QDoubleSpinBox* createComponentBox();
|
||||
|
||||
QDoubleSpinBox* _x;
|
||||
QDoubleSpinBox* _y;
|
||||
QDoubleSpinBox* _z;
|
||||
};
|
||||
|
||||
/// Editor for vector values.
|
||||
class Vec3Editor : public BaseVec3Editor {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::vec3 value MEMBER _value WRITE setValue NOTIFY valueChanged USER true)
|
||||
|
||||
public:
|
||||
|
||||
|
@ -153,24 +174,45 @@ public:
|
|||
|
||||
signals:
|
||||
|
||||
void vectorChanged(const glm::vec3& vector);
|
||||
void valueChanged(const glm::vec3& vector);
|
||||
|
||||
public slots:
|
||||
|
||||
void setVector(const glm::vec3& vector);
|
||||
void setValue(const glm::vec3& vector);
|
||||
|
||||
private slots:
|
||||
protected:
|
||||
|
||||
void updateVector();
|
||||
virtual void updateValue();
|
||||
|
||||
private:
|
||||
|
||||
QDoubleSpinBox* createComponentBox();
|
||||
glm::vec3 _value;
|
||||
};
|
||||
|
||||
/// Editor for quaternion values.
|
||||
class QuatEditor : public BaseVec3Editor {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::quat value MEMBER _value WRITE setValue NOTIFY valueChanged USER true)
|
||||
|
||||
public:
|
||||
|
||||
QDoubleSpinBox* _x;
|
||||
QDoubleSpinBox* _y;
|
||||
QDoubleSpinBox* _z;
|
||||
glm::vec3 _vector;
|
||||
QuatEditor(QWidget* parent);
|
||||
|
||||
signals:
|
||||
|
||||
void valueChanged(const glm::quat& value);
|
||||
|
||||
public slots:
|
||||
|
||||
void setValue(const glm::quat& value);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void updateValue();
|
||||
|
||||
private:
|
||||
|
||||
glm::quat _value;
|
||||
};
|
||||
|
||||
typedef QHash<QScriptString, QVariant> ScriptHash;
|
||||
|
|
|
@ -20,18 +20,17 @@ REGISTER_META_OBJECT(SharedObject)
|
|||
|
||||
SharedObject::SharedObject() :
|
||||
_id(++_lastID),
|
||||
_remoteID(0),
|
||||
_referenceCount(0) {
|
||||
_remoteID(0) {
|
||||
|
||||
_weakHash.insert(_id, this);
|
||||
}
|
||||
|
||||
void SharedObject::incrementReferenceCount() {
|
||||
_referenceCount++;
|
||||
_referenceCount.ref();
|
||||
}
|
||||
|
||||
void SharedObject::decrementReferenceCount() {
|
||||
if (--_referenceCount == 0) {
|
||||
if (!_referenceCount.deref()) {
|
||||
_weakHash.remove(_id);
|
||||
delete this;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef __interface__SharedObject__
|
||||
#define __interface__SharedObject__
|
||||
|
||||
#include <QAtomicInt>
|
||||
#include <QHash>
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
|
@ -42,7 +43,7 @@ public:
|
|||
|
||||
void setRemoteID(int remoteID) { _remoteID = remoteID; }
|
||||
|
||||
int getReferenceCount() const { return _referenceCount; }
|
||||
int getReferenceCount() const { return _referenceCount.load(); }
|
||||
void incrementReferenceCount();
|
||||
void decrementReferenceCount();
|
||||
|
||||
|
@ -62,7 +63,7 @@ private:
|
|||
|
||||
int _id;
|
||||
int _remoteID;
|
||||
int _referenceCount;
|
||||
QAtomicInt _referenceCount;
|
||||
|
||||
static int _lastID;
|
||||
static WeakSharedObjectHash _weakHash;
|
||||
|
|
|
@ -34,6 +34,16 @@ Q_DECLARE_METATYPE(JSONCallbackParameters)
|
|||
|
||||
const QString ACCOUNTS_GROUP = "accounts";
|
||||
|
||||
JSONCallbackParameters::JSONCallbackParameters() :
|
||||
jsonCallbackReceiver(NULL),
|
||||
jsonCallbackMethod(),
|
||||
errorCallbackReceiver(NULL),
|
||||
errorCallbackMethod(),
|
||||
updateReciever(NULL),
|
||||
updateSlot()
|
||||
{
|
||||
}
|
||||
|
||||
AccountManager::AccountManager() :
|
||||
_authURL(),
|
||||
_networkAccessManager(NULL),
|
||||
|
@ -170,18 +180,31 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
|
|||
if (!callbackParams.isEmpty()) {
|
||||
// if we have information for a callback, insert the callbackParams into our local map
|
||||
_pendingCallbackMap.insert(networkReply, callbackParams);
|
||||
|
||||
if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) {
|
||||
callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)),
|
||||
callbackParams.updateSlot.toStdString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// if we ended up firing of a request, hook up to it now
|
||||
connect(networkReply, SIGNAL(finished()), this, SLOT(passSuccessToCallback()));
|
||||
connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
this, SLOT(passErrorToCallback(QNetworkReply::NetworkError)));
|
||||
connect(networkReply, SIGNAL(finished()), SLOT(processReply()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AccountManager::passSuccessToCallback() {
|
||||
void AccountManager::processReply() {
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
|
||||
if (requestReply->error() == QNetworkReply::NoError) {
|
||||
passSuccessToCallback(requestReply);
|
||||
} else {
|
||||
passErrorToCallback(requestReply);
|
||||
}
|
||||
delete requestReply;
|
||||
}
|
||||
|
||||
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
|
||||
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
||||
|
@ -200,17 +223,15 @@ void AccountManager::passSuccessToCallback() {
|
|||
qDebug() << jsonResponse;
|
||||
}
|
||||
}
|
||||
delete requestReply;
|
||||
}
|
||||
|
||||
void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode) {
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
|
||||
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
||||
|
||||
if (callbackParams.errorCallbackReceiver) {
|
||||
// invoke the right method on the callback receiver
|
||||
QMetaObject::invokeMethod(callbackParams.errorCallbackReceiver, qPrintable(callbackParams.errorCallbackMethod),
|
||||
Q_ARG(QNetworkReply::NetworkError, errorCode),
|
||||
Q_ARG(QNetworkReply::NetworkError, requestReply->error()),
|
||||
Q_ARG(const QString&, requestReply->errorString()));
|
||||
|
||||
// remove the related reply-callback group from the map
|
||||
|
@ -218,10 +239,9 @@ void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode)
|
|||
} else {
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qDebug() << "Received error response from data-server that has no matching callback.";
|
||||
qDebug() << "Error" << errorCode << "-" << requestReply->errorString();
|
||||
qDebug() << "Error" << requestReply->error() << "-" << requestReply->errorString();
|
||||
}
|
||||
}
|
||||
delete requestReply;
|
||||
}
|
||||
|
||||
bool AccountManager::hasValidAccessToken() {
|
||||
|
|
|
@ -19,9 +19,7 @@
|
|||
|
||||
class JSONCallbackParameters {
|
||||
public:
|
||||
JSONCallbackParameters() :
|
||||
jsonCallbackReceiver(NULL), jsonCallbackMethod(),
|
||||
errorCallbackReceiver(NULL), errorCallbackMethod() {};
|
||||
JSONCallbackParameters();
|
||||
|
||||
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
|
||||
|
||||
|
@ -29,6 +27,8 @@ public:
|
|||
QString jsonCallbackMethod;
|
||||
QObject* errorCallbackReceiver;
|
||||
QString errorCallbackMethod;
|
||||
QObject* updateReciever;
|
||||
QString updateSlot;
|
||||
};
|
||||
|
||||
class AccountManager : public QObject {
|
||||
|
@ -70,13 +70,15 @@ signals:
|
|||
void loginComplete(const QUrl& authURL);
|
||||
void logoutComplete();
|
||||
private slots:
|
||||
void passSuccessToCallback();
|
||||
void passErrorToCallback(QNetworkReply::NetworkError errorCode);
|
||||
void processReply();
|
||||
private:
|
||||
AccountManager();
|
||||
AccountManager(AccountManager const& other); // not implemented
|
||||
void operator=(AccountManager const& other); // not implemented
|
||||
|
||||
void passSuccessToCallback(QNetworkReply* reply);
|
||||
void passErrorToCallback(QNetworkReply* reply);
|
||||
|
||||
Q_INVOKABLE void invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
||||
const JSONCallbackParameters& callbackParams,
|
||||
const QByteArray& dataByteArray,
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QFileDialog>
|
||||
#include <QStandardPaths>
|
||||
#include <QGridLayout>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QTemporaryDir>
|
||||
#include <QVariant>
|
||||
#include <QMessageBox>
|
||||
#include <QProgressBar>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
#include <QVariant>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "ModelUploader.h"
|
||||
|
@ -26,29 +27,26 @@ static const QString FILENAME_FIELD = "filename";
|
|||
static const QString TEXDIR_FIELD = "texdir";
|
||||
static const QString LOD_FIELD = "lod";
|
||||
|
||||
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
||||
static const QString MODEL_URL = "/api/v1/models";
|
||||
|
||||
static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
static const int TIMEOUT = 1000;
|
||||
static const int MAX_CHECK = 30;
|
||||
|
||||
// Class providing the QObject parent system to QTemporaryDir
|
||||
class TemporaryDir : public QTemporaryDir, public QObject {
|
||||
public:
|
||||
virtual ~TemporaryDir() {
|
||||
// ensuring the entire object gets deleted by the QObject parent.
|
||||
}
|
||||
};
|
||||
static const int QCOMPRESS_HEADER_POSITION = 0;
|
||||
static const int QCOMPRESS_HEADER_SIZE = 4;
|
||||
|
||||
ModelUploader::ModelUploader(bool isHead) :
|
||||
_zipDir(new TemporaryDir()),
|
||||
_lodCount(-1),
|
||||
_texturesCount(-1),
|
||||
_totalSize(0),
|
||||
_isHead(isHead),
|
||||
_readyToSend(false),
|
||||
_dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType))
|
||||
_dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)),
|
||||
_numberOfChecks(MAX_CHECK)
|
||||
{
|
||||
_zipDir->setParent(_dataMultiPart);
|
||||
|
||||
connect(&_timer, SIGNAL(timeout()), SLOT(checkS3()));
|
||||
}
|
||||
|
||||
ModelUploader::~ModelUploader() {
|
||||
|
@ -59,8 +57,9 @@ bool ModelUploader::zip() {
|
|||
// File Dialog
|
||||
QString filename = QFileDialog::getOpenFileName(NULL,
|
||||
"Select your .fst file ...",
|
||||
QStandardPaths::writableLocation(QStandardPaths::DownloadLocation),
|
||||
QStandardPaths::writableLocation(QStandardPaths::HomeLocation),
|
||||
"*.fst");
|
||||
qDebug() << QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||
if (filename == "") {
|
||||
// If the user canceled we return.
|
||||
return false;
|
||||
|
@ -79,11 +78,7 @@ bool ModelUploader::zip() {
|
|||
qDebug() << "Reading FST file : " << QFileInfo(fst).filePath();
|
||||
|
||||
// Compress and copy the fst
|
||||
if (!compressFile(QFileInfo(fst).filePath(), _zipDir->path() + "/" + QFileInfo(fst).fileName())) {
|
||||
return false;
|
||||
}
|
||||
if (!addPart(_zipDir->path() + "/" + QFileInfo(fst).fileName(),
|
||||
QString("fst"))) {
|
||||
if (!addPart(QFileInfo(fst).filePath(), QString("fst"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -103,6 +98,7 @@ bool ModelUploader::zip() {
|
|||
" name=\"model_name\"");
|
||||
textPart.setBody(line[1].toUtf8());
|
||||
_dataMultiPart->append(textPart);
|
||||
_url = S3_URL + ((_isHead)? "/models/heads/" : "/models/skeletons/") + line[1].toUtf8() + ".fst";
|
||||
} else if (line[0] == FILENAME_FIELD) {
|
||||
QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]);
|
||||
if (!fbx.exists() || !fbx.isFile()) { // Check existence
|
||||
|
@ -114,10 +110,7 @@ bool ModelUploader::zip() {
|
|||
return false;
|
||||
}
|
||||
// Compress and copy
|
||||
if (!compressFile(fbx.filePath(), _zipDir->path() + "/" + line[1])) {
|
||||
return false;
|
||||
}
|
||||
if (!addPart(_zipDir->path() + "/" + line[1], "fbx")) {
|
||||
if (!addPart(fbx.filePath(), "fbx")) {
|
||||
return false;
|
||||
}
|
||||
} else if (line[0] == TEXDIR_FIELD) { // Check existence
|
||||
|
@ -144,10 +137,7 @@ bool ModelUploader::zip() {
|
|||
return false;
|
||||
}
|
||||
// Compress and copy
|
||||
if (!compressFile(lod.filePath(), _zipDir->path() + "/" + line[1])) {
|
||||
return false;
|
||||
}
|
||||
if (!addPart(_zipDir->path() + "/" + line[1], QString("lod%1").arg(++_lodCount))) {
|
||||
if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -167,9 +157,9 @@ bool ModelUploader::zip() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ModelUploader::send() {
|
||||
if (!_readyToSend) {
|
||||
return false;
|
||||
void ModelUploader::send() {
|
||||
if (!zip()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONCallbackParameters callbackParams;
|
||||
|
@ -177,22 +167,51 @@ bool ModelUploader::send() {
|
|||
callbackParams.jsonCallbackMethod = "uploadSuccess";
|
||||
callbackParams.errorCallbackReceiver = this;
|
||||
callbackParams.errorCallbackMethod = "uploadFailed";
|
||||
callbackParams.updateReciever = this;
|
||||
callbackParams.updateSlot = SLOT(uploadUpdate(qint64, qint64));
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, callbackParams, QByteArray(), _dataMultiPart);
|
||||
_zipDir = NULL;
|
||||
_dataMultiPart = NULL;
|
||||
qDebug() << "Sending model...";
|
||||
_progressDialog = new QDialog();
|
||||
_progressBar = new QProgressBar(_progressDialog);
|
||||
_progressBar->setRange(0, 100);
|
||||
_progressBar->setValue(0);
|
||||
|
||||
return true;
|
||||
_progressDialog->setWindowTitle("Uploading model...");
|
||||
_progressDialog->setLayout(new QGridLayout(_progressDialog));
|
||||
_progressDialog->layout()->addWidget(_progressBar);
|
||||
|
||||
_progressDialog->exec();
|
||||
|
||||
delete _progressDialog;
|
||||
_progressDialog = NULL;
|
||||
_progressBar = NULL;
|
||||
}
|
||||
|
||||
void ModelUploader::uploadUpdate(qint64 bytesSent, qint64 bytesTotal) {
|
||||
if (_progressDialog) {
|
||||
_progressBar->setRange(0, bytesTotal);
|
||||
_progressBar->setValue(bytesSent);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelUploader::uploadSuccess(const QJsonObject& jsonResponse) {
|
||||
qDebug() << "Model sent with success to the data server.";
|
||||
qDebug() << "It might take a few minute for it to appear in your model browser.";
|
||||
deleteLater();
|
||||
if (_progressDialog) {
|
||||
_progressDialog->accept();
|
||||
}
|
||||
QMessageBox::information(NULL,
|
||||
QString("ModelUploader::uploadSuccess()"),
|
||||
QString("Your model is being processed by the system."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "Model sent with success";
|
||||
checkS3();
|
||||
}
|
||||
|
||||
void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QString& errorString) {
|
||||
if (_progressDialog) {
|
||||
_progressDialog->reject();
|
||||
}
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::uploadFailed()"),
|
||||
QString("Model could not be sent to the data server."),
|
||||
|
@ -201,6 +220,42 @@ void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QS
|
|||
deleteLater();
|
||||
}
|
||||
|
||||
void ModelUploader::checkS3() {
|
||||
qDebug() << "Checking S3 for " << _url;
|
||||
QNetworkRequest request(_url);
|
||||
QNetworkReply* reply = _networkAccessManager.head(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(processCheck()));
|
||||
}
|
||||
|
||||
void ModelUploader::processCheck() {
|
||||
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
|
||||
_timer.stop();
|
||||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
QMessageBox::information(NULL,
|
||||
QString("ModelUploader::processCheck()"),
|
||||
QString("Your model is now available in the browser."),
|
||||
QMessageBox::Ok);
|
||||
deleteLater();
|
||||
break;
|
||||
case QNetworkReply::ContentNotFoundError:
|
||||
if (--_numberOfChecks) {
|
||||
_timer.start(TIMEOUT);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::processCheck()"),
|
||||
QString("Could not verify that the model is present on the server."),
|
||||
QMessageBox::Ok);
|
||||
deleteLater();
|
||||
break;
|
||||
}
|
||||
|
||||
delete reply;
|
||||
}
|
||||
|
||||
bool ModelUploader::addTextures(const QFileInfo& texdir) {
|
||||
QStringList filter;
|
||||
filter << "*.png" << "*.tif" << "*.jpg" << "*.jpeg";
|
||||
|
@ -213,11 +268,7 @@ bool ModelUploader::addTextures(const QFileInfo& texdir) {
|
|||
foreach (QFileInfo info, list) {
|
||||
if (info.isFile()) {
|
||||
// Compress and copy
|
||||
if (!compressFile(info.filePath(), _zipDir->path() + "/" + info.fileName())) {
|
||||
return false;
|
||||
}
|
||||
if (!addPart(_zipDir->path() + "/" + info.fileName(),
|
||||
QString("texture%1").arg(++_texturesCount))) {
|
||||
if (!addPart(info.filePath(), QString("texture%1").arg(++_texturesCount))) {
|
||||
return false;
|
||||
}
|
||||
} else if (info.isDir()) {
|
||||
|
@ -230,54 +281,33 @@ bool ModelUploader::addTextures(const QFileInfo& texdir) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ModelUploader::compressFile(const QString &inFileName, const QString &outFileName) {
|
||||
QFile inFile(inFileName);
|
||||
inFile.open(QIODevice::ReadOnly);
|
||||
QByteArray buffer = inFile.readAll();
|
||||
|
||||
QFile outFile(outFileName);
|
||||
if (!outFile.open(QIODevice::WriteOnly)) {
|
||||
QDir(_zipDir->path()).mkpath(QFileInfo(outFileName).path());
|
||||
if (!outFile.open(QIODevice::WriteOnly)) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::compressFile()"),
|
||||
QString("Could not compress %1").arg(inFileName),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Could not compress %1").arg(inFileName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
QDataStream out(&outFile);
|
||||
out << qCompress(buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ModelUploader::addPart(const QString &path, const QString& name) {
|
||||
QFile* file = new QFile(path);
|
||||
if (!file->open(QIODevice::ReadOnly)) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::addPart()"),
|
||||
QString("Could not open %1").arg(path),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Could not open %1").arg(path);
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
QByteArray buffer = qCompress(file.readAll());
|
||||
|
||||
// Qt's qCompress() default compression level (-1) is the standard zLib compression.
|
||||
// Here remove Qt's custom header that prevent the data server from uncompressing the files with zLib.
|
||||
buffer.remove(QCOMPRESS_HEADER_POSITION, QCOMPRESS_HEADER_SIZE);
|
||||
|
||||
QHttpPart part;
|
||||
part.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
|
||||
part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data;"
|
||||
" name=\"" + name.toUtf8() + "\";"
|
||||
" filename=\"" + QFileInfo(*file).fileName().toUtf8() + "\"");
|
||||
part.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream");
|
||||
part.setBodyDevice(file);
|
||||
" filename=\"" + QFileInfo(file).fileName().toUtf8() + "\""));
|
||||
part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
||||
part.setBody(buffer);
|
||||
_dataMultiPart->append(part);
|
||||
file->setParent(_dataMultiPart);
|
||||
|
||||
|
||||
qDebug() << "File " << QFileInfo(*file).fileName() << " added to model.";
|
||||
_totalSize += file->size();
|
||||
qDebug() << "File " << QFileInfo(file).fileName() << " added to model.";
|
||||
_totalSize += file.size();
|
||||
if (_totalSize > MAX_SIZE) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
|
|
|
@ -10,9 +10,14 @@
|
|||
#ifndef __hifi__ModelUploader__
|
||||
#define __hifi__ModelUploader__
|
||||
|
||||
class TemporaryDir;
|
||||
class QHttpMultiPart;
|
||||
#include <QTimer>
|
||||
|
||||
class QDialog;
|
||||
class QFileInfo;
|
||||
class QHttpMultiPart;
|
||||
class QProgressBar;
|
||||
|
||||
class TemporaryDir;
|
||||
|
||||
class ModelUploader : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -21,15 +26,18 @@ public:
|
|||
ModelUploader(bool isHead);
|
||||
~ModelUploader();
|
||||
|
||||
bool zip();
|
||||
bool send();
|
||||
public slots:
|
||||
void send();
|
||||
|
||||
private slots:
|
||||
void uploadUpdate(qint64 bytesSent, qint64 bytesTotal);
|
||||
void uploadSuccess(const QJsonObject& jsonResponse);
|
||||
void uploadFailed(QNetworkReply::NetworkError errorCode, const QString& errorString);
|
||||
void checkS3();
|
||||
void processCheck();
|
||||
|
||||
private:
|
||||
TemporaryDir* _zipDir;
|
||||
QString _url;
|
||||
int _lodCount;
|
||||
int _texturesCount;
|
||||
int _totalSize;
|
||||
|
@ -37,10 +45,17 @@ private:
|
|||
bool _readyToSend;
|
||||
|
||||
QHttpMultiPart* _dataMultiPart;
|
||||
QNetworkAccessManager _networkAccessManager;
|
||||
|
||||
int _numberOfChecks;
|
||||
QTimer _timer;
|
||||
|
||||
QDialog* _progressDialog;
|
||||
QProgressBar* _progressBar;
|
||||
|
||||
|
||||
bool zip();
|
||||
bool addTextures(const QFileInfo& texdir);
|
||||
bool compressFile(const QString& inFileName, const QString& outFileName);
|
||||
bool addPart(const QString& path, const QString& name);
|
||||
};
|
||||
|
||||
|
|
64
libraries/voxels/src/VoxelTreeCommands.cpp
Normal file
64
libraries/voxels/src/VoxelTreeCommands.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// VoxelTreeCommands.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Clement on 4/4/14.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
//
|
||||
|
||||
#include "VoxelTree.h"
|
||||
|
||||
#include "VoxelTreeCommands.h"
|
||||
|
||||
AddVoxelCommand::AddVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender, QUndoCommand* parent) :
|
||||
QUndoCommand("Add Voxel", parent),
|
||||
_tree(tree),
|
||||
_packetSender(packetSender),
|
||||
_voxel(voxel)
|
||||
{
|
||||
}
|
||||
|
||||
void AddVoxelCommand::redo() {
|
||||
if (_tree) {
|
||||
_tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
|
||||
}
|
||||
if (_packetSender) {
|
||||
_packetSender->queueVoxelEditMessages(PacketTypeVoxelSet, 1, &_voxel);
|
||||
}
|
||||
}
|
||||
|
||||
void AddVoxelCommand::undo() {
|
||||
if (_tree) {
|
||||
_tree->deleteVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||
}
|
||||
if (_packetSender) {
|
||||
_packetSender->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &_voxel);
|
||||
}
|
||||
}
|
||||
|
||||
DeleteVoxelCommand::DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender, QUndoCommand* parent) :
|
||||
QUndoCommand("Delete Voxel", parent),
|
||||
_tree(tree),
|
||||
_packetSender(packetSender),
|
||||
_voxel(voxel)
|
||||
{
|
||||
}
|
||||
|
||||
void DeleteVoxelCommand::redo() {
|
||||
if (_tree) {
|
||||
_tree->deleteVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||
}
|
||||
if (_packetSender) {
|
||||
_packetSender->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &_voxel);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteVoxelCommand::undo() {
|
||||
if (_tree) {
|
||||
_tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
|
||||
}
|
||||
if (_packetSender) {
|
||||
_packetSender->queueVoxelEditMessages(PacketTypeVoxelSet, 1, &_voxel);
|
||||
}
|
||||
}
|
46
libraries/voxels/src/VoxelTreeCommands.h
Normal file
46
libraries/voxels/src/VoxelTreeCommands.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// VoxelTreeCommands.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Clement on 4/4/14.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__VoxelTreeCommands__
|
||||
#define __hifi__VoxelTreeCommands__
|
||||
|
||||
#include <QRgb>
|
||||
#include <QUndoCommand>
|
||||
|
||||
#include "VoxelDetail.h"
|
||||
#include "VoxelEditPacketSender.h"
|
||||
|
||||
class VoxelTree;
|
||||
|
||||
class AddVoxelCommand : public QUndoCommand {
|
||||
public:
|
||||
AddVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender = NULL, QUndoCommand* parent = NULL);
|
||||
|
||||
virtual void redo();
|
||||
virtual void undo();
|
||||
|
||||
private:
|
||||
VoxelTree* _tree;
|
||||
VoxelEditPacketSender* _packetSender;
|
||||
VoxelDetail _voxel;
|
||||
};
|
||||
|
||||
class DeleteVoxelCommand : public QUndoCommand {
|
||||
public:
|
||||
DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender = NULL, QUndoCommand* parent = NULL);
|
||||
|
||||
virtual void redo();
|
||||
virtual void undo();
|
||||
|
||||
private:
|
||||
VoxelTree* _tree;
|
||||
VoxelEditPacketSender* _packetSender;
|
||||
VoxelDetail _voxel;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelTreeCommands__) */
|
|
@ -6,6 +6,8 @@
|
|||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "VoxelTreeCommands.h"
|
||||
|
||||
#include "VoxelsScriptingInterface.h"
|
||||
|
||||
void VoxelsScriptingInterface::queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails) {
|
||||
|
@ -37,17 +39,26 @@ VoxelDetail VoxelsScriptingInterface::getVoxelAt(float x, float y, float z, floa
|
|||
}
|
||||
|
||||
void VoxelsScriptingInterface::setVoxelNonDestructive(float x, float y, float z, float scale,
|
||||
uchar red, uchar green, uchar blue) {
|
||||
uchar red, uchar green, uchar blue) {
|
||||
// setup a VoxelDetail struct with the data
|
||||
VoxelDetail addVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE,
|
||||
VoxelDetail addVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE,
|
||||
scale / (float)TREE_SCALE, red, green, blue};
|
||||
|
||||
// queue the add packet
|
||||
queueVoxelAdd(PacketTypeVoxelSet, addVoxelDetail);
|
||||
|
||||
// handle the local tree also...
|
||||
if (_tree) {
|
||||
_tree->createVoxel(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s, red, green, blue, false);
|
||||
if (_undoStack) {
|
||||
AddVoxelCommand* command = new AddVoxelCommand(_tree,
|
||||
addVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
|
||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||
_undoStack->push(command);
|
||||
} else {
|
||||
// queue the add packet
|
||||
queueVoxelAdd(PacketTypeVoxelSet, addVoxelDetail);
|
||||
_tree->createVoxel(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s, red, green, blue, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,26 +68,70 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
|
|||
VoxelDetail addVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE,
|
||||
scale / (float)TREE_SCALE, red, green, blue};
|
||||
|
||||
// queue the destructive add
|
||||
queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail);
|
||||
|
||||
// handle the local tree also...
|
||||
if (_tree) {
|
||||
_tree->createVoxel(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s, red, green, blue, true);
|
||||
if (_undoStack) {
|
||||
AddVoxelCommand* addCommand = new AddVoxelCommand(_tree,
|
||||
addVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
|
||||
VoxelTreeElement* deleteVoxelElement = _tree->getVoxelAt(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s);
|
||||
if (deleteVoxelElement) {
|
||||
nodeColor color;
|
||||
memcpy(&color, &deleteVoxelElement->getColor(), sizeof(nodeColor));
|
||||
VoxelDetail deleteVoxelDetail = {addVoxelDetail.x,
|
||||
addVoxelDetail.y,
|
||||
addVoxelDetail.z,
|
||||
addVoxelDetail.s,
|
||||
color[0],
|
||||
color[1],
|
||||
color[2]};
|
||||
DeleteVoxelCommand* delCommand = new DeleteVoxelCommand(_tree,
|
||||
deleteVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
_undoStack->beginMacro(addCommand->text());
|
||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||
_undoStack->push(delCommand);
|
||||
_undoStack->push(addCommand);
|
||||
_undoStack->endMacro();
|
||||
} else {
|
||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||
_undoStack->push(addCommand);
|
||||
}
|
||||
} else {
|
||||
// queue the destructive add
|
||||
queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail);
|
||||
_tree->createVoxel(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s, red, green, blue, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale) {
|
||||
|
||||
// setup a VoxelDetail struct with data
|
||||
VoxelDetail deleteVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE,
|
||||
scale / (float)TREE_SCALE, 0, 0, 0};
|
||||
VoxelDetail deleteVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE,
|
||||
scale / (float)TREE_SCALE};
|
||||
|
||||
getVoxelPacketSender()->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &deleteVoxelDetail);
|
||||
|
||||
// handle the local tree also...
|
||||
if (_tree) {
|
||||
_tree->deleteVoxelAt(deleteVoxelDetail.x, deleteVoxelDetail.y, deleteVoxelDetail.z, deleteVoxelDetail.s);
|
||||
VoxelTreeElement* deleteVoxelElement = _tree->getVoxelAt(deleteVoxelDetail.x, deleteVoxelDetail.y, deleteVoxelDetail.z, deleteVoxelDetail.s);
|
||||
if (deleteVoxelElement) {
|
||||
deleteVoxelDetail.red = deleteVoxelElement->getColor()[0];
|
||||
deleteVoxelDetail.green = deleteVoxelElement->getColor()[1];
|
||||
deleteVoxelDetail.blue = deleteVoxelElement->getColor()[2];
|
||||
}
|
||||
|
||||
if (_undoStack) {
|
||||
DeleteVoxelCommand* command = new DeleteVoxelCommand(_tree,
|
||||
deleteVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||
_undoStack->push(command);
|
||||
} else {
|
||||
getVoxelPacketSender()->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &deleteVoxelDetail);
|
||||
_tree->deleteVoxelAt(deleteVoxelDetail.x, deleteVoxelDetail.y, deleteVoxelDetail.z, deleteVoxelDetail.s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,16 +18,19 @@
|
|||
#include "VoxelEditPacketSender.h"
|
||||
#include "VoxelTree.h"
|
||||
|
||||
class QUndoStack;
|
||||
|
||||
/// handles scripting of voxel commands from JS passed to assigned clients
|
||||
class VoxelsScriptingInterface : public OctreeScriptingInterface {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VoxelsScriptingInterface() : _tree(NULL) {};
|
||||
VoxelsScriptingInterface() : _tree(NULL), _undoStack(NULL) {};
|
||||
VoxelEditPacketSender* getVoxelPacketSender() { return (VoxelEditPacketSender*)getPacketSender(); }
|
||||
|
||||
virtual NodeType_t getServerNodeType() const { return NodeType::VoxelServer; }
|
||||
virtual OctreeEditPacketSender* createPacketSender() { return new VoxelEditPacketSender(); }
|
||||
void setVoxelTree(VoxelTree* tree) { _tree = tree; }
|
||||
void setUndoStack(QUndoStack* undoStack) { _undoStack = undoStack; }
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -79,6 +82,7 @@ public slots:
|
|||
private:
|
||||
void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails);
|
||||
VoxelTree* _tree;
|
||||
QUndoStack* _undoStack;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelsScriptingInterface__) */
|
||||
|
|
Loading…
Reference in a new issue