Merge branch 'master' of https://github.com/highfidelity/hifi into authentication

This commit is contained in:
Stephen Birarda 2014-04-08 14:26:10 -07:00
commit 6681754994
38 changed files with 1156 additions and 272 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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__) */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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__) */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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);
}
}

View 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__) */

View file

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

View file

@ -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__) */