From 718439014ad8a2b1784de15d05a8a668f9c537cd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 24 Feb 2014 17:28:11 -0800 Subject: [PATCH 001/109] Compute the mesh extents for geometry and use them to set the spanner bounds. --- interface/src/MetavoxelSystem.cpp | 10 +++ interface/src/renderer/FBXReader.cpp | 11 ++++ interface/src/renderer/FBXReader.h | 1 + interface/src/ui/MetavoxelEditor.cpp | 30 ++++++--- libraries/metavoxels/src/MetavoxelUtil.cpp | 74 ++++++++++++++++++++++ libraries/metavoxels/src/MetavoxelUtil.h | 2 + 6 files changed, 118 insertions(+), 10 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 59a714ece5..b7d9ea9e03 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include @@ -274,6 +276,14 @@ void StaticModelRenderer::init(Spanner* spanner) { } void StaticModelRenderer::simulate(float deltaTime) { + // update the bounds + Box bounds; + if (_model->isActive()) { + const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents; + bounds = Box(extents.minimum, extents.maximum); + } + static_cast(parent())->setBounds(glm::translate(_model->getTranslation()) * + glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds); _model->simulate(deltaTime); } diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 8b881940ca..57a9768f7c 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1276,6 +1276,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) geometry.bindExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX); geometry.staticExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX); geometry.staticExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX); + geometry.meshExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX); + geometry.meshExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX); QVariantHash springs = mapping.value("spring").toHash(); QVariant defaultSpring = springs.value("default"); @@ -1287,6 +1289,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) extracted.mesh.springiness = springs.value(models.value(modelID).name, defaultSpring).toFloat(); glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID); + // compute the mesh extents from the transformed vertices + foreach (const glm::vec3& vertex, extracted.mesh.vertices) { + glm::vec3 transformedVertex = glm::vec3(modelTransform * glm::vec4(vertex, 1.0f)); + geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); + geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); + } + // look for textures, material properties int materialIndex = 0; int textureIndex = 0; @@ -1704,5 +1713,7 @@ FBXGeometry readSVO(const QByteArray& model) { geometry.meshes.append(mesh); + geometry.meshExtents.maximum = glm::vec3(1.0f, 1.0f, 1.0f); + return geometry; } diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index b89d0954b4..a26ef40646 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -163,6 +163,7 @@ public: Extents bindExtents; Extents staticExtents; + Extents meshExtents; QVector attachments; }; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 832c6b5d39..c30dc15b65 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -269,6 +269,12 @@ const float GRID_BRIGHTNESS = 0.5f; void MetavoxelEditor::render() { glDisable(GL_LIGHTING); + + MetavoxelTool* tool = getActiveTool(); + if (tool) { + tool->render(); + } + glDepthMask(GL_FALSE); glPushMatrix(); @@ -277,11 +283,6 @@ void MetavoxelEditor::render() { glm::vec3 axis = glm::axis(rotation); glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); - MetavoxelTool* tool = getActiveTool(); - if (tool) { - tool->render(); - } - glLineWidth(1.0f); // center the grid around the camera position on the plane @@ -375,7 +376,14 @@ void BoxSetTool::render() { resetState(); return; } + glDepthMask(GL_FALSE); + + glPushMatrix(); + glm::quat rotation = _editor->getGridRotation(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + glm::quat inverseRotation = glm::inverse(rotation); glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); @@ -439,6 +447,8 @@ void BoxSetTool::render() { glPopMatrix(); } + + glPopMatrix(); } bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) { @@ -524,11 +534,6 @@ InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : } void InsertSpannerTool::simulate(float deltaTime) { - SharedObjectPointer spanner = _editor->getValue().value(); - static_cast(spanner.data())->getRenderer()->simulate(deltaTime); -} - -void InsertSpannerTool::render() { _editor->detachValue(); Spanner* spanner = static_cast(_editor->getValue().value().data()); Transformable* transformable = qobject_cast(spanner); @@ -543,6 +548,11 @@ void InsertSpannerTool::render() { transformable->setTranslation(rotation * glm::vec3(glm::vec2(rayOrigin + rayDirection * distance), position)); } + spanner->getRenderer()->simulate(deltaTime); +} + +void InsertSpannerTool::render() { + Spanner* spanner = static_cast(_editor->getValue().value().data()); const float SPANNER_ALPHA = 0.25f; spanner->getRenderer()->render(SPANNER_ALPHA); } diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 2e93fb1804..e1c92f3e8a 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -162,6 +162,80 @@ bool Box::intersects(const Box& other) const { other.maximum.z >= minimum.z && other.minimum.z <= maximum.z; } +Box operator*(const glm::mat4& matrix, const Box& box) { + // start with the constant component + Box newBox(glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]), glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2])); + + // for each element, we choose the minimum or maximum based on the matrix sign + if (matrix[0][0] >= 0.0f) { + newBox.minimum.x += matrix[0][0] * box.minimum.x; + newBox.maximum.x += matrix[0][0] * box.maximum.x; + } else { + newBox.minimum.x += matrix[0][0] * box.maximum.x; + newBox.maximum.x += matrix[0][0] * box.minimum.x; + } + if (matrix[1][0] >= 0.0f) { + newBox.minimum.x += matrix[1][0] * box.minimum.y; + newBox.maximum.x += matrix[1][0] * box.maximum.y; + } else { + newBox.minimum.x += matrix[1][0] * box.maximum.y; + newBox.maximum.x += matrix[1][0] * box.minimum.y; + } + if (matrix[2][0] >= 0.0f) { + newBox.minimum.x += matrix[2][0] * box.minimum.z; + newBox.maximum.x += matrix[2][0] * box.maximum.z; + } else { + newBox.minimum.x += matrix[2][0] * box.maximum.z; + newBox.maximum.x += matrix[2][0] * box.minimum.z; + } + + if (matrix[0][1] >= 0.0f) { + newBox.minimum.y += matrix[0][1] * box.minimum.x; + newBox.maximum.y += matrix[0][1] * box.maximum.x; + } else { + newBox.minimum.y += matrix[0][1] * box.maximum.x; + newBox.maximum.y += matrix[0][1] * box.minimum.x; + } + if (matrix[1][1] >= 0.0f) { + newBox.minimum.y += matrix[1][1] * box.minimum.y; + newBox.maximum.y += matrix[1][1] * box.maximum.y; + } else { + newBox.minimum.y += matrix[1][1] * box.maximum.y; + newBox.maximum.y += matrix[1][1] * box.minimum.y; + } + if (matrix[2][1] >= 0.0f) { + newBox.minimum.y += matrix[2][1] * box.minimum.z; + newBox.maximum.y += matrix[2][1] * box.maximum.z; + } else { + newBox.minimum.y += matrix[2][1] * box.maximum.z; + newBox.maximum.y += matrix[2][1] * box.minimum.z; + } + + if (matrix[0][2] >= 0.0f) { + newBox.minimum.z += matrix[0][2] * box.minimum.x; + newBox.maximum.z += matrix[0][2] * box.maximum.x; + } else { + newBox.minimum.z += matrix[0][2] * box.maximum.x; + newBox.maximum.z += matrix[0][2] * box.minimum.x; + } + if (matrix[1][2] >= 0.0f) { + newBox.minimum.z += matrix[1][2] * box.minimum.y; + newBox.maximum.z += matrix[1][2] * box.maximum.y; + } else { + newBox.minimum.z += matrix[1][2] * box.maximum.y; + newBox.maximum.z += matrix[1][2] * box.minimum.y; + } + if (matrix[2][2] >= 0.0f) { + newBox.minimum.z += matrix[2][2] * box.minimum.z; + newBox.maximum.z += matrix[2][2] * box.maximum.z; + } else { + newBox.minimum.z += matrix[2][2] * box.maximum.z; + newBox.maximum.z += matrix[2][2] * box.minimum.z; + } + + return newBox; +} + QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(QMargins()); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 4b3fb43523..adb106b652 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -48,6 +48,8 @@ public: DECLARE_STREAMABLE_METATYPE(Box) +Box operator*(const glm::mat4& matrix, const Box& box); + /// Editor for meta-object values. class QMetaObjectEditor : public QWidget { Q_OBJECT From 5d7eadcca373edfda075feaa958ca312d4569685 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Feb 2014 13:48:46 -0800 Subject: [PATCH 002/109] Working on better streaming for spanners. --- .../metavoxels/src/AttributeRegistry.cpp | 39 ++++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 23 +++++++++++ libraries/metavoxels/src/MetavoxelData.cpp | 12 +++--- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index bd83987666..bc9d9b030a 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -14,6 +14,7 @@ REGISTER_META_OBJECT(QRgbAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) +REGISTER_META_OBJECT(SpannerSetAttribute) AttributeRegistry* AttributeRegistry::getInstance() { static AttributeRegistry registry; @@ -23,7 +24,7 @@ AttributeRegistry* AttributeRegistry::getInstance() { AttributeRegistry::AttributeRegistry() : _guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject, SharedObjectPointer(new DefaultMetavoxelGuide())))), - _spannersAttribute(registerAttribute(new SharedObjectSetAttribute("spanners", &Spanner::staticMetaObject))), + _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) { } @@ -145,6 +146,22 @@ Attribute::Attribute(const QString& name) { Attribute::~Attribute() { } +void Attribute::read(MetavoxelNode& root, Bitstream& in) { + root.read(this, in); +} + +void Attribute::write(const MetavoxelNode& root, Bitstream& out) { + root.write(this, out); +} + +void Attribute::readDelta(MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& in) { + root.readDelta(this, reference, in); +} + +void Attribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& out) { + root.writeDelta(this, reference, out); +} + QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : InlineAttribute(name, defaultValue) { } @@ -255,3 +272,23 @@ bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const { QWidget* SharedObjectSetAttribute::createEditor(QWidget* parent) const { return new SharedObjectEditor(_metaObject, parent); } + +SpannerSetAttribute::SpannerSetAttribute(const QString& name, const QMetaObject* metaObject) : + SharedObjectSetAttribute(name, metaObject) { +} + +void SpannerSetAttribute::read(MetavoxelNode& root, Bitstream& in) { + root.read(this, in); +} + +void SpannerSetAttribute::write(const MetavoxelNode& root, Bitstream& out) { + root.write(this, out); +} + +void SpannerSetAttribute::readDelta(MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& in) { + root.readDelta(this, reference, in); +} + +void SpannerSetAttribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& out) { + root.writeDelta(this, reference, out); +} diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 2e029f8201..000e18ca6b 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -23,6 +23,7 @@ class QScriptEngine; class QScriptValue; class Attribute; +class MetavoxelNode; typedef SharedObjectPointerTemplate AttributePointer; @@ -170,6 +171,12 @@ public: virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } + virtual void read(MetavoxelNode& root, Bitstream& in); + virtual void write(const MetavoxelNode& root, Bitstream& out); + + virtual void readDelta(MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& in); + virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& out); + virtual bool equal(void* first, void* second) const = 0; /// Merges the value of a parent and its children. @@ -309,4 +316,20 @@ private: const QMetaObject* _metaObject; }; +/// An attribute that takes the form of a set of spanners. +class SpannerSetAttribute : public SharedObjectSetAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE SpannerSetAttribute(const QString& name = QString(), + const QMetaObject* metaObject = &SharedObject::staticMetaObject); + + virtual void read(MetavoxelNode& root, Bitstream& in); + virtual void write(const MetavoxelNode& root, Bitstream& out); + + virtual void readDelta(MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& in); + virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& out); +}; + #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 5665e70f91..38b073a0b9 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -240,7 +240,7 @@ void MetavoxelData::read(Bitstream& in) { in >> attribute; MetavoxelNode*& root = _roots[attribute]; root = new MetavoxelNode(attribute); - root->read(attribute, in); + attribute->read(*root, in); } } @@ -249,7 +249,7 @@ void MetavoxelData::write(Bitstream& out) const { out << _roots.size(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { out << it.key(); - it.value()->write(it.key(), out); + it.key()->write(*it.value(), out); } } @@ -282,12 +282,12 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { if (root) { MetavoxelNode* oldRoot = root; root = new MetavoxelNode(attribute); - root->readDelta(attribute, *oldRoot, in); + attribute->readDelta(*root, *oldRoot, in); oldRoot->decrementReferenceCount(attribute); } else { root = new MetavoxelNode(attribute); - root->read(attribute, in); + attribute->read(*root, in); } } @@ -337,9 +337,9 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) c if (it.value() != referenceRoot) { out << it.key(); if (referenceRoot) { - it.value()->writeDelta(it.key(), *referenceRoot, out); + it.key()->writeDelta(*it.value(), *referenceRoot, out); } else { - it.value()->write(it.key(), out); + it.key()->write(*it.value(), out); } } } From 4130d0f28bd07ba5758bce17cb04c5239e4cad48 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Feb 2014 15:01:01 -0800 Subject: [PATCH 003/109] Added spheres as simpler spanners for testing. --- interface/src/MetavoxelSystem.cpp | 21 ++++++++++++++++ interface/src/MetavoxelSystem.h | 11 ++++++++ libraries/metavoxels/src/MetavoxelData.cpp | 24 ++++++++++++++++++ libraries/metavoxels/src/MetavoxelData.h | 29 ++++++++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b7d9ea9e03..c5176a65a2 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -20,6 +20,7 @@ #include "MetavoxelSystem.h" #include "renderer/Model.h" +REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(StaticModelRenderer) ProgramObject MetavoxelSystem::_program; @@ -256,6 +257,26 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } } +SphereRenderer::SphereRenderer() { +} + +void SphereRenderer::render(float alpha) { + Sphere* sphere = static_cast(parent()); + const QColor& color = sphere->getColor(); + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * 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::vec3 axis = glm::axis(rotation); + glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + + glutSolidSphere(sphere->getScale(), 10, 10); + + glPopMatrix(); +} + StaticModelRenderer::StaticModelRenderer() : _model(new Model(this)) { } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 4364192009..6b6aa70a87 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -124,6 +124,17 @@ private: QList _receiveRecords; }; +/// Renders spheres. +class SphereRenderer : public SpannerRenderer { + Q_OBJECT + +public: + + Q_INVOKABLE SphereRenderer(); + + virtual void render(float alpha); +}; + /// Renders static models. class StaticModelRenderer : public SpannerRenderer { Q_OBJECT diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 38b073a0b9..228b5833f4 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -19,6 +19,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelGuide) REGISTER_META_OBJECT(ScriptedMetavoxelGuide) REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) REGISTER_META_OBJECT(Spanner) +REGISTER_META_OBJECT(Sphere) REGISTER_META_OBJECT(StaticModel) MetavoxelData::MetavoxelData() : _size(1.0f) { @@ -882,6 +883,29 @@ void Transformable::setScale(float scale) { } } +Sphere::Sphere() : + _color(Qt::gray) { + + connect(this, SIGNAL(translationChanged(const glm::vec3&)), SLOT(updateBounds())); + connect(this, SIGNAL(scaleChanged(float)), SLOT(updateBounds())); + updateBounds(); +} + +void Sphere::setColor(const QColor& color) { + if (_color != color) { + emit colorChanged(_color = color); + } +} + +QByteArray Sphere::getRendererClassName() const { + return "SphereRenderer"; +} + +void Sphere::updateBounds() { + glm::vec3 extent(getScale(), getScale(), getScale()); + setBounds(Box(getTranslation() - extent, getTranslation() + extent)); +} + StaticModel::StaticModel() { } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 85bdd54938..ce89ca9b91 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -367,6 +367,35 @@ private: float _scale; }; +/// A sphere. +class Sphere : public Transformable { + Q_OBJECT + Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged) + +public: + + Q_INVOKABLE Sphere(); + + void setColor(const QColor& color); + const QColor& getColor() const { return _color; } + +signals: + + void colorChanged(const QColor& color); + +protected: + + virtual QByteArray getRendererClassName() const; + +private slots: + + void updateBounds(); + +private: + + QColor _color; +}; + /// A static 3D model loaded from the network. class StaticModel : public Transformable { Q_OBJECT From 1dfb72173de35d996641ec04cd3cff14155d400a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Feb 2014 16:44:00 -0800 Subject: [PATCH 004/109] Hide metavoxel editing bits when mouse is hidden. --- interface/src/ui/MetavoxelEditor.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index c30dc15b65..61f7b55968 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -371,6 +371,10 @@ BoxSetTool::BoxSetTool(MetavoxelEditor* editor) : } void BoxSetTool::render() { + if (Application::getInstance()->isMouseHidden()) { + resetState(); + return; + } QString selected = _editor->getSelectedAttribute(); if (selected.isNull()) { resetState(); @@ -534,6 +538,9 @@ InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : } void InsertSpannerTool::simulate(float deltaTime) { + if (Application::getInstance()->isMouseHidden()) { + return; + } _editor->detachValue(); Spanner* spanner = static_cast(_editor->getValue().value().data()); Transformable* transformable = qobject_cast(spanner); @@ -552,13 +559,16 @@ void InsertSpannerTool::simulate(float deltaTime) { } void InsertSpannerTool::render() { + if (Application::getInstance()->isMouseHidden()) { + return; + } Spanner* spanner = static_cast(_editor->getValue().value().data()); const float SPANNER_ALPHA = 0.25f; spanner->getRenderer()->render(SPANNER_ALPHA); } bool InsertSpannerTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SharedObjectSetAttribute"); + return attribute->inherits("SpannerSetAttribute"); } bool InsertSpannerTool::eventFilter(QObject* watched, QEvent* event) { @@ -584,7 +594,7 @@ RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : } bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SharedObjectSetAttribute"); + return attribute->inherits("SpannerSetAttribute"); } bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) { @@ -604,7 +614,7 @@ ClearSpannersTool::ClearSpannersTool(MetavoxelEditor* editor) : } bool ClearSpannersTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("SharedObjectSetAttribute"); + return attribute->inherits("SpannerSetAttribute"); } void ClearSpannersTool::clear() { From f910c4471bb02cec6e3545c55ee0e6af69748553 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 2 Mar 2014 17:40:58 -0800 Subject: [PATCH 005/109] Distinguish between "hard" and "soft" references to shared objects. Sending bitstreams use soft references and remove their mappings when only soft references remain. --- libraries/metavoxels/src/Bitstream.cpp | 23 +++-- libraries/metavoxels/src/Bitstream.h | 56 ++++++----- libraries/metavoxels/src/SharedObject.cpp | 27 +++-- libraries/metavoxels/src/SharedObject.h | 115 +++++++++++++--------- 4 files changed, 134 insertions(+), 87 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 919bc4cbc8..61e8371652 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -167,11 +167,18 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) { _scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets); _sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets); - // find out when shared objects' reference counts drop to one in order to clear their mappings - for (QHash::const_iterator it = mappings.sharedObjectOffsets.constBegin(); + // find out when shared objects are deleted in order to clear their mappings + for (QHash::const_iterator it = mappings.sharedObjectOffsets.constBegin(); it != mappings.sharedObjectOffsets.constEnd(); it++) { if (it.key()) { - connect(it.key().data(), SIGNAL(referenceCountDroppedToOne()), SLOT(clearSharedObject())); + if (it.key()->getHardReferenceCount() > 0) { + connect(it.key().data(), SIGNAL(allHardReferencesCleared(QObject*)), + SLOT(clearSharedObject(QObject*))); + } else { + // invoke on a queued connection so as to run after any other mappings are persisted + QMetaObject::invokeMethod(this, "clearSharedObject", Qt::QueuedConnection, + Q_ARG(QObject*, it.key().data())); + } } } } @@ -465,7 +472,7 @@ Bitstream& Bitstream::operator>(QScriptString& string) { return *this; } -Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { +Bitstream& Bitstream::operator<(const SoftSharedObjectPointer& object) { if (!object) { return *this << (int)0; } @@ -497,10 +504,12 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { return *this; } -void Bitstream::clearSharedObject() { - SharedObjectPointer object(static_cast(sender())); +void Bitstream::clearSharedObject(QObject* object) { object->disconnect(this); - emit sharedObjectCleared(_sharedObjectStreamer.takePersistentID(object)); + int id = _sharedObjectStreamer.takePersistentID(static_cast(object)); + if (id != 0) { + emit sharedObjectCleared(id); + } } void Bitstream::readProperties(QObject* object) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index c06c4c3b5f..fa6975adc1 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -55,26 +55,26 @@ private: /// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When /// subsequently streamed, only the ID is sent. -template class RepeatedValueStreamer { +template class RepeatedValueStreamer { public: RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream), _lastPersistentID(0), _lastTransientOffset(0) { } - QHash getAndResetTransientOffsets(); + QHash getAndResetTransientOffsets(); - void persistTransientOffsets(const QHash& transientOffsets); + void persistTransientOffsets(const QHash& transientOffsets); - QHash getAndResetTransientValues(); + QHash getAndResetTransientValues(); - void persistTransientValues(const QHash& transientValues); + void persistTransientValues(const QHash& transientValues); - int takePersistentID(T value) { return _persistentIDs.take(value); } + int takePersistentID(K value) { return _persistentIDs.take(value); } void removePersistentValue(int id) { _persistentValues.remove(id); } - RepeatedValueStreamer& operator<<(T value); - RepeatedValueStreamer& operator>>(T& value); + RepeatedValueStreamer& operator<<(K value); + RepeatedValueStreamer& operator>>(V& value); private: @@ -82,23 +82,24 @@ private: IDStreamer _idStreamer; int _lastPersistentID; int _lastTransientOffset; - QHash _persistentIDs; - QHash _transientOffsets; - QHash _persistentValues; - QHash _transientValues; + QHash _persistentIDs; + QHash _transientOffsets; + QHash _persistentValues; + QHash _transientValues; }; -template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { - QHash transientOffsets; +template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { + QHash transientOffsets; _transientOffsets.swap(transientOffsets); _lastTransientOffset = 0; _idStreamer.setBitsFromValue(_lastPersistentID); return transientOffsets; } -template inline void RepeatedValueStreamer::persistTransientOffsets(const QHash& transientOffsets) { +template inline void RepeatedValueStreamer::persistTransientOffsets( + const QHash& transientOffsets) { int oldLastPersistentID = _lastPersistentID; - for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { + for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { int& id = _persistentIDs[it.key()]; if (id == 0) { id = oldLastPersistentID + it.value(); @@ -108,16 +109,17 @@ template inline void RepeatedValueStreamer::persistTransientOffsets( _idStreamer.setBitsFromValue(_lastPersistentID); } -template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { - QHash transientValues; +template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { + QHash transientValues; _transientValues.swap(transientValues); _idStreamer.setBitsFromValue(_lastPersistentID); return transientValues; } -template inline void RepeatedValueStreamer::persistTransientValues(const QHash& transientValues) { +template inline void RepeatedValueStreamer::persistTransientValues( + const QHash& transientValues) { int oldLastPersistentID = _lastPersistentID; - for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { + for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { int& id = _persistentIDs[it.value()]; if (id == 0) { id = oldLastPersistentID + it.key(); @@ -128,7 +130,7 @@ template inline void RepeatedValueStreamer::persistTransientValues(c _idStreamer.setBitsFromValue(_lastPersistentID); } -template inline RepeatedValueStreamer& RepeatedValueStreamer::operator<<(T value) { +template inline RepeatedValueStreamer& RepeatedValueStreamer::operator<<(K value) { int id = _persistentIDs.value(value); if (id == 0) { int& offset = _transientOffsets[value]; @@ -145,7 +147,7 @@ template inline RepeatedValueStreamer& RepeatedValueStreamer::ope return *this; } -template inline RepeatedValueStreamer& RepeatedValueStreamer::operator>>(T& value) { +template inline RepeatedValueStreamer& RepeatedValueStreamer::operator>>(V& value) { int id; _idStreamer >> id; if (id <= _lastPersistentID) { @@ -153,7 +155,7 @@ template inline RepeatedValueStreamer& RepeatedValueStreamer::ope } else { int offset = id - _lastPersistentID; - typename QHash::iterator it = _transientValues.find(offset); + typename QHash::iterator it = _transientValues.find(offset); if (it == _transientValues.end()) { _stream > value; _transientValues.insert(offset, value); @@ -177,7 +179,7 @@ public: QHash typeStreamerOffsets; QHash attributeOffsets; QHash scriptStringOffsets; - QHash sharedObjectOffsets; + QHash sharedObjectOffsets; }; class ReadMappings { @@ -306,7 +308,7 @@ public: Bitstream& operator<(const QScriptString& string); Bitstream& operator>(QScriptString& string); - Bitstream& operator<(const SharedObjectPointer& object); + Bitstream& operator<(const SoftSharedObjectPointer& object); Bitstream& operator>(SharedObjectPointer& object); signals: @@ -315,7 +317,7 @@ signals: private slots: - void clearSharedObject(); + void clearSharedObject(QObject* object); private: @@ -329,7 +331,7 @@ private: RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; - RepeatedValueStreamer _sharedObjectStreamer; + RepeatedValueStreamer _sharedObjectStreamer; QHash > _transientSharedObjects; diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 7e10068afe..c3a224103d 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -18,19 +18,30 @@ REGISTER_META_OBJECT(SharedObject) -SharedObject::SharedObject() : _id(++_lastID), _referenceCount(0) { +static int sharedObjectPointerMetaTypeId = qRegisterMetaType(); +static int softSharedObjectPointerMetaTypeId = qRegisterMetaType(); + +SharedObject::SharedObject() : + _id(++_lastID), + _hardReferenceCount(0), + _softReferenceCount(0) { } -void SharedObject::incrementReferenceCount() { - _referenceCount++; +void SharedObject::decrementHardReferenceCount() { + _hardReferenceCount--; + if (_hardReferenceCount == 0) { + if (_softReferenceCount == 0) { + delete this; + } else { + emit allHardReferencesCleared(this); + } + } } -void SharedObject::decrementReferenceCount() { - if (--_referenceCount == 0) { +void SharedObject::decrementSoftReferenceCount() { + _softReferenceCount--; + if (_hardReferenceCount == 0 && _softReferenceCount == 0) { delete this; - - } else if (_referenceCount == 1) { - emit referenceCountDroppedToOne(); } } diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 9895073e44..44bfb73a22 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -27,10 +27,14 @@ public: int getID() { return _id; } - int getReferenceCount() const { return _referenceCount; } - void incrementReferenceCount(); - void decrementReferenceCount(); + int getHardReferenceCount() const { return _hardReferenceCount; } + + void incrementHardReferenceCount() { _hardReferenceCount++; } + void decrementHardReferenceCount(); + void incrementSoftReferenceCount() { _softReferenceCount++; } + void decrementSoftReferenceCount(); + /// Creates a new clone of this object. virtual SharedObject* clone() const; @@ -42,23 +46,29 @@ public: signals: - /// Emitted when the reference count drops to one. - void referenceCountDroppedToOne(); + /// Emitted when only soft reference counts remain. + void allHardReferencesCleared(QObject* object); private: int _id; - int _referenceCount; + int _hardReferenceCount; + int _softReferenceCount; static int _lastID; }; +typedef void (SharedObject::*SharedObjectFn)(); + /// A pointer to a shared object. -template class SharedObjectPointerTemplate { +template class SharedObjectPointerTemplate { public: SharedObjectPointerTemplate(T* data = NULL); - SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other); + SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other); + template SharedObjectPointerTemplate( + const SharedObjectPointerTemplate& other); ~SharedObjectPointerTemplate(); T* data() const { return _data; } @@ -66,7 +76,7 @@ public: /// "Detaches" this object, making a new copy if its reference count is greater than one. bool detach(); - void swap(SharedObjectPointerTemplate& other) { qSwap(_data, other._data); } + void swap(SharedObjectPointerTemplate& other) { qSwap(_data, other._data); } void reset(); @@ -75,81 +85,91 @@ public: T& operator*() const { return *_data; } T* operator->() const { return _data; } - template SharedObjectPointerTemplate staticCast() const; + template SharedObjectPointerTemplate staticCast() const; - SharedObjectPointerTemplate& operator=(T* data); - SharedObjectPointerTemplate& operator=(const SharedObjectPointerTemplate& other); + SharedObjectPointerTemplate& operator=(T* data); + SharedObjectPointerTemplate& operator=(const SharedObjectPointerTemplate& other) { + return *this = other.data(); } + template SharedObjectPointerTemplate& operator=( + const SharedObjectPointerTemplate& other) { return *this = other.data(); } - bool operator==(const SharedObjectPointerTemplate& other) const { return _data == other._data; } - bool operator!=(const SharedObjectPointerTemplate& other) const { return _data != other._data; } + bool operator==(T* data) const { return _data == data; } + bool operator!=(T* data) const { return _data != data; } + template bool operator==( + const SharedObjectPointerTemplate& other) const { return _data == other.data(); } + template bool operator!=( + const SharedObjectPointerTemplate& other) const { return _data != other.data(); } + private: T* _data; }; -template inline SharedObjectPointerTemplate::SharedObjectPointerTemplate(T* data) : _data(data) { +template inline + SharedObjectPointerTemplate::SharedObjectPointerTemplate(T* data) : _data(data) { if (_data) { - _data->incrementReferenceCount(); + (_data->*Inc)(); } } -template inline SharedObjectPointerTemplate::SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other) : - _data(other._data) { - +template inline + SharedObjectPointerTemplate::SharedObjectPointerTemplate( + const SharedObjectPointerTemplate& other) : _data(other.data()) { if (_data) { - _data->incrementReferenceCount(); + (_data->*Inc)(); } } -template inline SharedObjectPointerTemplate::~SharedObjectPointerTemplate() { +template template inline + SharedObjectPointerTemplate::SharedObjectPointerTemplate( + const SharedObjectPointerTemplate& other) : _data(other.data()) { if (_data) { - _data->decrementReferenceCount(); + (_data->*Inc)(); } } -template inline bool SharedObjectPointerTemplate::detach() { - if (_data && _data->getReferenceCount() > 1) { - _data->decrementReferenceCount(); - (_data = _data->clone())->incrementReferenceCount(); +template inline + SharedObjectPointerTemplate::~SharedObjectPointerTemplate() { + if (_data) { + (_data->*Dec)(); + } +} + +template inline bool SharedObjectPointerTemplate::detach() { + if (_data && _data->getHardReferenceCount() > 1) { + (_data->*Dec)(); + ((_data = _data->clone())->*Inc)(); return true; } return false; } -template inline void SharedObjectPointerTemplate::reset() { +template inline void SharedObjectPointerTemplate::reset() { if (_data) { - _data->decrementReferenceCount(); + (_data->*Dec)(); } _data = NULL; } -template template inline SharedObjectPointerTemplate SharedObjectPointerTemplate::staticCast() const { - return SharedObjectPointerTemplate(static_cast(_data)); +template template inline + SharedObjectPointerTemplate SharedObjectPointerTemplate::staticCast() const { + return SharedObjectPointerTemplate(static_cast(_data)); } -template inline SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=(T* data) { +template inline + SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=(T* data) { if (_data) { - _data->decrementReferenceCount(); + (_data->*Dec)(); } if ((_data = data)) { - _data->incrementReferenceCount(); + (_data->*Inc)(); } return *this; } -template inline SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=( - const SharedObjectPointerTemplate& other) { - if (_data) { - _data->decrementReferenceCount(); - } - if ((_data = other._data)) { - _data->incrementReferenceCount(); - } - return *this; -} - -template uint qHash(const SharedObjectPointerTemplate& pointer, uint seed = 0) { +template uint qHash( + const SharedObjectPointerTemplate& pointer, uint seed = 0) { return qHash(pointer.data(), seed); } @@ -161,6 +181,11 @@ typedef QSet SharedObjectSet; Q_DECLARE_METATYPE(SharedObjectSet) +typedef SharedObjectPointerTemplate SoftSharedObjectPointer; + +Q_DECLARE_METATYPE(SoftSharedObjectPointer) + /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { Q_OBJECT From 55dca45feb45cb3c283e186d816b430e5d87b5b8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 3 Mar 2014 10:20:50 -0800 Subject: [PATCH 006/109] Working on tests for shared objects. --- libraries/metavoxels/src/SharedObject.cpp | 6 +++ libraries/metavoxels/src/SharedObject.h | 5 +++ tests/metavoxels/src/MetavoxelTests.cpp | 47 ++++++++++++++++++++++- tests/metavoxels/src/MetavoxelTests.h | 33 ++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index c3a224103d..b542299d00 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -63,6 +63,12 @@ SharedObject* SharedObject::clone() const { bool SharedObject::equals(const SharedObject* other) const { // default behavior is to compare the properties + if (!other) { + return false; + } + if (other == this) { + return true; + } const QMetaObject* metaObject = this->metaObject(); if (metaObject != other->metaObject()) { return false; diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 44bfb73a22..f83079b0fa 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -173,6 +173,11 @@ template uint qHash( return qHash(pointer.data(), seed); } +template bool equals( + const SharedObjectPointerTemplate& first, const SharedObjectPointerTemplate& second) { + return first ? first->equals(second) : !second; +} + typedef SharedObjectPointerTemplate SharedObjectPointer; Q_DECLARE_METATYPE(SharedObjectPointer) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 530f7e3108..f06658da68 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -12,6 +12,9 @@ #include "MetavoxelTests.h" +REGISTER_META_OBJECT(TestSharedObjectA) +REGISTER_META_OBJECT(TestSharedObjectB) + MetavoxelTests::MetavoxelTests(int& argc, char** argv) : QCoreApplication(argc, argv) { } @@ -26,6 +29,8 @@ static int reliableMessagesSent = 0; static int reliableMessagesReceived = 0; static int streamedBytesSent = 0; static int streamedBytesReceived = 0; +static int sharedObjectsCreated = 0; +static int sharedObjectsDestroyed = 0; bool MetavoxelTests::run() { @@ -54,6 +59,7 @@ bool MetavoxelTests::run() { qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived; qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived; qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived; + qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; qDebug() << "All tests passed!"; @@ -74,6 +80,15 @@ static QByteArray createRandomBytes() { return createRandomBytes(MIN_BYTES, MAX_BYTES); } +static SharedObjectPointer createRandomSharedObject() { + switch (randIntInRange(0, 2)) { + case 0: return new TestSharedObjectA(randFloat()); + case 1: return new TestSharedObjectB(); + case 2: + default: return SharedObjectPointer(); + } +} + Endpoint::Endpoint(const QByteArray& datagramHeader) : _sequencer(new DatagramSequencer(datagramHeader, this)), _highPriorityMessagesToSend(0.0f), @@ -110,7 +125,7 @@ static QVariant createRandomMessage() { return QVariant::fromValue(message); } case 1: { - TestMessageB message = { createRandomBytes() }; + TestMessageB message = { createRandomBytes(), createRandomSharedObject() }; return QVariant::fromValue(message); } case 2: @@ -132,10 +147,15 @@ static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMe } if (type == TestMessageA::Type) { return firstMessage.value() == secondMessage.value(); + } else if (type == TestMessageB::Type) { - return firstMessage.value() == secondMessage.value(); + TestMessageB first = firstMessage.value(); + TestMessageB second = secondMessage.value(); + return first.foo == second.foo && equals(first.bar, second.bar); + } else if (type == TestMessageC::Type) { return firstMessage.value() == secondMessage.value(); + } else { return firstMessage == secondMessage; } @@ -277,3 +297,26 @@ void Endpoint::readReliableChannel() { } streamedBytesReceived += bytes.size(); } + +TestSharedObjectA::TestSharedObjectA(float foo) : + _foo(foo) { + sharedObjectsCreated++; +} + +TestSharedObjectA::~TestSharedObjectA() { + sharedObjectsDestroyed++; +} + +void TestSharedObjectA::setFoo(float foo) { + if (_foo != foo) { + emit fooChanged(_foo = foo); + } +} + +TestSharedObjectB::TestSharedObjectB() { + sharedObjectsCreated++; +} + +TestSharedObjectB::~TestSharedObjectB() { + sharedObjectsDestroyed++; +} diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index f19870ac15..3b69c28f79 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -64,6 +64,38 @@ private: CircularBuffer _dataStreamed; }; +/// A simple shared object. +class TestSharedObjectA : public SharedObject { + Q_OBJECT + Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged) + +public: + + Q_INVOKABLE TestSharedObjectA(float foo = 0.0f); + virtual ~TestSharedObjectA(); + + void setFoo(float foo); + float getFoo() const { return _foo; } + +signals: + + void fooChanged(float foo); + +private: + + float _foo; +}; + +/// Another simple shared object. +class TestSharedObjectB : public SharedObject { + Q_OBJECT + +public: + + Q_INVOKABLE TestSharedObjectB(); + virtual ~TestSharedObjectB(); +}; + /// A simple test message. class TestMessageA { STREAMABLE @@ -84,6 +116,7 @@ class TestMessageB { public: STREAM QByteArray foo; + STREAM SharedObjectPointer bar; }; DECLARE_STREAMABLE_METATYPE(TestMessageB) From 949e40a10ecadee5ba27681b7da1678f26fba1ec Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 3 Mar 2014 11:44:45 -0800 Subject: [PATCH 007/109] Revert complicated system of "soft" pointers, instead use hard references in write mappings and listen for object destruction. --- libraries/metavoxels/src/Bitstream.cpp | 22 ++-- libraries/metavoxels/src/Bitstream.h | 64 +++++----- .../metavoxels/src/DatagramSequencer.cpp | 2 + libraries/metavoxels/src/SharedObject.cpp | 26 +--- libraries/metavoxels/src/SharedObject.h | 120 +++++++----------- tests/metavoxels/src/MetavoxelTests.cpp | 9 ++ 6 files changed, 108 insertions(+), 135 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 61e8371652..11deb95b14 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -168,21 +168,18 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) { _sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets); // find out when shared objects are deleted in order to clear their mappings - for (QHash::const_iterator it = mappings.sharedObjectOffsets.constBegin(); + for (QHash::const_iterator it = mappings.sharedObjectOffsets.constBegin(); it != mappings.sharedObjectOffsets.constEnd(); it++) { if (it.key()) { - if (it.key()->getHardReferenceCount() > 0) { - connect(it.key().data(), SIGNAL(allHardReferencesCleared(QObject*)), - SLOT(clearSharedObject(QObject*))); - } else { - // invoke on a queued connection so as to run after any other mappings are persisted - QMetaObject::invokeMethod(this, "clearSharedObject", Qt::QueuedConnection, - Q_ARG(QObject*, it.key().data())); - } + connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*))); } } } +void Bitstream::persistAndResetWriteMappings() { + persistWriteMappings(getAndResetWriteMappings()); +} + Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(), _typeStreamerStreamer.getAndResetTransientValues(), @@ -200,6 +197,10 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { _sharedObjectStreamer.persistTransientValues(mappings.sharedObjectValues); } +void Bitstream::persistAndResetReadMappings() { + persistReadMappings(getAndResetReadMappings()); +} + Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -472,7 +473,7 @@ Bitstream& Bitstream::operator>(QScriptString& string) { return *this; } -Bitstream& Bitstream::operator<(const SoftSharedObjectPointer& object) { +Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { if (!object) { return *this << (int)0; } @@ -505,7 +506,6 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { } void Bitstream::clearSharedObject(QObject* object) { - object->disconnect(this); int id = _sharedObjectStreamer.takePersistentID(static_cast(object)); if (id != 0) { emit sharedObjectCleared(id); diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index fa6975adc1..9c42828a66 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -55,26 +55,26 @@ private: /// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When /// subsequently streamed, only the ID is sent. -template class RepeatedValueStreamer { +template class RepeatedValueStreamer { public: RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream), _lastPersistentID(0), _lastTransientOffset(0) { } - QHash getAndResetTransientOffsets(); + QHash getAndResetTransientOffsets(); - void persistTransientOffsets(const QHash& transientOffsets); + void persistTransientOffsets(const QHash& transientOffsets); - QHash getAndResetTransientValues(); + QHash getAndResetTransientValues(); - void persistTransientValues(const QHash& transientValues); + void persistTransientValues(const QHash& transientValues); - int takePersistentID(K value) { return _persistentIDs.take(value); } + int takePersistentID(P value) { return _persistentIDs.take(value); } - void removePersistentValue(int id) { _persistentValues.remove(id); } + void removePersistentValue(int id) { _persistentIDs.remove(_persistentValues.take(id)); } - RepeatedValueStreamer& operator<<(K value); - RepeatedValueStreamer& operator>>(V& value); + RepeatedValueStreamer& operator<<(T value); + RepeatedValueStreamer& operator>>(T& value); private: @@ -82,24 +82,24 @@ private: IDStreamer _idStreamer; int _lastPersistentID; int _lastTransientOffset; - QHash _persistentIDs; - QHash _transientOffsets; - QHash _persistentValues; - QHash _transientValues; + QHash _persistentIDs; + QHash _transientOffsets; + QHash _persistentValues; + QHash _transientValues; }; -template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { - QHash transientOffsets; +template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { + QHash transientOffsets; _transientOffsets.swap(transientOffsets); _lastTransientOffset = 0; _idStreamer.setBitsFromValue(_lastPersistentID); return transientOffsets; } -template inline void RepeatedValueStreamer::persistTransientOffsets( - const QHash& transientOffsets) { +template inline void RepeatedValueStreamer::persistTransientOffsets( + const QHash& transientOffsets) { int oldLastPersistentID = _lastPersistentID; - for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { + for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { int& id = _persistentIDs[it.key()]; if (id == 0) { id = oldLastPersistentID + it.value(); @@ -109,17 +109,17 @@ template inline void RepeatedValueStreamer::persistTrans _idStreamer.setBitsFromValue(_lastPersistentID); } -template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { - QHash transientValues; +template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { + QHash transientValues; _transientValues.swap(transientValues); _idStreamer.setBitsFromValue(_lastPersistentID); return transientValues; } -template inline void RepeatedValueStreamer::persistTransientValues( - const QHash& transientValues) { +template inline void RepeatedValueStreamer::persistTransientValues( + const QHash& transientValues) { int oldLastPersistentID = _lastPersistentID; - for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { + for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { int& id = _persistentIDs[it.value()]; if (id == 0) { id = oldLastPersistentID + it.key(); @@ -130,7 +130,7 @@ template inline void RepeatedValueStreamer::persistTrans _idStreamer.setBitsFromValue(_lastPersistentID); } -template inline RepeatedValueStreamer& RepeatedValueStreamer::operator<<(K value) { +template inline RepeatedValueStreamer& RepeatedValueStreamer::operator<<(T value) { int id = _persistentIDs.value(value); if (id == 0) { int& offset = _transientOffsets[value]; @@ -147,7 +147,7 @@ template inline RepeatedValueStreamer& RepeatedValueStre return *this; } -template inline RepeatedValueStreamer& RepeatedValueStreamer::operator>>(V& value) { +template inline RepeatedValueStreamer& RepeatedValueStreamer::operator>>(T& value) { int id; _idStreamer >> id; if (id <= _lastPersistentID) { @@ -155,7 +155,7 @@ template inline RepeatedValueStreamer& RepeatedValueStre } else { int offset = id - _lastPersistentID; - typename QHash::iterator it = _transientValues.find(offset); + typename QHash::iterator it = _transientValues.find(offset); if (it == _transientValues.end()) { _stream > value; _transientValues.insert(offset, value); @@ -179,7 +179,7 @@ public: QHash typeStreamerOffsets; QHash attributeOffsets; QHash scriptStringOffsets; - QHash sharedObjectOffsets; + QHash sharedObjectOffsets; }; class ReadMappings { @@ -230,12 +230,18 @@ public: /// Persists a set of write mappings recorded earlier. void persistWriteMappings(const WriteMappings& mappings); + /// Immediately persists and resets the write mappings. + void persistAndResetWriteMappings(); + /// Returns the set of transient mappings gathered during reading and resets them. ReadMappings getAndResetReadMappings(); /// Persists a set of read mappings recorded earlier. void persistReadMappings(const ReadMappings& mappings); + /// Immediately persists and resets the read mappings. + void persistAndResetReadMappings(); + /// Removes a shared object from the read mappings. void clearSharedObject(int id) { _sharedObjectStreamer.removePersistentValue(id); } @@ -308,7 +314,7 @@ public: Bitstream& operator<(const QScriptString& string); Bitstream& operator>(QScriptString& string); - Bitstream& operator<(const SoftSharedObjectPointer& object); + Bitstream& operator<(const SharedObjectPointer& object); Bitstream& operator>(SharedObjectPointer& object); signals: @@ -331,7 +337,7 @@ private: RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; - RepeatedValueStreamer _sharedObjectStreamer; + RepeatedValueStreamer _sharedObjectStreamer; QHash > _transientSharedObjects; diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 0c7b8ce8ab..052f480fcf 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -587,6 +587,7 @@ void ReliableChannel::sendMessage(const QVariant& message) { _dataStream << (quint32)0; _bitstream << message; _bitstream.flush(); + _bitstream.persistAndResetWriteMappings(); quint32 length = _buffer.pos() - placeholder; _buffer.writeBytes(placeholder, sizeof(quint32), (const char*)&length); @@ -745,6 +746,7 @@ void ReliableChannel::readData(QDataStream& in) { QVariant message; _bitstream >> message; _bitstream.reset(); + _bitstream.persistAndResetReadMappings(); emit receivedMessage(message); continue; } diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index b542299d00..b67354828a 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -18,29 +18,15 @@ REGISTER_META_OBJECT(SharedObject) -static int sharedObjectPointerMetaTypeId = qRegisterMetaType(); -static int softSharedObjectPointerMetaTypeId = qRegisterMetaType(); - -SharedObject::SharedObject() : - _id(++_lastID), - _hardReferenceCount(0), - _softReferenceCount(0) { +SharedObject::SharedObject() : _id(++_lastID), _referenceCount(0) { } -void SharedObject::decrementHardReferenceCount() { - _hardReferenceCount--; - if (_hardReferenceCount == 0) { - if (_softReferenceCount == 0) { - delete this; - } else { - emit allHardReferencesCleared(this); - } - } +void SharedObject::incrementReferenceCount() { + _referenceCount++; } -void SharedObject::decrementSoftReferenceCount() { - _softReferenceCount--; - if (_hardReferenceCount == 0 && _softReferenceCount == 0) { +void SharedObject::decrementReferenceCount() { + if (--_referenceCount == 0) { delete this; } } @@ -62,13 +48,13 @@ SharedObject* SharedObject::clone() const { } bool SharedObject::equals(const SharedObject* other) const { - // default behavior is to compare the properties if (!other) { return false; } if (other == this) { return true; } + // default behavior is to compare the properties const QMetaObject* metaObject = this->metaObject(); if (metaObject != other->metaObject()) { return false; diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index f83079b0fa..344a77cc94 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -27,14 +27,10 @@ public: int getID() { return _id; } - int getHardReferenceCount() const { return _hardReferenceCount; } - - void incrementHardReferenceCount() { _hardReferenceCount++; } - void decrementHardReferenceCount(); + int getReferenceCount() const { return _referenceCount; } + void incrementReferenceCount(); + void decrementReferenceCount(); - void incrementSoftReferenceCount() { _softReferenceCount++; } - void decrementSoftReferenceCount(); - /// Creates a new clone of this object. virtual SharedObject* clone() const; @@ -44,31 +40,20 @@ public: // Dumps the contents of this object to the debug output. virtual void dump(QDebug debug = QDebug(QtDebugMsg)) const; -signals: - - /// Emitted when only soft reference counts remain. - void allHardReferencesCleared(QObject* object); - private: int _id; - int _hardReferenceCount; - int _softReferenceCount; + int _referenceCount; static int _lastID; }; -typedef void (SharedObject::*SharedObjectFn)(); - /// A pointer to a shared object. -template class SharedObjectPointerTemplate { +template class SharedObjectPointerTemplate { public: SharedObjectPointerTemplate(T* data = NULL); - SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other); - template SharedObjectPointerTemplate( - const SharedObjectPointerTemplate& other); + SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other); ~SharedObjectPointerTemplate(); T* data() const { return _data; } @@ -76,7 +61,7 @@ public: /// "Detaches" this object, making a new copy if its reference count is greater than one. bool detach(); - void swap(SharedObjectPointerTemplate& other) { qSwap(_data, other._data); } + void swap(SharedObjectPointerTemplate& other) { qSwap(_data, other._data); } void reset(); @@ -85,96 +70,86 @@ public: T& operator*() const { return *_data; } T* operator->() const { return _data; } - template SharedObjectPointerTemplate staticCast() const; + template SharedObjectPointerTemplate staticCast() const; - SharedObjectPointerTemplate& operator=(T* data); - SharedObjectPointerTemplate& operator=(const SharedObjectPointerTemplate& other) { - return *this = other.data(); } - template SharedObjectPointerTemplate& operator=( - const SharedObjectPointerTemplate& other) { return *this = other.data(); } + SharedObjectPointerTemplate& operator=(T* data); + SharedObjectPointerTemplate& operator=(const SharedObjectPointerTemplate& other); - bool operator==(T* data) const { return _data == data; } - bool operator!=(T* data) const { return _data != data; } + bool operator==(const SharedObjectPointerTemplate& other) const { return _data == other._data; } + bool operator!=(const SharedObjectPointerTemplate& other) const { return _data != other._data; } - template bool operator==( - const SharedObjectPointerTemplate& other) const { return _data == other.data(); } - template bool operator!=( - const SharedObjectPointerTemplate& other) const { return _data != other.data(); } - private: T* _data; }; -template inline - SharedObjectPointerTemplate::SharedObjectPointerTemplate(T* data) : _data(data) { +template inline SharedObjectPointerTemplate::SharedObjectPointerTemplate(T* data) : _data(data) { if (_data) { - (_data->*Inc)(); + _data->incrementReferenceCount(); } } -template inline - SharedObjectPointerTemplate::SharedObjectPointerTemplate( - const SharedObjectPointerTemplate& other) : _data(other.data()) { +template inline SharedObjectPointerTemplate::SharedObjectPointerTemplate(const SharedObjectPointerTemplate& other) : + _data(other._data) { + if (_data) { - (_data->*Inc)(); + _data->incrementReferenceCount(); } } -template template inline - SharedObjectPointerTemplate::SharedObjectPointerTemplate( - const SharedObjectPointerTemplate& other) : _data(other.data()) { +template inline SharedObjectPointerTemplate::~SharedObjectPointerTemplate() { if (_data) { - (_data->*Inc)(); + _data->decrementReferenceCount(); } } -template inline - SharedObjectPointerTemplate::~SharedObjectPointerTemplate() { - if (_data) { - (_data->*Dec)(); - } -} - -template inline bool SharedObjectPointerTemplate::detach() { - if (_data && _data->getHardReferenceCount() > 1) { - (_data->*Dec)(); - ((_data = _data->clone())->*Inc)(); +template inline bool SharedObjectPointerTemplate::detach() { + if (_data && _data->getReferenceCount() > 1) { + _data->decrementReferenceCount(); + (_data = _data->clone())->incrementReferenceCount(); return true; } return false; } -template inline void SharedObjectPointerTemplate::reset() { +template inline void SharedObjectPointerTemplate::reset() { if (_data) { - (_data->*Dec)(); + _data->decrementReferenceCount(); } _data = NULL; } -template template inline - SharedObjectPointerTemplate SharedObjectPointerTemplate::staticCast() const { - return SharedObjectPointerTemplate(static_cast(_data)); +template template inline SharedObjectPointerTemplate SharedObjectPointerTemplate::staticCast() const { + return SharedObjectPointerTemplate(static_cast(_data)); } -template inline - SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=(T* data) { +template inline SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=(T* data) { if (_data) { - (_data->*Dec)(); + _data->decrementReferenceCount(); } if ((_data = data)) { - (_data->*Inc)(); + _data->incrementReferenceCount(); } return *this; } -template uint qHash( - const SharedObjectPointerTemplate& pointer, uint seed = 0) { +template inline SharedObjectPointerTemplate& SharedObjectPointerTemplate::operator=( + const SharedObjectPointerTemplate& other) { + if (_data) { + _data->decrementReferenceCount(); + } + if ((_data = other._data)) { + _data->incrementReferenceCount(); + } + return *this; +} + +template uint qHash(const SharedObjectPointerTemplate& pointer, uint seed = 0) { return qHash(pointer.data(), seed); } -template bool equals( - const SharedObjectPointerTemplate& first, const SharedObjectPointerTemplate& second) { +template bool equals(const SharedObjectPointerTemplate& first, + const SharedObjectPointerTemplate& second) { return first ? first->equals(second) : !second; } @@ -186,11 +161,6 @@ typedef QSet SharedObjectSet; Q_DECLARE_METATYPE(SharedObjectSet) -typedef SharedObjectPointerTemplate SoftSharedObjectPointer; - -Q_DECLARE_METATYPE(SoftSharedObjectPointer) - /// Allows editing shared object instances. class SharedObjectEditor : public QWidget { Q_OBJECT diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index f06658da68..b1f3315bc0 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -10,6 +10,8 @@ #include +#include + #include "MetavoxelTests.h" REGISTER_META_OBJECT(TestSharedObjectA) @@ -245,6 +247,9 @@ void Endpoint::sendDatagram(const QByteArray& datagram) { } void Endpoint::handleHighPriorityMessage(const QVariant& message) { + if (message.userType() == ClearSharedObjectMessage::Type) { + return; + } if (_other->_highPriorityMessagesSent.isEmpty()) { throw QString("Received unsent/already sent high priority message."); } @@ -274,6 +279,10 @@ void Endpoint::readMessage(Bitstream& in) { } void Endpoint::handleReliableMessage(const QVariant& message) { + if (message.userType() == ClearSharedObjectMessage::Type || + message.userType() == ClearMainChannelSharedObjectMessage::Type) { + return; + } if (_other->_reliableMessagesSent.isEmpty()) { throw QString("Received unsent/already sent reliable message."); } From 47fe928995d0ea360687e04cc508ee91101fda86 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Mar 2014 11:18:31 -0800 Subject: [PATCH 008/109] Workin on LOD support in streaming. --- libraries/metavoxels/src/MetavoxelData.cpp | 5 +++++ libraries/metavoxels/src/MetavoxelData.h | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 2189176858..5c418eccda 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -22,6 +22,11 @@ REGISTER_META_OBJECT(Spanner) REGISTER_META_OBJECT(Sphere) REGISTER_META_OBJECT(StaticModel) +MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) : + position(position), + threshold(threshold) { +} + MetavoxelData::MetavoxelData() : _size(1.0f) { } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index ce89ca9b91..2133ad68d8 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -31,6 +31,15 @@ class NetworkValue; class Spanner; class SpannerRenderer; +/// Determines whether to subdivide each node when traversing. +class MetavoxelLOD { +public: + glm::vec3 position; + float threshold; + + MetavoxelLOD(const glm::vec3& position = glm::vec3(), float threshold = 0.0f); +}; + /// The base metavoxel representation shared between server and client. class MetavoxelData { public: From 13b712592f281a90a1c156669e0e3a0575c9b779 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Mar 2014 11:21:59 -0800 Subject: [PATCH 009/109] Use different unused variable warning suppressor for GCC. --- libraries/metavoxels/src/Bitstream.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 9c42828a66..ac77d2317a 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -458,6 +458,13 @@ public: bool operator==(const X& first, const X& second); \ bool operator!=(const X& first, const X& second); \ static const int* _TypePtr##X = &X::Type; +#elif __GNUC__ +#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ + Bitstream& operator<<(Bitstream& out, const X& obj); \ + Bitstream& operator>>(Bitstream& in, X& obj); \ + bool operator==(const X& first, const X& second); \ + bool operator!=(const X& first, const X& second); \ + __attribute__((unused)) static const int* _TypePtr##X = &X::Type; #else #define STRINGIFY(x) #x #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ From 63b4a2453b00327ca12962b48fa5212d9a3d4f35 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Mar 2014 21:27:13 -0800 Subject: [PATCH 010/109] Lots of work on metavoxel LODs and spanners. --- .../src/metavoxels/MetavoxelServer.cpp | 10 +- .../src/metavoxels/MetavoxelServer.h | 3 +- interface/src/MetavoxelSystem.cpp | 24 +- interface/src/MetavoxelSystem.h | 10 + .../metavoxels/src/AttributeRegistry.cpp | 53 ++- libraries/metavoxels/src/AttributeRegistry.h | 18 +- libraries/metavoxels/src/MetavoxelData.cpp | 340 +++++++++++------- libraries/metavoxels/src/MetavoxelData.h | 57 ++- .../metavoxels/src/MetavoxelMessages.cpp | 4 +- libraries/metavoxels/src/MetavoxelMessages.h | 7 +- 10 files changed, 355 insertions(+), 171 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index fa60d3a88c..ed48340a93 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -111,13 +111,17 @@ int MetavoxelSession::parseData(const QByteArray& packet) { } void MetavoxelSession::sendDelta() { + // wait until we have a valid lod + if (!_lod.isValid()) { + return; + } Bitstream& out = _sequencer.startPacket(); out << QVariant::fromValue(MetavoxelDeltaMessage()); - _server->getData().writeDelta(_sendRecords.first().data, out); + _server->getData().writeDelta(_sendRecords.first().data, _sendRecords.first().lod, out, _lod); _sequencer.endPacket(); // record the send - SendRecord record = { _sequencer.getOutgoingPacketNumber(), _server->getData() }; + SendRecord record = { _sequencer.getOutgoingPacketNumber(), _server->getData(), _lod }; _sendRecords.append(record); } @@ -139,7 +143,7 @@ void MetavoxelSession::handleMessage(const QVariant& message) { int userType = message.userType(); if (userType == ClientStateMessage::Type) { ClientStateMessage state = message.value(); - _position = state.position; + _lod = state.lod; } else if (userType == MetavoxelEditMessage::Type) { _server->applyEdit(message.value()); diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index a7939ee115..bd7a280c43 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -78,6 +78,7 @@ private: public: int packetNumber; MetavoxelData data; + MetavoxelLOD lod; }; MetavoxelServer* _server; @@ -86,7 +87,7 @@ private: SharedNodePointer _node; - glm::vec3 _position; + MetavoxelLOD _lod; QList _sendRecords; }; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index abe8988a25..4c1b40dc23 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -182,11 +182,16 @@ MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); + connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); connect(&_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int))); + // insert the baseline send record + SendRecord sendRecord = { 0 }; + _sendRecords.append(sendRecord); + // insert the baseline receive record - ReceiveRecord record = { 0, _data }; - _receiveRecords.append(record); + ReceiveRecord receiveRecord = { 0, _data }; + _receiveRecords.append(receiveRecord); } MetavoxelClient::~MetavoxelClient() { @@ -206,9 +211,14 @@ void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) { void MetavoxelClient::simulate(float deltaTime) { Bitstream& out = _sequencer.startPacket(); - ClientStateMessage state = { Application::getInstance()->getCamera()->getPosition() }; + const float FIXED_LOD_THRESHOLD = 0.0001f; + ClientStateMessage state = { MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD) }; out << QVariant::fromValue(state); _sequencer.endPacket(); + + // record the send + SendRecord record = { _sequencer.getOutgoingPacketNumber(), state.lod }; + _sendRecords.append(record); } int MetavoxelClient::parseData(const QByteArray& packet) { @@ -227,7 +237,7 @@ void MetavoxelClient::readPacket(Bitstream& in) { handleMessage(message, in); // record the receipt - ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data }; + ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data, _sendRecords.first().lod }; _receiveRecords.append(record); // reapply local edits @@ -238,6 +248,10 @@ void MetavoxelClient::readPacket(Bitstream& in) { } } +void MetavoxelClient::clearSendRecordsBefore(int index) { + _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1); +} + void MetavoxelClient::clearReceiveRecordsBefore(int index) { _receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1); } @@ -245,7 +259,7 @@ void MetavoxelClient::clearReceiveRecordsBefore(int index) { void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { int userType = message.userType(); if (userType == MetavoxelDeltaMessage::Type) { - _data.readDelta(_receiveRecords.first().data, in); + _data.readDelta(_receiveRecords.first().data, _receiveRecords.first().lod, in, _sendRecords.first().lod); } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 6b6aa70a87..155f99ffbd 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -103,16 +103,25 @@ private slots: void readPacket(Bitstream& in); + void clearSendRecordsBefore(int index); + void clearReceiveRecordsBefore(int index); private: void handleMessage(const QVariant& message, Bitstream& in); + class SendRecord { + public: + int packetNumber; + MetavoxelLOD lod; + }; + class ReceiveRecord { public: int packetNumber; MetavoxelData data; + MetavoxelLOD lod; }; SharedNodePointer _node; @@ -121,6 +130,7 @@ private: MetavoxelData _data; + QList _sendRecords; QList _receiveRecords; }; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index bc9d9b030a..4b643ba847 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -49,6 +49,9 @@ void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) { } AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute) { + if (!attribute) { + return attribute; + } AttributePointer& pointer = _attributes[attribute->getName()]; if (!pointer) { pointer = attribute; @@ -146,20 +149,20 @@ Attribute::Attribute(const QString& name) { Attribute::~Attribute() { } -void Attribute::read(MetavoxelNode& root, Bitstream& in) { - root.read(this, in); +void Attribute::read(MetavoxelData& data, MetavoxelStreamState& state) { + data.createRoot(state.attribute)->read(state); } -void Attribute::write(const MetavoxelNode& root, Bitstream& out) { - root.write(this, out); +void Attribute::write(const MetavoxelNode& root, MetavoxelStreamState& state) { + root.write(state); } -void Attribute::readDelta(MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& in) { - root.readDelta(this, reference, in); +void Attribute::readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { + data.createRoot(state.attribute)->readDelta(reference, state); } -void Attribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& out) { - root.writeDelta(this, reference, out); +void Attribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { + root.writeDelta(reference, state); } QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : @@ -277,18 +280,36 @@ SpannerSetAttribute::SpannerSetAttribute(const QString& name, const QMetaObject* SharedObjectSetAttribute(name, metaObject) { } -void SpannerSetAttribute::read(MetavoxelNode& root, Bitstream& in) { - root.read(this, in); +void SpannerSetAttribute::read(MetavoxelData& data, MetavoxelStreamState& state) { + forever { + SharedObjectPointer object; + state.stream >> object; + if (!object) { + break; + } + data.insert(state.attribute, object); + } } -void SpannerSetAttribute::write(const MetavoxelNode& root, Bitstream& out) { - root.write(this, out); +void SpannerSetAttribute::write(const MetavoxelNode& root, MetavoxelStreamState& state) { + Spanner::incrementVisit(); + root.writeSpanners(state); + state.stream << SharedObjectPointer(); } -void SpannerSetAttribute::readDelta(MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& in) { - root.readDelta(this, reference, in); +void SpannerSetAttribute::readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { + forever { + SharedObjectPointer object; + state.stream >> object; + if (!object) { + break; + } + data.toggle(state.attribute, object); + } } -void SpannerSetAttribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& out) { - root.writeDelta(this, reference, out); +void SpannerSetAttribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { + Spanner::incrementVisit(); + root.writeSpannerDelta(reference, state); + state.stream << SharedObjectPointer(); } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 000e18ca6b..dae0e87f37 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -23,7 +23,9 @@ class QScriptEngine; class QScriptValue; class Attribute; +class MetavoxelData; class MetavoxelNode; +class MetavoxelStreamState; typedef SharedObjectPointerTemplate AttributePointer; @@ -171,11 +173,11 @@ public: virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } - virtual void read(MetavoxelNode& root, Bitstream& in); - virtual void write(const MetavoxelNode& root, Bitstream& out); + virtual void read(MetavoxelData& data, MetavoxelStreamState& state); + virtual void write(const MetavoxelNode& root, MetavoxelStreamState& state); - virtual void readDelta(MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& in); - virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& out); + virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); virtual bool equal(void* first, void* second) const = 0; @@ -325,11 +327,11 @@ public: Q_INVOKABLE SpannerSetAttribute(const QString& name = QString(), const QMetaObject* metaObject = &SharedObject::staticMetaObject); - virtual void read(MetavoxelNode& root, Bitstream& in); - virtual void write(const MetavoxelNode& root, Bitstream& out); + virtual void read(MetavoxelData& data, MetavoxelStreamState& state); + virtual void write(const MetavoxelNode& root, MetavoxelStreamState& state); - virtual void readDelta(MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& in); - virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, Bitstream& out); + virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); }; #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 5c418eccda..970b2a7d87 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -27,6 +27,10 @@ MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) : threshold(threshold) { } +bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size) const { + return size >= glm::distance(position, minimum + glm::vec3(size, size, size) * 0.5f) * threshold; +} + MetavoxelData::MetavoxelData() : _size(1.0f) { } @@ -62,7 +66,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { const QVector& inputs = visitor.getInputs(); const QVector& outputs = visitor.getOutputs(); MetavoxelVisitation firstVisitation = { NULL, visitor, QVector(inputs.size() + 1), - QVector(outputs.size()), { glm::vec3(_size, _size, _size) * -0.5f, _size, + QVector(outputs.size()), { getMinimum(), _size, QVector(inputs.size() + 1), QVector(outputs.size()) } }; for (int i = 0; i < inputs.size(); i++) { MetavoxelNode* node = _roots.value(inputs.at(i)); @@ -98,10 +102,27 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { } } -class InsertVisitor : public MetavoxelVisitor { +typedef void (*SpannerUpdateFunction)(SharedObjectSet& set, const SharedObjectPointer& object); + +void insertSpanner(SharedObjectSet& set, const SharedObjectPointer& object) { + set.insert(object); +} + +void removeSpanner(SharedObjectSet& set, const SharedObjectPointer& object) { + set.remove(object); +} + +void toggleSpanner(SharedObjectSet& set, const SharedObjectPointer& object) { + if (!set.remove(object)) { + set.insert(object); + } +} + +template class SpannerUpdateVisitor : public MetavoxelVisitor { public: - InsertVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + SpannerUpdateVisitor(const AttributePointer& attribute, const Box& bounds, + float granularity, const SharedObjectPointer& object); virtual bool visit(MetavoxelInfo& info); @@ -113,8 +134,8 @@ private: const SharedObjectPointer& _object; }; -InsertVisitor::InsertVisitor(const AttributePointer& attribute, const Box& bounds, - float granularity, const SharedObjectPointer& object) : +template SpannerUpdateVisitor::SpannerUpdateVisitor(const AttributePointer& attribute, + const Box& bounds, float granularity, const SharedObjectPointer& object) : MetavoxelVisitor(QVector() << attribute, QVector() << attribute), _attribute(attribute), _bounds(bounds), @@ -122,7 +143,7 @@ InsertVisitor::InsertVisitor(const AttributePointer& attribute, const Box& bound _object(object) { } -bool InsertVisitor::visit(MetavoxelInfo& info) { +template bool SpannerUpdateVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return false; } @@ -130,61 +151,45 @@ bool InsertVisitor::visit(MetavoxelInfo& info) { return true; } SharedObjectSet set = info.inputValues.at(0).getInlineValue(); - set.insert(_object); + F(set, _object); info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); return false; } +void MetavoxelData::insert(const AttributePointer& attribute, const SharedObjectPointer& object) { + Spanner* spanner = static_cast(object.data()); + insert(attribute, spanner->getBounds(), spanner->getGranularity(), object); +} + void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object) { // expand to fit the entire bounds while (!getBounds().contains(bounds)) { expand(); } - InsertVisitor visitor(attribute, bounds, granularity, object); + SpannerUpdateVisitor visitor(attribute, bounds, granularity, object); guide(visitor); } -class RemoveVisitor : public MetavoxelVisitor { -public: - - RemoveVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); - - virtual bool visit(MetavoxelInfo& info); - -private: - - const AttributePointer& _attribute; - const Box& _bounds; - float _longestSide; - const SharedObjectPointer& _object; -}; - -RemoveVisitor::RemoveVisitor(const AttributePointer& attribute, const Box& bounds, - float granularity, const SharedObjectPointer& object) : - MetavoxelVisitor(QVector() << attribute, QVector() << attribute), - _attribute(attribute), - _bounds(bounds), - _longestSide(qMax(bounds.getLongestSide(), granularity)), - _object(object) { -} - -bool RemoveVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return false; - } - if (info.size > _longestSide) { - return true; - } - SharedObjectSet set = info.inputValues.at(0).getInlineValue(); - set.remove(_object); - info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); - return false; +void MetavoxelData::remove(const AttributePointer& attribute, const SharedObjectPointer& object) { + Spanner* spanner = static_cast(object.data()); + remove(attribute, spanner->getBounds(), spanner->getGranularity(), object); } void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object) { - RemoveVisitor visitor(attribute, bounds, granularity, object); + SpannerUpdateVisitor visitor(attribute, bounds, granularity, object); + guide(visitor); +} + +void MetavoxelData::toggle(const AttributePointer& attribute, const SharedObjectPointer& object) { + Spanner* spanner = static_cast(object.data()); + toggle(attribute, spanner->getBounds(), spanner->getGranularity(), object); +} + +void MetavoxelData::toggle(const AttributePointer& attribute, const Box& bounds, + float granularity, const SharedObjectPointer& object) { + SpannerUpdateVisitor visitor(attribute, bounds, granularity, object); guide(visitor); } @@ -231,7 +236,7 @@ void MetavoxelData::expand() { _size *= 2.0f; } -void MetavoxelData::read(Bitstream& in) { +void MetavoxelData::read(Bitstream& in, const MetavoxelLOD& lod) { // clear out any existing roots decrementRootReferenceCounts(); _roots.clear(); @@ -239,27 +244,29 @@ void MetavoxelData::read(Bitstream& in) { in >> _size; // read in the new roots - int rootCount; - in >> rootCount; - for (int i = 0; i < rootCount; i++) { + forever { AttributePointer attribute; in >> attribute; - MetavoxelNode*& root = _roots[attribute]; - root = new MetavoxelNode(attribute); - attribute->read(*root, in); + if (!attribute) { + break; + } + MetavoxelStreamState state = { getMinimum(), _size, attribute, in, lod, lod }; + attribute->read(*this, state); } } -void MetavoxelData::write(Bitstream& out) const { +void MetavoxelData::write(Bitstream& out, const MetavoxelLOD& lod) const { out << _size; - out << _roots.size(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { out << it.key(); - it.key()->write(*it.value(), out); + MetavoxelStreamState state = { getMinimum(), _size, it.key(), out, lod, lod }; + it.key()->write(*it.value(), state); } + out << AttributePointer(); } -void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { +void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, + Bitstream& in, const MetavoxelLOD& lod) { // shallow copy the reference *this = reference; @@ -279,34 +286,36 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { } } - int changedCount; - in >> changedCount; - for (int i = 0; i < changedCount; i++) { + forever { AttributePointer attribute; in >> attribute; - MetavoxelNode*& root = _roots[attribute]; - if (root) { - MetavoxelNode* oldRoot = root; - root = new MetavoxelNode(attribute); - attribute->readDelta(*root, *oldRoot, in); + if (!attribute) { + break; + } + MetavoxelStreamState state = { getMinimum(), _size, attribute, in, lod, referenceLOD }; + MetavoxelNode* oldRoot = _roots.value(attribute); + if (oldRoot) { + oldRoot->incrementReferenceCount(); + attribute->readDelta(*this, *oldRoot, state); oldRoot->decrementReferenceCount(attribute); } else { - root = new MetavoxelNode(attribute); - attribute->read(*root, in); + attribute->read(*this, state); } } - int removedCount; - in >> removedCount; - for (int i = 0; i < removedCount; i++) { + forever { AttributePointer attribute; in >> attribute; + if (!attribute) { + break; + } _roots.take(attribute)->decrementReferenceCount(attribute); } } -void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const { +void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, + Bitstream& out, const MetavoxelLOD& lod) const { // first things first: there might be no change whatsoever if (_size == reference._size && _roots == reference._roots) { out << false; @@ -329,42 +338,29 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) c expandedReference = expanded; } - // count the number of roots added/changed, then write - int changedCount = 0; - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); - if (it.value() != referenceRoot) { - changedCount++; - } - } - out << changedCount; + // write the added/changed roots for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); if (it.value() != referenceRoot) { out << it.key(); + MetavoxelStreamState state = { getMinimum(), _size, it.key(), out, lod, referenceLOD }; if (referenceRoot) { - it.key()->writeDelta(*it.value(), *referenceRoot, out); + it.key()->writeDelta(*it.value(), *referenceRoot, state); } else { - it.key()->write(*it.value(), out); + it.key()->write(*it.value(), state); } } } + out << AttributePointer(); // same with nodes removed - int removedCount = 0; - for (QHash::const_iterator it = expandedReference->_roots.constBegin(); - it != expandedReference->_roots.constEnd(); it++) { - if (!_roots.contains(it.key())) { - removedCount++; - } - } - out << removedCount; for (QHash::const_iterator it = expandedReference->_roots.constBegin(); it != expandedReference->_roots.constEnd(); it++) { if (!_roots.contains(it.key())) { out << it.key(); } } + out << AttributePointer(); // delete the expanded reference if we had to expand if (expandedReference != &reference) { @@ -372,6 +368,14 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) c } } +MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) { + MetavoxelNode*& root = _roots[attribute]; + if (root) { + root->decrementReferenceCount(attribute); + } + return root = new MetavoxelNode(attribute); +} + void MetavoxelData::incrementRootReferenceCounts() { for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { it.value()->incrementReferenceCount(); @@ -384,6 +388,17 @@ 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); +} + +void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) { + minimum = getNextMinimum(lastMinimum, size, index); +} + MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceCount(1) { _attributeValue = attributeValue.copy(); for (int i = 0; i < CHILD_COUNT; i++) { @@ -431,50 +446,72 @@ bool MetavoxelNode::isLeaf() const { return true; } -void MetavoxelNode::read(const AttributePointer& attribute, Bitstream& in) { - clearChildren(attribute); +void MetavoxelNode::read(MetavoxelStreamState& state) { + clearChildren(state.attribute); + if (!state.shouldSubdivide()) { + state.attribute->read(state.stream, _attributeValue, true); + return; + } bool leaf; - in >> leaf; - attribute->read(in, _attributeValue, leaf); + state.stream >> leaf; + state.attribute->read(state.stream, _attributeValue, leaf); if (!leaf) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; for (int i = 0; i < CHILD_COUNT; i++) { - _children[i] = new MetavoxelNode(attribute); - _children[i]->read(attribute, in); + nextState.setMinimum(state.minimum, i); + _children[i] = new MetavoxelNode(state.attribute); + _children[i]->read(nextState); } } } -void MetavoxelNode::write(const AttributePointer& attribute, Bitstream& out) const { +void MetavoxelNode::write(MetavoxelStreamState& state) const { + if (!state.shouldSubdivide()) { + state.attribute->write(state.stream, _attributeValue, true); + return; + } bool leaf = isLeaf(); - out << leaf; - attribute->write(out, _attributeValue, leaf); + state.stream << leaf; + state.attribute->write(state.stream, _attributeValue, leaf); if (!leaf) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->write(attribute, out); + nextState.setMinimum(state.minimum, i); + _children[i]->write(nextState); } } } -void MetavoxelNode::readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in) { - clearChildren(attribute); +void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) { + clearChildren(state.attribute); + if (!state.shouldSubdivide()) { + state.attribute->readDelta(state.stream, _attributeValue, reference._attributeValue, true); + return; + } bool leaf; - in >> leaf; - attribute->readDelta(in, _attributeValue, reference._attributeValue, leaf); + state.stream >> leaf; + state.attribute->readDelta(state.stream, _attributeValue, reference._attributeValue, leaf); if (!leaf) { - if (reference.isLeaf()) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + if (reference.isLeaf() || !state.shouldSubdivideReference()) { for (int i = 0; i < CHILD_COUNT; i++) { - _children[i] = new MetavoxelNode(attribute); - _children[i]->read(attribute, in); + nextState.setMinimum(state.minimum, i); + _children[i] = new MetavoxelNode(state.attribute); + _children[i]->read(nextState); } } else { for (int i = 0; i < CHILD_COUNT; i++) { bool changed; - in >> changed; + state.stream >> changed; if (changed) { - _children[i] = new MetavoxelNode(attribute); - _children[i]->readDelta(attribute, *reference._children[i], in); + nextState.setMinimum(state.minimum, i); + _children[i] = new MetavoxelNode(state.attribute); + _children[i]->readDelta(*reference._children[i], nextState); } else { _children[i] = reference._children[i]; _children[i]->incrementReferenceCount(); @@ -484,28 +521,94 @@ void MetavoxelNode::readDelta(const AttributePointer& attribute, const Metavoxel } } -void MetavoxelNode::writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const { +void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const { + if (!state.shouldSubdivide()) { + state.attribute->writeDelta(state.stream, _attributeValue, reference._attributeValue, true); + return; + } bool leaf = isLeaf(); - out << leaf; - attribute->writeDelta(out, _attributeValue, reference._attributeValue, leaf); + state.stream << leaf; + state.attribute->writeDelta(state.stream, _attributeValue, reference._attributeValue, leaf); if (!leaf) { - if (reference.isLeaf()) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + if (reference.isLeaf() || !state.shouldSubdivideReference()) { for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->write(attribute, out); + nextState.setMinimum(state.minimum, i); + _children[i]->write(nextState); } } else { for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i] == reference._children[i]) { - out << false; + state.stream << false; } else { - out << true; - _children[i]->writeDelta(attribute, *reference._children[i], out); + nextState.setMinimum(state.minimum, i); + state.stream << true; + _children[i]->writeDelta(*reference._children[i], nextState); } } } } } +void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const { + foreach (const SharedObjectPointer& object, decodeInline(_attributeValue)) { + if (static_cast(object.data())->testAndSetVisited()) { + state.stream << object; + } + } + if (!state.shouldSubdivide() || isLeaf()) { + return; + } + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->writeSpanners(nextState); + } +} + +void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const { + SharedObjectSet oldSet = decodeInline(reference.getAttributeValue()); + SharedObjectSet newSet = decodeInline(_attributeValue); + foreach (const SharedObjectPointer& object, oldSet) { + if (static_cast(object.data())->testAndSetVisited() && !newSet.contains(object)) { + state.stream << object; + } + } + foreach (const SharedObjectPointer& object, newSet) { + if (static_cast(object.data())->testAndSetVisited() && !oldSet.contains(object)) { + state.stream << object; + } + } + if (isLeaf() || !state.shouldSubdivide()) { + if (!reference.isLeaf() && state.shouldSubdivideReference()) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + reference._children[i]->writeSpanners(nextState); + } + } + return; + } + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + if (reference.isLeaf() || !state.shouldSubdivideReference()) { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->writeSpanners(nextState); + } + return; + } + for (int i = 0; i < CHILD_COUNT; i++) { + if (_children[i] != reference._children[i]) { + nextState.setMinimum(state.minimum, i); + _children[i]->writeSpannerDelta(*reference._children[i], nextState); + } + } +} + void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) { if (--_referenceCount == 0) { destroy(attribute); @@ -604,10 +707,7 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { MetavoxelNode* child = node ? node->getChild(i) : NULL; nextVisitation.outputNodes[j] = child; } - nextVisitation.info.minimum = visitation.info.minimum + glm::vec3( - (i & X_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, - (i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, - (i & Z_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f); + nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, i); static_cast(nextVisitation.info.inputValues.last().getInlineValue< SharedObjectPointer>().data())->guide(nextVisitation); for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 2133ad68d8..aa2615e574 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -33,13 +33,21 @@ class SpannerRenderer; /// Determines whether to subdivide each node when traversing. class MetavoxelLOD { + STREAMABLE + public: - glm::vec3 position; - float threshold; + STREAM glm::vec3 position; + STREAM float threshold; MetavoxelLOD(const glm::vec3& position = glm::vec3(), float threshold = 0.0f); + + bool isValid() const { return threshold > 0.0f; } + + bool shouldSubdivide(const glm::vec3& minimum, float size) const; }; +DECLARE_STREAMABLE_METATYPE(MetavoxelLOD) + /// The base metavoxel representation shared between server and client. class MetavoxelData { public: @@ -52,25 +60,35 @@ public: float getSize() const { return _size; } + glm::vec3 getMinimum() const { return glm::vec3(_size, _size, _size) * -0.5f; } + Box getBounds() const; /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); + void insert(const AttributePointer& attribute, const SharedObjectPointer& object); void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + void remove(const AttributePointer& attribute, const SharedObjectPointer& object); void remove(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + void toggle(const AttributePointer& attribute, const SharedObjectPointer& object); + void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + void clear(const AttributePointer& attribute); /// Expands the tree, increasing its capacity in all dimensions. void expand(); - void read(Bitstream& in); - void write(Bitstream& out) const; + void read(Bitstream& in, const MetavoxelLOD& lod = MetavoxelLOD()); + void write(Bitstream& out, const MetavoxelLOD& lod = MetavoxelLOD()) const; - void readDelta(const MetavoxelData& reference, Bitstream& in); - void writeDelta(const MetavoxelData& reference, Bitstream& out) const; + void readDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& in, const MetavoxelLOD& lod); + void writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, + Bitstream& out, const MetavoxelLOD& lod) const; + + MetavoxelNode* createRoot(const AttributePointer& attribute); private: @@ -83,6 +101,22 @@ private: QHash _roots; }; +/// Holds the state used in streaming metavoxel data. +class MetavoxelStreamState { +public: + glm::vec3 minimum; + float size; + const AttributePointer& attribute; + Bitstream& stream; + const MetavoxelLOD& lod; + const MetavoxelLOD& referenceLOD; + + bool shouldSubdivide() const { return lod.shouldSubdivide(minimum, size); } + bool shouldSubdivideReference() const { return referenceLOD.shouldSubdivide(minimum, size); } + + void setMinimum(const glm::vec3& lastMinimum, int index); +}; + /// A single node within a metavoxel layer. class MetavoxelNode { public: @@ -104,11 +138,14 @@ public: bool isLeaf() const; - void read(const AttributePointer& attribute, Bitstream& in); - void write(const AttributePointer& attribute, Bitstream& out) const; + void read(MetavoxelStreamState& state); + void write(MetavoxelStreamState& state) const; - void readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in); - void writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const; + void readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state); + void writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; + + void writeSpanners(MetavoxelStreamState& state) const; + void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; /// Increments the node's reference count. void incrementReferenceCount() { _referenceCount++; } diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index c66ce61ee9..7ee5482179 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -6,7 +6,6 @@ // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // -#include "MetavoxelData.h" #include "MetavoxelMessages.h" void MetavoxelEditMessage::apply(MetavoxelData& data) const { @@ -106,8 +105,7 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh } void InsertSpannerEdit::apply(MetavoxelData& data) const { - Spanner* spanner = static_cast(this->spanner.data()); - data.insert(attribute, spanner->getBounds(), spanner->getGranularity(), this->spanner); + data.insert(attribute, spanner); } RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) : diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index d6f07c2966..980d198872 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -9,10 +9,7 @@ #ifndef __interface__MetavoxelMessages__ #define __interface__MetavoxelMessages__ -#include "AttributeRegistry.h" -#include "MetavoxelUtil.h" - -class MetavoxelData; +#include "MetavoxelData.h" /// Requests to close the session. class CloseSessionMessage { @@ -49,7 +46,7 @@ class ClientStateMessage { public: - STREAM glm::vec3 position; + STREAM MetavoxelLOD lod; }; DECLARE_STREAMABLE_METATYPE(ClientStateMessage) From c4f29005a0b4afce8a43b2214d86e18faed038ec Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 5 Mar 2014 14:13:20 -0800 Subject: [PATCH 011/109] Working on subdivision. --- .../metavoxels/src/AttributeRegistry.cpp | 26 ++++ libraries/metavoxels/src/AttributeRegistry.h | 6 + libraries/metavoxels/src/MetavoxelData.cpp | 133 ++++++++++++++++-- libraries/metavoxels/src/MetavoxelData.h | 9 ++ 4 files changed, 159 insertions(+), 15 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 4b643ba847..b90aa668f5 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -165,6 +165,14 @@ void Attribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& refer root.writeDelta(reference, state); } +void Attribute::readSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { + data.getRoot(state.attribute)->readSubdivision(state); +} + +void Attribute::writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { + root.writeSubdivision(state); +} + QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : InlineAttribute(name, defaultValue) { } @@ -313,3 +321,21 @@ void SpannerSetAttribute::writeDelta(const MetavoxelNode& root, const MetavoxelN root.writeSpannerDelta(reference, state); state.stream << SharedObjectPointer(); } + +void SpannerSetAttribute::readSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { + forever { + SharedObjectPointer object; + state.stream >> object; + if (!object) { + break; + } + data.insert(state.attribute, object); + } +} + +void SpannerSetAttribute::writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { + Spanner::incrementVisit(); + root.writeSpannerSubdivision(state); + state.stream << SharedObjectPointer(); +} + diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index dae0e87f37..8678e62b41 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -178,6 +178,9 @@ public: virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); + + virtual void readSubdivision(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); virtual bool equal(void* first, void* second) const = 0; @@ -332,6 +335,9 @@ public: virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); + + virtual void readSubdivision(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); }; #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 970b2a7d87..f3cce11e75 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -31,6 +31,10 @@ bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size) const { return size >= glm::distance(position, minimum + glm::vec3(size, size, size) * 0.5f) * threshold; } +bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference) const { + return shouldSubdivide(minimum, size) && !reference.shouldSubdivide(minimum, size); +} + MetavoxelData::MetavoxelData() : _size(1.0f) { } @@ -295,10 +299,15 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD MetavoxelStreamState state = { getMinimum(), _size, attribute, in, lod, referenceLOD }; MetavoxelNode* oldRoot = _roots.value(attribute); if (oldRoot) { - oldRoot->incrementReferenceCount(); - attribute->readDelta(*this, *oldRoot, state); - oldRoot->decrementReferenceCount(attribute); - + bool changed; + in >> changed; + if (changed) { + oldRoot->incrementReferenceCount(); + attribute->readDelta(*this, *oldRoot, state); + oldRoot->decrementReferenceCount(attribute); + } else { + attribute->readSubdivision(*this, state); + } } else { attribute->read(*this, state); } @@ -317,7 +326,9 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& out, const MetavoxelLOD& lod) const { // first things first: there might be no change whatsoever - if (_size == reference._size && _roots == reference._roots) { + glm::vec3 minimum = getMinimum(); + bool becameSubdivided = lod.becameSubdivided(minimum, _size, referenceLOD); + if (_size == reference._size && _roots == reference._roots && !becameSubdivided) { out << false; return; } @@ -338,14 +349,20 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO expandedReference = expanded; } - // write the added/changed roots + // write the added/changed/subdivided roots for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); - if (it.value() != referenceRoot) { - out << it.key(); - MetavoxelStreamState state = { getMinimum(), _size, it.key(), out, lod, referenceLOD }; + MetavoxelStreamState state = { minimum, _size, it.key(), out, lod, referenceLOD }; + if (it.value() != referenceRoot || becameSubdivided) { + out << it.key(); if (referenceRoot) { - it.key()->writeDelta(*it.value(), *referenceRoot, state); + if (it.value() == referenceRoot) { + out << false; + it.key()->writeSubdivision(*it.value(), state); + } else { + out << true; + it.key()->writeDelta(*it.value(), *referenceRoot, state); + } } else { it.key()->write(*it.value(), state); } @@ -506,15 +523,18 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta } } else { for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); bool changed; state.stream >> changed; - if (changed) { - nextState.setMinimum(state.minimum, i); + if (changed) { _children[i] = new MetavoxelNode(state.attribute); _children[i]->readDelta(*reference._children[i], nextState); } else { _children[i] = reference._children[i]; _children[i]->incrementReferenceCount(); + if (nextState.becameSubdivided()) { + _children[i]->readSubdivision(nextState); + } } } } @@ -539,10 +559,13 @@ void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamSt } } else { for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); if (_children[i] == reference._children[i]) { state.stream << false; - } else { - nextState.setMinimum(state.minimum, i); + if (nextState.becameSubdivided()) { + _children[i]->writeSubdivision(nextState); + } + } else { state.stream << true; _children[i]->writeDelta(*reference._children[i], nextState); } @@ -551,6 +574,63 @@ void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamSt } } +void MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { + bool leaf; + bool subdivideReference = state.shouldSubdivideReference(); + if (!subdivideReference) { + state.stream >> leaf; + } else { + leaf = isLeaf(); + } + if (leaf) { + clearChildren(state.attribute); + + } else { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + if (!subdivideReference) { + clearChildren(state.attribute); + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i] = new MetavoxelNode(state.attribute); + _children[i]->read(nextState); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + if (nextState.becameSubdivided()) { + _children[i]->readSubdivision(nextState); + } + } + } + } +} + +void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const { + bool leaf = isLeaf(); + bool subdivideReference = state.shouldSubdivideReference(); + if (!subdivideReference) { + state.stream << leaf; + } + if (!leaf) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + if (!subdivideReference) { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->write(nextState); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + if (nextState.becameSubdivided()) { + _children[i]->writeSubdivision(nextState); + } + } + } + } +} + void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const { foreach (const SharedObjectPointer& object, decodeInline(_attributeValue)) { if (static_cast(object.data())->testAndSetVisited()) { @@ -602,9 +682,32 @@ void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelS return; } for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); if (_children[i] != reference._children[i]) { - nextState.setMinimum(state.minimum, i); _children[i]->writeSpannerDelta(*reference._children[i], nextState); + + } else if (nextState.becameSubdivided()) { + _children[i]->writeSpannerSubdivision(nextState); + } + } +} + +void MetavoxelNode::writeSpannerSubdivision(MetavoxelStreamState& state) const { + if (!isLeaf()) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + if (!state.shouldSubdivideReference()) { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->writeSpanners(nextState); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + if (nextState.becameSubdivided()) { + _children[i]->writeSpannerSubdivision(nextState); + } + } } } } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index aa2615e574..0c11055b0f 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -44,6 +44,9 @@ public: bool isValid() const { return threshold > 0.0f; } bool shouldSubdivide(const glm::vec3& minimum, float size) const; + + /// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference. + bool becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelLOD) @@ -88,6 +91,7 @@ public: void writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& out, const MetavoxelLOD& lod) const; + MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); } MetavoxelNode* createRoot(const AttributePointer& attribute); private: @@ -113,6 +117,7 @@ public: bool shouldSubdivide() const { return lod.shouldSubdivide(minimum, size); } bool shouldSubdivideReference() const { return referenceLOD.shouldSubdivide(minimum, size); } + bool becameSubdivided() const { return lod.becameSubdivided(minimum, size, referenceLOD); } void setMinimum(const glm::vec3& lastMinimum, int index); }; @@ -144,8 +149,12 @@ public: void readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state); void writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; + void readSubdivision(MetavoxelStreamState& state); + void writeSubdivision(MetavoxelStreamState& state) const; + void writeSpanners(MetavoxelStreamState& state) const; void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; + void writeSpannerSubdivision(MetavoxelStreamState& state) const; /// Increments the node's reference count. void incrementReferenceCount() { _referenceCount++; } From 7f33762aab74f937b76c08f8788ec7622865809f Mon Sep 17 00:00:00 2001 From: stojce Date: Thu, 6 Mar 2014 01:49:27 +0100 Subject: [PATCH 012/109] fixed invalid define guard --- interface/src/starfield/Controller.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/starfield/Controller.h b/interface/src/starfield/Controller.h index c5ade48442..5403d2fb75 100755 --- a/interface/src/starfield/Controller.h +++ b/interface/src/starfield/Controller.h @@ -8,7 +8,7 @@ // #ifndef __interface__starfield__Controller__ -#define __interface__starfield__Confroller__ +#define __interface__starfield__Controller__ #include From 60b0281095b0172d60c22da6ec25076f49e70714 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 5 Mar 2014 17:12:02 -0800 Subject: [PATCH 013/109] Ordered visitation bits. --- interface/src/MetavoxelSystem.cpp | 33 +++-- interface/src/MetavoxelSystem.h | 10 +- libraries/metavoxels/src/MetavoxelData.cpp | 119 +++++++++++++----- libraries/metavoxels/src/MetavoxelData.h | 45 +++++-- .../metavoxels/src/MetavoxelMessages.cpp | 18 +-- 5 files changed, 162 insertions(+), 63 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 4c1b40dc23..b6b4c2b668 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -61,13 +61,14 @@ void MetavoxelSystem::simulate(float deltaTime) { // simulate the clients _points.clear(); _simulateVisitor.setDeltaTime(deltaTime); + _simulateVisitor.setOrder(-Application::getInstance()->getViewFrustum()->getDirection()); foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); MetavoxelClient* client = static_cast(node->getLinkedData()); if (client) { client->simulate(deltaTime); - client->getData().guide(_simulateVisitor); + client->guide(_simulateVisitor); } } } @@ -109,7 +110,7 @@ void MetavoxelSystem::render() { glEnableClientState(GL_NORMAL_ARRAY); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - + glDrawArrays(GL_POINTS, 0, _points.size()); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); @@ -127,7 +128,7 @@ void MetavoxelSystem::render() { QMutexLocker locker(&node->getMutex()); MetavoxelClient* client = static_cast(node->getLinkedData()); if (client) { - client->getData().guide(_renderVisitor); + client->guide(_renderVisitor); } } } @@ -147,15 +148,16 @@ MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector& points) : _points(points) { } -void MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) { +bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) { spanner->getRenderer()->simulate(_deltaTime); + return true; } -bool MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { +int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { SpannerVisitor::visit(info); if (!info.isLeaf) { - return true; + return _order; } QRgb color = info.inputValues.at(0).getInlineValue(); QRgb normal = info.inputValues.at(1).getInlineValue(); @@ -165,15 +167,16 @@ bool MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { { qRed(color), qGreen(color), qBlue(color), alpha }, { qRed(normal), qGreen(normal), qBlue(normal) } }; _points.append(point); } - return false; + return STOP_RECURSION; } MetavoxelSystem::RenderVisitor::RenderVisitor() : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()) { } -void MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) { +bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) { spanner->getRenderer()->render(1.0f); + return true; } MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : @@ -201,6 +204,16 @@ MetavoxelClient::~MetavoxelClient() { _sequencer.endPacket(); } +static MetavoxelLOD getLOD() { + const float FIXED_LOD_THRESHOLD = 0.01f; + return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD); +} + +void MetavoxelClient::guide(MetavoxelVisitor& visitor) { + visitor.setLOD(getLOD()); + _data.guide(visitor); +} + void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) { // apply immediately to local tree edit.apply(_data); @@ -211,8 +224,8 @@ void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) { void MetavoxelClient::simulate(float deltaTime) { Bitstream& out = _sequencer.startPacket(); - const float FIXED_LOD_THRESHOLD = 0.0001f; - ClientStateMessage state = { MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD) }; + + ClientStateMessage state = { getLOD() }; out << QVariant::fromValue(state); _sequencer.endPacket(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 155f99ffbd..c1833832a7 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -57,18 +57,20 @@ private: public: SimulateVisitor(QVector& points); void setDeltaTime(float deltaTime) { _deltaTime = deltaTime; } - virtual void visit(Spanner* spanner); - virtual bool visit(MetavoxelInfo& info); + void setOrder(const glm::vec3& direction) { _order = encodeOrder(direction); } + virtual bool visit(Spanner* spanner); + virtual int visit(MetavoxelInfo& info); private: QVector& _points; float _deltaTime; + int _order; }; class RenderVisitor : public SpannerVisitor { public: RenderVisitor(); - virtual void visit(Spanner* spanner); + virtual bool visit(Spanner* spanner); }; static ProgramObject _program; @@ -89,7 +91,7 @@ public: MetavoxelClient(const SharedNodePointer& node); virtual ~MetavoxelClient(); - MetavoxelData& getData() { return _data; } + void guide(MetavoxelVisitor& visitor); void applyEdit(const MetavoxelEditMessage& edit); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index f3cce11e75..18a290d6e2 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -32,7 +32,19 @@ bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size) const { } bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference) const { - return shouldSubdivide(minimum, size) && !reference.shouldSubdivide(minimum, size); + if (position == reference.position && threshold >= reference.threshold) { + return false; // first off, nothing becomes subdivided if it doesn't change + } + if (!shouldSubdivide(minimum, size)) { + return false; // this one must be subdivided + } + // the general check is whether we've gotten closer (as multiplied by the threshold) to any point in the volume, + // which we approximate as a sphere for simplicity + float halfSize = size * 0.5f; + glm::vec3 center = minimum + glm::vec3(halfSize, halfSize, halfSize); + float radius = sqrtf(3 * halfSize * halfSize); + return qMax(0.0f, glm::distance(position, center) - radius) * threshold <= + qMax(0.0f, glm::distance(reference.position, center) - radius) * reference.threshold; } MetavoxelData::MetavoxelData() : _size(1.0f) { @@ -128,7 +140,7 @@ public: SpannerUpdateVisitor(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); - virtual bool visit(MetavoxelInfo& info); + virtual int visit(MetavoxelInfo& info); private: @@ -147,17 +159,17 @@ template SpannerUpdateVisitor::SpannerUpdateVisitor( _object(object) { } -template bool SpannerUpdateVisitor::visit(MetavoxelInfo& info) { +template int SpannerUpdateVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { - return false; + return STOP_RECURSION; } if (info.size > _longestSide) { - return true; + return DEFAULT_ORDER; } SharedObjectSet set = info.inputValues.at(0).getInlineValue(); F(set, _object); info.outputValues[0] = AttributeValue(_attribute, encodeInline(set)); - return false; + return STOP_RECURSION; } void MetavoxelData::insert(const AttributePointer& attribute, const SharedObjectPointer& object) { @@ -737,9 +749,43 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) { } } -MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, const QVector& outputs) : +int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth, + int fifth, int sixth, int seventh, int eighth) { + return first | (second << 3) | (third << 6) | (fourth << 9) | + (fifth << 12) | (sixth << 15) | (seventh << 18) | (eighth << 21); +} + +class IndexDistance { +public: + int index; + float distance; +}; + +bool operator<(const IndexDistance& first, const IndexDistance& second) { + return first.distance < second.distance; +} + +int MetavoxelVisitor::encodeOrder(const glm::vec3& direction) { + QList indexDistances; + for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + IndexDistance indexDistance = { i, glm::dot(direction, getNextMinimum(glm::vec3(), 1.0f, i)) }; + indexDistances.append(indexDistance); + } + qStableSort(indexDistances); + return encodeOrder(indexDistances.at(0).index, indexDistances.at(1).index, indexDistances.at(2).index, + indexDistances.at(3).index, indexDistances.at(4).index, indexDistances.at(5).index, + indexDistances.at(6).index, indexDistances.at(7).index); +} + +const int MetavoxelVisitor::DEFAULT_ORDER = encodeOrder(0, 1, 2, 3, 4, 5, 6, 7); +const int MetavoxelVisitor::STOP_RECURSION = 0; +const int MetavoxelVisitor::SHORT_CIRCUIT = -1; + +MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, + const QVector& outputs, const MetavoxelLOD& lod) : _inputs(inputs), - _outputs(outputs) { + _outputs(outputs), + _lod(lod) { } MetavoxelVisitor::~MetavoxelVisitor() { @@ -750,8 +796,8 @@ void MetavoxelVisitor::prepare() { } SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& inputs, - const QVector& outputs) : - MetavoxelVisitor(inputs + spannerInputs, outputs), + const QVector& outputs, const MetavoxelLOD& lod) : + MetavoxelVisitor(inputs + spannerInputs, outputs, lod), _spannerInputCount(spannerInputs.size()) { } @@ -759,24 +805,30 @@ void SpannerVisitor::prepare() { Spanner::incrementVisit(); } -bool SpannerVisitor::visit(MetavoxelInfo& info) { +int SpannerVisitor::visit(MetavoxelInfo& info) { for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); if (spanner->testAndSetVisited()) { - visit(spanner); + if (!visit(spanner)) { + return SHORT_CIRCUIT; + } } } } - return !info.isLeaf; + return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; } DefaultMetavoxelGuide::DefaultMetavoxelGuide() { } -void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - visitation.info.isLeaf = visitation.allInputNodesLeaves(); - bool keepGoing = visitation.visitor.visit(visitation.info); +bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { + bool shouldSubdivide = visitation.visitor.getLOD().shouldSubdivide(visitation.info.minimum, visitation.info.size); + visitation.info.isLeaf = !shouldSubdivide || visitation.allInputNodesLeaves(); + int encodedOrder = visitation.visitor.visit(visitation.info); + if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { + return false; + } for (int i = 0; i < visitation.outputNodes.size(); i++) { OwnedAttributeValue& value = visitation.info.outputValues[i]; if (!value.getAttribute()) { @@ -790,29 +842,35 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { node = new MetavoxelNode(value); } } - if (!keepGoing) { - return; + if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) { + return true; } MetavoxelVisitation nextVisitation = { &visitation, visitation.visitor, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()), { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + const int ORDER_ELEMENT_BITS = 3; + const int ORDER_ELEMENT_MASK = (1 << ORDER_ELEMENT_BITS) - 1; + int index = encodedOrder & ORDER_ELEMENT_MASK; + encodedOrder >>= ORDER_ELEMENT_BITS; for (int j = 0; j < visitation.inputNodes.size(); j++) { MetavoxelNode* node = visitation.inputNodes.at(j); - MetavoxelNode* child = node ? node->getChild(i) : NULL; + MetavoxelNode* child = (node && shouldSubdivide) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? child->getAttributeValue(visitation.info.inputValues[j].getAttribute()) : visitation.info.inputValues[j]; } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = node ? node->getChild(i) : NULL; + MetavoxelNode* child = (node && shouldSubdivide) ? node->getChild(index) : NULL; nextVisitation.outputNodes[j] = child; } - nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, i); - static_cast(nextVisitation.info.inputValues.last().getInlineValue< - SharedObjectPointer>().data())->guide(nextVisitation); + nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index); + if (!static_cast(nextVisitation.info.inputValues.last().getInlineValue< + SharedObjectPointer>().data())->guide(nextVisitation)) { + return false; + } for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { OwnedAttributeValue& value = nextVisitation.info.outputValues[j]; if (!value.getAttribute()) { @@ -839,10 +897,10 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { // it's a leaf; we need to split it up AttributeValue nodeValue = node->getAttributeValue(value.getAttribute()); for (int k = 1; k < MetavoxelNode::CHILD_COUNT; k++) { - node->setChild((i + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue)); + node->setChild((index + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue)); } } - node->setChild(i, nextVisitation.outputNodes.at(j)); + node->setChild(index, nextVisitation.outputNodes.at(j)); value = AttributeValue(); } } @@ -854,12 +912,13 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { value = node->getAttributeValue(value.getAttribute()); } } + return true; } ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(10.0) { } -void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { +bool ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { AttributePointer colorAttribute = AttributeRegistry::getInstance()->getColorAttribute(); for (int i = 0; i < visitation.info.inputValues.size(); i++) { AttributeValue& attributeValue = visitation.info.inputValues[i]; @@ -872,7 +931,7 @@ void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } - DefaultMetavoxelGuide::guide(visitation); + return DefaultMetavoxelGuide::guide(visitation); } static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, @@ -933,7 +992,7 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin ScriptedMetavoxelGuide::ScriptedMetavoxelGuide() { } -void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { +bool ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { QScriptValue guideFunction; if (_guideFunction) { guideFunction = _guideFunction->getValue(); @@ -944,8 +1003,7 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } if (!guideFunction.isValid()) { // before we load, just use the default behavior - DefaultMetavoxelGuide::guide(visitation); - return; + return DefaultMetavoxelGuide::guide(visitation); } QScriptEngine* engine = guideFunction.engine(); if (!_minimumHandle.isValid()) { @@ -983,6 +1041,7 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { if (engine->hasUncaughtException()) { qDebug() << "Script error: " << engine->uncaughtException().toString(); } + return true; } void ScriptedMetavoxelGuide::setURL(const ParameterizedURL& url) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 0c11055b0f..48246bb633 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -194,8 +194,24 @@ public: class MetavoxelVisitor { public: + /// Encodes a visitation order sequence for the children of a metavoxel. + static int encodeOrder(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth); + + /// Encodes a visitation order sequence that visits each child as sorted along the specified direction. + static int encodeOrder(const glm::vec3& direction); + + /// The default visitation order. + static const int DEFAULT_ORDER; + + /// A special "order" that instructs the guide to stop recursion. + static const int STOP_RECURSION; + + /// A special "order" that short-circuits the tour. + static const int SHORT_CIRCUIT; + MetavoxelVisitor(const QVector& inputs, - const QVector& outputs = QVector()); + const QVector& outputs = QVector(), + const MetavoxelLOD& lod = MetavoxelLOD()); virtual ~MetavoxelVisitor(); /// Returns a reference to the list of input attributes desired. @@ -204,18 +220,24 @@ public: /// Returns a reference to the list of output attributes provided. const QVector& getOutputs() const { return _outputs; } + /// Returns a reference to the level of detail that will determine subdivision levels. + const MetavoxelLOD& getLOD() const { return _lod; } + + void setLOD(const MetavoxelLOD& lod) { _lod = lod; } + /// Prepares for a new tour of the metavoxel data. virtual void prepare(); /// Visits a metavoxel. /// \param info the metavoxel data - /// \return if true, continue descending; if false, stop - virtual bool visit(MetavoxelInfo& info) = 0; + /// \return the encoded order in which to traverse the children, zero to stop recursion, or -1 to short-circuit the tour + virtual int visit(MetavoxelInfo& info) = 0; protected: QVector _inputs; QVector _outputs; + MetavoxelLOD _lod; }; typedef QSharedPointer MetavoxelVisitorPointer; @@ -226,13 +248,15 @@ public: SpannerVisitor(const QVector& spannerInputs, const QVector& inputs = QVector(), - const QVector& outputs = QVector()); + const QVector& outputs = QVector(), + const MetavoxelLOD& lod = MetavoxelLOD()); /// Visits a spanner. - virtual void visit(Spanner* spanner) = 0; + /// \return true to continue, false to short-circuit the tour + virtual bool visit(Spanner* spanner) = 0; virtual void prepare(); - virtual bool visit(MetavoxelInfo& info); + virtual int visit(MetavoxelInfo& info); protected: @@ -246,7 +270,8 @@ class MetavoxelGuide : public SharedObject { public: /// Guides the specified visitor to the contained voxels. - virtual void guide(MetavoxelVisitation& visitation) = 0; + /// \return true to keep going, false to short circuit the tour + virtual bool guide(MetavoxelVisitation& visitation) = 0; }; /// Guides visitors through the explicit content of the system. @@ -257,7 +282,7 @@ public: Q_INVOKABLE DefaultMetavoxelGuide(); - virtual void guide(MetavoxelVisitation& visitation); + virtual bool guide(MetavoxelVisitation& visitation); }; /// A temporary test guide that just makes the existing voxels throb with delight. @@ -269,7 +294,7 @@ public: Q_INVOKABLE ThrobbingMetavoxelGuide(); - virtual void guide(MetavoxelVisitation& visitation); + virtual bool guide(MetavoxelVisitation& visitation); private: @@ -285,7 +310,7 @@ public: Q_INVOKABLE ScriptedMetavoxelGuide(); - virtual void guide(MetavoxelVisitation& visitation); + virtual bool guide(MetavoxelVisitation& visitation); public slots: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 7ee5482179..bcb59dad77 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -24,7 +24,7 @@ public: BoxSetEditVisitor(const BoxSetEdit& edit); - virtual bool visit(MetavoxelInfo& info); + virtual int visit(MetavoxelInfo& info); private: @@ -36,26 +36,26 @@ BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) : _edit(edit) { } -bool BoxSetEditVisitor::visit(MetavoxelInfo& info) { +int BoxSetEditVisitor::visit(MetavoxelInfo& info) { // find the intersection between volume and voxel glm::vec3 minimum = glm::max(info.minimum, _edit.region.minimum); glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.region.maximum); glm::vec3 size = maximum - minimum; if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { - return false; // disjoint + return STOP_RECURSION; // disjoint } float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); if (volume >= 1.0f) { info.outputValues[0] = _edit.value; - return false; // entirely contained + return STOP_RECURSION; // entirely contained } if (info.size <= _edit.granularity) { if (volume >= 0.5f) { info.outputValues[0] = _edit.value; } - return false; // reached granularity limit; take best guess + return STOP_RECURSION; // reached granularity limit; take best guess } - return true; // subdivide + return DEFAULT_ORDER; // subdivide } void BoxSetEdit::apply(MetavoxelData& data) const { @@ -77,7 +77,7 @@ public: GlobalSetEditVisitor(const GlobalSetEdit& edit); - virtual bool visit(MetavoxelInfo& info); + virtual int visit(MetavoxelInfo& info); private: @@ -89,9 +89,9 @@ GlobalSetEditVisitor::GlobalSetEditVisitor(const GlobalSetEdit& edit) : _edit(edit) { } -bool GlobalSetEditVisitor::visit(MetavoxelInfo& info) { +int GlobalSetEditVisitor::visit(MetavoxelInfo& info) { info.outputValues[0] = _edit.value; - return false; // entirely contained + return STOP_RECURSION; // entirely contained } void GlobalSetEdit::apply(MetavoxelData& data) const { From 438b07290785f1617fa471563b5ef138f4140512 Mon Sep 17 00:00:00 2001 From: stojce Date: Thu, 6 Mar 2014 02:29:12 +0100 Subject: [PATCH 014/109] editVoxels - importing voxels --- examples/editVoxels.js | 92 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 9a014639f0..24ae2a7330 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -26,6 +26,10 @@ var PIXELS_PER_EXTRUDE_VOXEL = 16; var WHEEL_PIXELS_PER_SCALE_CHANGE = 100; var MAX_VOXEL_SCALE = 1.0; var MIN_VOXEL_SCALE = 1.0 / Math.pow(2.0, 8.0); +var WHITE_COLOR = { red: 255, green: 255, blue: 255 }; + +var MAX_PASTE_VOXEL_SCALE = 256; +var MIN_PASTE_VOXEL_SCALE = .256; var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting var previewLineWidth = 1.5; @@ -199,6 +203,8 @@ var voxelToolAt = 0; var recolorToolAt = 1; var eyedropperToolAt = 2; +var pasteModeColor = { red: 132, green: 61, blue: 255 }; + var voxelTool = Overlays.addOverlay("image", { x: 0, y: 0, width: toolWidth, height: toolHeight, subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, @@ -262,7 +268,7 @@ var thumb = Overlays.addOverlay("image", { visible: false }); -var pointerVoxelScale = 0; // this is the voxel scale used for click to add or delete +var pointerVoxelScale = Math.floor(MAX_VOXEL_SCALE + MIN_VOXEL_SCALE) / 2; // this is the voxel scale used for click to add or delete var pointerVoxelScaleSet = false; // if voxel scale has not yet been set, we use the intersection size var pointerVoxelScaleSteps = 8; // the number of slider position steps @@ -308,6 +314,23 @@ function setAudioPosition() { audioOptions.position = Vec3.sum(camera, forwardVector); } +function getNewPasteVoxel(pickRay) { + + var voxelSize = MIN_PASTE_VOXEL_SCALE + (MAX_PASTE_VOXEL_SCALE - MIN_PASTE_VOXEL_SCALE) * pointerVoxelScale - 1; + var distance = 1 + 30 * voxelSize / MAX_PASTE_VOXEL_SCALE; + var origin = { x: pickRay.direction.x * NEW_VOXEL_DISTANCE_FROM_CAMERA + voxelSize, y: pickRay.direction.y, z: pickRay.direction.z }; + + origin.x += pickRay.origin.x; + origin.y += pickRay.origin.y; + origin.z += pickRay.origin.z; + + origin.x -= voxelSize / 2; + origin.y -= voxelSize / 2; + origin.z -= voxelSize / 2; + + return {origin: origin, voxelSize: voxelSize}; +} + function getNewVoxelPosition() { var camera = Camera.getPosition(); var forwardVector = Quat.getFront(MyAvatar.orientation); @@ -337,6 +360,7 @@ var trackAsOrbitOrPan = false; var voxelToolSelected = true; var recolorToolSelected = false; var eyedropperToolSelected = false; +var pasteMode = false; function playRandomAddSound(audioOptions) { if (Math.random() < 0.33) { @@ -523,6 +547,38 @@ function showPreviewVoxel() { function showPreviewLines() { var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); + + if (pasteMode) { // free voxel pasting + + Overlays.editOverlay(voxelPreview, { visible: false }); + Overlays.editOverlay(linePreviewLeft, { visible: false }); + + var pasteVoxel = getNewPasteVoxel(pickRay); + + // X axis + Overlays.editOverlay(linePreviewBottom, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x + pasteVoxel.voxelSize, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z }, + visible: true + }); + + // Y axis + Overlays.editOverlay(linePreviewRight, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y + pasteVoxel.voxelSize, z: pasteVoxel.origin.z }, + visible: true + }); + + // Z axis + Overlays.editOverlay(linePreviewTop, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z - pasteVoxel.voxelSize }, + visible: true + }); + + return; + } + var intersection = Voxels.findRayIntersection(pickRay); if (intersection.intersects) { @@ -617,6 +673,8 @@ function trackKeyReleaseEvent(event) { if (editToolsOn) { if (event.text == "ESC") { pointerVoxelScaleSet = false; + pasteMode = false; + moveTools(); } if (event.text == "-") { thumbX -= thumbDeltaPerStep; @@ -821,6 +879,15 @@ function mousePressEvent(event) { var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Voxels.findRayIntersection(pickRay); audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction); + + if (pasteMode) { + var pasteVoxel = getNewPasteVoxel(pickRay); + Clipboard.pasteVoxel(pasteVoxel.origin.x, pasteVoxel.origin.y, pasteVoxel.origin.z, pasteVoxel.voxelSize); + pasteMode = false; + moveTools(); + return; + } + if (intersection.intersects) { // if the user hasn't updated the if (!pointerVoxelScaleSet) { @@ -974,21 +1041,28 @@ function cleanupMenus() { function menuItemEvent(menuItem) { // handle clipboard items - if (selectToolSelected) { + if (editToolsOn) { + var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); var intersection = Voxels.findRayIntersection(pickRay); selectedVoxel = calculateVoxelFromIntersection(intersection,"select"); if (menuItem == "Copy") { print("copying..."); Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + pasteMode = true; + moveTools(); } if (menuItem == "Cut") { print("cutting..."); Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + pasteMode = true; + moveTools(); } if (menuItem == "Paste") { print("pasting..."); Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + pasteMode = false; + moveTools(); } if (menuItem == "Delete") { print("deleting..."); @@ -1002,6 +1076,8 @@ function menuItemEvent(menuItem) { if (menuItem == "Import Voxels") { print("import"); Clipboard.importVoxels(); + pasteMode = true; + moveTools(); } if (menuItem == "Nudge") { print("nudge"); @@ -1156,19 +1232,23 @@ function moveTools() { recolorToolOffset = 1, eyedropperToolOffset = 1; - if (trackAsRecolor || recolorToolSelected) { + var voxelToolColor = WHITE_COLOR; + + if (recolorToolSelected) { recolorToolOffset = 2; - } else if (trackAsEyedropper || eyedropperToolSelected) { + } else if (eyedropperToolSelected) { eyedropperToolOffset = 2; - } else if (trackAsOrbitOrPan) { - // nothing gets selected in this case... } else { + if (pasteMode) { + voxelToolColor = pasteModeColor; + } voxelToolOffset = 2; } Overlays.editOverlay(voxelTool, { subImage: { x: 0, y: toolHeight * voxelToolOffset, width: toolWidth, height: toolHeight }, x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * voxelToolAt), width: toolWidth, height: toolHeight, + color: voxelToolColor, visible: editToolsOn }); From 23556f0cf7ce8ac55c6c4326685ca3383d21e06e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 6 Mar 2014 12:38:11 -0800 Subject: [PATCH 015/109] Fix for merging after streaming, added ability to "set" a spanner's attributes in the actual metavoxel data. --- interface/src/ui/MetavoxelEditor.cpp | 44 +++++++++--- interface/src/ui/MetavoxelEditor.h | 40 +++++++++-- libraries/metavoxels/src/MetavoxelData.cpp | 68 ++++++++++++++++++- libraries/metavoxels/src/MetavoxelData.h | 13 ++++ .../metavoxels/src/MetavoxelMessages.cpp | 37 ++++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 15 ++++ libraries/metavoxels/src/MetavoxelUtil.cpp | 11 +++ libraries/metavoxels/src/MetavoxelUtil.h | 6 ++ 8 files changed, 218 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 61f7b55968..1259be3083 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -100,6 +100,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new InsertSpannerTool(this)); addTool(new RemoveSpannerTool(this)); addTool(new ClearSpannersTool(this)); + addTool(new SetSpannerTool(this)); updateAttributes(); @@ -529,15 +530,15 @@ void GlobalSetTool::apply() { Application::getInstance()->getMetavoxels()->applyEdit(message); } -InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Insert Spanner") { +PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText) : + MetavoxelTool(editor, name) { - QPushButton* button = new QPushButton("Insert"); + QPushButton* button = new QPushButton(placeText); layout()->addWidget(button); - connect(button, SIGNAL(clicked()), SLOT(insert())); + connect(button, SIGNAL(clicked()), SLOT(place())); } -void InsertSpannerTool::simulate(float deltaTime) { +void PlaceSpannerTool::simulate(float deltaTime) { if (Application::getInstance()->isMouseHidden()) { return; } @@ -558,7 +559,7 @@ void InsertSpannerTool::simulate(float deltaTime) { spanner->getRenderer()->simulate(deltaTime); } -void InsertSpannerTool::render() { +void PlaceSpannerTool::render() { if (Application::getInstance()->isMouseHidden()) { return; } @@ -567,28 +568,36 @@ void InsertSpannerTool::render() { spanner->getRenderer()->render(SPANNER_ALPHA); } -bool InsertSpannerTool::appliesTo(const AttributePointer& attribute) const { +bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { return attribute->inherits("SpannerSetAttribute"); } -bool InsertSpannerTool::eventFilter(QObject* watched, QEvent* event) { +bool PlaceSpannerTool::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::MouseButtonPress) { - insert(); + place(); return true; } return false; } -void InsertSpannerTool::insert() { +void PlaceSpannerTool::place() { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); if (!attribute) { return; } SharedObjectPointer spanner = _editor->getValue().value(); - MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) }; + 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)); +} + RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : MetavoxelTool(editor, "Remove Spanner", false) { } @@ -625,3 +634,16 @@ void ClearSpannersTool::clear() { MetavoxelEditMessage message = { QVariant::fromValue(ClearSpannersEdit(attribute)) }; Application::getInstance()->getMetavoxels()->applyEdit(message); } + +SetSpannerTool::SetSpannerTool(MetavoxelEditor* editor) : + PlaceSpannerTool(editor, "Set Spanner", "Set") { +} + +bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const { + return attribute == AttributeRegistry::getInstance()->getSpannersAttribute(); +} + +QVariant SetSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { + static_cast(spanner.data())->setGranularity(_editor->getGridSpacing()); + return QVariant::fromValue(SetSpannerEdit(spanner)); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 5b580129a9..76ef8baf6f 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -139,13 +139,13 @@ private slots: void apply(); }; -/// Allows inserting a spanner into the scene. -class InsertSpannerTool : public MetavoxelTool { +/// Base class for insert/set spanner tools. +class PlaceSpannerTool : public MetavoxelTool { Q_OBJECT public: - InsertSpannerTool(MetavoxelEditor* editor); + PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText); virtual void simulate(float deltaTime); @@ -155,9 +155,26 @@ public: virtual bool eventFilter(QObject* watched, QEvent* event); +protected: + + virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) = 0; + private slots: - void insert(); + void place(); +}; + +/// Allows inserting a spanner into the scene. +class InsertSpannerTool : public PlaceSpannerTool { + Q_OBJECT + +public: + + InsertSpannerTool(MetavoxelEditor* editor); + +protected: + + virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); }; /// Allows removing a spanner from the scene. @@ -188,4 +205,19 @@ private slots: void clear(); }; +/// Allows setting the value by placing a spanner. +class SetSpannerTool : public PlaceSpannerTool { + Q_OBJECT + +public: + + SetSpannerTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + +protected: + + virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); +}; + #endif /* defined(__interface__MetavoxelEditor__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 18a290d6e2..f1c0b928c9 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -493,6 +493,7 @@ void MetavoxelNode::read(MetavoxelStreamState& state) { _children[i] = new MetavoxelNode(state.attribute); _children[i]->read(nextState); } + mergeChildren(state.attribute); } } @@ -549,7 +550,8 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta } } } - } + } + mergeChildren(state.attribute); } } @@ -1085,6 +1087,15 @@ void Spanner::setBounds(const Box& bounds) { emit boundsChanged(_bounds = bounds); } +const QVector& Spanner::getAttributes() const { + static QVector emptyVector; + return emptyVector; +} + +bool Spanner::getAttributeValues(MetavoxelInfo& info) const { + return false; +} + bool Spanner::testAndSetVisited() { if (_lastVisit == _visit) { return false; @@ -1164,6 +1175,43 @@ void Sphere::setColor(const QColor& color) { } } +const QVector& Sphere::getAttributes() const { + static QVector attributes = QVector() << + AttributeRegistry::getInstance()->getColorAttribute() << AttributeRegistry::getInstance()->getNormalAttribute(); + return attributes; +} + +bool Sphere::getAttributeValues(MetavoxelInfo& info) const { + // bounds check + Box bounds = info.getBounds(); + if (!getBounds().intersects(bounds)) { + return false; + } + // count the points inside the sphere + int pointsWithin = 0; + for (int i = 0; i < Box::VERTEX_COUNT; i++) { + if (glm::distance(bounds.getVertex(i), getTranslation()) <= getScale()) { + pointsWithin++; + } + } + if (pointsWithin == Box::VERTEX_COUNT) { + // entirely contained + info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); + getNormal(info); + return false; + } + if (info.size <= getGranularity()) { + // best guess + if (pointsWithin > 0) { + info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(qRgba( + _color.red(), _color.green(), _color.blue(), _color.alpha() * pointsWithin / Box::VERTEX_COUNT))); + getNormal(info); + } + return false; + } + return true; +} + QByteArray Sphere::getRendererClassName() const { return "SphereRenderer"; } @@ -1173,6 +1221,24 @@ void Sphere::updateBounds() { setBounds(Box(getTranslation() - extent, getTranslation() + extent)); } +void Sphere::getNormal(MetavoxelInfo& info) const { + glm::vec3 normal = info.getCenter() - getTranslation(); + float length = glm::length(normal); + QRgb color; + if (length > EPSILON) { + const float NORMAL_SCALE = 127.0f; + float scale = NORMAL_SCALE / length; + const int BYTE_MASK = 0xFF; + color = qRgb((int)(normal.x * scale) & BYTE_MASK, (int)(normal.y * scale) & BYTE_MASK, + (int)(normal.z * scale) & BYTE_MASK); + + } else { + const QRgb DEFAULT_NORMAL = 0x007F00; + color = DEFAULT_NORMAL; + } + info.outputValues[1] = AttributeValue(getAttributes().at(1), encodeInline(color)); +} + StaticModel::StaticModel() { } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 48246bb633..5b149c071f 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -188,6 +188,7 @@ public: bool isLeaf; Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); } + glm::vec3 getCenter() const { return minimum + glm::vec3(size, size, size) * 0.5f; } }; /// Interface for visitors to metavoxels. @@ -374,6 +375,13 @@ public: void setGranularity(float granularity) { _granularity = granularity; } float getGranularity() const { return _granularity; } + /// Returns a reference to the list of attributes associated with this spanner. + virtual const QVector& getAttributes() const; + + /// Sets the attribute values associated with this spanner in the supplied info. + /// \return true to recurse, false to stop + virtual bool getAttributeValues(MetavoxelInfo& info) const; + /// Checks whether we've visited this object on the current traversal. If we have, returns false. /// If we haven't, sets the last visit identifier and returns true. bool testAndSetVisited(); @@ -459,6 +467,9 @@ public: void setColor(const QColor& color); const QColor& getColor() const { return _color; } + virtual const QVector& getAttributes() const; + virtual bool getAttributeValues(MetavoxelInfo& info) const; + signals: void colorChanged(const QColor& color); @@ -473,6 +484,8 @@ private slots: private: + void getNormal(MetavoxelInfo& info) const; + QColor _color; }; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index bcb59dad77..1c36f7df46 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -123,3 +123,40 @@ ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : void ClearSpannersEdit::apply(MetavoxelData& data) const { data.clear(attribute); } + +class SetSpannerEditVisitor : public MetavoxelVisitor { +public: + + SetSpannerEditVisitor(Spanner* spanner); + + virtual int visit(MetavoxelInfo& info); + +private: + + Spanner* _spanner; +}; + +SetSpannerEditVisitor::SetSpannerEditVisitor(Spanner* spanner) : + MetavoxelVisitor(QVector(), spanner->getAttributes()), + _spanner(spanner) { +} + +int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { + return _spanner->getAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION; +} + +SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) : + spanner(spanner) { +} + +void SetSpannerEdit::apply(MetavoxelData& data) const { + Spanner* spanner = static_cast(this->spanner.data()); + + // expand to fit the entire spanner + while (!data.getBounds().contains(spanner->getBounds())) { + data.expand(); + } + + SetSpannerEditVisitor visitor(spanner); + data.guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 980d198872..fe229cc727 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -161,4 +161,19 @@ public: DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit) +/// An edit that sets a spanner's attributes in the voxel tree. +class SetSpannerEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM SharedObjectPointer spanner; + + SetSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer()); + + virtual void apply(MetavoxelData& data) const; +}; + +DECLARE_STREAMABLE_METATYPE(SetSpannerEdit) + #endif /* defined(__interface__MetavoxelMessages__) */ diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 86cbe87fd8..9e96b43d3f 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -162,6 +162,17 @@ bool Box::intersects(const Box& other) const { other.maximum.z >= minimum.z && other.minimum.z <= maximum.z; } +const int X_MAXIMUM_FLAG = 1; +const int Y_MAXIMUM_FLAG = 2; +const int Z_MAXIMUM_FLAG = 4; + +glm::vec3 Box::getVertex(int index) const { + return glm::vec3( + (index & X_MAXIMUM_FLAG) ? maximum.x : minimum.x, + (index & Y_MAXIMUM_FLAG) ? maximum.y : minimum.y, + (index & Z_MAXIMUM_FLAG) ? maximum.z : minimum.z); +} + Box operator*(const glm::mat4& matrix, const Box& box) { // start with the constant component Box newBox(glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]), glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2])); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index adb106b652..45a9476e4d 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -34,6 +34,8 @@ class Box { public: + static const int VERTEX_COUNT = 8; + STREAM glm::vec3 minimum; STREAM glm::vec3 maximum; @@ -44,6 +46,10 @@ public: bool intersects(const Box& other) const; float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); } + + glm::vec3 getVertex(int index) const; + + glm::vec3 getCenter() const { return (minimum + maximum) * 0.5f; } }; DECLARE_STREAMABLE_METATYPE(Box) From dbef17db597bac9b735c52717492be7af28e60d8 Mon Sep 17 00:00:00 2001 From: stojce Date: Thu, 6 Mar 2014 22:42:34 +0100 Subject: [PATCH 016/109] code style --- examples/editVoxels.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index c4694df5f8..8197f4f801 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -316,19 +316,18 @@ function setAudioPosition() { function getNewPasteVoxel(pickRay) { - var voxelSize = MIN_PASTE_VOXEL_SCALE + (MAX_PASTE_VOXEL_SCALE - MIN_PASTE_VOXEL_SCALE) * pointerVoxelScale - 1; - var distance = 1 + 30 * voxelSize / MAX_PASTE_VOXEL_SCALE; - var origin = { x: pickRay.direction.x * NEW_VOXEL_DISTANCE_FROM_CAMERA + voxelSize, y: pickRay.direction.y, z: pickRay.direction.z }; + var voxelSize = MIN_PASTE_VOXEL_SCALE + (MAX_PASTE_VOXEL_SCALE - MIN_PASTE_VOXEL_SCALE) * pointerVoxelScale - 1; + var origin = { x: pickRay.direction.x, y: pickRay.direction.y, z: pickRay.direction.z }; - origin.x += pickRay.origin.x; - origin.y += pickRay.origin.y; - origin.z += pickRay.origin.z; + origin.x += pickRay.origin.x; + origin.y += pickRay.origin.y; + origin.z += pickRay.origin.z; + + origin.x -= voxelSize / 2; + origin.y -= voxelSize / 2; + origin.z += voxelSize / 2; - origin.x -= voxelSize / 2; - origin.y -= voxelSize / 2; - origin.z -= voxelSize / 2; - - return {origin: origin, voxelSize: voxelSize}; + return {origin: origin, voxelSize: voxelSize}; } function getNewVoxelPosition() { From cfa618d6940a7c0571a303fc92ac8a6a9dc30c55 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 8 Mar 2014 23:31:23 -0800 Subject: [PATCH 017/109] cleanup constructors --- .../src/octree/OctreeQueryNode.cpp | 16 +++++++------ libraries/octree/src/JurisdictionListener.cpp | 7 +----- libraries/octree/src/JurisdictionSender.cpp | 2 +- .../octree/src/OctreeEditPacketSender.cpp | 9 ++++---- libraries/octree/src/OctreePersistThread.cpp | 3 ++- libraries/octree/src/OctreeSceneStats.cpp | 23 +++++++++---------- .../octree/src/OctreeScriptingInterface.cpp | 6 ++--- libraries/octree/src/Plane.cpp | 5 ---- libraries/script-engine/src/ScriptEngine.cpp | 23 +++++++++++-------- libraries/script-engine/src/ScriptEngine.h | 3 ++- 10 files changed, 47 insertions(+), 50 deletions(-) diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 3ff9d6820b..8cb577d2f9 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -15,7 +15,14 @@ OctreeQueryNode::OctreeQueryNode() : _viewSent(false), + _octreePacket(new unsigned char[MAX_PACKET_SIZE]), + _octreePacketAt(_octreePacket), _octreePacketAvailableBytes(MAX_PACKET_SIZE), + _octreePacketWaiting(false), + _lastOctreePacket(new unsigned char[MAX_PACKET_SIZE]), + _lastOctreePacketLength(0), + _duplicatePacketCount(0), + _firstSuppressedPacket(usecTimestampNow()), _maxSearchLevel(1), _maxLevelReachedInLastSearch(1), _lastTimeBagEmpty(0), @@ -27,14 +34,9 @@ OctreeQueryNode::OctreeQueryNode() : _lastClientBoundaryLevelAdjust(0), _lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE), _lodChanged(false), - _lodInitialized(false) + _lodInitialized(false), + _sequenceNumber(0) { - _octreePacket = new unsigned char[MAX_PACKET_SIZE]; - _octreePacketAt = _octreePacket; - _lastOctreePacket = new unsigned char[MAX_PACKET_SIZE]; - _lastOctreePacketLength = 0; - _duplicatePacketCount = 0; - _sequenceNumber = 0; } OctreeQueryNode::~OctreeQueryNode() { diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index 1ad6c69912..49cdfac741 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -16,13 +16,12 @@ #include "JurisdictionListener.h" JurisdictionListener::JurisdictionListener(NodeType_t type) : + _nodeType(type), _packetSender(JurisdictionListener::DEFAULT_PACKETS_PER_SECOND) { - _nodeType = type; ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled); - //qDebug("JurisdictionListener::JurisdictionListener(NodeType_t type=%c)", type); // tell our NodeList we want to hear about nodes with our node type NodeList::getInstance()->addNodeTypeToInterestSet(type); @@ -35,8 +34,6 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) { } bool JurisdictionListener::queueJurisdictionRequest() { - //qDebug() << "JurisdictionListener::queueJurisdictionRequest()"; - static unsigned char buffer[MAX_PACKET_SIZE]; unsigned char* bufferOut = &buffer[0]; ssize_t sizeOut = populatePacketHeader(reinterpret_cast(bufferOut), PacketTypeJurisdictionRequest); @@ -71,7 +68,6 @@ void JurisdictionListener::processPacket(const SharedNodePointer& sendingNode, c } bool JurisdictionListener::process() { - //qDebug() << "JurisdictionListener::process()"; bool continueProcessing = isStillRunning(); // If we're still running, and we don't have any requests waiting to be sent, then queue our jurisdiction requests @@ -80,7 +76,6 @@ bool JurisdictionListener::process() { } if (continueProcessing) { - //qDebug() << "JurisdictionListener::process() calling _packetSender.process()"; continueProcessing = _packetSender.process(); } if (continueProcessing) { diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index e47bcf0619..1d26e4a53a 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -19,9 +19,9 @@ JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NodeType_t type) : ReceivedPacketProcessor(), _jurisdictionMap(map), + _nodeType(type), _packetSender(JurisdictionSender::DEFAULT_PACKETS_PER_SECOND) { - _nodeType = type; } JurisdictionSender::~JurisdictionSender() { diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index e47730ccd4..bb8ff9d8a0 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -17,10 +17,11 @@ #include "OctreeEditPacketSender.h" -EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) { - _nodeUUID = nodeUUID; - _currentType = type; - _currentSize = length; +EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) : + _nodeUUID(nodeUUID), + _currentType(type), + _currentSize(length) +{ memcpy(_currentBuffer, buffer, length); }; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 3de3085a36..29715f9d90 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -19,7 +19,8 @@ OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, _filename(filename), _persistInterval(persistInterval), _initialLoadComplete(false), - _loadTimeUSecs(0) { + _loadTimeUSecs(0) +{ } bool OctreePersistThread::process() { diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 8a5a731cff..794884334f 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -20,23 +20,22 @@ const int samples = 100; OctreeSceneStats::OctreeSceneStats() : - _elapsedAverage(samples), + _isReadyToSend(false), + _isStarted(false), + _lastFullElapsed(0), + _elapsedAverage(samples), _bitsPerOctreeAverage(samples), + _lastFullTotalEncodeTime(0), + _incomingPacket(0), + _incomingBytes(0), + _incomingWastedBytes(0), + _incomingLastSequence(0), + _incomingOutOfOrder(0), + _incomingLikelyLost(0), _incomingFlightTimeAverage(samples), _jurisdictionRoot(NULL) { reset(); - _isReadyToSend = false; - _isStarted = false; - _lastFullTotalEncodeTime = 0; - _lastFullElapsed = 0; - _incomingPacket = 0; - _incomingBytes = 0; - _incomingWastedBytes = 0; - _incomingLastSequence = 0; - _incomingOutOfOrder = 0; - _incomingLikelyLost = 0; - } // copy constructor diff --git a/libraries/octree/src/OctreeScriptingInterface.cpp b/libraries/octree/src/OctreeScriptingInterface.cpp index 1ed82564b6..982496bc58 100644 --- a/libraries/octree/src/OctreeScriptingInterface.cpp +++ b/libraries/octree/src/OctreeScriptingInterface.cpp @@ -12,14 +12,12 @@ OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender, JurisdictionListener* jurisdictionListener) : - _packetSender(NULL), - _jurisdictionListener(NULL), + _packetSender(packetSender), + _jurisdictionListener(jurisdictionListener), _managedPacketSender(false), _managedJurisdictionListener(false), _initialized(false) { - setPacketSender(packetSender); - setJurisdictionListener(jurisdictionListener); } OctreeScriptingInterface::~OctreeScriptingInterface() { diff --git a/libraries/octree/src/Plane.cpp b/libraries/octree/src/Plane.cpp index 0a1a7ed86c..6a356784b7 100755 --- a/libraries/octree/src/Plane.cpp +++ b/libraries/octree/src/Plane.cpp @@ -12,11 +12,6 @@ #include -// These are some useful utilities that vec3 is missing -void printVec3(const char* name, const glm::vec3& v) { - printf("%s x=%f y=%f z=%f\n", name, v.x, v.y, v.z); -} - void Plane::set3Points(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { glm::vec3 linev1v2, linev1v3; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 59ce841969..f29b4e471d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -44,22 +44,28 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface) : + + _scriptContents(scriptContents), + _isFinished(false), + _isRunning(false), + _isInitialized(false), + _engine(), _isAvatar(false), _avatarIdentityTimer(NULL), _avatarBillboardTimer(NULL), - _avatarData(NULL) + _timerFunctionMap(), + _controllerScriptingInterface(controllerScriptingInterface), + _avatarData(NULL), + _wantMenuItems(wantMenuItems), + _scriptMenuName(), + _fileNameString(fileNameString), + _quatLibrary(), + _vec3Library() { - _scriptContents = scriptContents; - _isFinished = false; - _isRunning = false; - _isInitialized = false; - _fileNameString = fileNameString; - QByteArray fileNameAscii = fileNameString.toLocal8Bit(); const char* scriptMenuName = fileNameAscii.data(); // some clients will use these menu features - _wantMenuItems = wantMenuItems; if (!fileNameString.isEmpty()) { _scriptMenuName = "Stop "; _scriptMenuName.append(scriptMenuName); @@ -69,7 +75,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co _scriptMenuName.append(_scriptNumber); } _scriptNumber++; - _controllerScriptingInterface = controllerScriptingInterface; } ScriptEngine::~ScriptEngine() { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 6b9ec9c9eb..186524eb7f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -96,13 +96,14 @@ private: static VoxelsScriptingInterface _voxelsScriptingInterface; static ParticlesScriptingInterface _particlesScriptingInterface; + static int _scriptNumber; + AbstractControllerScriptingInterface* _controllerScriptingInterface; AudioScriptingInterface _audioScriptingInterface; AvatarData* _avatarData; bool _wantMenuItems; QString _scriptMenuName; QString _fileNameString; - static int _scriptNumber; Quat _quatLibrary; Vec3 _vec3Library; }; From 7fb872c1d7f31ae6bbeb98930a702e77a17e9a2b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 8 Mar 2014 23:53:44 -0800 Subject: [PATCH 018/109] remove dead VoxelSceneStats class and clean up Voxel Stats to be OctreeStats --- interface/src/Application.cpp | 36 ++++++------- interface/src/Application.h | 12 ++--- interface/src/Menu.cpp | 26 +++++----- interface/src/Menu.h | 12 ++--- ...lStatsDialog.cpp => OctreeStatsDialog.cpp} | 50 +++++++++---------- ...VoxelStatsDialog.h => OctreeStatsDialog.h} | 18 +++---- 6 files changed, 77 insertions(+), 77 deletions(-) rename interface/src/ui/{VoxelStatsDialog.cpp => OctreeStatsDialog.cpp} (90%) rename interface/src/ui/{VoxelStatsDialog.h => OctreeStatsDialog.h} (78%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 28285cd7da..a2ab5fcb2a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -60,7 +60,7 @@ #include #include #include -#include +#include #include #include "Application.h" @@ -1825,9 +1825,9 @@ void Application::updateDialogs(float deltaTime) { bandwidthDialog->update(); } - VoxelStatsDialog* voxelStatsDialog = Menu::getInstance()->getVoxelStatsDialog(); - if (voxelStatsDialog) { - voxelStatsDialog->update(); + OctreeStatsDialog* octreeStatsDialog = Menu::getInstance()->getOctreeStatsDialog(); + if (octreeStatsDialog) { + octreeStatsDialog->update(); } } @@ -2733,9 +2733,9 @@ void Application::displayStats() { unsigned long totalNodes = 0; unsigned long totalInternal = 0; unsigned long totalLeaves = 0; - for(NodeToVoxelSceneStatsIterator i = _octreeServerSceneStats.begin(); i != _octreeServerSceneStats.end(); i++) { + for(NodeToOctreeSceneStatsIterator i = _octreeServerSceneStats.begin(); i != _octreeServerSceneStats.end(); i++) { //const QUuid& uuid = i->first; - VoxelSceneStats& stats = i->second; + OctreeSceneStats& stats = i->second; serverCount++; if (_statsExpanded) { if (serverCount > 1) { @@ -3288,11 +3288,11 @@ void Application::nodeKilled(SharedNodePointer node) { } // also clean up scene stats for that server - _voxelSceneStatsLock.lockForWrite(); + _octreeSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { _octreeServerSceneStats.erase(nodeUUID); } - _voxelSceneStatsLock.unlock(); + _octreeSceneStatsLock.unlock(); } else if (node->getType() == NodeType::ParticleServer) { QUuid nodeUUID = node->getUUID(); @@ -3319,11 +3319,11 @@ void Application::nodeKilled(SharedNodePointer node) { } // also clean up scene stats for that server - _voxelSceneStatsLock.lockForWrite(); + _octreeSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { _octreeServerSceneStats.erase(nodeUUID); } - _voxelSceneStatsLock.unlock(); + _octreeSceneStatsLock.unlock(); } else if (node->getType() == NodeType::AvatarMixer) { // our avatar mixer has gone away - clear the hash of avatars @@ -3338,12 +3338,12 @@ void Application::trackIncomingVoxelPacket(const QByteArray& packet, const Share QUuid nodeUUID = sendingNode->getUUID(); // now that we know the node ID, let's add these stats to the stats for that node... - _voxelSceneStatsLock.lockForWrite(); + _octreeSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { - VoxelSceneStats& stats = _octreeServerSceneStats[nodeUUID]; + OctreeSceneStats& stats = _octreeServerSceneStats[nodeUUID]; stats.trackIncomingOctreePacket(packet, wasStatsPacket, sendingNode->getClockSkewUsec()); } - _voxelSceneStatsLock.unlock(); + _octreeSceneStatsLock.unlock(); } } @@ -3353,7 +3353,7 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin // parse the incoming stats datas stick it in a temporary object for now, while we // determine which server it belongs to - VoxelSceneStats temp; + OctreeSceneStats temp; int statsMessageLength = temp.unpackFromMessage(reinterpret_cast(packet.data()), packet.size()); // quick fix for crash... why would voxelServer be NULL? @@ -3361,14 +3361,14 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin QUuid nodeUUID = sendingNode->getUUID(); // now that we know the node ID, let's add these stats to the stats for that node... - _voxelSceneStatsLock.lockForWrite(); + _octreeSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { _octreeServerSceneStats[nodeUUID].unpackFromMessage(reinterpret_cast(packet.data()), packet.size()); } else { _octreeServerSceneStats[nodeUUID] = temp; } - _voxelSceneStatsLock.unlock(); + _octreeSceneStatsLock.unlock(); VoxelPositionSize rootDetails; voxelDetailsForCode(temp.getJurisdictionRoot(), rootDetails); @@ -3397,8 +3397,8 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin } // store jurisdiction details for later use // This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it - // but VoxelSceneStats thinks it's just returning a reference to it's contents. So we need to make a copy of the - // details from the VoxelSceneStats to construct the JurisdictionMap + // but OctreeSceneStats thinks it's just returning a reference to it's contents. So we need to make a copy of the + // details from the OctreeSceneStats to construct the JurisdictionMap JurisdictionMap jurisdictionMap; jurisdictionMap.copyContents(temp.getJurisdictionRoot(), temp.getJurisdictionEndNodes()); (*jurisdiction)[nodeUUID] = jurisdictionMap; diff --git a/interface/src/Application.h b/interface/src/Application.h index a93d3d1352..234f264447 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -65,7 +65,7 @@ #include "renderer/VoxelShader.h" #include "ui/BandwidthDialog.h" #include "ui/ChatEntry.h" -#include "ui/VoxelStatsDialog.h" +#include "ui/OctreeStatsDialog.h" #include "ui/RearMirrorTools.h" #include "ui/LodToolsDialog.h" #include "ui/LogDialog.h" @@ -170,9 +170,9 @@ public: BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } QSettings* getSettings() { return _settings; } QMainWindow* getWindow() { return _window; } - NodeToVoxelSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } - void lockVoxelSceneStats() { _voxelSceneStatsLock.lockForRead(); } - void unlockVoxelSceneStats() { _voxelSceneStatsLock.unlock(); } + NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } + void lockOctreeSceneStats() { _octreeSceneStatsLock.lockForRead(); } + void unlockOctreeSceneStats() { _octreeSceneStatsLock.unlock(); } QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } GeometryCache* getGeometryCache() { return &_geometryCache; } @@ -459,8 +459,8 @@ private: NodeToJurisdictionMap _voxelServerJurisdictions; NodeToJurisdictionMap _particleServerJurisdictions; - NodeToVoxelSceneStats _octreeServerSceneStats; - QReadWriteLock _voxelSceneStatsLock; + NodeToOctreeSceneStats _octreeServerSceneStats; + QReadWriteLock _octreeSceneStatsLock; std::vector _voxelFades; ControllerScriptingInterface _controllerScriptingInterface; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0674826342..e4fa0c49e0 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -66,7 +66,7 @@ Menu::Menu() : _faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION), _frustumDrawMode(FRUSTUM_DRAW_MODE_ALL), _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET), - _voxelStatsDialog(NULL), + _octreeStatsDialog(NULL), _lodToolsDialog(NULL), _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), @@ -213,7 +213,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); - addActionToQMenuAndActionHash(viewMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails())); + addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails())); QMenu* developerMenu = addMenu("Developer"); @@ -341,7 +341,7 @@ Menu::Menu() : Menu::~Menu() { bandwidthDetailsClosed(); - voxelStatsDetailsClosed(); + octreeStatsDetailsClosed(); } void Menu::loadSettings(QSettings* settings) { @@ -1033,20 +1033,20 @@ void Menu::bandwidthDetailsClosed() { } } -void Menu::voxelStatsDetails() { - if (!_voxelStatsDialog) { - _voxelStatsDialog = new VoxelStatsDialog(Application::getInstance()->getGLWidget(), +void Menu::octreeStatsDetails() { + if (!_octreeStatsDialog) { + _octreeStatsDialog = new OctreeStatsDialog(Application::getInstance()->getGLWidget(), Application::getInstance()->getOcteeSceneStats()); - connect(_voxelStatsDialog, SIGNAL(closed()), SLOT(voxelStatsDetailsClosed())); - _voxelStatsDialog->show(); + connect(_octreeStatsDialog, SIGNAL(closed()), SLOT(octreeStatsDetailsClosed())); + _octreeStatsDialog->show(); } - _voxelStatsDialog->raise(); + _octreeStatsDialog->raise(); } -void Menu::voxelStatsDetailsClosed() { - if (_voxelStatsDialog) { - delete _voxelStatsDialog; - _voxelStatsDialog = NULL; +void Menu::octreeStatsDetailsClosed() { + if (_octreeStatsDialog) { + delete _octreeStatsDialog; + _octreeStatsDialog = NULL; } } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 204b93dd4a..d8a7672972 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -53,7 +53,7 @@ class QSettings; class BandwidthDialog; class LodToolsDialog; class MetavoxelEditor; -class VoxelStatsDialog; +class OctreeStatsDialog; class MenuItemProperties; class Menu : public QMenuBar { @@ -73,7 +73,7 @@ public: BandwidthDialog* getBandwidthDialog() const { return _bandwidthDialog; } FrustumDrawMode getFrustumDrawMode() const { return _frustumDrawMode; } ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; } - VoxelStatsDialog* getVoxelStatsDialog() const { return _voxelStatsDialog; } + OctreeStatsDialog* getOctreeStatsDialog() const { return _octreeStatsDialog; } LodToolsDialog* getLodToolsDialog() const { return _lodToolsDialog; } int getMaxVoxels() const { return _maxVoxels; } QAction* getUseVoxelShader() const { return _useVoxelShader; } @@ -111,7 +111,7 @@ public slots: void loginForCurrentDomain(); void bandwidthDetails(); - void voxelStatsDetails(); + void octreeStatsDetails(); void lodTools(); void loadSettings(QSettings* settings = NULL); void saveSettings(QSettings* settings = NULL); @@ -135,7 +135,7 @@ private slots: void goToDomainDialog(); void goToLocation(); void bandwidthDetailsClosed(); - void voxelStatsDetailsClosed(); + void octreeStatsDetailsClosed(); void lodToolsClosed(); void cycleFrustumRenderMode(); void runTests(); @@ -187,7 +187,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QPointer _MetavoxelEditor; - VoxelStatsDialog* _voxelStatsDialog; + OctreeStatsDialog* _octreeStatsDialog; LodToolsDialog* _lodToolsDialog; int _maxVoxels; float _voxelSizeScale; @@ -286,7 +286,7 @@ namespace MenuOption { const QString Quit = "Quit"; const QString Voxels = "Voxels"; const QString VoxelMode = "Cycle Voxel Mode"; - const QString VoxelStats = "Voxel Stats"; + const QString OctreeStats = "Voxel and Particle Statistics"; const QString VoxelTextures = "Voxel Textures"; } diff --git a/interface/src/ui/VoxelStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp similarity index 90% rename from interface/src/ui/VoxelStatsDialog.cpp rename to interface/src/ui/OctreeStatsDialog.cpp index 0ac0a2b933..974964da04 100644 --- a/interface/src/ui/VoxelStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -1,5 +1,5 @@ // -// VoxelStatsDialog.cpp +// OctreeStatsDialog.cpp // interface // // Created by Brad Hefta-Gaub on 7/19/13. @@ -14,13 +14,13 @@ #include #include -#include +#include #include "Application.h" -#include "ui/VoxelStatsDialog.h" +#include "ui/OctreeStatsDialog.h" -VoxelStatsDialog::VoxelStatsDialog(QWidget* parent, NodeToVoxelSceneStats* model) : +OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) : QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint), _model(model) { @@ -52,7 +52,7 @@ VoxelStatsDialog::VoxelStatsDialog(QWidget* parent, NodeToVoxelSceneStats* model layout()->setSizeConstraint(QLayout::SetFixedSize); } -void VoxelStatsDialog::RemoveStatItem(int item) { +void OctreeStatsDialog::RemoveStatItem(int item) { QLabel* myLabel = _labels[item]; QWidget* automaticLabel = _form->labelForField(myLabel); _form->removeWidget(myLabel); @@ -62,7 +62,7 @@ void VoxelStatsDialog::RemoveStatItem(int item) { _labels[item] = NULL; } -void VoxelStatsDialog::moreless(const QString& link) { +void OctreeStatsDialog::moreless(const QString& link) { QStringList linkDetails = link.split("-"); const int COMMAND_ITEM = 0; const int SERVER_NUMBER_ITEM = 1; @@ -80,7 +80,7 @@ void VoxelStatsDialog::moreless(const QString& link) { } -int VoxelStatsDialog::AddStatItem(const char* caption, unsigned colorRGBA) { +int OctreeStatsDialog::AddStatItem(const char* caption, unsigned colorRGBA) { char strBuf[64]; const int STATS_LABEL_WIDTH = 600; @@ -109,13 +109,13 @@ int VoxelStatsDialog::AddStatItem(const char* caption, unsigned colorRGBA) { return _statCount; } -VoxelStatsDialog::~VoxelStatsDialog() { +OctreeStatsDialog::~OctreeStatsDialog() { for (int i = 0; i < _statCount; i++) { delete _labels[i]; } } -void VoxelStatsDialog::paintEvent(QPaintEvent* event) { +void OctreeStatsDialog::paintEvent(QPaintEvent* event) { // Update labels @@ -171,11 +171,11 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) { unsigned long totalInternal = 0; unsigned long totalLeaves = 0; - Application::getInstance()->lockVoxelSceneStats(); - NodeToVoxelSceneStats* sceneStats = Application::getInstance()->getOcteeSceneStats(); - for(NodeToVoxelSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) { + Application::getInstance()->lockOctreeSceneStats(); + NodeToOctreeSceneStats* sceneStats = Application::getInstance()->getOcteeSceneStats(); + for(NodeToOctreeSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) { //const QUuid& uuid = i->first; - VoxelSceneStats& stats = i->second; + OctreeSceneStats& stats = i->second; serverCount++; // calculate server node totals @@ -194,7 +194,7 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) { sendingMode << "S"; } } - Application::getInstance()->unlockVoxelSceneStats(); + Application::getInstance()->unlockOctreeSceneStats(); sendingMode << " - " << serverCount << " servers"; if (movingServerCount > 0) { sendingMode << " "; @@ -221,7 +221,7 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) { this->QDialog::paintEvent(event); } -void VoxelStatsDialog::showAllOctreeServers() { +void OctreeStatsDialog::showAllOctreeServers() { int serverCount = 0; showOctreeServersOfType(serverCount, NodeType::VoxelServer, "Voxel", @@ -239,7 +239,7 @@ void VoxelStatsDialog::showAllOctreeServers() { } } -void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName, +void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions) { QLocale locale(QLocale::English); @@ -303,10 +303,10 @@ void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t serv // now lookup stats details for this server... if (_extraServerDetails[serverCount-1] != LESS) { - Application::getInstance()->lockVoxelSceneStats(); - NodeToVoxelSceneStats* sceneStats = Application::getInstance()->getOcteeSceneStats(); + Application::getInstance()->lockOctreeSceneStats(); + NodeToOctreeSceneStats* sceneStats = Application::getInstance()->getOcteeSceneStats(); if (sceneStats->find(nodeUUID) != sceneStats->end()) { - VoxelSceneStats& stats = sceneStats->at(nodeUUID); + OctreeSceneStats& stats = sceneStats->at(nodeUUID); switch (_extraServerDetails[serverCount-1]) { case MOST: { @@ -323,9 +323,9 @@ void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t serv "Encode Time: " << lastFullEncodeString.toLocal8Bit().constData() << " ms " << "Send Time: " << lastFullSendString.toLocal8Bit().constData() << " ms "; - for (int i = 0; i < VoxelSceneStats::ITEM_COUNT; i++) { - VoxelSceneStats::Item item = (VoxelSceneStats::Item)(i); - VoxelSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); + for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { + OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); + OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); extraDetails << "
" << itemInfo.caption << " " << stats.getItemValue(item); } } // fall through... since MOST has all of MORE @@ -386,7 +386,7 @@ void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t serv } break; } } - Application::getInstance()->unlockVoxelSceneStats(); + Application::getInstance()->unlockOctreeSceneStats(); } else { linkDetails << " " << " [more...]"; linkDetails << " " << " [most...]"; @@ -397,12 +397,12 @@ void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t serv } } -void VoxelStatsDialog::reject() { +void OctreeStatsDialog::reject() { // Just regularly close upon ESC this->QDialog::close(); } -void VoxelStatsDialog::closeEvent(QCloseEvent* event) { +void OctreeStatsDialog::closeEvent(QCloseEvent* event) { this->QDialog::closeEvent(event); emit closed(); } diff --git a/interface/src/ui/VoxelStatsDialog.h b/interface/src/ui/OctreeStatsDialog.h similarity index 78% rename from interface/src/ui/VoxelStatsDialog.h rename to interface/src/ui/OctreeStatsDialog.h index f3a54241b5..ef190e6a52 100644 --- a/interface/src/ui/VoxelStatsDialog.h +++ b/interface/src/ui/OctreeStatsDialog.h @@ -1,30 +1,30 @@ // -// VoxelStatsDialog.h +// OctreeStatsDialog.h // interface // // Created by Brad Hefta-Gaub on 7/19/13. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#ifndef __hifi__VoxelStatsDialog__ -#define __hifi__VoxelStatsDialog__ +#ifndef __hifi__OctreeStatsDialog__ +#define __hifi__OctreeStatsDialog__ #include #include #include -#include +#include #define MAX_STATS 100 #define MAX_VOXEL_SERVERS 50 #define DEFAULT_COLOR 0 -class VoxelStatsDialog : public QDialog { +class OctreeStatsDialog : public QDialog { Q_OBJECT public: // Sets up the UI - VoxelStatsDialog(QWidget* parent, NodeToVoxelSceneStats* model); - ~VoxelStatsDialog(); + OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model); + ~OctreeStatsDialog(); signals: void closed(); @@ -53,7 +53,7 @@ private: QFormLayout* _form; QLabel* _labels[MAX_STATS]; - NodeToVoxelSceneStats* _model; + NodeToOctreeSceneStats* _model; int _statCount; int _sendingMode; @@ -66,5 +66,5 @@ private: details _extraServerDetails[MAX_VOXEL_SERVERS]; }; -#endif /* defined(__interface__VoxelStatsDialog__) */ +#endif /* defined(__interface__OctreeStatsDialog__) */ From f4d487464a2df051edae4911d2575a23303cd338 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 9 Mar 2014 00:01:47 -0800 Subject: [PATCH 019/109] more constructor cleanup --- libraries/particles/src/ParticleTree.cpp | 3 +-- libraries/script-engine/src/ScriptEngine.cpp | 1 - libraries/voxels/src/VoxelSceneStats.cpp | 14 ---------- libraries/voxels/src/VoxelSceneStats.h | 28 -------------------- libraries/voxels/src/VoxelTree.cpp | 3 ++- 5 files changed, 3 insertions(+), 46 deletions(-) delete mode 100644 libraries/voxels/src/VoxelSceneStats.cpp delete mode 100644 libraries/voxels/src/VoxelSceneStats.h diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index a254305789..57f6a6e665 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -9,8 +9,7 @@ #include "ParticleTree.h" ParticleTree::ParticleTree(bool shouldReaverage) : Octree(shouldReaverage) { - ParticleTreeElement* rootNode = createNewElement(); - _rootNode = rootNode; + _rootNode = createNewElement(); } ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f29b4e471d..04916687fb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -78,7 +78,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co } ScriptEngine::~ScriptEngine() { - //printf("ScriptEngine::~ScriptEngine()...\n"); } void ScriptEngine::setIsAvatar(bool isAvatar) { diff --git a/libraries/voxels/src/VoxelSceneStats.cpp b/libraries/voxels/src/VoxelSceneStats.cpp deleted file mode 100644 index 2f486a2fa4..0000000000 --- a/libraries/voxels/src/VoxelSceneStats.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// -// VoxelSceneStats.cpp -// hifi -// -// Created by Brad Hefta-Gaub on 7/18/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// - -#include "VoxelSceneStats.h" - - -// currently an alias for OctreeSceneStats - diff --git a/libraries/voxels/src/VoxelSceneStats.h b/libraries/voxels/src/VoxelSceneStats.h deleted file mode 100644 index bea563563c..0000000000 --- a/libraries/voxels/src/VoxelSceneStats.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// VoxelSceneStats.h -// hifi -// -// Created by Brad Hefta-Gaub on 7/18/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// - -#ifndef __hifi__VoxelSceneStats__ -#define __hifi__VoxelSceneStats__ - -#include -#include - -/// Collects statistics for calculating and sending a scene from a voxel server to an interface client -class VoxelSceneStats : public OctreeSceneStats { - - // currently an alias for OctreeSceneStats - -}; - -/// Map between node IDs and their reported VoxelSceneStats. Typically used by classes that need to know which nodes sent -/// which voxel stats -typedef std::map NodeToVoxelSceneStats; -typedef std::map::iterator NodeToVoxelSceneStatsIterator; - -#endif /* defined(__hifi__VoxelSceneStats__) */ diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index ea784b2e16..4c756c2257 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -20,7 +20,8 @@ // Voxel Specific operations.... -VoxelTree::VoxelTree(bool shouldReaverage) : Octree(shouldReaverage) { +VoxelTree::VoxelTree(bool shouldReaverage) : Octree(shouldReaverage) +{ _rootNode = createNewElement(); } From e2415642f07421a9fce7156e68f052b7579ba3fa Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 9 Mar 2014 00:10:31 -0800 Subject: [PATCH 020/109] temporary fix to OSX 10.8 problem by delaying creation of _networkAccessManager --- libraries/shared/src/AccountManager.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/AccountManager.cpp b/libraries/shared/src/AccountManager.cpp index 888d42ddda..7915a60690 100644 --- a/libraries/shared/src/AccountManager.cpp +++ b/libraries/shared/src/AccountManager.cpp @@ -35,7 +35,7 @@ const QString ACCOUNTS_GROUP = "accounts"; AccountManager::AccountManager() : _authURL(), - _networkAccessManager(new QNetworkAccessManager(this)), + _networkAccessManager(NULL), _pendingCallbackMap(), _accountInfo() { @@ -109,6 +109,11 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray) { + + if (!_networkAccessManager) { + _networkAccessManager = new QNetworkAccessManager(this); + } + if (hasValidAccessToken()) { QNetworkRequest authenticatedRequest; @@ -229,6 +234,11 @@ bool AccountManager::checkAndSignalForAccessToken() { } void AccountManager::requestAccessToken(const QString& login, const QString& password) { + + if (!_networkAccessManager) { + _networkAccessManager = new QNetworkAccessManager(this); + } + QNetworkRequest request; QUrl grantURL = _authURL; From 741bf9fe9cd8e37a08c8212a48fbc9b8725f884e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 10 Mar 2014 09:22:51 -0700 Subject: [PATCH 021/109] remove double definition of GLEW_STATIC --- interface/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index bdf34e7c5d..0aaf180047 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -32,7 +32,7 @@ elseif (WIN32) add_definitions( -D_USE_MATH_DEFINES ) # apparently needed to get M_PI and other defines from cmath/math.h add_definitions( -DWINDOWS_LEAN_AND_MEAN ) # needed to make sure windows doesn't go to crazy with its defines - set(GL_HEADERS "#define GLEW_STATIC\n#include \n#include \n#include ") + set(GL_HEADERS "#include \n#include \n#include ") endif () # set up the external glm library From 6d5515a41d7ad8da170227348370cf8102535e50 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 10 Mar 2014 09:27:01 -0700 Subject: [PATCH 022/109] clarify that Windows builds are 32-bit, fix double GLEW_STATIC --- BUILD.md | 4 +++- cmake/modules/FindGLUT.cmake | 8 ++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/BUILD.md b/BUILD.md index 1796aaa575..6021519b3c 100644 --- a/BUILD.md +++ b/BUILD.md @@ -126,7 +126,9 @@ They can be set in your ENV or by passing them to the cmake command on the comma Each of those designates the root directory that contains the sub-folders for each library. For example, if the GLEW_ROOT_DIR is `C:\libs\glew`, then we would expect to find an `include` folder and a `lib` folder inside `C:\libs\glew`. ####Freeglut DLL -As with the Qt libraries, you will need to make sure the directory containing `freeglut.dll` is in your path. The directory to add to your path in which the DLL is found is `FREEGLUT_DIR/bin`. If you are on 64-bit windows, the directory in your path should be the x64 subdirectory in `bin`. +As with the Qt libraries, you will need to make sure the directory containing `freeglut.dll` is in your path. The directory to add to your path in which the DLL is found is `FREEGLUT_DIR/bin`. + +*NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit version of freeglut.dll for interface.exe to run. The 32-bit version of the static library is the one linked by CMakeLists.txt* ####Building in Visual Studio Follow the same build steps from the CMake section, but pass a different generator to CMake. diff --git a/cmake/modules/FindGLUT.cmake b/cmake/modules/FindGLUT.cmake index 29ed46cb34..173f5a24ae 100644 --- a/cmake/modules/FindGLUT.cmake +++ b/cmake/modules/FindGLUT.cmake @@ -17,12 +17,8 @@ else () if (WIN32) set(WIN_GLUT_SEARCH_DIRS "${GLUT_ROOT_DIR}" "$ENV{GLUT_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/freeglut" "${OPENGL_INCLUDE_DIR}") find_path(GLUT_INCLUDE_DIR GL/glut.h PATH_SUFFIXES include HINTS ${WIN_GLUT_SEARCH_DIRS}) - - if (CMAKE_CL_64) - set(WIN_ARCH_DIR "x64") - endif() - - find_library(GLUT_glut_LIBRARY freeglut PATH_SUFFIXES lib/${WIN_ARCH_DIR} HINTS ${WIN_GLUT_SEARCH_DIRS}) + + find_library(GLUT_glut_LIBRARY freeglut PATH_SUFFIXES lib HINTS ${WIN_GLUT_SEARCH_DIRS}) else () find_path(GLUT_INCLUDE_DIR GL/glut.h "${GLUT_LOCATION}/include" From 4e382099fb8231cebd152a8840cb932a1cd7b88f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 10 Mar 2014 09:27:32 -0700 Subject: [PATCH 023/109] have FindGLEW only look for 32-bit static lib --- cmake/modules/FindGLEW.cmake | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake index b4514d3182..684acca762 100644 --- a/cmake/modules/FindGLEW.cmake +++ b/cmake/modules/FindGLEW.cmake @@ -19,14 +19,8 @@ else () set(WIN_GLEW_SEARCH_DIRS "${GLEW_ROOT_DIR}" "$ENV{GLEW_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/glew") find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${WIN_GLEW_SEARCH_DIRS}) - - if (CMAKE_CL_64) - set(WIN_ARCH_DIR "x64") - else() - set(WIN_ARCH_DIR "Win32") - endif() - - find_library(GLEW_LIBRARY glew32s PATH_SUFFIXES "lib/Release/${WIN_ARCH_DIR}" HINTS ${WIN_GLEW_SEARCH_DIRS}) + + find_library(GLEW_LIBRARY glew32s PATH_SUFFIXES "lib/Release/Win32" HINTS ${WIN_GLEW_SEARCH_DIRS}) endif () include(FindPackageHandleStandardArgs) From 002738037c020f55aae2ba901921fd76587c6c3b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 10 Mar 2014 09:28:20 -0700 Subject: [PATCH 024/109] only look for the 32-bit oculus lib on windows --- cmake/modules/FindLibOVR.cmake | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 015a1de42b..47b7d27015 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -36,26 +36,14 @@ else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) if (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) set(LIBOVR_LIBRARIES "${OVR_LIBRARY};${UDEV_LIBRARY};${XINERAMA_LIBRARY}" CACHE INTERNAL "Oculus libraries") endif (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) - elseif (WIN32) - if (CMAKE_CL_64) - set(WINDOWS_ARCH_DIR "Win32") - - if (CMAKE_BUILD_TYPE MATCHES DEBUG) - set(WINDOWS_LIBOVR_NAME "libovrd.lib") - else() - set(WINDOWS_LIBOVR_NAME "libovr.lib") - endif() + elseif (WIN32) + if (CMAKE_BUILD_TYPE MATCHES DEBUG) + set(WINDOWS_LIBOVR_NAME "libovrd.lib") else() - set(WINDOWS_ARCH_DIR "x64") - - if (CMAKE_BUILD_TYPE MATCHES DEBUG) - set(WINDOWS_LIBOVR_NAME "libovr64d.lib") - else() - set(WINDOWS_LIBOVR_NAME "libovr64.lib") - endif() + set(WINDOWS_LIBOVR_NAME "libovr.lib") endif() - find_library(LIBOVR_LIBRARIES "Lib/${WINDOWS_ARCH_DIR}/${LIBOVR_NAME}" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARIES "Lib/Win32/${LIBOVR_NAME}" HINTS ${LIBOVR_SEARCH_DIRS}) endif () if (LIBOVR_INCLUDE_DIRS AND LIBOVR_LIBRARIES) From d2f947aee131832b008a2f8a2dd2fb9beafdbdb6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 10:49:38 -0700 Subject: [PATCH 025/109] Working on LRU cache for resources. --- interface/src/renderer/GeometryCache.cpp | 12 +++++- interface/src/renderer/GeometryCache.h | 1 + interface/src/renderer/TextureCache.cpp | 52 ++++++++++++++---------- interface/src/renderer/TextureCache.h | 3 ++ libraries/metavoxels/src/ScriptCache.cpp | 2 +- libraries/shared/src/ResourceCache.cpp | 42 +++++++++++++++++++ libraries/shared/src/ResourceCache.h | 16 +++++++- 7 files changed, 103 insertions(+), 25 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 222edcf46a..a7973f4a1b 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -294,7 +294,8 @@ QSharedPointer GeometryCache::getGeometry(const QUrl& url, cons QSharedPointer GeometryCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { - QSharedPointer geometry(new NetworkGeometry(url, fallback.staticCast(), delayLoad)); + QSharedPointer geometry(new NetworkGeometry(url, fallback.staticCast(), delayLoad), + &Resource::allReferencesCleared); geometry->setLODParent(geometry); return geometry.staticCast(); } @@ -536,6 +537,15 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { QThreadPool::globalInstance()->start(new GeometryReader(_self, url, reply, _mapping)); } +void NetworkGeometry::reinsert() { + Resource::reinsert(); + + _lodParent = qWeakPointerCast(_self); + foreach (const QSharedPointer& lod, _lods) { + lod->setLODParent(_lodParent); + } +} + void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { _geometry = geometry; diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 8c59ee7dc5..6e9604e79e 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -89,6 +89,7 @@ public: protected: virtual void downloadFinished(QNetworkReply* reply); + virtual void reinsert() const; Q_INVOKABLE void setGeometry(const FBXGeometry& geometry); diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 9f67b3bd0b..e3f750e676 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -128,9 +128,12 @@ QSharedPointer TextureCache::getTexture(const QUrl& url, bool no } QSharedPointer texture = _dilatableNetworkTextures.value(url); if (texture.isNull()) { - texture = QSharedPointer(new DilatableNetworkTexture(url)); + texture = QSharedPointer(new DilatableNetworkTexture(url), &Resource::allReferencesCleared); texture->setSelf(texture); + texture->setCache(this); _dilatableNetworkTextures.insert(url, texture); + } else { + _unusedResources.removeOne(texture); } return texture; } @@ -229,7 +232,7 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) { QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { - return QSharedPointer(new NetworkTexture(url, *(const bool*)extra)); + return QSharedPointer(new NetworkTexture(url, *(const bool*)extra), &Resource::allReferencesCleared); } QOpenGLFramebufferObject* TextureCache::createFramebufferObject() { @@ -352,26 +355,6 @@ DilatableNetworkTexture::DilatableNetworkTexture(const QUrl& url) : { } -void DilatableNetworkTexture::imageLoaded(const QImage& image) { - _image = image; - - // scan out from the center to find inner and outer radii - int halfWidth = image.width() / 2; - int halfHeight = image.height() / 2; - const int BLACK_THRESHOLD = 32; - while (_innerRadius < halfWidth && qGray(image.pixel(halfWidth + _innerRadius, halfHeight)) < BLACK_THRESHOLD) { - _innerRadius++; - } - _outerRadius = _innerRadius; - const int TRANSPARENT_THRESHOLD = 32; - while (_outerRadius < halfWidth && qAlpha(image.pixel(halfWidth + _outerRadius, halfHeight)) > TRANSPARENT_THRESHOLD) { - _outerRadius++; - } - - // clear out any textures we generated before loading - _dilatedTextures.clear(); -} - QSharedPointer DilatableNetworkTexture::getDilatedTexture(float dilation) { QSharedPointer texture = _dilatedTextures.value(dilation); if (texture.isNull()) { @@ -400,3 +383,28 @@ QSharedPointer DilatableNetworkTexture::getDilatedTexture(float dilatio return texture; } +void DilatableNetworkTexture::imageLoaded(const QImage& image) { + _image = image; + + // scan out from the center to find inner and outer radii + int halfWidth = image.width() / 2; + int halfHeight = image.height() / 2; + const int BLACK_THRESHOLD = 32; + while (_innerRadius < halfWidth && qGray(image.pixel(halfWidth + _innerRadius, halfHeight)) < BLACK_THRESHOLD) { + _innerRadius++; + } + _outerRadius = _innerRadius; + const int TRANSPARENT_THRESHOLD = 32; + while (_outerRadius < halfWidth && qAlpha(image.pixel(halfWidth + _outerRadius, halfHeight)) > TRANSPARENT_THRESHOLD) { + _outerRadius++; + } + + // clear out any textures we generated before loading + _dilatedTextures.clear(); +} + +void DilatableNetworkTexture::reinsert() { + static_cast(_cache.data())->_dilatableNetworkTextures.insert(_url, + qWeakPointerCast(_self)); +} + diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index dba34a0d0e..0111a2826d 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -76,6 +76,8 @@ protected: private: + friend class DilatableNetworkTexture; + QOpenGLFramebufferObject* createFramebufferObject(); GLuint _permutationNormalTextureID; @@ -151,6 +153,7 @@ public: protected: virtual void imageLoaded(const QImage& image); + virtual void reinsert(); private: diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 702a36de2a..cabf075563 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -51,7 +51,7 @@ QSharedPointer ScriptCache::getValue(const ParameterizedURL& url) QSharedPointer ScriptCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { - return QSharedPointer(new NetworkProgram(this, url)); + return QSharedPointer(new NetworkProgram(this, url), &Resource::allReferencesCleared); } NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) : diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index a7c0302a9c..6434376cd8 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -18,6 +18,13 @@ ResourceCache::ResourceCache(QObject* parent) : QObject(parent) { } +ResourceCache::~ResourceCache() { + // make sure our unused resources know we're out of commission + foreach (const QSharedPointer& resource, _unusedResources) { + resource->setCache(NULL); + } +} + QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& fallback, bool delayLoad, void* extra) { if (!url.isValid() && fallback.isValid()) { return getResource(fallback, QUrl(), delayLoad); @@ -27,11 +34,25 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& resource = createResource(url, fallback.isValid() ? getResource(fallback, QUrl(), true) : QSharedPointer(), delayLoad, extra); resource->setSelf(resource); + resource->setCache(this); _resources.insert(url, resource); + + } else { + _unusedResources.removeOne(resource); } return resource; } +void ResourceCache::addUnusedResource(const QSharedPointer& resource) { + const int RETAINED_RESOURCE_COUNT = 1; + if (_unusedResources.size() > RETAINED_RESOURCE_COUNT) { + // unload the oldest resource + QSharedPointer oldResource = _unusedResources.takeFirst(); + oldResource->setCache(NULL); + } + _unusedResources.append(resource); +} + void ResourceCache::attemptRequest(Resource* resource) { if (_requestLimit <= 0) { // wait until a slot becomes available @@ -74,6 +95,7 @@ int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT; QList > ResourceCache::_pendingRequests; Resource::Resource(const QUrl& url, bool delayLoad) : + _url(url), _request(url), _startedLoading(false), _failedToLoad(false), @@ -141,6 +163,22 @@ float Resource::getLoadPriority() { return highestPriority; } +void Resource::allReferencesCleared() { + if (_cache) { + // create and reinsert new shared pointer + QSharedPointer self(this, &Resource::allReferencesCleared); + setSelf(self); + reinsert(); + + // add to the unused list + _cache->_unusedResources.append(self); + + } else { + qDebug() << "deleting" << _url; + delete this; + } +} + void Resource::attemptRequest() { _startedLoading = true; ResourceCache::attemptRequest(this); @@ -155,6 +193,10 @@ void Resource::finishedLoading(bool success) { _loadPriorities.clear(); } +void Resource::reinsert() { + _cache->_resources.insert(_url, _self); +} + const int REPLY_TIMEOUT_MS = 5000; void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index 8aed2bfcb0..2826902598 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -38,9 +38,12 @@ public: static int getRequestLimit() { return _requestLimit; } ResourceCache(QObject* parent = NULL); + virtual ~ResourceCache(); protected: + QList > _unusedResources; + /// Loads a resource from the specified URL. /// \param fallback a fallback URL to load if the desired one is unavailable /// \param delayLoad if true, don't load the resource immediately; wait until load is first requested @@ -52,13 +55,15 @@ protected: virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) = 0; + void addUnusedResource(const QSharedPointer& resource); + static void attemptRequest(Resource* resource); static void requestCompleted(); private: friend class Resource; - + QHash > _resources; static QNetworkAccessManager* _networkAccessManager; @@ -95,6 +100,10 @@ public: void setSelf(const QWeakPointer& self) { _self = self; } + void setCache(ResourceCache* cache) { _cache = cache; } + + void allReferencesCleared(); + protected slots: void attemptRequest(); @@ -107,12 +116,17 @@ protected: /// Should be called by subclasses when all the loading that will be done has been done. Q_INVOKABLE void finishedLoading(bool success); + /// Reinserts this resource into the cache. + virtual void reinsert(); + + QUrl _url; QNetworkRequest _request; bool _startedLoading; bool _failedToLoad; bool _loaded; QHash, float> _loadPriorities; QWeakPointer _self; + QPointer _cache; private slots: From c6655382d6895decfdbff0b264db180405dce2dd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 10:52:46 -0700 Subject: [PATCH 026/109] Initialize scale to unity. Closes #2232. --- interface/src/renderer/Model.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c52e4d4603..f6cfb08816 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -21,6 +21,7 @@ using namespace std; Model::Model(QObject* parent) : QObject(parent), + _scale(1.0f, 1.0f, 1.0f), _shapesAreDirty(true), _lodDistance(0.0f), _pupilDilation(0.0f) { From 34ddddae1b8449c90911fb74288c72f728c5a392 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 12:06:07 -0700 Subject: [PATCH 027/109] Basic LRU cache. Closes #2234. --- interface/src/renderer/GeometryCache.h | 2 +- interface/src/renderer/TextureCache.cpp | 2 +- libraries/shared/src/ResourceCache.cpp | 25 +++++++++++++++++-------- libraries/shared/src/ResourceCache.h | 10 +++++++++- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 6e9604e79e..da1955039d 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -89,7 +89,7 @@ public: protected: virtual void downloadFinished(QNetworkReply* reply); - virtual void reinsert() const; + virtual void reinsert(); Q_INVOKABLE void setGeometry(const FBXGeometry& geometry); diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index e3f750e676..80eef66ff5 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -133,7 +133,7 @@ QSharedPointer TextureCache::getTexture(const QUrl& url, bool no texture->setCache(this); _dilatableNetworkTextures.insert(url, texture); } else { - _unusedResources.removeOne(texture); + _unusedResources.remove(texture->getLRUKey()); } return texture; } diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index 6434376cd8..65c68b23ef 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -15,7 +15,8 @@ #include "ResourceCache.h" ResourceCache::ResourceCache(QObject* parent) : - QObject(parent) { + QObject(parent), + _lastLRUKey(0) { } ResourceCache::~ResourceCache() { @@ -38,19 +39,21 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& _resources.insert(url, resource); } else { - _unusedResources.removeOne(resource); + _unusedResources.remove(resource->getLRUKey()); } return resource; } void ResourceCache::addUnusedResource(const QSharedPointer& resource) { - const int RETAINED_RESOURCE_COUNT = 1; + const int RETAINED_RESOURCE_COUNT = 50; if (_unusedResources.size() > RETAINED_RESOURCE_COUNT) { // unload the oldest resource - QSharedPointer oldResource = _unusedResources.takeFirst(); - oldResource->setCache(NULL); + QMap >::iterator it = _unusedResources.begin(); + it.value()->setCache(NULL); + _unusedResources.erase(it); } - _unusedResources.append(resource); + resource->setLRUKey(++_lastLRUKey); + _unusedResources.insert(resource->getLRUKey(), resource); } void ResourceCache::attemptRequest(Resource* resource) { @@ -100,6 +103,7 @@ Resource::Resource(const QUrl& url, bool delayLoad) : _startedLoading(false), _failedToLoad(false), _loaded(false), + _lruKey(0), _reply(NULL), _attempts(0) { @@ -171,10 +175,9 @@ void Resource::allReferencesCleared() { reinsert(); // add to the unused list - _cache->_unusedResources.append(self); + _cache->addUnusedResource(self); } else { - qDebug() << "deleting" << _url; delete this; } } @@ -231,6 +234,7 @@ void Resource::makeRequest() { connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); + connect(_reply, SIGNAL(finished()), SLOT(handleReplyFinished())); _replyTimer = new QTimer(this); connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout())); @@ -275,6 +279,11 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) } } +void Resource::handleReplyFinished() { + qDebug() << "Got finished without download progress/error?" << _url; + handleDownloadProgress(0, 0); +} + uint qHash(const QPointer& value, uint seed) { return qHash(value.data(), seed); } diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index 2826902598..a544f43731 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -42,7 +42,7 @@ public: protected: - QList > _unusedResources; + QMap > _unusedResources; /// Loads a resource from the specified URL. /// \param fallback a fallback URL to load if the desired one is unavailable @@ -65,6 +65,7 @@ private: friend class Resource; QHash > _resources; + int _lastLRUKey; static QNetworkAccessManager* _networkAccessManager; static int _requestLimit; @@ -80,6 +81,9 @@ public: Resource(const QUrl& url, bool delayLoad = false); ~Resource(); + /// Returns the key last used to identify this resource in the unused map. + int getLRUKey() const { return _lruKey; } + /// Makes sure that the resource has started loading. void ensureLoading(); @@ -132,16 +136,20 @@ private slots: void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void handleReplyError(); + void handleReplyFinished(); void handleReplyTimeout(); private: + void setLRUKey(int lruKey) { _lruKey = lruKey; } + void makeRequest(); void handleReplyError(QNetworkReply::NetworkError error, QDebug debug); friend class ResourceCache; + int _lruKey; QNetworkReply* _reply; QTimer* _replyTimer; qint64 _bytesReceived; From 7fc55e5596fab96961674e41f425450dba7d801f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 13:00:58 -0700 Subject: [PATCH 028/109] Better merging for normals. --- .../metavoxels/src/AttributeRegistry.cpp | 36 ++++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 17 +++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index b90aa668f5..8a444467de 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -12,6 +12,7 @@ #include "MetavoxelData.h" REGISTER_META_OBJECT(QRgbAttribute) +REGISTER_META_OBJECT(PackedNormalAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) @@ -26,7 +27,7 @@ AttributeRegistry::AttributeRegistry() : SharedObjectPointer(new DefaultMetavoxelGuide())))), _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), - _normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) { + _normalAttribute(registerAttribute(new PackedNormalAttribute("normal", qRgb(0, 127, 0)))) { } static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { @@ -217,6 +218,39 @@ QWidget* QRgbAttribute::createEditor(QWidget* parent) const { return editor; } +PackedNormalAttribute::PackedNormalAttribute(const QString& name, QRgb defaultValue) : + QRgbAttribute(name, defaultValue) { +} + +bool PackedNormalAttribute::merge(void*& parent, void* children[]) const { + QRgb firstValue = decodeInline(children[0]); + int totalRed = (char)qRed(firstValue); + int totalGreen = (char)qGreen(firstValue); + int totalBlue = (char)qBlue(firstValue); + bool allChildrenEqual = true; + for (int i = 1; i < Attribute::MERGE_COUNT; i++) { + QRgb value = decodeInline(children[i]); + totalRed += (char)qRed(value); + totalGreen += (char)qGreen(value); + totalBlue += (char)qBlue(value); + allChildrenEqual &= (firstValue == value); + } + parent = encodeInline(packNormal(glm::normalize(glm::vec3(totalRed, totalGreen, totalBlue)))); + return allChildrenEqual; +} + +const float CHAR_SCALE = 127.0f; +const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE; + +QRgb packNormal(const glm::vec3& normal) { + return qRgb((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE)); +} + +glm::vec3 unpackNormal(QRgb value) { + return glm::vec3((char)qRed(value) * INVERSE_CHAR_SCALE, (char)qGreen(value) * INVERSE_CHAR_SCALE, + (char)qBlue(value) * INVERSE_CHAR_SCALE); +} + SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const SharedObjectPointer& defaultValue) : InlineAttribute(name, defaultValue), diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 8678e62b41..3dc6aa7484 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -272,6 +272,23 @@ public: virtual QWidget* createEditor(QWidget* parent = NULL) const; }; +/// Provides appropriate averaging for packed normals. +class PackedNormalAttribute : public QRgbAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE PackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); + + virtual bool merge(void*& parent, void* children[]) const; +}; + +/// Packs a normal into an RGB value. +QRgb packNormal(const glm::vec3& normal); + +/// Unpacks a normal from an RGB value. +glm::vec3 unpackNormal(QRgb value); + /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT From 802cc3eeedecce625c5fd09ca79d379fb2a8cb6d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 14:58:31 -0700 Subject: [PATCH 029/109] Some untested ray intersection bits. --- libraries/metavoxels/src/MetavoxelData.cpp | 69 ++++++++++++++++++++++ libraries/metavoxels/src/MetavoxelData.h | 55 +++++++++++++++-- libraries/metavoxels/src/MetavoxelUtil.cpp | 52 ++++++++++++++++ libraries/metavoxels/src/MetavoxelUtil.h | 4 ++ 4 files changed, 175 insertions(+), 5 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index f1c0b928c9..0afb334aca 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "MetavoxelData.h" #include "MetavoxelUtil.h" #include "ScriptCache.h" @@ -821,6 +823,65 @@ int SpannerVisitor::visit(MetavoxelInfo& info) { return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; } +RayIntersectionVisitor::RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, + const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : + MetavoxelVisitor(inputs, outputs, lod), + _origin(origin), + _direction(direction), + _order(encodeOrder(direction)) { +} + +int RayIntersectionVisitor::visit(MetavoxelInfo& info) { + float distance; + if (!info.getBounds().findRayIntersection(_origin, _direction, distance)) { + return STOP_RECURSION; + } + return visit(info, distance); +} + +RayIntersectionSpannerVisitor::RayIntersectionSpannerVisitor(const glm::vec3& origin, const glm::vec3& direction, + const QVector& spannerInputs, const QVector& inputs, + const QVector& outputs, const MetavoxelLOD& lod) : + RayIntersectionVisitor(origin, direction, inputs + spannerInputs, outputs, lod), + _spannerInputCount(spannerInputs.size()) { +} + +void RayIntersectionSpannerVisitor::prepare() { + Spanner::incrementVisit(); +} + +class SpannerDistance { +public: + Spanner* spanner; + float distance; +}; + +bool operator<(const SpannerDistance& first, const SpannerDistance& second) { + return first.distance < second.distance; +} + +int RayIntersectionSpannerVisitor::visit(MetavoxelInfo& info, float distance) { + QVarLengthArray spannerDistances; + for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { + foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { + Spanner* spanner = static_cast(object.data()); + if (spanner->testAndSetVisited()) { + SpannerDistance spannerDistance = { spanner }; + if (spanner->findRayIntersection(_origin, _direction, spannerDistance.distance)) { + spannerDistances.append(spannerDistance); + } + } + } + qStableSort(spannerDistances); + foreach (const SpannerDistance& spannerDistance, spannerDistances) { + if (!visit(spannerDistance.spanner, spannerDistance.distance)) { + return SHORT_CIRCUIT; + } + } + } + return info.isLeaf ? STOP_RECURSION : _order; +} + DefaultMetavoxelGuide::DefaultMetavoxelGuide() { } @@ -1119,6 +1180,10 @@ SpannerRenderer* Spanner::getRenderer() { return _renderer; } +bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + return _bounds.findRayIntersection(origin, direction, distance); +} + QByteArray Spanner::getRendererClassName() const { return "SpannerRendererer"; } @@ -1212,6 +1277,10 @@ bool Sphere::getAttributeValues(MetavoxelInfo& info) const { return true; } +bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance); +} + QByteArray Sphere::getRendererClassName() const { return "SphereRenderer"; } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 5b149c071f..921dccff13 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -191,7 +191,7 @@ public: glm::vec3 getCenter() const { return minimum + glm::vec3(size, size, size) * 0.5f; } }; -/// Interface for visitors to metavoxels. +/// Base class for visitors to metavoxels. class MetavoxelVisitor { public: @@ -241,9 +241,7 @@ protected: MetavoxelLOD _lod; }; -typedef QSharedPointer MetavoxelVisitorPointer; - -/// Interface for visitors to spanners. +/// Base class for visitors to spanners. class SpannerVisitor : public MetavoxelVisitor { public: @@ -264,6 +262,49 @@ protected: int _spannerInputCount; }; +/// Base class for ray intersection visitors. +class RayIntersectionVisitor : public MetavoxelVisitor { +public: + + RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, + const QVector& inputs, + const QVector& outputs = QVector(), + const MetavoxelLOD& lod = MetavoxelLOD()); + + /// Visits a metavoxel that the ray intersects. + virtual int visit(MetavoxelInfo& info, float distance) = 0; + + virtual int visit(MetavoxelInfo& info); + +protected: + + glm::vec3 _origin; + glm::vec3 _direction; + int _order; +}; + +/// Base class for ray intersection spanner visitors. +class RayIntersectionSpannerVisitor : public RayIntersectionVisitor { +public: + + RayIntersectionSpannerVisitor(const glm::vec3& origin, const glm::vec3& direction, + const QVector& spannerInputs, + const QVector& inputs = QVector(), + const QVector& outputs = QVector(), + const MetavoxelLOD& lod = MetavoxelLOD()); + + /// Visits a spanner that the ray intersects. + /// \return true to continue, false to short-circuit the tour + virtual bool visit(Spanner* spanner, float distance) = 0; + + virtual void prepare(); + virtual int visit(MetavoxelInfo& info, float distance); + +protected: + + int _spannerInputCount; +}; + /// Interface for objects that guide metavoxel visitors. class MetavoxelGuide : public SharedObject { Q_OBJECT @@ -389,6 +430,9 @@ public: /// Returns a pointer to the renderer, creating it if necessary. SpannerRenderer* getRenderer(); + /// Finds the intersection between the described ray and this spanner. + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + signals: void boundsWillChange(); @@ -469,7 +513,8 @@ public: virtual const QVector& getAttributes() const; virtual bool getAttributeValues(MetavoxelInfo& info) const; - + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + signals: void colorChanged(const QColor& color); diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 9e96b43d3f..7993b3bcb6 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -150,6 +150,12 @@ Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) : minimum(minimum), maximum(maximum) { } +bool Box::contains(const glm::vec3& point) const { + return point.x >= minimum.x && point.x <= maximum.x && + point.y >= minimum.y && point.y <= maximum.y && + point.z >= minimum.z && point.z <= maximum.z; +} + bool Box::contains(const Box& other) const { return other.minimum.x >= minimum.x && other.maximum.x <= maximum.x && other.minimum.y >= minimum.y && other.maximum.y <= maximum.y && @@ -173,6 +179,52 @@ glm::vec3 Box::getVertex(int index) const { (index & Z_MAXIMUM_FLAG) ? maximum.z : minimum.z); } +// finds the intersection between a ray and the facing plane on one axis +static bool findIntersection(float origin, float direction, float minimum, float maximum, float& distance) { + if (direction > EPSILON) { + distance = (minimum - origin) / direction; + return true; + } else if (direction < -EPSILON) { + distance = (maximum - origin) / direction; + return true; + } + return false; +} + +// determines whether a value is within the extents +static bool isWithin(float value, float minimum, float maximum) { + return value >= minimum && value <= maximum; +} + +bool Box::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + // handle the trivial case where the box contains the origin + if (contains(origin)) { + distance = 0.0f; + return true; + } + // check each axis + float axisDistance; + if ((findIntersection(origin.x, direction.x, minimum.x, maximum.x, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, minimum.y, maximum.y) && + isWithin(origin.z + axisDistance*direction.z, minimum.z, maximum.z))) { + distance = axisDistance; + return true; + } + if ((findIntersection(origin.y, direction.y, minimum.y, maximum.y, axisDistance) && axisDistance >= 0 && + isWithin(origin.x + axisDistance*direction.x, minimum.x, maximum.x) && + isWithin(origin.z + axisDistance*direction.z, minimum.z, maximum.z))) { + distance = axisDistance; + return true; + } + if ((findIntersection(origin.z, direction.z, minimum.z, maximum.z, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, minimum.y, maximum.y) && + isWithin(origin.x + axisDistance*direction.x, minimum.x, maximum.x))) { + distance = axisDistance; + return true; + } + return false; +} + Box operator*(const glm::mat4& matrix, const Box& box) { // start with the constant component Box newBox(glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]), glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2])); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 45a9476e4d..3f450212b1 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -41,6 +41,8 @@ public: Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); + bool contains(const glm::vec3& point) const; + bool contains(const Box& other) const; bool intersects(const Box& other) const; @@ -50,6 +52,8 @@ public: glm::vec3 getVertex(int index) const; glm::vec3 getCenter() const { return (minimum + maximum) * 0.5f; } + + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; }; DECLARE_STREAMABLE_METATYPE(Box) From 853bba27ca2a45f1273b921b78fd37b2713bd1c3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 15:13:24 -0700 Subject: [PATCH 030/109] Applied Ryan's tweaks to Visage mapping and config file. --- .gitignore | 5 +- interface/resources/visage/tracker.cfg | 334 +++++++++++++++++++++++++ interface/src/devices/Visage.cpp | 20 +- 3 files changed, 345 insertions(+), 14 deletions(-) create mode 100644 interface/resources/visage/tracker.cfg diff --git a/.gitignore b/.gitignore index d66359205d..eb2f3ec303 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,8 @@ interface/external/sixense/* # Ignore Visage interface/external/visage/* !interface/external/visage/readme.txt -interface/resources/visage/ +interface/resources/visage/* +!interface/resources/visage/tracker.cfg # Ignore interfaceCache for Linux users -interface/interfaceCache/ \ No newline at end of file +interface/interfaceCache/ diff --git a/interface/resources/visage/tracker.cfg b/interface/resources/visage/tracker.cfg new file mode 100644 index 0000000000..b5ed3302fa --- /dev/null +++ b/interface/resources/visage/tracker.cfg @@ -0,0 +1,334 @@ +display_width 800 + +max_init_image_width 800 +max_work_image_width 384 +fast_image_resize 1 + +camera_input 0 +camera_device 0 +camera_width 800 +camera_height 600 +camera_frame_rate 30 +camera_mirror 1 +camera_settings_dialog 0 +camera_auto_settings 0 + +video_file_sync 1 + +automatic_initialisation 1 +init_yaw_threshold 0.005 +init_roll_threshold 10 +init_velocity_threshold 0.015 +init_timeout 1000 +init_timeout_enable 6 +init_display_status 1 +recovery_timeout 800 + +display_video 1 +display_model_texture 0 +display_tri_mask 0 +display_model_wireframe 0 +display_results 1 +display_track_points 0 + +detect_strip_area_threshold 1000 +detect_strip_angle_threshold 0.15 +detect_strip_ratio_threshold 0.2 +detect_strip_perfect_ratio 6.848 +detect_strip_roi_y_offset 0 +detect_strip_roi_width 2 +detect_strip_roi_height 4 + +smoothing_factors + 150 15 -2 100 -1 50 50 0 +#translation rotation action_units eyebrows mouth gaze eye_closure other + +process_eyes 1 +leye_closing_au 12 +reye_closing_au 12 +eye_h_rotation_au 15 +eye_v_rotation_au 16 +eye_points_coords +162 1.0 0.0 0.0 +157 0.0 0.0 1.0 + +bdts_points_use + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +#3.5 3.6 3.7 3.8 3.11 3.12 8.1 8.2 8.3 8.4 9.1 9.2 9.3 4.1 4.2 4.3 4.4 4.5 4.6 2.1 + +bdts_points_angle + 0 0 20 -20 20 -20 0 0 20 -20 20 -20 0 25 -25 20 -20 20 -20 0 0 + +model_filename candide3-exp.wfm +fdp_filename candide3.fdp +bdts_data_path bdtsdata + +au_use + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +#AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 + +au_names +au_nose_wrinkler +au_jaw_z_push +au_jaw_x_push +au_jaw_drop +au_lower_lip_drop +au_upper_lip_raiser +au_lip_stretcher_left +au_lip_corner_depressor +au_lip_presser +au_left_outer_brow_raiser +au_left_inner_brow_raiser +au_left_brow_lowerer +au_leye_closed +au_lid_tightener +au_upper_lid_raiser +au_rotate_eyes_left +au_rotate_eyes_down +au_lower_lip_x_push +au_lip_stretcher_right +au_right_outer_brow_raiser +au_right_inner_brow_raiser +au_right_brow_lowerer +au_reye_closed + + +ekf_sensitivity + 100 100 100 100 100 100 100 100 100 100 100 200 200 100 100 400 400 100 100 100 100 300 300 100 100 400 400 100 100 +#x y z rx ry rz AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 + +au_gravity + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +#AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 + +gravity_threshold 0.0 + +camera_focus 3 + +tex_size 512 + +ROI_num 14 + +#ROI 0 - nose region (setting default values) +feature_points_num[0] 7 +feature_points_tri_use_num[0] 21 +feature_points_tri_use_list[0] +48 154 54 55 152 45 46 47 103 109 +108 107 106 101 104 110 111 105 99 102 +100 +bound_rect_modifiers[0] -10 -10 -20 10 +feature_points_min_distance[0] 0.78125 +feature_point_block_size[0] 0.46875 +search_range[0] 4.0625 4.0625 +refine_range[0] 0.3125 0.3125 +patch_size[0] 4.6875 +match_method[0] 5 +bad_match_threshold[0] 0.35 +sensitivity_falloff[0] 10 +init_angle[0] 0 +feature_points_coords[0] +97 0.18 0.54 0.28 +90 0.18 0.28 0.54 +51 0.76 0.09 0.15 +97 0.36 0.10 0.54 +90 0.36 0.54 0.10 +69 0.55 0.10 0.35 +67 0.55 0.35 0.10 + +#ROI 1 - left eyebrow region +feature_points_num[1] 4 +feature_points_tri_use_num[1] 2 +feature_points_tri_use_list[1] +25 26 +bound_rect_modifiers[1] 2 2 40 2 +feature_points_coords[1] +25 0.0 0.0 1.0 +26 0.15 0.1 0.75 +25 1.0 0.0 0.0 +26 0.2 0.4 0.4 + +#ROI 2 - right eyebrow region +feature_points_num[2] 4 +feature_points_tri_use_num[2] 2 +feature_points_tri_use_list[2] +33 34 +bound_rect_modifiers[2] 2 2 40 2 +feature_points_coords[2] +33 0.15 0.75 0.1 +34 1.0 0.0 0.0 +33 0.0 0.0 1.0 +33 0.2 0.4 0.4 + +#ROI 3 - left outer eye corner region +feature_points_num[3] 3 +feature_points_tri_use_num[3] 4 +feature_points_tri_use_list[3] +112 23 39 115 +bound_rect_modifiers[3] -5 -5 -25 -25 +feature_points_coords[3] +23 0 0.2 0.8 +119 0.8351877 0.08414026 0.08067206 +116 0.3218788 0.4626164 0.2155048 + +#ROI 4 - right outer eye corner region +feature_points_num[4] 3 +feature_points_tri_use_num[4] 4 +feature_points_tri_use_list[4] +142 36 42 145 +bound_rect_modifiers[4] -5 -5 -25 -25 +feature_points_coords[4] +36 0 0.8 0.2 +151 0.8566859 0.0405132 0.1028009 +146 0.2871178 0.3467231 0.366159 + +#ROI 5 - left inner eye corner region +feature_points_num[5] 3 +feature_points_tri_use_num[5] 5 +feature_points_tri_use_list[5] +122 28 99 100 125 +bound_rect_modifiers[5] -5 -5 -25 -40 +feature_points_coords[5] +99 0.2 0.1 0.7 +122 0.2664618 0.3707059 0.3628323 +125 0.387767 0.4655813 0.1466517 + +#ROI 6 - right inner eye corner region +feature_points_num[6] 3 +feature_points_tri_use_num[6] 5 +feature_points_tri_use_list[6] +132 31 102 104 135 +bound_rect_modifiers[6] -5 -5 -25 -40 +feature_points_coords[6] +102 0.2 0.7 0.1 +132 0.3673472 0.2426805 0.3899724 +135 0.2833096 0.1345753 0.5821151 + +#ROI 7 - lower lip region +feature_points_num[7] 6 +feature_points_tri_use_num[7] 6 +feature_points_tri_use_list[7] +79 80 82 84 52 53 +bound_rect_modifiers[7] 0 0 0 -80 +refine_range[7] 0.5 1 +patch_size[7] 7 +feature_points_coords[7] +80 0.0 0.0 1.0 +80 1.0 0.0 0.0 +80 0.0 1.0 0.0 +79 0.0 0.0 1.0 +82 0.1 0.3 0.6 +84 0.1 0.6 0.3 + +#ROI 8 - upper lip region +feature_points_num[8] 6 +feature_points_tri_use_num[8] 10 +feature_points_tri_use_list[8] +69 49 155 51 50 153 44 67 57 60 +bound_rect_modifiers[8] 0 0 -70 0 +refine_range[8] 0.5 1 +patch_size[8] 7 +feature_points_coords[8] +67 0.1 0.45 0.45 +69 0.1 0.45 0.45 +57 0.0 0.0 1.0 +60 0.0 1.0 0.0 +51 0.0 1.0 0.0 +51 0.3 0.7 0.0 + +#ROI 9 - left lip corner region +feature_points_num[9] 4 +feature_points_tri_use_num[9] 4 +feature_points_tri_use_list[9] +87 89 81 68 +bound_rect_modifiers[9] 0 0 -35 -35 +refine_range[9] 1 1 +patch_size[9] 7 +feature_points_coords[9] +87 0.0 0.0 1.0 +87 0.2 0.0 0.8 +66 0.33 0.33 0.34 +72 0.33 0.33 0.34 + +#ROI 10 - right lip corner region +feature_points_num[10] 4 +feature_points_tri_use_num[10] 4 +feature_points_tri_use_list[10] +94 96 83 70 +bound_rect_modifiers[10] 0 0 -35 -35 +refine_range[10] 1 1 +patch_size[10] 7 +feature_points_coords[10] +94 0.0 1.0 0.0 +94 0.2 0.8 0.0 +64 0.33 0.33 0.34 +78 0.33 0.33 0.34 + +#ROI 11 - jaw region +feature_points_num[11] 4 +feature_points_tri_use_num[11] 2 +feature_points_tri_use_list[11] +52 53 +bound_rect_modifiers[11] 0 0 -30 0 +refine_range[11] 0.5 1 +patch_size[11] 7 +bad_match_threshold[11] 0.6 +feature_points_coords[11] +52 0.4 0.3 0.3 +53 0.4 0.3 0.3 +87 0.0 0.5 0.5 +94 0.0 0.5 0.5 + +feature_points_num[12] 6 +feature_points_tri_use_num[12] 3 +feature_points_tri_use_list[12] +188 189 190 191 +bound_rect_modifiers[12] -30 0 0 -30 +init_angle[12] -20 + +feature_points_num[13] 6 +feature_points_tri_use_num[13] 3 +feature_points_tri_use_list[13] +192 193 194 195 +bound_rect_modifiers[13] 0 -30 0 -30 +init_angle[13] 20 + +recovery_frames 4 +global_bad_match_threshold 0.5 +visibility_check 1 + +rotation_limit +-1.570796 1.570796 + 1.570796 4.712389 +-3.2 3.2 + +translation_limit +-4 4 +-3 3 + 0 11 + +action_unit_limit +-0.5 0.5 +-0.5 0.5 +-1.0 1.0 +-0.05 1.2 +-0.05 1.5 +-0.05 1.5 +-1.0 1.0 +-1.5 1.5 +-1.5 1.5 +-1.5 2 +-1.5 2 +-1 1.5 +-2.8 2.8 +-1.5 1.5 +-1.5 1.5 +-100 100 +-100 100 +-1.5 1.5 +-1.5 1.5 +-1.5 1.5 +-1.5 1.5 +-1.5 1.5 +-2.8 2.8 + + diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 348858bf3b..aed286e04b 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -38,7 +38,7 @@ Visage::Visage() : #ifdef HAVE_VISAGE QByteArray licensePath = Application::resourcesPath() + "visage/license.vlc"; initializeLicenseManager(licensePath.data()); - _tracker = new VisageTracker2(Application::resourcesPath() + "visage/Facial Features Tracker - Asymmetric.cfg"); + _tracker = new VisageTracker2(Application::resourcesPath() + "visage/tracker.cfg"); if (_tracker->trackFromCam()) { _data = new FaceData(); @@ -65,29 +65,25 @@ static int rightEyeBlinkIndex = 1; static QMultiHash > createActionUnitNameMap() { QMultiHash > blendshapeMap; - blendshapeMap.insert("Sneer", QPair("au_nose_wrinkler", 1.0f)); blendshapeMap.insert("JawFwd", QPair("au_jaw_z_push", 1.0f)); blendshapeMap.insert("JawLeft", QPair("au_jaw_x_push", 1.0f)); blendshapeMap.insert("JawOpen", QPair("au_jaw_drop", 1.0f)); blendshapeMap.insert("LipsLowerDown", QPair("au_lower_lip_drop", 1.0f)); - blendshapeMap.insert("LipsUpperUp", QPair("au_upper_lip_raiser", 1.0f)); - blendshapeMap.insert("LipsStretch_R", QPair("au_lip_stretcher_left", 1.0f)); + blendshapeMap.insert("LipsUpperOpen", QPair("au_upper_lip_raiser", 1.0f)); + blendshapeMap.insert("LipsStretch_R", QPair("au_lip_stretcher_left", 0.5f)); blendshapeMap.insert("MouthSmile_L", QPair("au_lip_corner_depressor", -1.0f)); blendshapeMap.insert("MouthSmile_R", QPair("au_lip_corner_depressor", -1.0f)); - blendshapeMap.insert("LipsPucker", QPair("au_lip_presser", 0.0f)); blendshapeMap.insert("BrowsU_R", QPair("au_left_outer_brow_raiser", 1.0f)); - blendshapeMap.insert("BrowsU_C", QPair("au_left_inner_brow_raiser", 0.5f)); + blendshapeMap.insert("BrowsU_C", QPair("au_left_inner_brow_raiser", 1.0f)); blendshapeMap.insert("BrowsD_R", QPair("au_left_brow_lowerer", 1.0f)); blendshapeMap.insert("EyeBlink_L", QPair("au_leye_closed", 1.0f)); - blendshapeMap.insert("EyeBlink_R", QPair("au_leye_closed", 1.0f)); - blendshapeMap.insert("EyeSquint_L", QPair("au_lid_tightener", 1.0f)); - blendshapeMap.insert("EyeSquint_R", QPair("au_lid_tightener", 1.0f)); + blendshapeMap.insert("EyeBlink_R", QPair("au_reye_closed", 1.0f)); blendshapeMap.insert("EyeOpen_L", QPair("au_upper_lid_raiser", 1.0f)); blendshapeMap.insert("EyeOpen_R", QPair("au_upper_lid_raiser", 1.0f)); - blendshapeMap.insert("MouthLeft", QPair("au_lower_lip_x_push", 0.0f)); - blendshapeMap.insert("LipsStretch_L", QPair("au_lip_stretcher_right", 1.0f)); + blendshapeMap.insert("LipLowerOpen", QPair("au_lower_lip_x_push", 1.0f)); + blendshapeMap.insert("LipsStretch_L", QPair("au_lip_stretcher_right", 0.5f)); blendshapeMap.insert("BrowsU_L", QPair("au_right_outer_brow_raiser", 1.0f)); - blendshapeMap.insert("BrowsU_C", QPair("au_right_inner_brow_raiser", 0.5f)); + blendshapeMap.insert("BrowsU_C", QPair("au_right_inner_brow_raiser", 1.0f)); blendshapeMap.insert("BrowsD_L", QPair("au_right_brow_lowerer", 1.0f)); QMultiHash > actionUnitNameMap; From 2005b8dd9d725371d807125faac67b0fd3574890 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 15:16:48 -0700 Subject: [PATCH 031/109] Slight tweak to Visage instructions (in order not to overwrite our custom configuration). --- interface/external/visage/readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/external/visage/readme.txt b/interface/external/visage/readme.txt index 8aa28f81c4..3aff1039dc 100644 --- a/interface/external/visage/readme.txt +++ b/interface/external/visage/readme.txt @@ -5,8 +5,8 @@ Andrzej Kapolka, February 11, 2014 1. Copy the Visage sdk folders (lib, include, dependencies) into the interface/external/visage folder. This readme.txt should be there as well. -2. Copy the Visage configuration data folder (visageSDK-MacOS/Samples/MacOSX/data/) to interface/resources/visage - (i.e., so that interface/resources/visage/candide3.wfm is accessible) +2. Copy the contents of the Visage configuration data folder (visageSDK-MacOS/Samples/MacOSX/data/) to + interface/resources/visage (i.e., so that interface/resources/visage/candide3.wfm is accessible) 3. Copy the Visage license file to interface/resources/visage/license.vlc. From 06a0332fd59622ed4b74f76fe433275dc290f49c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 10 Mar 2014 16:44:33 -0700 Subject: [PATCH 032/109] remove space bar behavior from editrVoxels.js --- examples/editVoxels.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 4a194c0f5d..b083128485 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -927,13 +927,6 @@ function keyPressEvent(event) { } } - // do this even if not in edit tools - if (event.text == " ") { - // Reset my orientation! - var orientation = { x:0, y:0, z:0, w:1 }; - Camera.setOrientation(orientation); - MyAvatar.orientation = orientation; - } trackKeyPressEvent(event); // used by preview support } From 9e5aae4a39ada274342626bbeedc6171080b32ca Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 17:01:27 -0700 Subject: [PATCH 033/109] Remove spanner bits. --- .../src/metavoxels/MetavoxelServer.cpp | 2 +- interface/src/MetavoxelSystem.cpp | 29 ++++++++++- interface/src/MetavoxelSystem.h | 5 ++ interface/src/ui/MetavoxelEditor.cpp | 13 ++++- libraries/metavoxels/src/Bitstream.cpp | 10 +++- libraries/metavoxels/src/Bitstream.h | 9 ++-- libraries/metavoxels/src/DatagramSequencer.h | 3 ++ libraries/metavoxels/src/MetavoxelData.cpp | 48 +++++++++++++++++-- libraries/metavoxels/src/MetavoxelData.h | 8 +++- .../metavoxels/src/MetavoxelMessages.cpp | 22 +++++---- libraries/metavoxels/src/MetavoxelMessages.h | 16 +++---- libraries/metavoxels/src/SharedObject.cpp | 19 +++++++- libraries/metavoxels/src/SharedObject.h | 22 ++++++++- 13 files changed, 175 insertions(+), 31 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index ed48340a93..98c91d44e7 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -25,7 +25,7 @@ MetavoxelServer::MetavoxelServer(const QByteArray& packet) : } void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) { - edit.apply(_data); + edit.apply(_data, SharedObject::getWeakHash()); } const QString METAVOXEL_SERVER_LOGGING_NAME = "metavoxel-server"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 047422d3dd..da73bfb371 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -44,6 +44,31 @@ void MetavoxelSystem::init() { connect(NodeList::getInstance(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachClient(const SharedNodePointer&))); } +SharedObjectPointer MetavoxelSystem::findFirstRaySpannerIntersection( + const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance) { + SharedObjectPointer closestSpanner; + float closestDistance = FLT_MAX; + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getType() == NodeType::MetavoxelServer) { + QMutexLocker locker(&node->getMutex()); + MetavoxelClient* client = static_cast(node->getLinkedData()); + if (client) { + float clientDistance; + SharedObjectPointer clientSpanner = client->getData().findFirstRaySpannerIntersection( + origin, direction, attribute, clientDistance); + if (clientSpanner && clientDistance < closestDistance) { + closestSpanner = clientSpanner; + closestDistance = clientDistance; + } + } + } + } + if (closestSpanner) { + distance = closestDistance; + } + return closestSpanner; +} + void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit) { foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { @@ -215,7 +240,7 @@ void MetavoxelClient::guide(MetavoxelVisitor& visitor) { void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) { // apply immediately to local tree - edit.apply(_data); + edit.apply(_data, _sequencer.getWeakSharedObjectHash()); // start sending it out _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); @@ -255,7 +280,7 @@ void MetavoxelClient::readPacket(Bitstream& in) { // reapply local edits foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) { if (message.data.userType() == MetavoxelEditMessage::Type) { - message.data.value().apply(_data); + message.data.value().apply(_data, _sequencer.getWeakSharedObjectHash()); } } } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index c1833832a7..c605205244 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -35,6 +35,9 @@ public: void init(); + SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, + const AttributePointer& attribute, float& distance); + void applyEdit(const MetavoxelEditMessage& edit); void simulate(float deltaTime); @@ -91,6 +94,8 @@ public: MetavoxelClient(const SharedNodePointer& node); virtual ~MetavoxelClient(); + MetavoxelData& getData() { return _data; } + void guide(MetavoxelVisitor& visitor); void applyEdit(const MetavoxelEditMessage& edit); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index cf3f726c70..9d88a571b8 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -607,8 +607,19 @@ bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const { } bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); + if (!attribute) { + return false; + } if (event->type() == QEvent::MouseButtonPress) { - + float distance; + SharedObjectPointer spanner = Application::getInstance()->getMetavoxels()->findFirstRaySpannerIntersection( + Application::getInstance()->getMouseRayOrigin(), Application::getInstance()->getMouseRayDirection(), + attribute, distance); + if (spanner) { + MetavoxelEditMessage message = { QVariant::fromValue(RemoveSpannerEdit(attribute, spanner->getRemoteID())) }; + Application::getInstance()->getMetavoxels()->applyEdit(message); + } return true; } return false; diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 03a34b100e..0af44eaae6 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -201,6 +201,13 @@ void Bitstream::persistAndResetReadMappings() { persistReadMappings(getAndResetReadMappings()); } +void Bitstream::clearSharedObject(int id) { + SharedObjectPointer object = _sharedObjectStreamer.takePersistentValue(id); + if (object) { + _weakSharedObjectHash.remove(object->getRemoteID()); + } +} + Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -487,7 +494,7 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { object = SharedObjectPointer(); return *this; } - QPointer& pointer = _transientSharedObjects[id]; + QPointer& pointer = _weakSharedObjectHash[id]; if (pointer) { const QMetaObject* metaObject; _metaObjectStreamer >> metaObject; @@ -500,6 +507,7 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { QObject* rawObject; *this >> rawObject; pointer = static_cast(rawObject); + pointer->setRemoteID(id); } object = static_cast(pointer.data()); return *this; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index ac77d2317a..297d265d97 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -71,7 +71,7 @@ public: int takePersistentID(P value) { return _persistentIDs.take(value); } - void removePersistentValue(int id) { _persistentIDs.remove(_persistentValues.take(id)); } + T takePersistentValue(int id) { T value = _persistentValues.take(id); _persistentIDs.remove(value); return value; } RepeatedValueStreamer& operator<<(T value); RepeatedValueStreamer& operator>>(T& value); @@ -242,8 +242,11 @@ public: /// Immediately persists and resets the read mappings. void persistAndResetReadMappings(); + /// Returns a reference to the weak hash storing shared objects for this stream. + const WeakSharedObjectHash& getWeakSharedObjectHash() const { return _weakSharedObjectHash; } + /// Removes a shared object from the read mappings. - void clearSharedObject(int id) { _sharedObjectStreamer.removePersistentValue(id); } + void clearSharedObject(int id); Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); @@ -339,7 +342,7 @@ private: RepeatedValueStreamer _scriptStringStreamer; RepeatedValueStreamer _sharedObjectStreamer; - QHash > _transientSharedObjects; + WeakSharedObjectHash _weakSharedObjectHash; static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 13bdf4c7bf..30a131390d 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -34,6 +34,9 @@ public: DatagramSequencer(const QByteArray& datagramHeader = QByteArray(), QObject* parent = NULL); + /// Returns a reference to the weak hash mapping remote ids to shared objects. + const WeakSharedObjectHash& getWeakSharedObjectHash() const { return _inputStream.getWeakSharedObjectHash(); } + /// Returns the packet number of the last packet sent. int getOutgoingPacketNumber() const { return _outgoingPacketNumber; } diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 0afb334aca..047faf5e71 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -218,6 +218,48 @@ void MetavoxelData::clear(const AttributePointer& attribute) { } } +class FirstRaySpannerIntersectionVisitor : public RaySpannerIntersectionVisitor { +public: + + FirstRaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, + const AttributePointer& attribute, const MetavoxelLOD& lod); + + Spanner* getSpanner() const { return _spanner; } + float getDistance() const { return _distance; } + + virtual bool visit(Spanner* spanner, float distance); + +private: + + Spanner* _spanner; + float _distance; +}; + +FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor( + const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, const MetavoxelLOD& lod) : + RaySpannerIntersectionVisitor(origin, direction, QVector() << attribute, + QVector(), QVector(), lod), + _spanner(NULL) { +} + +bool FirstRaySpannerIntersectionVisitor::visit(Spanner* spanner, float distance) { + _spanner = spanner; + _distance = distance; + return false; +} + +SharedObjectPointer MetavoxelData::findFirstRaySpannerIntersection( + const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, + float& distance, const MetavoxelLOD& lod) { + FirstRaySpannerIntersectionVisitor visitor(origin, direction, attribute, lod); + guide(visitor); + if (!visitor.getSpanner()) { + return SharedObjectPointer(); + } + distance = visitor.getDistance(); + return SharedObjectPointer(visitor.getSpanner()); +} + const int X_MAXIMUM_FLAG = 1; const int Y_MAXIMUM_FLAG = 2; const int Z_MAXIMUM_FLAG = 4; @@ -839,14 +881,14 @@ int RayIntersectionVisitor::visit(MetavoxelInfo& info) { return visit(info, distance); } -RayIntersectionSpannerVisitor::RayIntersectionSpannerVisitor(const glm::vec3& origin, const glm::vec3& direction, +RaySpannerIntersectionVisitor::RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const QVector& spannerInputs, const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : RayIntersectionVisitor(origin, direction, inputs + spannerInputs, outputs, lod), _spannerInputCount(spannerInputs.size()) { } -void RayIntersectionSpannerVisitor::prepare() { +void RaySpannerIntersectionVisitor::prepare() { Spanner::incrementVisit(); } @@ -860,7 +902,7 @@ bool operator<(const SpannerDistance& first, const SpannerDistance& second) { return first.distance < second.distance; } -int RayIntersectionSpannerVisitor::visit(MetavoxelInfo& info, float distance) { +int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { QVarLengthArray spannerDistances; for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 921dccff13..80ff431172 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -81,6 +81,10 @@ public: void clear(const AttributePointer& attribute); + /// Convenience function that finds the first spanner intersecting the provided ray. + SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, + const AttributePointer& attribute, float& distance, const MetavoxelLOD& lod = MetavoxelLOD()); + /// Expands the tree, increasing its capacity in all dimensions. void expand(); @@ -284,10 +288,10 @@ protected: }; /// Base class for ray intersection spanner visitors. -class RayIntersectionSpannerVisitor : public RayIntersectionVisitor { +class RaySpannerIntersectionVisitor : public RayIntersectionVisitor { public: - RayIntersectionSpannerVisitor(const glm::vec3& origin, const glm::vec3& direction, + RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const QVector& spannerInputs, const QVector& inputs = QVector(), const QVector& outputs = QVector(), diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 1c36f7df46..ca37746780 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -8,8 +8,8 @@ #include "MetavoxelMessages.h" -void MetavoxelEditMessage::apply(MetavoxelData& data) const { - static_cast(edit.data())->apply(data); +void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + static_cast(edit.data())->apply(data, objects); } MetavoxelEdit::~MetavoxelEdit() { @@ -58,7 +58,7 @@ int BoxSetEditVisitor::visit(MetavoxelInfo& info) { return DEFAULT_ORDER; // subdivide } -void BoxSetEdit::apply(MetavoxelData& data) const { +void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit while (!data.getBounds().contains(region)) { data.expand(); @@ -94,7 +94,7 @@ int GlobalSetEditVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; // entirely contained } -void GlobalSetEdit::apply(MetavoxelData& data) const { +void GlobalSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { GlobalSetEditVisitor visitor(*this); data.guide(visitor); } @@ -104,7 +104,7 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh spanner(spanner) { } -void InsertSpannerEdit::apply(MetavoxelData& data) const { +void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { data.insert(attribute, spanner); } @@ -113,14 +113,20 @@ RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) id(id) { } -void RemoveSpannerEdit::apply(MetavoxelData& data) const { +void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + SharedObject* object = objects.value(id); + if (!object) { + qDebug() << "Missing object to remove" << id; + return; + } + data.remove(attribute, object); } ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : attribute(attribute) { } -void ClearSpannersEdit::apply(MetavoxelData& data) const { +void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { data.clear(attribute); } @@ -149,7 +155,7 @@ SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) : spanner(spanner) { } -void SetSpannerEdit::apply(MetavoxelData& data) const { +void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { Spanner* spanner = static_cast(this->spanner.data()); // expand to fit the entire spanner diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index fe229cc727..25db8e5464 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -66,7 +66,7 @@ public: STREAM QVariant edit; - void apply(MetavoxelData& data) const; + void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelEditMessage) @@ -77,7 +77,7 @@ public: virtual ~MetavoxelEdit(); - virtual void apply(MetavoxelData& data) const = 0; + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const = 0; }; /// An edit that sets the region within a box to a value. @@ -93,7 +93,7 @@ public: BoxSetEdit(const Box& region = Box(), float granularity = 0.0f, const OwnedAttributeValue& value = OwnedAttributeValue()); - virtual void apply(MetavoxelData& data) const; + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; DECLARE_STREAMABLE_METATYPE(BoxSetEdit) @@ -108,7 +108,7 @@ public: GlobalSetEdit(const OwnedAttributeValue& value = OwnedAttributeValue()); - virtual void apply(MetavoxelData& data) const; + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; DECLARE_STREAMABLE_METATYPE(GlobalSetEdit) @@ -125,7 +125,7 @@ public: InsertSpannerEdit(const AttributePointer& attribute = AttributePointer(), const SharedObjectPointer& spanner = SharedObjectPointer()); - virtual void apply(MetavoxelData& data) const; + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; DECLARE_STREAMABLE_METATYPE(InsertSpannerEdit) @@ -141,7 +141,7 @@ public: RemoveSpannerEdit(const AttributePointer& attribute = AttributePointer(), int id = 0); - virtual void apply(MetavoxelData& data) const; + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; DECLARE_STREAMABLE_METATYPE(RemoveSpannerEdit) @@ -156,7 +156,7 @@ public: ClearSpannersEdit(const AttributePointer& attribute = AttributePointer()); - virtual void apply(MetavoxelData& data) const; + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit) @@ -171,7 +171,7 @@ public: SetSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer()); - virtual void apply(MetavoxelData& data) const; + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; DECLARE_STREAMABLE_METATYPE(SetSpannerEdit) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index b4c83231ab..e95958ac76 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -18,7 +18,12 @@ REGISTER_META_OBJECT(SharedObject) -SharedObject::SharedObject() : _id(++_lastID), _referenceCount(0) { +SharedObject::SharedObject() : + _id(++_lastID), + _remoteID(0), + _referenceCount(0) { + + _weakHash.insert(_id, this); } void SharedObject::incrementReferenceCount() { @@ -27,6 +32,7 @@ void SharedObject::incrementReferenceCount() { void SharedObject::decrementReferenceCount() { if (--_referenceCount == 0) { + _weakHash.remove(_id); delete this; } } @@ -86,6 +92,17 @@ void SharedObject::dump(QDebug debug) const { } int SharedObject::_lastID = 0; +WeakSharedObjectHash SharedObject::_weakHash; + +void pruneWeakSharedObjectHash(WeakSharedObjectHash& hash) { + for (WeakSharedObjectHash::iterator it = hash.begin(); it != hash.end(); ) { + if (!it.value()) { + it = hash.erase(it); + } else { + it++; + } + } +} SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nullable, QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 344a77cc94..66e6474972 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -9,23 +9,38 @@ #ifndef __interface__SharedObject__ #define __interface__SharedObject__ +#include #include #include +#include #include #include #include class QComboBox; +class SharedObject; + +typedef QHash > WeakSharedObjectHash; + /// A QObject that may be shared over the network. class SharedObject : public QObject { Q_OBJECT public: + /// Returns the weak hash under which all local shared objects are registered. + static const WeakSharedObjectHash& getWeakHash() { return _weakHash; } + Q_INVOKABLE SharedObject(); - int getID() { return _id; } + /// Returns the unique local ID for this object. + int getID() const { return _id; } + + /// Returns the unique remote ID for this object, or zero if this is a local object. + int getRemoteID() const { return _remoteID; } + + void setRemoteID(int remoteID) { _remoteID = remoteID; } int getReferenceCount() const { return _referenceCount; } void incrementReferenceCount(); @@ -43,11 +58,16 @@ public: private: int _id; + int _remoteID; int _referenceCount; static int _lastID; + static WeakSharedObjectHash _weakHash; }; +/// Removes the null references from the supplied hash. +void pruneWeakSharedObjectHash(WeakSharedObjectHash& hash); + /// A pointer to a shared object. template class SharedObjectPointerTemplate { public: From 7080a5f50369771946dfcf2100d4d2a25d8c6794 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 10 Mar 2014 17:08:57 -0700 Subject: [PATCH 034/109] remove qt5 homebrew formula - it lives at highfidelity/homebrew-formulas --- qt5.rb | 116 --------------------------------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 qt5.rb diff --git a/qt5.rb b/qt5.rb deleted file mode 100644 index 2e5a8c7c42..0000000000 --- a/qt5.rb +++ /dev/null @@ -1,116 +0,0 @@ -# this is a Qt5 formula patched to remove support for the CoreWLAN module on OS X -# which was causing us problems with constant WLAN scans - -# the change is a removal of the following line in bearer.pro -# mac:contains(QT_CONFIG, corewlan):SUBDIRS += corewlan - -require 'formula' - -class Qt5HeadDownloadStrategy < GitDownloadStrategy - include FileUtils - - def support_depth? - # We need to make a local clone so we can't use "--depth 1" - false - end - - def stage - @clone.cd { reset } - safe_system 'git', 'clone', @clone, '.' - ln_s @clone, 'qt' - safe_system './init-repository', '--mirror', "#{Dir.pwd}/" - rm 'qt' - end -end - -class Qt5 < Formula - homepage 'http://qt-project.org/' - url 'https://highfidelity-public.s3.amazonaws.com/packages/qt-everywhere-opensource-nocorewlan-src-5.2.0.tar.gz' - sha1 '07ef1ca133db4a5168d8a716e99d145432832a24' - head 'git://gitorious.org/qt/qt5.git', :branch => 'stable', - :using => Qt5HeadDownloadStrategy - - keg_only "Qt 5 conflicts Qt 4 (which is currently much more widely used)." - - option :universal - option 'with-docs', 'Build documentation' - option 'developer', 'Build and link with developer options' - - depends_on "d-bus" => :optional - depends_on "mysql" => :optional - - odie 'qt5: --with-qtdbus has been renamed to --with-d-bus' if build.include? 'with-qtdbus' - odie 'qt5: --with-demos-examples is no longer supported' if build.include? 'with-demos-examples' - odie 'qt5: --with-debug-and-release is no longer supported' if build.include? 'with-debug-and-release' - - def install - ENV.universal_binary if build.universal? - args = ["-prefix", prefix, - "-system-zlib", - "-confirm-license", "-opensource", - "-nomake", "examples", - "-release"] - - unless MacOS::CLT.installed? - # ... too stupid to find CFNumber.h, so we give a hint: - ENV.append 'CXXFLAGS', "-I#{MacOS.sdk_path}/System/Library/Frameworks/CoreFoundation.framework/Headers" - end - - # https://bugreports.qt-project.org/browse/QTBUG-34382 - args << "-no-xcb" - - args << "-L#{MacOS::X11.lib}" << "-I#{MacOS::X11.include}" if MacOS::X11.installed? - - args << "-plugin-sql-mysql" if build.with? 'mysql' - - if build.with? 'd-bus' - dbus_opt = Formula.factory('d-bus').opt_prefix - args << "-I#{dbus_opt}/lib/dbus-1.0/include" - args << "-I#{dbus_opt}/include/dbus-1.0" - args << "-L#{dbus_opt}/lib" - args << "-ldbus-1" - end - - if MacOS.prefer_64_bit? or build.universal? - args << '-arch' << 'x86_64' - end - - if !MacOS.prefer_64_bit? or build.universal? - args << '-arch' << 'x86' - end - - args << '-developer-build' if build.include? 'developer' - - system "./configure", *args - system "make" - ENV.j1 - system "make install" - - # Some config scripts will only find Qt in a "Frameworks" folder - cd prefix do - ln_s lib, frameworks - end - - # The pkg-config files installed suggest that headers can be found in the - # `include` directory. Make this so by creating symlinks from `include` to - # the Frameworks' Headers folders. - Pathname.glob(lib + '*.framework/Headers').each do |path| - framework_name = File.basename(File.dirname(path), '.framework') - ln_s path.realpath, include+framework_name - end - - Pathname.glob(bin + '*.app').each do |path| - mv path, prefix - end - end - - test do - system "#{bin}/qmake", "-project" - end - - def caveats; <<-EOS.undent - We agreed to the Qt opensource license for you. - If this is unacceptable you should uninstall. - EOS - end -end \ No newline at end of file From 8f13f3850248172a2ab613641112759dfe2407f1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 10 Mar 2014 17:09:32 -0700 Subject: [PATCH 035/109] update the BUILD guide to point to the new formula location --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 1796aaa575..ccbe7d8cec 100644 --- a/BUILD.md +++ b/BUILD.md @@ -55,7 +55,7 @@ Should you choose not to install Qt5 via a package manager that handles dependen brew install cmake qt5 glm zlib -*High Fidelity has a [homebrew formula](https://raw.github.com/highfidelity/hifi/master/qt5.rb) for a patched version of Qt 5.2.0 stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.* +*High Fidelity has a [homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) for a patched version of Qt 5.2.0 stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.* #####Xcode If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. From 9053379a888740c8e8f5d552acef92bd9738c8c3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 10 Mar 2014 17:36:34 -0700 Subject: [PATCH 036/109] updates to BUILD for addition of qxmpp --- BUILD.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/BUILD.md b/BUILD.md index 6021519b3c..26aaea3f03 100644 --- a/BUILD.md +++ b/BUILD.md @@ -4,6 +4,7 @@ Dependencies * [Qt](http://qt-project.org/downloads) ~> 5.2.0 * [zLib](http://www.zlib.net/) ~> 1.2.8 * [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.0 +* [qxmpp](https://code.google.com/p/qxmpp/) ~> 0.7.6 #####Linux only * [freeglut](http://freeglut.sourceforge.net/) ~> 2.8.0 @@ -53,9 +54,12 @@ Should you choose not to install Qt5 via a package manager that handles dependen #####Package Managers [Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all hifi dependencies very simple. - brew install cmake qt5 glm zlib + brew install cmake qt5 glm zlib qxmpp + +We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. + +*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.2.0 stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.* -*High Fidelity has a [homebrew formula](https://raw.github.com/highfidelity/hifi/master/qt5.rb) for a patched version of Qt 5.2.0 stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.* #####Xcode If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. @@ -94,7 +98,7 @@ NOTE: zLib should configure itself correctly on install. However, sometimes zLib ####External Libraries We don't currently have a Windows installer, so before running Interface, you will need to ensure that all required resources are loadable. -CMake will need to know where the headers and libraries for required external dependencies are. If you installed ZLIB using the installer, the FindZLIB cmake module will be able to find it. This isn't the case for glm, freeglut, and GLEW. +CMake will need to know where the headers and libraries for required external dependencies are. If you installed ZLIB using the installer, the FindZLIB cmake module will be able to find it. This isn't the case for the others. You have the choice of setting a variable specific to each library, or having a folder using a defined structure that contains all of the libs. @@ -112,6 +116,9 @@ The recommended route is to place all of the dependencies in one place and set o -> bin -> include -> lib + -> qxmpp + -> include + -> lib For all three external libraries you should be able to simply copy the extracted folder that you get from the download links provided at the top of the guide. The `root_lib_dir` in the above example can be wherever you choose on your system - as long as the environment variable HIFI_LIB_DIR is set to it. @@ -125,10 +132,10 @@ They can be set in your ENV or by passing them to the cmake command on the comma Each of those designates the root directory that contains the sub-folders for each library. For example, if the GLEW_ROOT_DIR is `C:\libs\glew`, then we would expect to find an `include` folder and a `lib` folder inside `C:\libs\glew`. -####Freeglut DLL -As with the Qt libraries, you will need to make sure the directory containing `freeglut.dll` is in your path. The directory to add to your path in which the DLL is found is `FREEGLUT_DIR/bin`. +*NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit version of libraries for interface.exe to run. The 32-bit version of the static library is the one linked by our CMake find modules* -*NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit version of freeglut.dll for interface.exe to run. The 32-bit version of the static library is the one linked by CMakeLists.txt* +#### DLLs +As with the Qt libraries, you will need to make sure the directory containing dynamically-linked libraries is in your path. For example, for a dynamically linked build of freeglut, the directory to add to your path in which the DLL is found is `FREEGLUT_DIR/bin`. Where possible, you can use static builds of the external dependencies to avoid this requirement. ####Building in Visual Studio Follow the same build steps from the CMake section, but pass a different generator to CMake. @@ -138,7 +145,6 @@ Follow the same build steps from the CMake section, but pass a different generat ####Running Interface If you need to debug Interface, you can run interface from within Visual Studio (see the section below). You can also run Interface by launching it from command line or File Explorer from $YOUR_HIFI_PATH\build\interface\Debug\interface.exe - ####Debugging Interface * In the Solution Explorer, right click interface and click Set as StartUp Project * Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug From 0b8852cfbe6696a4de232885055d6c0b413d1046 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 10 Mar 2014 17:40:06 -0700 Subject: [PATCH 037/109] clarify when it is possible to drag-and-drop external dependency --- BUILD.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index 26aaea3f03..ab27bfe52d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -120,10 +120,10 @@ The recommended route is to place all of the dependencies in one place and set o -> include -> lib -For all three external libraries you should be able to simply copy the extracted folder that you get from the download links provided at the top of the guide. The `root_lib_dir` in the above example can be wherever you choose on your system - as long as the environment variable HIFI_LIB_DIR is set to it. - *NOTE: Be careful with glm. For the folder other libraries would normally call 'include', the folder containing the headers, glm opts to use 'glm'. You will have a glm folder nested inside the top-level glm folder.* +For many of the external libraries where precompiled binaries are readily available you should be able to simply copy the extracted folder that you get from the download links provided at the top of the guide. Otherwise you may need to build from source and install the built product to this directory. The `root_lib_dir` in the above example can be wherever you choose on your system - as long as the environment variable HIFI_LIB_DIR is set to it. + Should you want to define a location for each library, these are the associated variables you will want to set: `GLM_ROOT_DIR, GLUT_ROOT_DIR, GLEW_ROOT_DIR` From ae6a59ef78481dbb89db4fbb324fd4703f0614f6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 10 Mar 2014 17:53:20 -0700 Subject: [PATCH 038/109] Ray intersections for models, don't load resources if there's no network manager installed. --- interface/src/MetavoxelSystem.cpp | 4 ++++ interface/src/MetavoxelSystem.h | 1 + libraries/metavoxels/src/MetavoxelData.cpp | 14 ++++++++++++-- libraries/metavoxels/src/MetavoxelData.h | 6 +++++- libraries/shared/src/ResourceCache.cpp | 2 +- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index da73bfb371..54cc3b4cdb 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -360,6 +360,10 @@ void StaticModelRenderer::render(float alpha) { _model->render(alpha); } +bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + return _model->findRayIntersection(origin, direction, distance); +} + void StaticModelRenderer::applyTranslation(const glm::vec3& translation) { _model->setTranslation(translation); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index c605205244..2af3262d15 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -163,6 +163,7 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); virtual void render(float alpha); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; private slots: diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 047faf5e71..d64cbc7d94 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -1177,9 +1177,9 @@ AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const { const float DEFAULT_GRANULARITY = 0.01f; Spanner::Spanner() : + _renderer(NULL), _granularity(DEFAULT_GRANULARITY), - _lastVisit(0), - _renderer(NULL) { + _lastVisit(0) { } void Spanner::setBounds(const Box& bounds) { @@ -1247,6 +1247,10 @@ void SpannerRenderer::render(float alpha) { // nothing by default } +bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + return false; +} + Transformable::Transformable() : _scale(1.0f) { } @@ -1359,6 +1363,12 @@ void StaticModel::setURL(const QUrl& url) { } } +bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + // delegate to renderer, if we have one + return _renderer ? _renderer->findRayIntersection(origin, direction, distance) : + Spanner::findRayIntersection(origin, direction, distance); +} + QByteArray StaticModel::getRendererClassName() const { return "StaticModelRenderer"; } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 80ff431172..c5ac570876 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -444,6 +444,8 @@ signals: protected: + SpannerRenderer* _renderer; + /// Returns the name of the class to instantiate in order to render this spanner. virtual QByteArray getRendererClassName() const; @@ -452,7 +454,6 @@ private: Box _bounds; float _granularity; int _lastVisit; ///< the identifier of the last visit - SpannerRenderer* _renderer; static int _visit; ///< the global visit counter }; @@ -468,6 +469,7 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); virtual void render(float alpha); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; }; /// An object with a 3D transform. @@ -550,6 +552,8 @@ public: void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + signals: void urlChanged(const QUrl& url); diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index a7c0302a9c..9d7aa35c31 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -81,7 +81,7 @@ Resource::Resource(const QUrl& url, bool delayLoad) : _reply(NULL), _attempts(0) { - if (!url.isValid()) { + if (!(url.isValid() && ResourceCache::getNetworkAccessManager())) { _startedLoading = _failedToLoad = true; return; } From dc0b9712bb54560ce99679737e6f848410a9357b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 11 Mar 2014 09:40:54 -0700 Subject: [PATCH 039/109] updates to BUILD guide to suggest using tapped formulas --- BUILD.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index ab27bfe52d..72fd87f906 100644 --- a/BUILD.md +++ b/BUILD.md @@ -54,7 +54,11 @@ Should you choose not to install Qt5 via a package manager that handles dependen #####Package Managers [Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all hifi dependencies very simple. - brew install cmake qt5 glm zlib qxmpp + brew tap highfidelity/homebrew-formulas + brew install cmake glm zlib + brew install highfidelity/formulas/qt5 + brew link qt5 --force + brew install highfidelity/formuals/qxmpp We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. From f3e8e6845791ff88d3ab035caf9d00f2f2a8ae73 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 11 Mar 2014 09:41:33 -0700 Subject: [PATCH 040/109] fix a typo for path to tapped qxmpp formula --- BUILD.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index 72fd87f906..1bc5145704 100644 --- a/BUILD.md +++ b/BUILD.md @@ -58,9 +58,9 @@ Should you choose not to install Qt5 via a package manager that handles dependen brew install cmake glm zlib brew install highfidelity/formulas/qt5 brew link qt5 --force - brew install highfidelity/formuals/qxmpp + brew install highfidelity/formulas/qxmpp -We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. +We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 and qxmpp are installed from formulas in this repository. *Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.2.0 stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.* From d1623e6e4d7fa0d07f30121d69ad3da1941c16dc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 11 Mar 2014 11:45:13 -0700 Subject: [PATCH 041/109] fixes for warnings produced by Xcode 5.1 --- animation-server/src/AnimationServer.cpp | 5 ----- domain-server/src/DomainServer.cpp | 2 -- interface/src/Application.cpp | 4 ---- interface/src/Audio.cpp | 3 --- interface/src/avatar/Avatar.cpp | 22 ---------------------- interface/src/avatar/MyAvatar.cpp | 7 ------- interface/src/starfield/Controller.h | 2 +- libraries/avatars/src/AvatarData.cpp | 2 -- libraries/particles/src/Particle.cpp | 5 ----- 9 files changed, 1 insertion(+), 51 deletions(-) diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp index a013eaaf5c..f59d9db098 100644 --- a/animation-server/src/AnimationServer.cpp +++ b/animation-server/src/AnimationServer.cpp @@ -364,7 +364,6 @@ float danceFloorGradientIncrement = 1.0f / FRAMES_PER_BEAT; const float DANCE_FLOOR_MAX_GRADIENT = 1.0f; const float DANCE_FLOOR_MIN_GRADIENT = 0.0f; const int DANCE_FLOOR_VOXELS_PER_PACKET = 100; -const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR_WIDTH * DANCE_FLOOR_LENGTH); int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH]; void sendDanceFloor() { @@ -467,8 +466,6 @@ const float BILLBOARD_MAX_GRADIENT = 1.0f; const float BILLBOARD_MIN_GRADIENT = 0.0f; const float BILLBOARD_LIGHT_SIZE = 0.125f / TREE_SCALE; // approximately 1/8 meter per light const int VOXELS_PER_PACKET = 81; -const int PACKETS_PER_BILLBOARD = VOXELS_PER_PACKET / (BILLBOARD_HEIGHT * BILLBOARD_WIDTH); - // top to bottom... bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = { @@ -541,14 +538,12 @@ static void sendBillboard() { } bool roadInitialized = false; -const int ROAD_WIDTH_METERS = 3.0f; const int BRICKS_ACROSS_ROAD = 32; const float ROAD_BRICK_SIZE = 0.125f/TREE_SCALE; //(ROAD_WIDTH_METERS / TREE_SCALE) / BRICKS_ACROSS_ROAD; // in voxel units const int ROAD_LENGTH = 1.0f / ROAD_BRICK_SIZE; // in bricks const int ROAD_WIDTH = BRICKS_ACROSS_ROAD; // in bricks glm::vec3 roadPosition(0.5f - (ROAD_BRICK_SIZE * BRICKS_ACROSS_ROAD), 0.0f, 0.0f); const int BRICKS_PER_PACKET = 32; // guessing -const int PACKETS_PER_ROAD = VOXELS_PER_PACKET / (ROAD_LENGTH * ROAD_WIDTH); void doBuildStreet() { if (roadInitialized) { diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6eb59bb3df..e5979c561f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -359,8 +359,6 @@ void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packe sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); } -const int NUM_BYTES_DATA_SERVER_REGISTRATION_TOKEN = 16; - int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a2ab5fcb2a..032e849f0a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1138,7 +1138,6 @@ void Application::touchEndEvent(QTouchEvent* event) { } -const bool USE_MOUSEWHEEL = false; void Application::wheelEvent(QWheelEvent* event) { _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts @@ -1327,7 +1326,6 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox (mouseVoxel.z + mouseVoxel.s / 2.f) * TREE_SCALE); } -const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500; struct SendVoxelsOperationArgs { const unsigned char* newBaseOctCode; }; @@ -1607,8 +1605,6 @@ void Application::shrinkMirrorView() { } } -const float MAX_AVATAR_EDIT_VELOCITY = 1.0f; -const float MAX_VOXEL_EDIT_DISTANCE = 50.0f; const float HEAD_SPHERE_RADIUS = 0.07f; bool Application::isLookingAtMyAvatar(Avatar* avatar) { diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 7f6f1bcb05..7ab5f0f37b 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -32,9 +32,6 @@ #include "Menu.h" #include "Util.h" -static const float JITTER_BUFFER_LENGTH_MSECS = 12; -static const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS * NUM_AUDIO_CHANNELS * (SAMPLE_RATE / 1000.0); - static const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0; static const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 2301822e28..ec2e658652 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -31,28 +31,6 @@ using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); -const float YAW_MAG = 500.0f; -const float MY_HAND_HOLDING_PULL = 0.2f; -const float YOUR_HAND_HOLDING_PULL = 1.0f; -const float BODY_SPRING_DEFAULT_TIGHTNESS = 1000.0f; -const float BODY_SPRING_FORCE = 300.0f; -const float BODY_SPRING_DECAY = 16.0f; -const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions -const float COLLISION_BODY_FORCE = 30.0f; // pertains to avatar-to-avatar collisions -const float HEAD_ROTATION_SCALE = 0.70f; -const float HEAD_ROLL_SCALE = 0.40f; -const float HEAD_MAX_PITCH = 45; -const float HEAD_MIN_PITCH = -45; -const float HEAD_MAX_YAW = 85; -const float HEAD_MIN_YAW = -85; -const float AVATAR_BRAKING_STRENGTH = 40.0f; -const float MOUSE_RAY_TOUCH_RANGE = 0.01f; -const float FLOATING_HEIGHT = 0.13f; -const bool USING_HEAD_LEAN = false; -const float LEAN_SENSITIVITY = 0.15f; -const float LEAN_MAX = 0.45f; -const float LEAN_AVERAGING = 10.0f; -const float HEAD_RATE_MAX = 50.f; const int NUM_BODY_CONE_SIDES = 9; const float CHAT_MESSAGE_SCALE = 0.0015f; const float CHAT_MESSAGE_HEIGHT = 0.1f; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7108e0d0cc..ab72546e89 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -37,12 +37,7 @@ const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const float YAW_MAG = 500.0f; const float PITCH_MAG = 100.0f; const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions -const float COLLISION_BODY_FORCE = 30.0f; // pertains to avatar-to-avatar collisions const float COLLISION_RADIUS_SCALE = 0.125f; -const float MOUSE_RAY_TOUCH_RANGE = 0.01f; -const bool USING_HEAD_LEAN = false; -const float SKIN_COLOR[] = {1.0f, 0.84f, 0.66f}; -const float DARK_SKIN_COLOR[] = {0.9f, 0.78f, 0.63f}; const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; @@ -329,8 +324,6 @@ void MyAvatar::simulate(float deltaTime) { maybeUpdateBillboard(); } -const float MAX_PITCH = 90.0f; - // Update avatar head rotation with sensor data void MyAvatar::updateFromGyros(float deltaTime) { Faceshift* faceshift = Application::getInstance()->getFaceshift(); diff --git a/interface/src/starfield/Controller.h b/interface/src/starfield/Controller.h index c5ade48442..5403d2fb75 100755 --- a/interface/src/starfield/Controller.h +++ b/interface/src/starfield/Controller.h @@ -8,7 +8,7 @@ // #ifndef __interface__starfield__Controller__ -#define __interface__starfield__Confroller__ +#define __interface__starfield__Controller__ #include diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2483e0514b..7a1781f554 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -26,8 +26,6 @@ using namespace std; -static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed - QNetworkAccessManager* AvatarData::networkAccessManager = NULL; AvatarData::AvatarData() : diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 1a0ca7680b..f4a5785bad 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -815,11 +815,6 @@ void Particle::applyHardCollision(const CollisionInfo& collisionInfo) { setVelocity(velocity); } -// MIN_VALID_SPEED is obtained by computing speed gained at one gravity during the shortest expected frame period -// This is a HACK for particles that bounce in a 1.0 gravitational field and should eventually be made more universal. -const float MIN_EXPECTED_FRAME_PERIOD = 0.005f; // 1/200th of a second -const float MIN_VALID_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE); - void Particle::update(const quint64& now) { float timeElapsed = (float)(now - _lastUpdated) / (float)(USECS_PER_SECOND); _lastUpdated = now; From 66e322ec55c5d6e23c8f9fa7f24781f03ee287b5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 12:21:58 -0700 Subject: [PATCH 042/109] Added per-attribute LOD threshold multiplier; things like spanners need different LOD thresholds than, say, voxels. --- .../metavoxels/src/AttributeRegistry.cpp | 7 ++- libraries/metavoxels/src/AttributeRegistry.h | 8 +++ libraries/metavoxels/src/MetavoxelData.cpp | 50 +++++++++++++++---- libraries/metavoxels/src/MetavoxelData.h | 13 +++-- 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 8a444467de..9ec37acd80 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -28,6 +28,10 @@ AttributeRegistry::AttributeRegistry() : _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), _colorAttribute(registerAttribute(new QRgbAttribute("color"))), _normalAttribute(registerAttribute(new PackedNormalAttribute("normal", qRgb(0, 127, 0)))) { + + // our baseline LOD threshold is for voxels; spanners are a different story + const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 4.0f; + _spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER); } static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { @@ -143,7 +147,8 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const OwnedAttributeValue& o return *this; } -Attribute::Attribute(const QString& name) { +Attribute::Attribute(const QString& name) : + _lodThresholdMultiplier(1.0f) { setObjectName(name); } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 3dc6aa7484..3405f90315 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -153,6 +153,7 @@ public: /// Represents a registered attribute. class Attribute : public SharedObject { Q_OBJECT + Q_PROPERTY(float lodThresholdMultiplier MEMBER _lodThresholdMultiplier) public: @@ -163,6 +164,9 @@ public: Q_INVOKABLE QString getName() const { return objectName(); } + float getLODThresholdMultiplier() const { return _lodThresholdMultiplier; } + void setLODThresholdMultiplier(float multiplier) { _lodThresholdMultiplier = multiplier; } + void* create() const { return create(getDefaultValue()); } virtual void* create(void* copy) const = 0; virtual void destroy(void* value) const = 0; @@ -197,6 +201,10 @@ public: /// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable. /// The widget should have a single "user" property that will be used to get/set the value. virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; } + +private: + + float _lodThresholdMultiplier; }; /// A simple attribute class that stores its values inline. diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index d64cbc7d94..8a6b0bc16a 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -29,15 +29,16 @@ MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) : threshold(threshold) { } -bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size) const { - return size >= glm::distance(position, minimum + glm::vec3(size, size, size) * 0.5f) * threshold; +bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size, float multiplier) const { + return size >= glm::distance(position, minimum + glm::vec3(size, size, size) * 0.5f) * threshold * multiplier; } -bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference) const { +bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, + const MetavoxelLOD& reference, float multiplier) const { if (position == reference.position && threshold >= reference.threshold) { return false; // first off, nothing becomes subdivided if it doesn't change } - if (!shouldSubdivide(minimum, size)) { + if (!shouldSubdivide(minimum, size, multiplier)) { return false; // this one must be subdivided } // the general check is whether we've gotten closer (as multiplied by the threshold) to any point in the volume, @@ -468,6 +469,18 @@ static glm::vec3 getNextMinimum(const glm::vec3& minimum, float nextSize, int in (index & Z_MAXIMUM_FLAG) ? nextSize : 0.0f); } +bool MetavoxelStreamState::shouldSubdivide() const { + return lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier()); +} + +bool MetavoxelStreamState::shouldSubdivideReference() const { + return referenceLOD.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier()); +} + +bool MetavoxelStreamState::becameSubdivided() const { + return lod.becameSubdivided(minimum, size, referenceLOD, attribute->getLODThresholdMultiplier()); +} + void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) { minimum = getNextMinimum(lastMinimum, size, index); } @@ -831,7 +844,16 @@ MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : _inputs(inputs), _outputs(outputs), - _lod(lod) { + _lod(lod), + _minimumLODThresholdMultiplier(FLT_MAX) { + + // find the minimum LOD threshold multiplier over all attributes + foreach (const AttributePointer& attribute, _inputs) { + _minimumLODThresholdMultiplier = qMin(attribute->getLODThresholdMultiplier(), _minimumLODThresholdMultiplier); + } + foreach (const AttributePointer& attribute, _outputs) { + _minimumLODThresholdMultiplier = qMin(attribute->getLODThresholdMultiplier(), _minimumLODThresholdMultiplier); + } } MetavoxelVisitor::~MetavoxelVisitor() { @@ -928,8 +950,11 @@ DefaultMetavoxelGuide::DefaultMetavoxelGuide() { } bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - bool shouldSubdivide = visitation.visitor.getLOD().shouldSubdivide(visitation.info.minimum, visitation.info.size); - visitation.info.isLeaf = !shouldSubdivide || visitation.allInputNodesLeaves(); + // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute + float lodBase = glm::distance(visitation.visitor.getLOD().position, visitation.info.getCenter()) * + visitation.visitor.getLOD().threshold; + visitation.info.isLeaf = (visitation.info.size < lodBase * visitation.visitor.getMinimumLODThresholdMultiplier()) || + visitation.allInputNodesLeaves(); int encodedOrder = visitation.visitor.visit(visitation.info); if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { return false; @@ -955,20 +980,23 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + // the encoded order tells us the child indices for each iteration const int ORDER_ELEMENT_BITS = 3; const int ORDER_ELEMENT_MASK = (1 << ORDER_ELEMENT_BITS) - 1; int index = encodedOrder & ORDER_ELEMENT_MASK; encodedOrder >>= ORDER_ELEMENT_BITS; for (int j = 0; j < visitation.inputNodes.size(); j++) { MetavoxelNode* node = visitation.inputNodes.at(j); - MetavoxelNode* child = (node && shouldSubdivide) ? node->getChild(index) : NULL; + const AttributeValue& parentValue = visitation.info.inputValues.at(j); + MetavoxelNode* child = (node && (visitation.info.size >= lodBase * + parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? - child->getAttributeValue(visitation.info.inputValues[j].getAttribute()) : - visitation.info.inputValues[j]; + child->getAttributeValue(parentValue.getAttribute()) : parentValue; } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = (node && shouldSubdivide) ? node->getChild(index) : NULL; + MetavoxelNode* child = (node && (visitation.info.size >= lodBase * + visitation.visitor.getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.outputNodes[j] = child; } nextVisitation.info.minimum = getNextMinimum(visitation.info.minimum, nextVisitation.info.size, index); diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index c5ac570876..7e0a863a3b 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -43,10 +43,10 @@ public: bool isValid() const { return threshold > 0.0f; } - bool shouldSubdivide(const glm::vec3& minimum, float size) const; + bool shouldSubdivide(const glm::vec3& minimum, float size, float multiplier = 1.0f) const; /// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference. - bool becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference) const; + bool becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference, float multiplier = 1.0f) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelLOD) @@ -119,9 +119,9 @@ public: const MetavoxelLOD& lod; const MetavoxelLOD& referenceLOD; - bool shouldSubdivide() const { return lod.shouldSubdivide(minimum, size); } - bool shouldSubdivideReference() const { return referenceLOD.shouldSubdivide(minimum, size); } - bool becameSubdivided() const { return lod.becameSubdivided(minimum, size, referenceLOD); } + bool shouldSubdivide() const; + bool shouldSubdivideReference() const; + bool becameSubdivided() const; void setMinimum(const glm::vec3& lastMinimum, int index); }; @@ -230,6 +230,8 @@ public: void setLOD(const MetavoxelLOD& lod) { _lod = lod; } + float getMinimumLODThresholdMultiplier() const { return _minimumLODThresholdMultiplier; } + /// Prepares for a new tour of the metavoxel data. virtual void prepare(); @@ -243,6 +245,7 @@ protected: QVector _inputs; QVector _outputs; MetavoxelLOD _lod; + float _minimumLODThresholdMultiplier; }; /// Base class for visitors to spanners. From 35d0fa71891c490ac6ad43e37dc4771d39027991 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 13:46:58 -0700 Subject: [PATCH 043/109] Don't render avatars until they've been simulated, init before first simulate. Closes #2262. --- interface/src/avatar/AvatarManager.cpp | 5 ++++- interface/src/avatar/SkeletonModel.cpp | 10 +++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 57e800a401..88ce2b4399 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -54,6 +54,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ++avatarIterator; continue; } + if (!avatar->isInitialized()) { + avatar->init(); + } if (avatar->getOwningAvatarMixer()) { // this avatar's mixer is still around, go ahead and simulate it avatar->simulate(deltaTime); @@ -80,7 +83,7 @@ void AvatarManager::renderAvatars(bool forShadowMapOrMirror, bool selfAvatarOnly foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) { Avatar* avatar = static_cast(avatarPointer.data()); if (!avatar->isInitialized()) { - avatar->init(); + continue; } if (avatar == static_cast(_myAvatar.data())) { _myAvatar->render(forShadowMapOrMirror); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c57656be58..e1014297e8 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -19,18 +19,14 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) : } void SkeletonModel::simulate(float deltaTime, bool delayLoad) { - if (!isActive()) { - Model::simulate(deltaTime, delayLoad); - return; - } setTranslation(_owningAvatar->getPosition()); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(180.0f, glm::vec3(0.0f, 1.0f, 0.0f))); const float MODEL_SCALE = 0.0006f; setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE); - + Model::simulate(deltaTime, delayLoad); - - if (!_owningAvatar->isMyAvatar()) { + + if (!(isActive() && _owningAvatar->isMyAvatar())) { return; // only simulate for own avatar } From bc3bba924041d1342e3b4bdee990e05df36866f1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 14:01:23 -0700 Subject: [PATCH 044/109] Get rid of unused, expensive "average color" nonsense. --- interface/src/renderer/GeometryCache.cpp | 24 ------------------------ interface/src/renderer/GeometryCache.h | 3 --- interface/src/renderer/Model.cpp | 4 ---- interface/src/renderer/Model.h | 3 --- interface/src/renderer/TextureCache.cpp | 17 ++++------------- interface/src/renderer/TextureCache.h | 6 +----- 6 files changed, 5 insertions(+), 52 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index a7973f4a1b..a13a538b0e 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -378,30 +378,6 @@ QSharedPointer NetworkGeometry::getLODOrFallback(float distance return lod; } -glm::vec4 NetworkGeometry::computeAverageColor() const { - glm::vec4 totalColor; - int totalTriangles = 0; - for (int i = 0; i < _meshes.size(); i++) { - const FBXMesh& mesh = _geometry.meshes.at(i); - if (mesh.isEye) { - continue; // skip eyes - } - const NetworkMesh& networkMesh = _meshes.at(i); - for (int j = 0; j < mesh.parts.size(); j++) { - const FBXMeshPart& part = mesh.parts.at(j); - const NetworkMeshPart& networkPart = networkMesh.parts.at(j); - glm::vec4 color = glm::vec4(part.diffuseColor, 1.0f); - if (networkPart.diffuseTexture) { - color *= networkPart.diffuseTexture->getAverageColor(); - } - int triangles = part.quadIndices.size() * 2 + part.triangleIndices.size(); - totalColor += color * (float) triangles; - totalTriangles += triangles; - } - } - return (totalTriangles == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / (float) totalTriangles; -} - void NetworkGeometry::setLoadPriority(const QPointer& owner, float priority) { Resource::setLoadPriority(owner, priority); diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index da1955039d..cc0775051a 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -79,9 +79,6 @@ public: const FBXGeometry& getFBXGeometry() const { return _geometry; } const QVector& getMeshes() const { return _meshes; } - /// Returns the average color of all meshes in the geometry. - glm::vec4 computeAverageColor() const; - virtual void setLoadPriority(const QPointer& owner, float priority); virtual void setLoadPriorities(const QHash, float>& priorities); virtual void clearLoadPriority(const QPointer& owner); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f6cfb08816..0df6ad721c 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -497,10 +497,6 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo } } -glm::vec4 Model::computeAverageColor() const { - return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); -} - bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { const glm::vec3 relativeOrigin = origin - _translation; const FBXGeometry& geometry = _geometry->getFBXGeometry(); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 763c30ad39..c7aadee8dc 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -175,9 +175,6 @@ public: /// Returns the extended length from the right hand to its first free ancestor. float getRightArmLength() const; - /// Returns the average color of all meshes in the geometry. - glm::vec4 computeAverageColor() const; - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; /// \param shapes list of pointers shapes to test against Model diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 80eef66ff5..db019eca95 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -257,7 +257,6 @@ Texture::~Texture() { NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : Resource(url), - _averageColor(1.0f, 1.0f, 1.0f, 1.0f), _translucent(false) { if (!url.isValid()) { @@ -300,27 +299,20 @@ void ImageReader::run() { image = image.convertToFormat(QImage::Format_ARGB32); } - // sum up the colors for the average and check for translucency - glm::vec4 accumulated; + // check for translucency int translucentPixels = 0; const int EIGHT_BIT_MAXIMUM = 255; + const int RGB_BITS = 24; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { - QRgb pixel = image.pixel(x, y); - accumulated.r += qRed(pixel); - accumulated.g += qGreen(pixel); - accumulated.b += qBlue(pixel); - - int alpha = qAlpha(pixel); + int alpha = image.pixel(x, y) >> RGB_BITS; if (alpha != 0 && alpha != EIGHT_BIT_MAXIMUM) { translucentPixels++; } - accumulated.a += alpha; } } int imageArea = image.width() * image.height(); QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), - Q_ARG(const glm::vec4&, accumulated / (float) (imageArea * EIGHT_BIT_MAXIMUM)), Q_ARG(bool, translucentPixels >= imageArea / 2)); _reply->deleteLater(); } @@ -330,8 +322,7 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) { QThreadPool::globalInstance()->start(new ImageReader(_self, reply)); } -void NetworkTexture::setImage(const QImage& image, const glm::vec4& averageColor, bool translucent) { - _averageColor = averageColor; +void NetworkTexture::setImage(const QImage& image, bool translucent) { _translucent = translucent; finishedLoading(true); diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 0111a2826d..d7e61954ce 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -119,9 +119,6 @@ public: NetworkTexture(const QUrl& url, bool normalMap); - /// Returns the average color over the entire texture. - const glm::vec4& getAverageColor() const { return _averageColor; } - /// Checks whether it "looks like" this texture is translucent /// (majority of pixels neither fully opaque or fully transparent). bool isTranslucent() const { return _translucent; } @@ -131,11 +128,10 @@ protected: virtual void downloadFinished(QNetworkReply* reply); virtual void imageLoaded(const QImage& image); - Q_INVOKABLE void setImage(const QImage& image, const glm::vec4& averageColor, bool translucent); + Q_INVOKABLE void setImage(const QImage& image, bool translucent); private: - glm::vec4 _averageColor; bool _translucent; }; From e96e59148155bacb56391e44e9827914d2c945aa Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 14:25:34 -0700 Subject: [PATCH 045/109] Build fix for Visage: added missing include. --- interface/src/devices/Visage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index aed286e04b..4d1ea2b40b 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -14,6 +14,7 @@ #include #endif +#include "Application.h" #include "Visage.h" #include "renderer/FBXReader.h" From a4fc2397c7fc326feea3f4652a4bb872d519de06 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 14:36:29 -0700 Subject: [PATCH 046/109] Fixed Visage includes. --- interface/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0aaf180047..f73cf0fd64 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -139,7 +139,8 @@ if (VISAGE_FOUND AND NOT DISABLE_VISAGE) include_directories(SYSTEM "${VISAGE_INCLUDE_DIRS}") if (APPLE) add_definitions(-DMAC_OS_X) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment -isystem ${VISAGE_INCLUDE_DIRS}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment") + include_directories(SYSTEM "${VISAGE_INCLUDE_DIRS}") find_library(AVFoundation AVFoundation) find_library(CoreMedia CoreMedia) find_library(NEW_STD_LIBRARY libc++.dylib /usr/lib/) From f8e6593bcf7b320cc23cbbc54f7f993019207a41 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 15:24:14 -0700 Subject: [PATCH 047/109] Turn off mipmaps. Performance is actually substantially better without them. --- interface/src/renderer/TextureCache.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index db019eca95..91cf1c8b6e 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -113,8 +113,7 @@ GLuint TextureCache::getFileTextureID(const QString& filename) { glBindTexture(GL_TEXTURE_2D, id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); _fileTextureIDs.insert(filename, id); @@ -330,8 +329,7 @@ void NetworkTexture::setImage(const QImage& image, bool translucent) { glBindTexture(GL_TEXTURE_2D, getID()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); } @@ -364,8 +362,7 @@ QSharedPointer DilatableNetworkTexture::getDilatedTexture(float dilatio glBindTexture(GL_TEXTURE_2D, texture->getID()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dilatedImage.width(), dilatedImage.height(), 1, GL_BGRA, GL_UNSIGNED_BYTE, dilatedImage.constBits()); - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); } From ecb54600ebde2621db2e1797f43e8a4aca1ef585 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 15:34:41 -0700 Subject: [PATCH 048/109] Another compile fix. --- interface/src/devices/Visage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 4d1ea2b40b..2f1e6e745a 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -37,9 +37,9 @@ Visage::Visage() : _estimatedEyeYaw(0.0f) { #ifdef HAVE_VISAGE - QByteArray licensePath = Application::resourcesPath() + "visage/license.vlc"; + QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc"; initializeLicenseManager(licensePath.data()); - _tracker = new VisageTracker2(Application::resourcesPath() + "visage/tracker.cfg"); + _tracker = new VisageTracker2(Application::resourcesPath().toLatin1() + "visage/tracker.cfg"); if (_tracker->trackFromCam()) { _data = new FaceData(); From 8d69eff86744c877681f3f84dd022c8563523bac Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 15:38:19 -0700 Subject: [PATCH 049/109] Need to reorder includes due to HANDLE definition conflict. --- interface/src/devices/Visage.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 2f1e6e745a..727c083265 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -10,14 +10,15 @@ #include -#ifdef HAVE_VISAGE -#include -#endif - #include "Application.h" #include "Visage.h" #include "renderer/FBXReader.h" +// this has to go after our normal includes, because its definition of HANDLE conflicts with Qt's +#ifdef HAVE_VISAGE +#include +#endif + namespace VisageSDK { #ifdef WIN32 void __declspec(dllimport) initializeLicenseManager(char* licenseKeyFileName); From dcaef45f2e2749f94390644d18da71cd493e3905 Mon Sep 17 00:00:00 2001 From: Clement Date: Tue, 11 Mar 2014 16:01:21 -0700 Subject: [PATCH 050/109] More work on editVoxel.js --- examples/addVoxelOnMouseClickExample.js | 20 +- examples/editVoxels.js | 344 ++++++++++++------ interface/src/Application.cpp | 14 +- interface/src/Application.h | 5 +- interface/src/ClipboardScriptingInterface.cpp | 14 +- interface/src/ClipboardScriptingInterface.h | 5 +- interface/src/VoxelSystem.cpp | 2 - interface/src/ui/LocalVoxelsOverlay.cpp | 4 +- interface/src/ui/Volume3DOverlay.cpp | 3 + 9 files changed, 273 insertions(+), 138 deletions(-) diff --git a/examples/addVoxelOnMouseClickExample.js b/examples/addVoxelOnMouseClickExample.js index 244a017ae4..4450a16c22 100644 --- a/examples/addVoxelOnMouseClickExample.js +++ b/examples/addVoxelOnMouseClickExample.js @@ -15,16 +15,16 @@ function mousePressEvent(event) { var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Voxels.findRayIntersection(pickRay); if (intersection.intersects) { - + // Note: due to the current C++ "click on voxel" behavior, these values may be the animated color for the voxel - print("clicked on voxel.red/green/blue=" + intersection.voxel.red + ", " + print("clicked on voxel.red/green/blue=" + intersection.voxel.red + ", " + intersection.voxel.green + ", " + intersection.voxel.blue); - print("clicked on voxel.x/y/z/s=" + intersection.voxel.x + ", " + print("clicked on voxel.x/y/z/s=" + intersection.voxel.x + ", " + intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s); print("clicked on face=" + intersection.face); print("clicked on distance=" + intersection.distance); - - var newVoxel = { + + var newVoxel = { x: intersection.voxel.x, y: intersection.voxel.y, z: intersection.voxel.z, @@ -32,7 +32,7 @@ function mousePressEvent(event) { red: 255, green: 0, blue: 255 }; - + if (intersection.face == "MIN_X_FACE") { newVoxel.x -= newVoxel.s; } else if (intersection.face == "MAX_X_FACE") { @@ -46,11 +46,11 @@ function mousePressEvent(event) { } else if (intersection.face == "MAX_Z_FACE") { newVoxel.z += newVoxel.s; } - - print("Voxels.setVoxel("+newVoxel.x + ", " - + newVoxel.y + ", " + newVoxel.z + ", " + newVoxel.s + ", " + + print("Voxels.setVoxel("+newVoxel.x + ", " + + newVoxel.y + ", " + newVoxel.z + ", " + newVoxel.s + ", " + newVoxel.red + ", " + newVoxel.green + ", " + newVoxel.blue + ")" ); - + Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue); } } diff --git a/examples/editVoxels.js b/examples/editVoxels.js index c4abfd7fff..960840b680 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -8,10 +8,10 @@ // Captures mouse clicks and edits voxels accordingly. // // click = create a new voxel on this face, same color as old (default color picker state) -// right click or control + click = delete this voxel +// right click or control + click = delete this voxel // shift + click = recolor this voxel // 1 - 8 = pick new color from palette -// 9 = create a new voxel in front of the camera +// 9 = create a new voxel in front of the camera // // Click and drag to create more new voxels in the same direction // @@ -36,19 +36,19 @@ var previewLineWidth = 1.5; var oldMode = Camera.getMode(); -var isAdding = false; -var isExtruding = false; +var isAdding = false; +var isExtruding = false; var isOrbiting = false; var isOrbitingFromTouch = false; var isPanning = false; var isPanningFromTouch = false; var touchPointsToOrbit = 2; // you can change these, but be mindful that on some track pads 2 touch points = right click+drag -var touchPointsToPan = 3; +var touchPointsToPan = 3; var orbitAzimuth = 0.0; var orbitAltitude = 0.0; var orbitCenter = { x: 0, y: 0, z: 0 }; var orbitPosition = { x: 0, y: 0, z: 0 }; -var torsoToEyeVector = { x: 0, y: 0, z: 0 }; +var torsoToEyeVector = { x: 0, y: 0, z: 0 }; var orbitRadius = 0.0; var extrudeDirection = { x: 0, y: 0, z: 0 }; var extrudeScale = 0.0; @@ -60,7 +60,7 @@ var wheelPixelsMoved = 0; var mouseX = 0; -var mouseY = 0; +var mouseY = 0; // Create a table of the different colors you can choose var colors = new Array(); @@ -76,7 +76,7 @@ colors[8] = { red: 31, green: 64, blue: 64 }; var numColors = 9; var whichColor = -1; // Starting color is 'Copy' mode -// Create sounds for adding, deleting, recoloring voxels +// Create sounds for adding, deleting, recoloring voxels var addSound1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+2.raw"); var addSound2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+3.raw"); var addSound3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+4.raw"); @@ -93,7 +93,7 @@ var editToolsOn = true; // starts out off // previewAsVoxel - by default, we will preview adds/deletes/recolors as just 4 lines on the intersecting face. But if you // the preview to show a full voxel then set this to true and the voxel will be displayed for voxel editing -var previewAsVoxel = false; +var previewAsVoxel = true; var voxelPreview = Overlays.addOverlay("cube", { position: { x: 0, y: 0, z: 0}, @@ -104,7 +104,7 @@ var voxelPreview = Overlays.addOverlay("cube", { visible: false, lineWidth: 4 }); - + var linePreviewTop = Overlays.addOverlay("line3d", { position: { x: 0, y: 0, z: 0}, end: { x: 0, y: 0, z: 0}, @@ -228,7 +228,7 @@ var eyedropperTool = Overlays.addOverlay("image", { visible: false, alpha: 0.9 }); - + // This will create a couple of image overlays that make a "slider", we will demonstrate how to trap mouse messages to // move the slider @@ -277,6 +277,88 @@ var pointerVoxelScaleMin = Math.pow(2, (1-pointerVoxelScaleOriginStep)); var pointerVoxelScaleMax = Math.pow(2, (pointerVoxelScaleSteps-pointerVoxelScaleOriginStep)); var thumbDeltaPerStep = thumbExtents / (pointerVoxelScaleSteps - 1); + + +///////////////////////////////////// IMPORT MODULE /////////////////////////////// +// Move the following code to a separate file when include will be available. +var importTree; +var importPreview; +var isImporting; +var importPosition; +var importScale; + +function initImport() { + importPreview = Overlays.addOverlay("localvoxels", { + name: "import", + position: { x: 0, y: 0, z: 0}, + scale: 0, + visible: false + }); + isImporting = false; + importPosition = { x: 0, y: 0, z: 0 }; + importScale = 0; +} + +function importVoxels() { + if (Clipboard.importVoxels() == 0) { + isImporting = true; + if (importScale <= 0) { + importScale = 1; + } + } else { + isImporting = false; + } + + return isImporting; +} + +function moveImport(position) { + if (0 < position.x && 0 < position.y && 0 < position.z) { + importPosition = position; + Overlays.editOverlay(importPreview, { + position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } + }); + } +} + +function rescaleImport(scale) { + if (0 < scale) { + importScale = scale; + Overlays.editOverlay(importPreview, { + scale: importScale + }); + } +} + +function showImport(doShow) { + Overlays.editOverlay(importPreview, { + visible: doShow + }); +} + +function placeImport() { + if (isImporting) { + Clipboard.pasteVoxel(importPosition.x, importPosition.y, importPosition.z, importScale); + isImporting = false; + } +} + +function cancelImport() { + if (isImporting) { + isImporting = false; + showImport(false); + } +} + +function cleanupImport() { + Overlays.deleteOverlay(importPreview); + isImporting = false; + importPostion = { x: 0, y: 0, z: 0 }; + importScale = 0; +} +/////////////////////////////////// END IMPORT MODULE ///////////////////////////// +initImport(); + if (editToolsOn) { moveTools(); } @@ -301,9 +383,16 @@ function calcScaleFromThumb(newThumbX) { thumbAt = newThumbX - minThumbX; thumbStep = Math.floor((thumbAt/ thumbExtents) * (pointerVoxelScaleSteps-1)) + 1; pointerVoxelScale = Math.pow(2, (thumbStep-pointerVoxelScaleOriginStep)); + + // if importing, rescale import ... + if (isImporting) { + var importScale = (pointerVoxelScale / MAX_VOXEL_SCALE) * MAX_PASTE_VOXEL_SCALE; + rescaleImport(importScale); + } + // now reset the display accordingly... calcThumbFromScale(pointerVoxelScale); - + // if the user moved the thumb, then they are fixing the voxel scale pointerVoxelScaleSet = true; } @@ -322,7 +411,7 @@ function getNewPasteVoxel(pickRay) { origin.x += pickRay.origin.x; origin.y += pickRay.origin.y; origin.z += pickRay.origin.z; - + origin.x -= voxelSize / 2; origin.y -= voxelSize / 2; origin.z += voxelSize / 2; @@ -330,7 +419,7 @@ function getNewPasteVoxel(pickRay) { return {origin: origin, voxelSize: voxelSize}; } -function getNewVoxelPosition() { +function getNewVoxelPosition() { var camera = Camera.getPosition(); var forwardVector = Quat.getFront(MyAvatar.orientation); var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, NEW_VOXEL_DISTANCE_FROM_CAMERA)); @@ -377,27 +466,27 @@ function calculateVoxelFromIntersection(intersection, operation) { var wantDebug = false; if (wantDebug) { - print(">>>>> calculateVoxelFromIntersection().... intersection voxel.red/green/blue=" + intersection.voxel.red + ", " + print(">>>>> calculateVoxelFromIntersection().... intersection voxel.red/green/blue=" + intersection.voxel.red + ", " + intersection.voxel.green + ", " + intersection.voxel.blue); - print(" intersection voxel.x/y/z/s=" + intersection.voxel.x + ", " + print(" intersection voxel.x/y/z/s=" + intersection.voxel.x + ", " + intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s); print(" intersection face=" + intersection.face); print(" intersection distance=" + intersection.distance); - print(" intersection intersection.x/y/z=" + intersection.intersection.x + ", " + print(" intersection intersection.x/y/z=" + intersection.intersection.x + ", " + intersection.intersection.y + ", " + intersection.intersection.z); } - + var voxelSize; if (pointerVoxelScaleSet) { - voxelSize = pointerVoxelScale; + voxelSize = pointerVoxelScale; } else { - voxelSize = intersection.voxel.s; + voxelSize = intersection.voxel.s; } var x; var y; var z; - + // if our "target voxel size" is larger than the voxel we intersected with, then we need to find the closest // ancestor voxel of our target size that contains our intersected voxel. if (voxelSize > intersection.voxel.s) { @@ -438,7 +527,7 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantAddAdjust) { resultVoxel.x -= voxelSize; } - + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; @@ -462,7 +551,7 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantAddAdjust) { resultVoxel.y -= voxelSize; } - + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; @@ -474,7 +563,7 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantAddAdjust) { resultVoxel.y += voxelSize; } - + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust}; resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; @@ -486,7 +575,7 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantAddAdjust) { resultVoxel.z -= voxelSize; } - + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; @@ -505,7 +594,7 @@ function calculateVoxelFromIntersection(intersection, operation) { resultVoxel.topRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; } - + return resultVoxel; } @@ -515,7 +604,7 @@ function showPreviewVoxel() { var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); var intersection = Voxels.findRayIntersection(pickRay); - // if the user hasn't updated the + // if the user hasn't updated the if (!pointerVoxelScaleSet) { calcThumbFromScale(intersection.voxel.s); } @@ -548,41 +637,41 @@ function showPreviewLines() { var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); if (pasteMode) { // free voxel pasting - + Overlays.editOverlay(voxelPreview, { visible: false }); Overlays.editOverlay(linePreviewLeft, { visible: false }); var pasteVoxel = getNewPasteVoxel(pickRay); // X axis - Overlays.editOverlay(linePreviewBottom, { - position: pasteVoxel.origin, - end: {x: pasteVoxel.origin.x + pasteVoxel.voxelSize, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z }, - visible: true + Overlays.editOverlay(linePreviewBottom, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x + pasteVoxel.voxelSize, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z }, + visible: true }); // Y axis - Overlays.editOverlay(linePreviewRight, { - position: pasteVoxel.origin, - end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y + pasteVoxel.voxelSize, z: pasteVoxel.origin.z }, - visible: true + Overlays.editOverlay(linePreviewRight, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y + pasteVoxel.voxelSize, z: pasteVoxel.origin.z }, + visible: true }); // Z axis - Overlays.editOverlay(linePreviewTop, { - position: pasteVoxel.origin, - end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z - pasteVoxel.voxelSize }, - visible: true + Overlays.editOverlay(linePreviewTop, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z - pasteVoxel.voxelSize }, + visible: true }); return; } var intersection = Voxels.findRayIntersection(pickRay); - + if (intersection.intersects) { - // if the user hasn't updated the + // if the user hasn't updated the if (!pointerVoxelScaleSet) { calcThumbFromScale(intersection.voxel.s); } @@ -699,14 +788,14 @@ function trackKeyReleaseEvent(event) { trackAsOrbitOrPan = false; moveTools(); } - + // on F1 toggle the preview mode between cubes and lines if (event.text == "F1") { previewAsVoxel = !previewAsVoxel; } showPreviewGuides(); - } + } } function startOrbitMode(event) { @@ -715,7 +804,7 @@ function startOrbitMode(event) { var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Voxels.findRayIntersection(pickRay); - // start orbit camera! + // start orbit camera! var cameraPosition = Camera.getPosition(); torsoToEyeVector = Vec3.subtract(cameraPosition, MyAvatar.position); torsoToEyeVector.x = 0.0; @@ -724,12 +813,12 @@ function startOrbitMode(event) { Camera.setMode("independent"); Camera.keepLookingAt(intersection.intersection); // get position for initial azimuth, elevation - orbitCenter = intersection.intersection; + orbitCenter = intersection.intersection; var orbitVector = Vec3.subtract(cameraPosition, orbitCenter); - orbitRadius = Vec3.length(orbitVector); + orbitRadius = Vec3.length(orbitVector); orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x); orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector)); - + //print("startOrbitMode..."); } @@ -737,17 +826,17 @@ function handleOrbitingMove(event) { var cameraOrientation = Camera.getOrientation(); var origEulers = Quat.safeEulerAngles(cameraOrientation); var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation)); - var dx = event.x - mouseX; + var dx = event.x - mouseX; var dy = event.y - mouseY; orbitAzimuth += dx / ORBIT_RATE_AZIMUTH; orbitAltitude += dy / ORBIT_RATE_ALTITUDE; - var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius, + var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius, y:Math.sin(orbitAltitude) * orbitRadius, - z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius }; + z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius }; orbitPosition = Vec3.sum(orbitCenter, orbitVector); Camera.setPosition(orbitPosition); - mouseX = event.x; + mouseX = event.x; mouseY = event.y; //print("handleOrbitingMove..."); } @@ -763,7 +852,7 @@ function endOrbitMode(event) { } function startPanMode(event, intersection) { - // start pan camera! + // start pan camera! print("handle PAN mode!!!"); } @@ -781,14 +870,14 @@ function mousePressEvent(event) { // if our tools are off, then don't do anything if (!editToolsOn) { - return; + return; } - - // Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence. - // but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to + + // Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence. + // but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to // cancel any touch mode, and then let the right-click through if (isOrbitingFromTouch || isPanningFromTouch) { - + // if the user is holding the ALT key AND they are clicking the RIGHT button (or on multi-touch doing a two // finger touch, then we want to let the new panning behavior take over. // if it's any other case we still want to bail @@ -805,15 +894,15 @@ function mousePressEvent(event) { } // let things fall through } else { - return; + return; } } - + // no clicking on overlays while in panning mode if (!trackAsOrbitOrPan) { var clickedOnSomething = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - + // If the user clicked on the thumb, handle the slider logic if (clickedOverlay == thumb) { @@ -870,7 +959,7 @@ function mousePressEvent(event) { return; // no further processing } } - + // TODO: does any of this stuff need to execute if we're panning or orbiting? trackMouseEvent(event); // used by preview support mouseX = event.x; @@ -879,6 +968,15 @@ function mousePressEvent(event) { var intersection = Voxels.findRayIntersection(pickRay); audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction); + if (isImporting) { + print("placing import..."); + placeImport(); + showImport(false); + pasteMode = false; + moveTools(); + return; + } + if (pasteMode) { var pasteVoxel = getNewPasteVoxel(pickRay); Clipboard.pasteVoxel(pasteVoxel.origin.x, pasteVoxel.origin.y, pasteVoxel.origin.z, pasteVoxel.voxelSize); @@ -888,11 +986,11 @@ function mousePressEvent(event) { } if (intersection.intersects) { - // if the user hasn't updated the + // if the user hasn't updated the if (!pointerVoxelScaleSet) { calcThumbFromScale(intersection.voxel.s); } - + // Note: touch and mouse events can cross paths, so we want to ignore any mouse events that would // start a pan or orbit if we're already doing a pan or orbit via touch... if ((event.isAlt || trackAsOrbitOrPan) && !(isOrbitingFromTouch || isPanningFromTouch)) { @@ -916,14 +1014,14 @@ function mousePressEvent(event) { colors[whichColor].blue = intersection.voxel.blue; moveTools(); } - + } else if (recolorToolSelected || trackAsRecolor) { // Recolor Voxel voxelDetails = calculateVoxelFromIntersection(intersection,"recolor"); // doing this erase then set will make sure we only recolor just the target voxel Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); - Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, + Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue); Audio.playSound(changeColorSound, audioOptions); Overlays.editOverlay(voxelPreview, { visible: false }); @@ -931,17 +1029,17 @@ function mousePressEvent(event) { // Add voxel on face if (whichColor == -1) { // Copy mode - use clicked voxel color - newColor = { + newColor = { red: intersection.voxel.red, green: intersection.voxel.green, blue: intersection.voxel.blue }; } else { - newColor = { + newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue }; } - + voxelDetails = calculateVoxelFromIntersection(intersection,"add"); Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, @@ -951,11 +1049,11 @@ function mousePressEvent(event) { lastVoxelScale = voxelDetails.s; playRandomAddSound(audioOptions); - + Overlays.editOverlay(voxelPreview, { visible: false }); dragStart = { x: event.x, y: event.y }; isAdding = true; - } + } } } @@ -974,14 +1072,14 @@ function keyPressEvent(event) { Audio.playSound(clickSound, audioOptions); moveTools(); } else if (event.text == "0") { - // Create a brand new 1 meter voxel in front of your avatar - var color = whichColor; + // Create a brand new 1 meter voxel in front of your avatar + var color = whichColor; if (color == -1) color = 0; var newPosition = getNewVoxelPosition(); - var newVoxel = { + var newVoxel = { x: newPosition.x, y: newPosition.y , - z: newPosition.z, + z: newPosition.z, s: NEW_VOXEL_SIZE, red: colors[color].red, green: colors[color].green, @@ -992,7 +1090,7 @@ function keyPressEvent(event) { playRandomAddSound(audioOptions); } } - + trackKeyPressEvent(event); // used by preview support } @@ -1051,24 +1149,36 @@ function menuItemEvent(menuItem) { moveTools(); } if (menuItem == "Paste") { - print("pasting..."); - Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + if (isImporting) { + print("placing import..."); + placeImport(); + showImport(false); + } else { + print("pasting..."); + Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } pasteMode = false; moveTools(); } if (menuItem == "Delete") { print("deleting..."); - Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + if (isImporting) { + cancelImport(); + } else { + Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } } - + if (menuItem == "Export Voxels") { print("export"); Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } if (menuItem == "Import Voxels") { - print("import"); - Clipboard.importVoxels(); - pasteMode = true; + print("importing..."); + if (importVoxels()) { + showImport(true); + pasteMode = true; + } moveTools(); } if (menuItem == "Nudge") { @@ -1083,11 +1193,11 @@ function mouseMoveEvent(event) { return; } - // if we're panning or orbiting from touch, ignore these... because our touch takes precedence. + // if we're panning or orbiting from touch, ignore these... because our touch takes precedence. if (isOrbitingFromTouch || isPanningFromTouch) { - return; + return; } - + // double check that we didn't accidentally miss a pan or orbit click request if (trackAsOrbitOrPan && !isPanning && !isOrbiting) { if (event.isLeftButton && !event.isRightButton) { @@ -1109,7 +1219,7 @@ function mouseMoveEvent(event) { thumbX = maxThumbX; } calcScaleFromThumb(thumbX); - + } else if (isOrbiting) { handleOrbitingMove(event); } else if (isPanning) { @@ -1118,8 +1228,8 @@ function mouseMoveEvent(event) { // Watch the drag direction to tell which way to 'extrude' this voxel if (!isExtruding) { var pickRay = Camera.computePickRay(event.x, event.y); - var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x, - y: pickRay.origin.y - lastVoxelPosition.y, + var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x, + y: pickRay.origin.y - lastVoxelPosition.y, z: pickRay.origin.z - lastVoxelPosition.z }; var distance = Vec3.length(lastVoxelDistance); var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance }; @@ -1138,22 +1248,22 @@ function mouseMoveEvent(event) { else if (dy < -lastVoxelScale) extrudeDirection.y = -extrudeScale; else if (dz > lastVoxelScale) extrudeDirection.z = extrudeScale; else if (dz < -lastVoxelScale) extrudeDirection.z = -extrudeScale; - else isExtruding = false; + else isExtruding = false; } else { // We have got an extrusion direction, now look for mouse move beyond threshold to add new voxel - var dx = event.x - mouseX; + var dx = event.x - mouseX; var dy = event.y - mouseY; if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) { lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection); Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); - Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, + Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue); mouseX = event.x; mouseY = event.y; } } } - + // update the add voxel/delete voxel overlay preview trackMouseEvent(event); } @@ -1161,13 +1271,13 @@ function mouseMoveEvent(event) { function mouseReleaseEvent(event) { // if our tools are off, then don't do anything if (!editToolsOn) { - return; + return; } if (isMovingSlider) { isMovingSlider = false; } - + if (isOrbiting) { endOrbitMode(event); isOrbiting = false; @@ -1178,7 +1288,7 @@ function mouseReleaseEvent(event) { isPanning = false; } isAdding = false; - isExtruding = false; + isExtruding = false; } function moveTools() { @@ -1206,7 +1316,7 @@ function moveTools() { if (s == (numColors - 1)) { extraWidth = swatchExtraPadding; } - + Overlays.editOverlay(swatches[s], { x: swatchX, y: swatchesY, @@ -1270,16 +1380,16 @@ function touchBeginEvent(event) { if (!editToolsOn) { return; } - + // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... if (isOrbiting || isPanning) { return; - } - + } + if (event.isAlt || trackAsOrbitOrPan) { if (event.touchPoints == touchPointsToOrbit) { // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... + // come in as 2 then 3 touches... if (isPanningFromTouch) { print("touchBeginEvent... calling endPanMode()"); endPanMode(event); @@ -1289,7 +1399,7 @@ function touchBeginEvent(event) { isOrbitingFromTouch = true; } else if (event.touchPoints == touchPointsToPan) { // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... + // come in as 2 then 3 touches... if (isOrbitingFromTouch) { endOrbitMode(event); isOrbitingFromTouch = false; @@ -1308,11 +1418,11 @@ function touchUpdateEvent(event) { // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... if (isOrbiting || isPanning) { return; - } - + } + if (isOrbitingFromTouch) { // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... + // come in as 2 then 3 touches... if (event.touchPoints == touchPointsToPan) { //print("we now have touchPointsToPan touches... switch to pan..."); endOrbitMode(event); @@ -1326,7 +1436,7 @@ function touchUpdateEvent(event) { if (isPanningFromTouch) { //print("touchUpdateEvent... isPanningFromTouch... event.touchPoints=" + event.touchPoints); // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... + // come in as 2 then 3 touches... if (event.touchPoints == touchPointsToOrbit) { //print("we now have touchPointsToOrbit touches... switch to orbit..."); //print("touchUpdateEvent... calling endPanMode()"); @@ -1349,8 +1459,8 @@ function touchEndEvent(event) { // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... if (isOrbiting || isPanning) { return; - } - + } + if (isOrbitingFromTouch) { endOrbitMode(event); isOrbitingFromTouch = false; @@ -1368,10 +1478,10 @@ var lastFingerDeleteVoxel = { x: -1, y: -1, z: -1}; // off of the build-able are function checkControllers() { var controllersPerPalm = 2; // palm and finger for (var palm = 0; palm < 2; palm++) { - var palmController = palm * controllersPerPalm; - var fingerTipController = palmController + 1; + var palmController = palm * controllersPerPalm; + var fingerTipController = palmController + 1; var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController); - + var BUTTON_COUNT = 6; var BUTTON_BASE = palm * BUTTON_COUNT; var BUTTON_1 = BUTTON_BASE + 1; @@ -1389,7 +1499,7 @@ function checkControllers() { Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE); Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE, newColor.red, newColor.green, newColor.blue); - + lastFingerAddVoxel = fingerTipPosition; } } else if (Controller.isButtonPressed(BUTTON_2)) { @@ -1407,7 +1517,7 @@ function update(deltaTime) { windowDimensions = newWindowDimensions; moveTools(); } - + if (editToolsOn) { checkControllers(); } @@ -1425,7 +1535,7 @@ function wheelEvent(event) { pointerVoxelScale /= 2.0; if (pointerVoxelScale < MIN_VOXEL_SCALE) { pointerVoxelScale = MIN_VOXEL_SCALE; - } + } } else { pointerVoxelScale *= 2.0; if (pointerVoxelScale > MAX_VOXEL_SCALE) { @@ -1435,6 +1545,11 @@ function wheelEvent(event) { calcThumbFromScale(pointerVoxelScale); trackMouseEvent(event); wheelPixelsMoved = 0; + + if (isImporting) { + var importScale = (pointerVoxelScale / MAX_VOXEL_SCALE) * MAX_PASTE_VOXEL_SCALE; + rescaleImport(importScale); + } } } @@ -1467,6 +1582,7 @@ function scriptEnding() { Overlays.deleteOverlay(thumb); Controller.releaseKeyEvents({ text: "+" }); Controller.releaseKeyEvents({ text: "-" }); + cleanupImport(); cleanupMenus(); } Script.scriptEnding.connect(scriptEnding); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 032e849f0a..771f02750d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -140,9 +140,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _fps(120.0f), _justStarted(true), _voxelImporter(NULL), + _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard), _wantToKillLocalVoxels(false), _audioScope(256, 200, true), - _myAvatar(), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _mouseX(0), _mouseY(0), @@ -1387,6 +1387,8 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { } void Application::importVoxels() { + int result = 1; + if (!_voxelImporter) { _voxelImporter = new VoxelImporter(_window); _voxelImporter->loadSettings(_settings); @@ -1394,6 +1396,7 @@ void Application::importVoxels() { if (!_voxelImporter->exec()) { qDebug() << "[DEBUG] Import succeeded." << endl; + result = 0; } else { qDebug() << "[DEBUG] Import failed." << endl; if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) { @@ -1404,6 +1407,8 @@ void Application::importVoxels() { // restore the main window's active state _window->activateWindow(); + + emit importDone(result); } void Application::cutVoxels(const VoxelDetail& sourceVoxel) { @@ -1495,10 +1500,9 @@ void Application::init() { // Cleanup of the original shared tree _sharedVoxelSystem.init(); - VoxelTree* tmpTree = _sharedVoxelSystem.getTree(); - _sharedVoxelSystem.changeTree(&_clipboard); - delete tmpTree; - + + _voxelImporter = new VoxelImporter(_window); + _environment.init(); _glowEffect.init(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 234f264447..c6fc7140eb 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -225,6 +225,9 @@ signals: /// Fired when we're rendering in-world interface elements; allows external parties to hook in. void renderingInWorldInterface(); + /// Fired when the import window is closed with a return code. + void importDone(int); + public slots: void domainChanged(const QString& domainHostname); void updateWindowTitle(); @@ -351,13 +354,13 @@ private: glm::vec3 _gravity; // Frame Rate Measurement + int _frameCount; float _fps; timeval _applicationStartupTime; timeval _timerStart, _timerEnd; timeval _lastTimeUpdated; bool _justStarted; - Stars _stars; BuckyBalls _buckyBalls; diff --git a/interface/src/ClipboardScriptingInterface.cpp b/interface/src/ClipboardScriptingInterface.cpp index d9a8f04d90..e8a02b041b 100644 --- a/interface/src/ClipboardScriptingInterface.cpp +++ b/interface/src/ClipboardScriptingInterface.cpp @@ -9,6 +9,7 @@ #include "ClipboardScriptingInterface.h" ClipboardScriptingInterface::ClipboardScriptingInterface() { + connect(this, SIGNAL(readyToImport()), Application::getInstance(), SLOT(importVoxels())); } void ClipboardScriptingInterface::cutVoxel(const VoxelDetail& sourceVoxel) { @@ -70,12 +71,17 @@ void ClipboardScriptingInterface::exportVoxel(float x, float y, float z, float s z / (float)TREE_SCALE, s / (float)TREE_SCALE }; - QMetaObject::invokeMethod(Application::getInstance(), "exportVoxels", - Q_ARG(const VoxelDetail&, sourceVoxel)); + Application::getInstance()->exportVoxels(sourceVoxel); } -void ClipboardScriptingInterface::importVoxels() { - QMetaObject::invokeMethod(Application::getInstance(), "importVoxels"); +bool ClipboardScriptingInterface::importVoxels() { + qDebug() << "[DEBUG] Importing ... "; + QEventLoop loop; + connect(Application::getInstance(), SIGNAL(importDone(int)), &loop, SLOT(quit())); + emit readyToImport(); + int returnCode = loop.exec(); + + return returnCode; } void ClipboardScriptingInterface::nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) { diff --git a/interface/src/ClipboardScriptingInterface.h b/interface/src/ClipboardScriptingInterface.h index 99747f56f6..78e72d6de0 100644 --- a/interface/src/ClipboardScriptingInterface.h +++ b/interface/src/ClipboardScriptingInterface.h @@ -18,6 +18,9 @@ class ClipboardScriptingInterface : public QObject { public: ClipboardScriptingInterface(); +signals: + void readyToImport(); + public slots: void cutVoxel(const VoxelDetail& sourceVoxel); void cutVoxel(float x, float y, float z, float s); @@ -34,7 +37,7 @@ public slots: void exportVoxel(const VoxelDetail& sourceVoxel); void exportVoxel(float x, float y, float z, float s); - void importVoxels(); + bool importVoxels(); void nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec); void nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec); diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index aa628bac7b..a8bdeb1114 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1177,8 +1177,6 @@ void VoxelSystem::init() { } void VoxelSystem::changeTree(VoxelTree* newTree) { - disconnect(_tree, 0, this, 0); - _tree = newTree; _tree->setDirtyBit(); diff --git a/interface/src/ui/LocalVoxelsOverlay.cpp b/interface/src/ui/LocalVoxelsOverlay.cpp index 01a885dedc..248a5b6733 100644 --- a/interface/src/ui/LocalVoxelsOverlay.cpp +++ b/interface/src/ui/LocalVoxelsOverlay.cpp @@ -40,9 +40,11 @@ void LocalVoxelsOverlay::update(float deltatime) { _voxelSystem->init(); } - if (_voxelCount != _tree->getOctreeElementsCount()) { + if (_visible && _voxelCount != _tree->getOctreeElementsCount()) { _voxelCount = _tree->getOctreeElementsCount(); + _tree->lockForWrite(); _voxelSystem->forceRedrawEntireTree(); + _tree->unlock(); } } diff --git a/interface/src/ui/Volume3DOverlay.cpp b/interface/src/ui/Volume3DOverlay.cpp index dbc1582cc5..a4e6c76a12 100644 --- a/interface/src/ui/Volume3DOverlay.cpp +++ b/interface/src/ui/Volume3DOverlay.cpp @@ -31,6 +31,9 @@ void Volume3DOverlay::setProperties(const QScriptValue& properties) { if (properties.property("size").isValid()) { setSize(properties.property("size").toVariant().toFloat()); } + if (properties.property("scale").isValid()) { + setSize(properties.property("scale").toVariant().toFloat()); + } if (properties.property("isSolid").isValid()) { setIsSolid(properties.property("isSolid").toVariant().toBool()); From 07d3eb886a9fc515c5ebe8d3584193b3f4bb0339 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 4 Mar 2014 21:27:06 +0200 Subject: [PATCH 051/109] Added reading and storing of the XMPP password of the current user. --- .../shared/src/DataServerAccountInfo.cpp | 25 +++++++++++++++---- libraries/shared/src/DataServerAccountInfo.h | 4 +++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/libraries/shared/src/DataServerAccountInfo.cpp b/libraries/shared/src/DataServerAccountInfo.cpp index 7225653998..2bcd2dbcd0 100644 --- a/libraries/shared/src/DataServerAccountInfo.cpp +++ b/libraries/shared/src/DataServerAccountInfo.cpp @@ -12,21 +12,26 @@ DataServerAccountInfo::DataServerAccountInfo() : _accessToken(), - _username() + _username(), + _xmppPassword() { } DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) : _accessToken(jsonObject), - _username() + _username(), + _xmppPassword() { - setUsername(jsonObject["user"].toObject()["username"].toString()); + QJsonObject userJSONObject = jsonObject["user"].toObject(); + setUsername(userJSONObject["username"].toString()); + setXMPPPassword(userJSONObject["xmpp_password"].toString()); } DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) { _accessToken = otherInfo._accessToken; _username = otherInfo._username; + _xmppPassword = otherInfo._xmppPassword; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -40,6 +45,7 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { swap(_accessToken, otherInfo._accessToken); swap(_username, otherInfo._username); + swap(_xmppPassword, otherInfo._xmppPassword); } void DataServerAccountInfo::setUsername(const QString& username) { @@ -50,12 +56,21 @@ void DataServerAccountInfo::setUsername(const QString& username) { } } +void DataServerAccountInfo::setXMPPPassword(const QString& xmppPassword) +{ + if (_xmppPassword != xmppPassword) { + _xmppPassword = xmppPassword; + + qDebug() << "XMPP password changed to " << xmppPassword; + } +} + QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username; + out << info._accessToken << info._username << info._xmppPassword; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username; + in >> info._accessToken >> info._username >> info._xmppPassword; return in; } \ No newline at end of file diff --git a/libraries/shared/src/DataServerAccountInfo.h b/libraries/shared/src/DataServerAccountInfo.h index da7bdbe42e..c840468319 100644 --- a/libraries/shared/src/DataServerAccountInfo.h +++ b/libraries/shared/src/DataServerAccountInfo.h @@ -25,6 +25,9 @@ public: const QString& getUsername() const { return _username; } void setUsername(const QString& username); + + const QString& getXMPPPassword() const { return _xmppPassword; } + void setXMPPPassword(const QString& xmppPassword); friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info); friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info); @@ -33,6 +36,7 @@ private: OAuthAccessToken _accessToken; QString _username; + QString _xmppPassword; }; #endif /* defined(__hifi__DataServerAccountInfo__) */ From 1c2e752f3ce87055d52289c5713aad0a06447c2f Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 5 Mar 2014 19:57:59 +0200 Subject: [PATCH 052/109] Integrated QXmpp in the build system. --- CMakeLists.txt | 6 ++++++ cmake/modules/FindQxmpp.cmake | 38 +++++++++++++++++++++++++++++++++ interface/CMakeLists.txt | 4 ++++ libraries/shared/CMakeLists.txt | 5 ++--- 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 cmake/modules/FindQxmpp.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 33589ddb57..b3e1af23e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,12 @@ if (APPLE) endif (DARWIN_VERSION GREATER 12) endif (APPLE) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") + +find_package(qxmpp REQUIRED) +add_definitions(-DQXMPP_STATIC) +include_directories(SYSTEM ${QXMPP_INCLUDE_DIR}) + # targets not supported on windows if (NOT WIN32) add_subdirectory(animation-server) diff --git a/cmake/modules/FindQxmpp.cmake b/cmake/modules/FindQxmpp.cmake new file mode 100644 index 0000000000..f50212d63f --- /dev/null +++ b/cmake/modules/FindQxmpp.cmake @@ -0,0 +1,38 @@ +# Try to find the qxmpp library +# +# You can provide a QXMPP_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# QXMPP_FOUND - system found qxmpp +# QXMPP_INCLUDE_DIRS - the qxmpp include directory +# QXMPP_LIBRARIES - Link this to use qxmpp +# +# Created on 3/10/2014 by Stephen Birarda +# Copyright (c) 2014 High Fidelity +# + +if (QXMPP_LIBRARIES AND QXMPP_INCLUDE_DIRS) + # in cache already + set(QXMPP_FOUND TRUE) +else () + + set(QXMPP_SEARCH_DIRS "${QXMPP_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/qxmpp") + + find_path(QXMPP_INCLUDE_DIR QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${QXMPP_SEARCH_DIRS}) + + find_library(QXMPP_LIBRARY NAMES qxmpp qxmpp0 PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIR QXMPP_LIBRARY) + + if (QXMPP_FOUND) + if (NOT QXMPP_FIND_QUIETLY) + message(STATUS "Found qxmpp: ${QXMPP_LIBRARY}") + endif (NOT QXMPP_FIND_QUIETLY) + else () + if (QXMPP_FIND_REQUIRED) + message(FATAL_ERROR "Could not find qxmpp") + endif (SIXENSE_FIND_REQUIRED) + endif () +endif () \ No newline at end of file diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index f73cf0fd64..9f176d1ee8 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -65,6 +65,10 @@ endforeach(EXTERNAL_SOURCE_SUBDIR) find_package(Qt5 COMPONENTS Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml UiTools) +find_package(Qxmpp REQUIRED) +add_definitions(-DQXMPP_STATIC) +include_directories(SYSTEM ${QXMPP_INCLUDE_DIR}) + # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) # have qt5 wrap them and generate the appropriate header files diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 658542637f..40473c924a 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -10,7 +10,7 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros") set(TARGET_NAME shared) project(${TARGET_NAME}) -find_package(Qt5 COMPONENTS Network Widgets) +find_package(Qt5 COMPONENTS Network Widgets Xml) include(${MACRO_DIR}/SetupHifiLibrary.cmake) setup_hifi_library(${TARGET_NAME}) @@ -19,7 +19,6 @@ setup_hifi_library(${TARGET_NAME}) include(${MACRO_DIR}/IncludeGLM.cmake) include_glm(${TARGET_NAME} "${ROOT_DIR}") - set(EXTERNAL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external") if (WIN32) @@ -33,4 +32,4 @@ if (UNIX AND NOT APPLE) target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}") endif (UNIX AND NOT APPLE) -target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets) \ No newline at end of file +target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Xml "${QXMPP_LIBRARY}") From 12becb9d19777de3f63c2365bc81de140d853305 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 16:22:35 -0700 Subject: [PATCH 053/109] improve octree stats display --- interface/src/ui/OctreeStatsDialog.cpp | 57 ++++++----- libraries/octree/src/Octree.cpp | 2 - libraries/octree/src/OctreeSceneStats.cpp | 114 +++++++++++++++++++--- libraries/octree/src/OctreeSceneStats.h | 22 ++++- libraries/voxels/src/VoxelTree.cpp | 2 - 5 files changed, 152 insertions(+), 45 deletions(-) diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index 974964da04..92578b6b34 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -157,9 +157,9 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { statsValue.str(""); statsValue << - "Total: " << localTotalString.toLocal8Bit().constData() << " / " << - "Internal: " << localInternalString.toLocal8Bit().constData() << " / " << - "Leaves: " << localLeavesString.toLocal8Bit().constData() << ""; + "Total: " << qPrintable(localTotalString) << " / " << + "Internal: " << qPrintable(localInternalString) << " / " << + "Leaves: " << qPrintable(localLeavesString) << ""; label->setText(statsValue.str().c_str()); // iterate all the current voxel stats, and list their sending modes, total their voxels, etc... @@ -212,9 +212,9 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { label = _labels[_serverVoxels]; statsValue.str(""); statsValue << - "Total: " << serversTotalString.toLocal8Bit().constData() << " / " << - "Internal: " << serversInternalString.toLocal8Bit().constData() << " / " << - "Leaves: " << serversLeavesString.toLocal8Bit().constData() << ""; + "Total: " << qPrintable(serversTotalString) << " / " << + "Internal: " << qPrintable(serversInternalString) << " / " << + "Leaves: " << qPrintable(serversLeavesString) << ""; label->setText(statsValue.str().c_str()); showAllOctreeServers(); @@ -290,7 +290,7 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser AABox serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s); serverBounds.scale(TREE_SCALE); serverDetails << " jurisdiction: " - << rootCodeHex.toLocal8Bit().constData() + << qPrintable(rootCodeHex) << " [" << rootDetails.x << ", " << rootDetails.y << ", " @@ -320,8 +320,8 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser QString lastFullSendString = locale.toString(lastFullSend); extraDetails << "
" << "Last Full Scene... " << - "Encode Time: " << lastFullEncodeString.toLocal8Bit().constData() << " ms " << - "Send Time: " << lastFullSendString.toLocal8Bit().constData() << " ms "; + "Encode Time: " << qPrintable(lastFullEncodeString) << " ms " << + "Send Time: " << qPrintable(lastFullSendString) << " ms "; for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); @@ -334,42 +334,51 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser QString internalString = locale.toString((uint)stats.getTotalInternal()); QString leavesString = locale.toString((uint)stats.getTotalLeaves()); - serverDetails << "
" << "Node UUID: " << - nodeUUID.toString().toLocal8Bit().constData() << " "; + serverDetails << "
" << "Node UUID: " << qPrintable(nodeUUID.toString()) << " "; serverDetails << "
" << "Voxels: " << - totalString.toLocal8Bit().constData() << " total " << - internalString.toLocal8Bit().constData() << " internal " << - leavesString.toLocal8Bit().constData() << " leaves "; + qPrintable(totalString) << " total " << + qPrintable(internalString) << " internal " << + qPrintable(leavesString) << " leaves "; QString incomingPacketsString = locale.toString((uint)stats.getIncomingPackets()); QString incomingBytesString = locale.toString((uint)stats.getIncomingBytes()); QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes()); QString incomingOutOfOrderString = locale.toString((uint)stats.getIncomingOutOfOrder()); + QString incomingLateString = locale.toString((uint)stats.getIncomingLate()); + QString incomingReallyLateString = locale.toString((uint)stats.getIncomingReallyLate()); + QString incomingEarlyString = locale.toString((uint)stats.getIncomingEarly()); QString incomingLikelyLostString = locale.toString((uint)stats.getIncomingLikelyLost()); + QString incomingRecovered = locale.toString((uint)stats.getIncomingRecovered()); + QString incomingDuplicateString = locale.toString((uint)stats.getIncomingPossibleDuplicate()); int clockSkewInMS = node->getClockSkewUsec() / (int)USECS_PER_MSEC; QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage()); QString incomingPingTimeString = locale.toString(node->getPingMs()); QString incomingClockSkewString = locale.toString(clockSkewInMS); - serverDetails << "
" << "Incoming Packets: " << - incomingPacketsString.toLocal8Bit().constData() << - " Out of Order: " << incomingOutOfOrderString.toLocal8Bit().constData() << - " Likely Lost: " << incomingLikelyLostString.toLocal8Bit().constData(); + serverDetails << "
" << "Incoming Packets: " << qPrintable(incomingPacketsString) << + "/ Lost: " << qPrintable(incomingLikelyLostString) << + "/ Recovered: " << qPrintable(incomingRecovered); + + serverDetails << "
" << " Out of Order: " << qPrintable(incomingOutOfOrderString) << + "/ Early: " << qPrintable(incomingEarlyString) << + "/ Late: " << qPrintable(incomingLateString) << + "/ Really Late: " << qPrintable(incomingReallyLateString) << + "/ Duplicate: " << qPrintable(incomingDuplicateString); serverDetails << "
" << - " Average Flight Time: " << incomingFlightTimeString.toLocal8Bit().constData() << " msecs"; + " Average Flight Time: " << qPrintable(incomingFlightTimeString) << " msecs"; serverDetails << "
" << - " Average Ping Time: " << incomingPingTimeString.toLocal8Bit().constData() << " msecs"; + " Average Ping Time: " << qPrintable(incomingPingTimeString) << " msecs"; serverDetails << "
" << - " Average Clock Skew: " << incomingClockSkewString.toLocal8Bit().constData() << " msecs"; - + " Average Clock Skew: " << qPrintable(incomingClockSkewString) << " msecs"; + serverDetails << "
" << "Incoming" << - " Bytes: " << incomingBytesString.toLocal8Bit().constData() << - " Wasted Bytes: " << incomingWastedBytesString.toLocal8Bit().constData(); + " Bytes: " << qPrintable(incomingBytesString) << + " Wasted Bytes: " << qPrintable(incomingWastedBytesString); serverDetails << extraDetails.str(); if (_extraServerDetails[serverCount-1] == MORE) { diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index f85ed7f487..4c8ed8c9f6 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -15,8 +15,6 @@ #include #include // to load voxels from file -#include - #include #include "CoverageMap.h" diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 794884334f..7d1f3f0cec 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -30,8 +30,13 @@ OctreeSceneStats::OctreeSceneStats() : _incomingBytes(0), _incomingWastedBytes(0), _incomingLastSequence(0), - _incomingOutOfOrder(0), _incomingLikelyLost(0), + _incomingRecovered(0), + _incomingEarly(0), + _incomingLate(0), + _incomingReallyLate(0), + _incomingPossibleDuplicate(0), + _missingSequenceNumbers(), _incomingFlightTimeAverage(samples), _jurisdictionRoot(NULL) { @@ -134,8 +139,14 @@ void OctreeSceneStats::copyFromOther(const OctreeSceneStats& other) { _incomingBytes = other._incomingBytes; _incomingWastedBytes = other._incomingWastedBytes; _incomingLastSequence = other._incomingLastSequence; - _incomingOutOfOrder = other._incomingOutOfOrder; _incomingLikelyLost = other._incomingLikelyLost; + _incomingRecovered = other._incomingRecovered; + _incomingEarly = other._incomingEarly; + _incomingLate = other._incomingLate; + _incomingReallyLate = other._incomingReallyLate; + _incomingPossibleDuplicate = other._incomingPossibleDuplicate; + + _missingSequenceNumbers = other._missingSequenceNumbers; } @@ -823,18 +834,95 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet, float flightTimeMsecs = flightTime / USECS_PER_MSEC; _incomingFlightTimeAverage.updateAverage(flightTimeMsecs); + // track out of order and possibly lost packets... + const bool wantExtraDebugging = false; + if (sequence == _incomingLastSequence) { + if (wantExtraDebugging) { + qDebug() << "last packet duplicate got:" << sequence << "_incomingLastSequence:" << _incomingLastSequence; + } + } else { + OCTREE_PACKET_SEQUENCE expected = _incomingLastSequence+1; + if (sequence != expected) { + if (wantExtraDebugging) { + qDebug() << "out of order... got:" << sequence << "expected:" << expected; + } + + // if the sequence is less than our expected, then this might be a packet + // that was delayed and so we should find it in our lostSequence list + if (sequence < expected) { + if (wantExtraDebugging) { + qDebug() << "this packet is later than expected..."; + } + if (sequence < std::max(0, (expected - MAX_MISSING_SEQUENCE_OLD_AGE))) { + _incomingReallyLate++; + } else { + _incomingLate++; + } + + if (_missingSequenceNumbers.contains(sequence)) { + if (wantExtraDebugging) { + qDebug() << "found it in _missingSequenceNumbers"; + } + _missingSequenceNumbers.remove(sequence); + _incomingLikelyLost--; + _incomingRecovered++; + } else { + // if we're still in our pruning window, and we didn't find it in our missing list, + // than this is really unexpected and can probably only happen if the packet was a + // duplicate + if (sequence >= std::max(0, (expected - MAX_MISSING_SEQUENCE_OLD_AGE))) { + if (wantExtraDebugging) { + qDebug() << "sequence:" << sequence << "WAS NOT found in _missingSequenceNumbers, and not that old... (expected - MAX_MISSING_SEQUENCE_OLD_AGE):" << (expected - MAX_MISSING_SEQUENCE_OLD_AGE); + } + _incomingPossibleDuplicate++; + } + } + } + + if (sequence > expected) { + if (wantExtraDebugging) { + qDebug() << "this packet is earlier than expected..."; + } + _incomingEarly++; + + // hmm... so, we either didn't get some packets, or this guy came early... + unsigned int missing = sequence - expected; + if (wantExtraDebugging) { + qDebug() << ">>>>>>>> missing gap=" << missing; + } + _incomingLikelyLost += missing; + for(unsigned int missingSequence = expected; missingSequence < sequence; missingSequence++) { + _missingSequenceNumbers << missingSequence; + } + } + } + } + + // only bump the last sequence if it was greater than our previous last sequence, this will keep us from + // accidentally going backwards when an out of order (recovered) packet comes in + if (sequence > _incomingLastSequence) { + _incomingLastSequence = sequence; + } - // detect out of order packets - if (sequence < _incomingLastSequence) { - _incomingOutOfOrder++; + // do some garbage collecting on our _missingSequenceNumbers + if (_missingSequenceNumbers.size() > MAX_MISSING_SEQUENCE) { + if (wantExtraDebugging) { + qDebug() << "too many _missingSequenceNumbers:" << _missingSequenceNumbers.size(); + } + foreach(unsigned int missingItem, _missingSequenceNumbers) { + if (wantExtraDebugging) { + qDebug() << "checking item:" << missingItem << "is it in need of pruning?"; + qDebug() << "(_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE):" + << (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE); + } + if (missingItem <= std::max(0, (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE))) { + if (wantExtraDebugging) { + qDebug() << "pruning really old missing sequence:" << missingItem; + } + _missingSequenceNumbers.remove(missingItem); + } + } } - - // detect likely lost packets - OCTREE_PACKET_SEQUENCE expected = _incomingLastSequence+1; - if (sequence > expected) { - _incomingLikelyLost++; - } - - _incomingLastSequence = sequence; + } diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index e106f53589..25bc5e2c21 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -159,8 +159,13 @@ public: unsigned int getIncomingPackets() const { return _incomingPacket; } unsigned long getIncomingBytes() const { return _incomingBytes; } unsigned long getIncomingWastedBytes() const { return _incomingWastedBytes; } - unsigned int getIncomingOutOfOrder() const { return _incomingOutOfOrder; } + unsigned int getIncomingOutOfOrder() const { return _incomingLate + _incomingEarly; } unsigned int getIncomingLikelyLost() const { return _incomingLikelyLost; } + unsigned int getIncomingRecovered() const { return _incomingRecovered; } + unsigned int getIncomingEarly() const { return _incomingEarly; } + unsigned int getIncomingLate() const { return _incomingLate; } + unsigned int getIncomingReallyLate() const { return _incomingReallyLate; } + unsigned int getIncomingPossibleDuplicate() const { return _incomingPossibleDuplicate; } float getIncomingFlightTimeAverage() { return _incomingFlightTimeAverage.getAverage(); } private: @@ -251,9 +256,18 @@ private: unsigned int _incomingPacket; unsigned long _incomingBytes; unsigned long _incomingWastedBytes; - unsigned int _incomingLastSequence; - unsigned int _incomingOutOfOrder; - unsigned int _incomingLikelyLost; + + const uint16_t MAX_MISSING_SEQUENCE = 100; /// how many items in our _missingSequenceNumbers before we start to prune them + const uint16_t MAX_MISSING_SEQUENCE_OLD_AGE = 1000; /// age we allow items in _missingSequenceNumbers to be before pruning + + uint16_t _incomingLastSequence; /// last incoming sequence number + unsigned int _incomingLikelyLost; /// count of packets likely lost, may be off by _incomingReallyLate count + unsigned int _incomingRecovered; /// packets that were late, and we had in our missing list, we consider recovered + unsigned int _incomingEarly; /// out of order earlier than expected + unsigned int _incomingLate; /// out of order later than expected + unsigned int _incomingReallyLate; /// out of order and later than MAX_MISSING_SEQUENCE_OLD_AGE late + unsigned int _incomingPossibleDuplicate; /// out of order possibly a duplicate + QSet _missingSequenceNumbers; SimpleMovingAverage _incomingFlightTimeAverage; // features related items diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 4c756c2257..5c48244a39 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -8,8 +8,6 @@ #include -#include - #include #include #include From 437cadf360fbe03e602d34fd69cf3852db3dd4c3 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 7 Mar 2014 16:41:13 +0200 Subject: [PATCH 054/109] Added a basic version of the chat window. --- interface/src/Menu.cpp | 20 ++- interface/src/Menu.h | 6 + interface/src/ui/ChatWindow.cpp | 129 ++++++++++++++++ interface/src/ui/ChatWindow.h | 40 +++++ interface/src/ui/FlowLayout.cpp | 213 ++++++++++++++++++++++++++ interface/src/ui/FlowLayout.h | 78 ++++++++++ interface/ui/chatWindow.ui | 125 +++++++++++++++ libraries/shared/src/AccountManager.h | 2 + 8 files changed, 612 insertions(+), 1 deletion(-) create mode 100644 interface/src/ui/ChatWindow.cpp create mode 100644 interface/src/ui/ChatWindow.h create mode 100644 interface/src/ui/FlowLayout.cpp create mode 100644 interface/src/ui/FlowLayout.h create mode 100644 interface/ui/chatWindow.ui diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e4fa0c49e0..60e93b0cea 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -161,7 +161,7 @@ Menu::Menu() : QMenu* toolsMenu = addMenu("Tools"); addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::FstUploader, 0, Application::getInstance(), SLOT(uploadFST())); - + addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, 0, this, SLOT(showChat())); QMenu* viewMenu = addMenu("View"); @@ -1021,6 +1021,24 @@ void Menu::showMetavoxelEditor() { _MetavoxelEditor->raise(); } +void Menu::showChat() { + if (!_chatWindow) { + _chatWindow = new ChatWindow(); + QMainWindow* mainWindow = Application::getInstance()->getWindow(); + + // the height of the title bar is given by frameGeometry().height() - geometry().height() + // however, frameGeometry() is initialised after showing (Qt queries the OS windowing system) + // on the other hand, moving a window after showing it flickers; so just use some reasonable value + int titleBarHeight = 16; + _chatWindow->setGeometry(mainWindow->width() - _chatWindow->width(), + mainWindow->geometry().y() + titleBarHeight, + _chatWindow->width(), + mainWindow->height() - titleBarHeight); + _chatWindow->show(); + } + _chatWindow->raise(); +} + void Menu::audioMuteToggled() { QAction *muteAction = _actionHash.value(MenuOption::MuteAudio); muteAction->setChecked(Application::getInstance()->getAudio()->getMuted()); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d8a7672972..cd32a9c8df 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -18,6 +18,8 @@ #include #include +#include + const float ADJUST_LOD_DOWN_FPS = 40.0; const float ADJUST_LOD_UP_FPS = 55.0; @@ -53,6 +55,7 @@ class QSettings; class BandwidthDialog; class LodToolsDialog; class MetavoxelEditor; +class ChatWindow; class OctreeStatsDialog; class MenuItemProperties; @@ -140,6 +143,7 @@ private slots: void cycleFrustumRenderMode(); void runTests(); void showMetavoxelEditor(); + void showChat(); void audioMuteToggled(); private: @@ -187,6 +191,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QPointer _MetavoxelEditor; + QPointer _chatWindow; OctreeStatsDialog* _octreeStatsDialog; LodToolsDialog* _lodToolsDialog; int _maxVoxels; @@ -252,6 +257,7 @@ namespace MenuOption { 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"; diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp new file mode 100644 index 0000000000..5770eff31e --- /dev/null +++ b/interface/src/ui/ChatWindow.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include + +#include "ChatWindow.h" +#include "ui_chatwindow.h" +#include "FlowLayout.h" + +#include +#include +#include +#include + +const QString DEFAULT_SERVER = "chat.highfidelity.io"; +const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io"; + +const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); + +ChatWindow::ChatWindow() : + QDialog(Application::getInstance()->getGLWidget(), Qt::Tool), + ui(new Ui::ChatWindow) { + ui->setupUi(this); + + FlowLayout* flowLayout = new FlowLayout(); + flowLayout->setContentsMargins(0, 8, 0, 8); + ui->usersWidget->setLayout(flowLayout); + + ui->messagePlainTextEdit->installEventFilter(this); + + ui->numOnlineLabel->hide(); + ui->usersWidget->hide(); + ui->messagesScrollArea->hide(); + ui->messagePlainTextEdit->hide(); + + setAttribute(Qt::WA_DeleteOnClose); + + _xmppClient.addExtension(&_xmppMUCManager); + connect(&_xmppClient, SIGNAL(connected()), this, SLOT(connected())); + connect(&_xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(error(QXmppClient::Error))); + connect(&_xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage))); + + AccountManager& accountManager = AccountManager::getInstance(); + QString user = accountManager.getUsername(); + const QString& password = accountManager.getXMPPPassword(); + _xmppClient.connectToServer(user + "@" + DEFAULT_SERVER, password); +} + +ChatWindow::~ChatWindow() { + delete ui; +} + +bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { + Q_UNUSED(sender); + + if (event->type() != QEvent::KeyPress) { + return false; + } + QKeyEvent* keyEvent = static_cast(event); + if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && + (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { + _xmppClient.sendMessage(_chatRoom->jid(), ui->messagePlainTextEdit->document()->toPlainText()); + ui->messagePlainTextEdit->document()->clear(); + return true; + } + return false; +} + +QString ChatWindow::getParticipantName(const QString& participant) { + return participant.right(participant.count() - 1 - _chatRoom->jid().count()); +} + +void ChatWindow::connected() { + _chatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM); + connect(_chatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); + _chatRoom->setNickName(AccountManager::getInstance().getUsername()); + _chatRoom->join(); + + ui->connectingToXMPPLabel->hide(); + ui->numOnlineLabel->show(); + ui->usersWidget->show(); + ui->messagesScrollArea->show(); + ui->messagePlainTextEdit->show(); +} + +void ChatWindow::error(QXmppClient::Error error) { + ui->connectingToXMPPLabel->setText(QString::number(error)); +} + +void ChatWindow::participantsChanged() { + QStringList participants = _chatRoom->participants(); + ui->numOnlineLabel->setText(tr("%1 online now:").arg(participants.count())); + + while (QLayoutItem* item = ui->usersWidget->layout()->takeAt(0)) { + delete item; + } + foreach (const QString& participant, participants) { + QLabel* userLabel = new QLabel(getParticipantName(participant)); + userLabel->setStyleSheet( + "background-color: palette(light);" + "border-radius: 5px;" + "color: #267077;" + "padding: 2px;" + "border: 1px solid palette(shadow);" + "font-weight: bold"); + ui->usersWidget->layout()->addWidget(userLabel); + } +} + +void ChatWindow::messageReceived(const QXmppMessage& message) { + QLabel* userLabel = new QLabel(getParticipantName(message.from())); + QFont font = userLabel->font(); + font.setBold(true); + userLabel->setFont(font); + userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "\\1")); + messageLabel->setWordWrap(true); + messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + messageLabel->setOpenExternalLinks(true); + + ui->messagesFormLayout->addRow(userLabel, messageLabel); + ui->messagesFormLayout->parentWidget()->updateGeometry(); + Application::processEvents(); + QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); + verticalScrollBar->setSliderPosition(verticalScrollBar->maximum()); +} diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h new file mode 100644 index 0000000000..c1963b5342 --- /dev/null +++ b/interface/src/ui/ChatWindow.h @@ -0,0 +1,40 @@ +#ifndef __interface__ChatWindow__ +#define __interface__ChatWindow__ + +#include + +#include + +#include +#include + +namespace Ui { +class ChatWindow; +} + +class ChatWindow : public QDialog { + Q_OBJECT + +public: + ChatWindow(); + ~ChatWindow(); + +protected: + bool eventFilter(QObject* sender, QEvent* event); + +private: + Ui::ChatWindow* ui; + QXmppClient _xmppClient; + QXmppMucManager _xmppMUCManager; + QXmppMucRoom* _chatRoom; + + QString getParticipantName(const QString& participant); + +private slots: + void connected(); + void error(QXmppClient::Error error); + void participantsChanged(); + void messageReceived(const QXmppMessage& message); +}; + +#endif /* defined(__interface__ChatWindow__) */ diff --git a/interface/src/ui/FlowLayout.cpp b/interface/src/ui/FlowLayout.cpp new file mode 100644 index 0000000000..5387d9499d --- /dev/null +++ b/interface/src/ui/FlowLayout.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "flowlayout.h" +//! [1] +FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) + : m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} +//! [1] + +//! [2] +FlowLayout::~FlowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) + delete item; +} +//! [2] + +//! [3] +void FlowLayout::addItem(QLayoutItem *item) +{ + itemList.append(item); +} +//! [3] + +//! [4] +int FlowLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { + return m_hSpace; + } else { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int FlowLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { + return m_vSpace; + } else { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} +//! [4] + +//! [5] +int FlowLayout::count() const +{ + return itemList.size(); +} + +QLayoutItem *FlowLayout::itemAt(int index) const +{ + return itemList.value(index); +} + +QLayoutItem *FlowLayout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) + return itemList.takeAt(index); + else + return 0; +} +//! [5] + +//! [6] +Qt::Orientations FlowLayout::expandingDirections() const +{ + return 0; +} +//! [6] + +//! [7] +bool FlowLayout::hasHeightForWidth() const +{ + return true; +} + +int FlowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} +//! [7] + +//! [8] +void FlowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize FlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize FlowLayout::minimumSize() const +{ + QSize size; + QLayoutItem *item; + foreach (item, itemList) + size = size.expandedTo(item->minimumSize()); + + size += QSize(2*margin(), 2*margin()); + return size; +} +//! [8] + +//! [9] +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; +//! [9] + +//! [10] + QLayoutItem *item; + foreach (item, itemList) { + QWidget *wid = item->widget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); +//! [10] +//! [11] + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + bottom; +} +//! [11] +//! [12] +int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) { + return -1; + } else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, 0, pw); + } else { + return static_cast(parent)->spacing(); + } +} +//! [12] diff --git a/interface/src/ui/FlowLayout.h b/interface/src/ui/FlowLayout.h new file mode 100644 index 0000000000..f7107be6b2 --- /dev/null +++ b/interface/src/ui/FlowLayout.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include +#include +#include +//! [0] +class FlowLayout : public QLayout +{ +public: + explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~FlowLayout(); + + void addItem(QLayoutItem *item); + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const; + bool hasHeightForWidth() const; + int heightForWidth(int) const; + int count() const; + QLayoutItem *itemAt(int index) const; + QSize minimumSize() const; + void setGeometry(const QRect &rect); + QSize sizeHint() const; + QLayoutItem *takeAt(int index); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList; + int m_hSpace; + int m_vSpace; +}; +//! [0] + +#endif // FLOWLAYOUT_H diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui new file mode 100644 index 0000000000..554b42833e --- /dev/null +++ b/interface/ui/chatWindow.ui @@ -0,0 +1,125 @@ + + + ChatWindow + + + + 0 + 0 + 400 + 608 + + + + Chat + + + + 0 + + + 8 + + + 8 + + + 8 + + + 8 + + + + + + 0 + 0 + + + + Connecting to XMPP... + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + font-weight: bold; color: palette(shadow) + + + online now: + + + + + + + + + + true + + + + + 0 + 0 + 382 + 356 + + + + + QFormLayout::AllNonFixedFieldsGrow + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + + + + + 0 + 0 + + + + + + + + messagePlainTextEdit + messagesScrollArea + + + + diff --git a/libraries/shared/src/AccountManager.h b/libraries/shared/src/AccountManager.h index 8b3c6b362c..ee821fa43c 100644 --- a/libraries/shared/src/AccountManager.h +++ b/libraries/shared/src/AccountManager.h @@ -52,6 +52,8 @@ public: void requestAccessToken(const QString& login, const QString& password); QString getUsername() const { return _accountInfo.getUsername(); } + + const QString& getXMPPPassword() const { return _accountInfo.getXMPPPassword(); } void destroy() { delete _networkAccessManager; } From 9eb217794c5e038138a2d2e9cdbf4fbe5a96f1bd Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 10 Mar 2014 12:08:33 +0200 Subject: [PATCH 055/109] Added time stamps to the chat. Enabled translations. --- interface/CMakeLists.txt | 8 +- interface/interface_en.ts | 161 ++ interface/src/main.cpp | 5 + interface/src/ui/ChatWindow.cpp | 79 +- interface/src/ui/ChatWindow.h | 18 +- interface/ui/chatWindow.ui | 20 +- libraries/shared/src/qtimespan.cpp | 2290 ++++++++++++++++++++++++++++ libraries/shared/src/qtimespan.h | 301 ++++ 8 files changed, 2863 insertions(+), 19 deletions(-) create mode 100644 interface/interface_en.ts create mode 100644 libraries/shared/src/qtimespan.cpp create mode 100644 libraries/shared/src/qtimespan.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 9f176d1ee8..6ec0b6f679 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -10,6 +10,8 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros") set(TARGET_NAME interface) project(${TARGET_NAME}) +find_package(Qt5LinguistTools REQUIRED) + # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") set(FACESHIFT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift") @@ -77,6 +79,10 @@ qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") # add them to the interface source files set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}") +set(QM ${TARGET_NAME}_en.qm) +set(TS ${TARGET_NAME}_en.ts) +qt5_create_translation(${QM} ${INTERFACE_SRCS} ${QT_UI_FILES} ${TS}) + if (APPLE) # configure CMake to use a custom Info.plist SET_TARGET_PROPERTIES( ${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in ) @@ -105,7 +111,7 @@ if (APPLE) endif() # create the executable, make it a bundle on OS X -add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS}) +add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM}) # link in the hifi shared library include(${MACRO_DIR}/LinkHifiLibrary.cmake) diff --git a/interface/interface_en.ts b/interface/interface_en.ts new file mode 100644 index 0000000000..8198d97a8f --- /dev/null +++ b/interface/interface_en.ts @@ -0,0 +1,161 @@ + + + + + Application + + + Export Voxels + + + + + Sparse Voxel Octree Files (*.svo) + + + + + Open Script + + + + + JavaScript Files (*.js) + + + + + ChatWindow + + + + Chat + + + + + + Connecting to XMPP... + + + + + + online now: + + + + + day + + %n day + %n days + + + + + hour + + %n hour + %n hours + + + + + minute + + %n minute + %n minutes + + + + second + + %n second + %n seconds + + + + + %1 online now: + + + + + Dialog + + + + + + Update Required + + + + + + Download + + + + + + Skip Version + + + + + + Close + + + + + Menu + + + Open .ini config file + + + + + + Text files (*.ini) + + + + + Save .ini config file + + + + + QObject + + + + Import Voxels + + + + + Loading ... + + + + + Place voxels + + + + + <b>Import</b> %1 as voxels + + + + + Cancel + + + + diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 2d30fc06bc..f2c516792b 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -19,6 +19,7 @@ #include #include +#include #include int main(int argc, const char * argv[]) { @@ -40,6 +41,10 @@ int main(int argc, const char * argv[]) { { QSettings::setDefaultFormat(QSettings::IniFormat); Application app(argc, const_cast(argv), startup_time); + + QTranslator translator; + translator.load("interface_en"); + app.installTranslator(&translator); qDebug( "Created QT Application."); exitCode = app.exec(); diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 5770eff31e..4e4b869cda 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -1,13 +1,24 @@ +// +// ChatWindow.cpp +// interface +// +// Created by Dimitar Dobrev on 3/6/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + #include #include #include +#include #include #include #include +#include #include "ChatWindow.h" #include "ui_chatwindow.h" #include "FlowLayout.h" +#include "qtimespan.h" #include #include @@ -16,16 +27,17 @@ const QString DEFAULT_SERVER = "chat.highfidelity.io"; const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io"; +const int NUM_MESSAGES_TO_TIME_STAMP = 20; const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); ChatWindow::ChatWindow() : QDialog(Application::getInstance()->getGLWidget(), Qt::Tool), - ui(new Ui::ChatWindow) { + ui(new Ui::ChatWindow), + numMessagesAfterLastTimeStamp(0) { ui->setupUi(this); - FlowLayout* flowLayout = new FlowLayout(); - flowLayout->setContentsMargins(0, 8, 0, 8); + FlowLayout* flowLayout = new FlowLayout(0, 4, 4); ui->usersWidget->setLayout(flowLayout); ui->messagePlainTextEdit->installEventFilter(this); @@ -61,8 +73,11 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { QKeyEvent* keyEvent = static_cast(event); if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { - _xmppClient.sendMessage(_chatRoom->jid(), ui->messagePlainTextEdit->document()->toPlainText()); - ui->messagePlainTextEdit->document()->clear(); + QString message = ui->messagePlainTextEdit->document()->toPlainText(); + if (!message.trimmed().isEmpty()) { + _xmppClient.sendMessage(_chatRoom->jid(), message); + ui->messagePlainTextEdit->document()->clear(); + } return true; } return false; @@ -72,6 +87,26 @@ QString ChatWindow::getParticipantName(const QString& participant) { return participant.right(participant.count() - 1 - _chatRoom->jid().count()); } +void ChatWindow::addTimeStamp() { + QTimeSpan timePassed = QDateTime::currentDateTime() - lastMessageStamp; + int times[] = { timePassed.daysPart(), timePassed.hoursPart(), timePassed.minutesPart() }; + QString strings[] = { tr("day", 0, times[0]), tr("hour", 0, times[1]), tr("minute", 0, times[2]) }; + QString timeString = ""; + for (int i = 0; i < 3; i++) { + if (times[i] > 0) { + timeString += strings[i] + " "; + } + } + timeString.chop(1); + QLabel* timeLabel = new QLabel(timeString); + timeLabel->setStyleSheet("color: palette(shadow);" + "background-color: palette(highlight);" + "padding: 4px;"); + timeLabel->setAlignment(Qt::AlignHCenter); + ui->messagesFormLayout->addRow(timeLabel); + numMessagesAfterLastTimeStamp = 0; +} + void ChatWindow::connected() { _chatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM); connect(_chatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); @@ -83,6 +118,17 @@ void ChatWindow::connected() { ui->usersWidget->show(); ui->messagesScrollArea->show(); ui->messagePlainTextEdit->show(); + + QTimer* timer = new QTimer(this); + timer->setInterval(10 * 60 * 1000); + connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); + timer->start(); +} + +void ChatWindow::timeout() { + if (numMessagesAfterLastTimeStamp >= NUM_MESSAGES_TO_TIME_STAMP) { + addTimeStamp(); + } } void ChatWindow::error(QXmppClient::Error error) { @@ -98,13 +144,12 @@ void ChatWindow::participantsChanged() { } foreach (const QString& participant, participants) { QLabel* userLabel = new QLabel(getParticipantName(participant)); - userLabel->setStyleSheet( - "background-color: palette(light);" - "border-radius: 5px;" - "color: #267077;" - "padding: 2px;" - "border: 1px solid palette(shadow);" - "font-weight: bold"); + userLabel->setStyleSheet("background-color: palette(light);" + "border-radius: 5px;" + "color: #267077;" + "padding: 2px;" + "border: 1px solid palette(shadow);" + "font-weight: bold"); ui->usersWidget->layout()->addWidget(userLabel); } } @@ -115,15 +160,25 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { font.setBold(true); userLabel->setFont(font); userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + userLabel->setStyleSheet("padding: 4px;"); QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "\\1")); messageLabel->setWordWrap(true); messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); messageLabel->setOpenExternalLinks(true); + messageLabel->setStyleSheet("padding: 4px;"); ui->messagesFormLayout->addRow(userLabel, messageLabel); ui->messagesFormLayout->parentWidget()->updateGeometry(); Application::processEvents(); QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); verticalScrollBar->setSliderPosition(verticalScrollBar->maximum()); + + ++numMessagesAfterLastTimeStamp; + if (message.stamp().isValid()) { + lastMessageStamp = message.stamp().toLocalTime(); + } + else { + lastMessageStamp = QDateTime::currentDateTime(); + } } diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h index c1963b5342..afa1947268 100644 --- a/interface/src/ui/ChatWindow.h +++ b/interface/src/ui/ChatWindow.h @@ -1,7 +1,17 @@ +// +// ChatWindow.h +// interface +// +// Created by Dimitar Dobrev on 3/6/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + #ifndef __interface__ChatWindow__ #define __interface__ChatWindow__ #include +#include +#include #include @@ -23,15 +33,19 @@ protected: bool eventFilter(QObject* sender, QEvent* event); private: + QString getParticipantName(const QString& participant); + void addTimeStamp(); + Ui::ChatWindow* ui; QXmppClient _xmppClient; QXmppMucManager _xmppMUCManager; QXmppMucRoom* _chatRoom; - - QString getParticipantName(const QString& participant); + int numMessagesAfterLastTimeStamp; + QDateTime lastMessageStamp; private slots: void connected(); + void timeout(); void error(QXmppClient::Error error); void participantsChanged(); void messageReceived(const QXmppMessage& message); diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 554b42833e..1723e72665 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -54,7 +54,7 @@ - font-weight: bold; color: palette(shadow) + font-weight: bold; color: palette(shadow); margin-bottom: 4px; online now: @@ -66,6 +66,12 @@ + + margin-top: 12px; + + + Qt::ScrollBarAlwaysOff + true @@ -74,16 +80,19 @@ 0 0 - 382 - 356 + 358 + 328 + + margin-top: 0px; + QFormLayout::AllNonFixedFieldsGrow - 4 + 0 4 @@ -112,6 +121,9 @@ 0 + + Qt::ScrollBarAlwaysOff + diff --git a/libraries/shared/src/qtimespan.cpp b/libraries/shared/src/qtimespan.cpp new file mode 100644 index 0000000000..2ddddb30f3 --- /dev/null +++ b/libraries/shared/src/qtimespan.cpp @@ -0,0 +1,2290 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +//#include "private/qdatetime_p.h" +#include +#include +#include +#include "qdatastream.h" +#include "qlocale.h" +#include "qtimespan.h" +#include "qdebug.h" +#include "qcoreapplication.h" +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) +#include +#endif +#ifndef Q_WS_WIN +#include +#endif + +#include +#if defined(Q_OS_WINCE) +#include "qfunctions_wince.h" +#endif + +#if defined(Q_WS_MAC) +#include +#endif + +#if defined(Q_OS_SYMBIAN) +#include +#endif + + + +/*! + \class QTimeSpan + \brief The QTimeSpan represents a span of time + \since 4.8 + + QTimeSpan represents a span of time, which is optionally in reference to a specific + point in time. A QTimeSpan behaves slightly different if it has a reference date or time + or not. + + \section1 Constructing a QTimeSpan + A QTimeSpan can be created by initializing it directly with a length and optionally with + a reference (start) date, or by substracting two QDate or QDateTime values. By substracting + QDate or QDateTime values, you create a QTimeSpan with the QDate or QDateTime on the right + hand side of the - operator as the reference date. + + \code + //Creates a QTimeSpan representing the time from October 10, 1975 to now + QDate birthDay(1975, 10, 10); + QTimeSpan age = QDate::currentDate() - birthDay; + \endcode + + QTimeSpan defines a series of constants that can be used for initializing a QTimeSpan. + Second, Minute, Hour, Day and Week are all QTimeSpan instances with the corresponding + length and no reference date. You can use those to create new instances. See the + section on Date arithmatic below. + + \code + //Creates a QTimeSpan representing 2 days, 4 hours and 31 minutes. + QTimeSpan span(2 * QTimeSpan::Day + 4 * QTimeSpan::Hour + 31 * QTimeSpan::Minute); + \endcode + + Finally, a QTimeSpan can be constructed by using one of the static constructors + fromString or fromTimeUnit. + + \section1 Date arithmatic + A negative QTimeSpan means that the reference date lies before the referenced date. Call + normalize() to ensure that the reference date is smaller or equal than the referenced date. + + Basic arithmatic can be done with QTimeSpan. QTimeSpans can be added up or substracted, or + be multiplied by a scalar factor. For this, the usual operators are implemented. The union + of QTimeSpans will yield the minimal QTimeSpan that covers both the original QTimeSpans, + while the intersection will yield the overlap between them (or an empty one if there is no + overlap). Please refer to the method documentation for details on what happens to a + reference date when using these methods. + + QTimeSpans can also be added to or substracted from a QDate, QTime or QDateTime. This will yield + a new QDate, QTime or QDateTime moved by the value of the QTimeSpan. Note that the QTimeSpan must + be the right-hand argument of the operator. You can not add a QDate to a QTimeSpan, but you + can do the reverse. + + \code + QTimeSpan span(QTimeSpan::Hour * 5 + 45 * QTimeSpan::Minute); + QDateTime t1 = QDateTime::currentDateTime(); + QDateTime t2 = t1 + span; // t2 is now the date time 5 hours and 45 minutes in the future. + \endcode + + \section1 Accessing the length of a QTimeSpan + There are two sets of methods that return the length of a QTimeSpan. The to* methods such + as toSeconds and toMinutes return the total time in the requested unit. That may be a + fractional number. + + \code + QTimeSpan span = QTimeSpan::Hour * 6; + qreal days = span.toDays(); //yields 0.25 + \endcode + + On the other hand, you may be interested in a number of units at the same time. If you want + to know the number of days, hours and minutes in a QTimeSpan, you can use the to*Part + methods such as toDayPart and toHourPart. These functions take a QTimeSpan::TimeSpanFormat + argument to indicate the units you want to use for the presentation of the QTimeSpan. + This is used to calculate the number of the requested time units. You can also use the + parts method directly, passing pointers to ints for the units you are interested in and 0 + for the other units. + + \section1 Using months and years + QTimeSpan can be used to describe any length of time, ranging from milliseconds to decades and + beyond (up to the maximum value of a qint64 milliseconds; enough for any application not + dealing with geological or astronomical time scales.) It is natural to use units like months + and years when dealing with longer time periods, such as the age of people. The problem with + these units is that unlike the time units for a week or shorter, the length of a month or a + year is not fixed. It it dependent on the reference date. The time period '1 month' has a + different meaning when we are speaking of februari or januari. + + QTimeSpan can only use the month and year time units if a valid reference date has been + set. Without a valid reference date, month and year as time units are meaningless and their + use will result in an assert. The largest unit of time that can be expressed without a reference + date is a week. The time period of one month is understood to mean the period + from a day and time one month to the same date and time in the next. If the next month does + not have that date and time, one month will be taken to mean the period to the end of that + month. + + \example The time from januari 2, 12 PM to februari 2, 12 PM will be understood as exactly one month. + \example The time from januari 30, 2 PM to march 1, 00:00:00.000 will also be one month, because + februari does not have 30 days but 28 or 29 depending on the year. + \example The time from januari 30, 2 PM to march 30, 2 PM will be 2 months. + + The same goes for years. + + QTimeSpan stores the length of time intervals as a 64 bits integer representing milliseconds. + That means that arithmatic with time periods set as months or years may not always yield what you + expect. A time period set as the year describing the whole of 2007 that you multiply by two or + add to itself, will not end up having a length of two years, but of 1 year, 11 months and 30 + days, as 2008 is one day longer than 2007. When months and years are used, they are + converted to the exact time span they describe in reference to the reference date set for + the QTimeSpan. With another reference date, or when negated, that time span may or may not + describe the same number of years and months. +*/ + +QT_BEGIN_NAMESPACE + +const QTimeSpan QTimeSpan::Second = QTimeSpan(qint64(1000)); +const QTimeSpan QTimeSpan::Minute = QTimeSpan(qint64(1000 * 60)); +const QTimeSpan QTimeSpan::Hour = QTimeSpan(qint64(1000 * 60 * 60)); +const QTimeSpan QTimeSpan::Day = QTimeSpan(qint64(1000 * 60 * 60 * 24)); +const QTimeSpan QTimeSpan::Week = QTimeSpan(qint64(1000 * 60 * 60 * 24 * 7)); + +class QTimeSpanPrivate : public QSharedData { +public: + qint64 interval; + QDateTime reference; + static const int s_daysPerMonth[12]; + + void addUnit(QTimeSpan* self, Qt::TimeSpanUnit unit, qreal value) + { + if (unit >= Qt::Months) { + QTimeSpan tempSpan(self->referencedDate()); + tempSpan.setFromTimeUnit(unit, value); + interval += tempSpan.toMSecs(); + } else { + switch (unit) { + case Qt::Weeks: + interval += value * 1000LL * 60LL * 60LL * 24LL * 7LL; + break; + case Qt::Days: + interval += value * 1000LL * 60LL * 60LL * 24LL; + break; + case Qt::Hours: + interval += value * 1000LL * 60LL * 60LL ; + break; + case Qt::Minutes: + interval += value * 1000LL * 60LL; + break; + case Qt::Seconds: + interval += value * 1000LL; + break; + case Qt::Milliseconds: + interval += value; + break; + default: + break; + } + } + } + + class TimePartHash: public QHash + { + public: + TimePartHash(Qt::TimeSpanFormat format) + { + for (int i(Qt::Milliseconds); i <= Qt::Years; i *= 2) { + Qt::TimeSpanUnit u = static_cast(i); + if (format.testFlag(u)) { + int* newValue = new int; + *newValue = 0; + insert(u, newValue); //perhaps we can optimize this not to new each int individually? + + } else { + insert(u, 0); + } + } + } + + ~TimePartHash() + { + qDeleteAll(*this); + } + + inline bool fill(const QTimeSpan& span) + { + bool result = span.parts(value(Qt::Milliseconds), + value(Qt::Seconds), + value(Qt::Minutes), + value(Qt::Hours), + value(Qt::Days), + value(Qt::Weeks), + value(Qt::Months), + value(Qt::Years)); + return result; + } + + inline void addUnit(const Qt::TimeSpanUnit unit) + { + if (value(unit) != 0) + return; + + int* newValue = new int; + *newValue = 0; + insert(unit, newValue); + } + + }; + + //returns a string representation of time in a single time unit + QString unitString(Qt::TimeSpanUnit unit, int num) const + { + switch (unit) { + case::Qt::Milliseconds: + return qApp->translate("QTimeSpanPrivate", "%n millisecond(s)", 0, num); + case::Qt::Seconds: + return qApp->translate("QTimeSpanPrivate", "%n second(s)", 0, num); + case::Qt::Minutes: + return qApp->translate("QTimeSpanPrivate", "%n minute(s)", 0, num); + case::Qt::Hours: + return qApp->translate("QTimeSpanPrivate", "%n hour(s)", 0, num); + case::Qt::Days: + return qApp->translate("QTimeSpanPrivate", "%n day(s)", 0, num); + case::Qt::Weeks: + return qApp->translate("QTimeSpanPrivate", "%n week(s)", 0, num); + case::Qt::Months: + return qApp->translate("QTimeSpanPrivate", "%n month(s)", 0, num); + case::Qt::Years: + return qApp->translate("QTimeSpanPrivate", "%n year(s)", 0, num); + default: + return QString(); + } + } + +#ifndef QT_NO_DATESTRING + struct TimeFormatToken + { + Qt::TimeSpanUnit type; //Qt::NoUnit is used for string literal types + int length; //number of characters to use + QString string; //only used for string literals + }; + + QList parseFormatString(const QString& formatString, Qt::TimeSpanFormat &format) const + { + QHash tokenHash; + tokenHash.insert(QChar('y'), Qt::Years); + tokenHash.insert(QChar('M'), Qt::Months); + tokenHash.insert(QChar('w'), Qt::Weeks); + tokenHash.insert(QChar('d'), Qt::Days); + tokenHash.insert(QChar('h'), Qt::Hours); + tokenHash.insert(QChar('m'), Qt::Minutes); + tokenHash.insert(QChar('s'), Qt::Seconds); + tokenHash.insert(QChar('z'), Qt::Milliseconds); + + + QList tokenList; + format = Qt::NoUnit; + int pos(0); + int length(formatString.length()); + bool inLiteral(false); + while (pos < length) { + const QChar currentChar(formatString[pos]); + if (inLiteral) { + if (currentChar == QLatin1Char('\'')) { + inLiteral = false; //exit literal string mode + if ((pos+1)interval = 0; +} + +/*! + \brief Constructor + + Constructs QTimeSpan of size msecs milliseconds. The reference date will + be invalid. +*/ +QTimeSpan::QTimeSpan(qint64 msecs) + : d(new QTimeSpanPrivate) +{ + d->interval = msecs; +} + +/*! + \brief Copy Constructor +*/ +QTimeSpan::QTimeSpan(const QTimeSpan& other): + d(other.d) +{ +} + +/*! + \brief Constructor + + Constructs QTimeSpan of size msecs milliseconds from the given reference date + and time. +*/ +QTimeSpan::QTimeSpan(const QDateTime &reference, qint64 msecs) + : d(new QTimeSpanPrivate) +{ + d->interval = msecs; + d->reference = reference; +} + +/*! + \brief Constructor + + Constructs QTimeSpan of size msecs milliseconds from the given reference date. + The reference time will be 0:00:00.000 +*/ +QTimeSpan::QTimeSpan(const QDate &reference, quint64 msecs) + : d(new QTimeSpanPrivate) +{ + d->interval = msecs; + d->reference = QDateTime(reference); +} + +/*! + \brief Constructor + + Constructs QTimeSpan of size msecs milliseconds from the given reference time. + The reference date will be today's date. +*/ +QTimeSpan::QTimeSpan(const QTime &reference, quint64 msecs) + : d(new QTimeSpanPrivate) +{ + d->interval = msecs; + QDateTime todayReference(QDate::currentDate()); + todayReference.setTime(reference); + d->reference = todayReference; +} + +/*! + \brief Constructor + + Constructs a QTimeSpan of the same length as other from the given reference date time. +*/ +QTimeSpan::QTimeSpan(const QDateTime& reference, const QTimeSpan& other) + : d(new QTimeSpanPrivate) +{ + d->reference = reference; + d->interval = other.d->interval; +} + +/*! + \brief Constructor + + Constructs a QTimeSpan of the same length as other from the given reference date. + The reference time will be 00:00:00.000 +*/ +QTimeSpan::QTimeSpan(const QDate& reference, const QTimeSpan& other) + : d(new QTimeSpanPrivate) +{ + d->reference = QDateTime(reference); + d->interval = other.d->interval; +} + +/*! + \brief Constructor + + Constructs a QTimeSpan of the same length as other from the given reference time. + The reference date will be today's date. +*/ +QTimeSpan::QTimeSpan(const QTime& reference, const QTimeSpan& other) + : d(new QTimeSpanPrivate) +{ + QDateTime todayReference(QDate::currentDate()); + todayReference.setTime(reference); + d->reference = todayReference; + d->interval = other.d->interval; +} + + +/*! + \brief Destructor +*/ +QTimeSpan::~QTimeSpan() +{ +} + +/*! + \returns true if the time span is 0; that is, if no time is spanned by + this instance. There may or may not be a valid reference date. + + \sa isNull hasValidReference +*/ +bool QTimeSpan::isEmpty() const +{ + return d->interval == 0; +} + +/*! + \returns true if the time span is 0; that is, if no time is spanned by + this instance and there is no valid reference date. + + \sa isEmpty +*/ +bool QTimeSpan::isNull() const +{ + return isEmpty() && (!hasValidReference()); +} + +/*! + \brief Assignment operator +*/ +QTimeSpan& QTimeSpan::operator=(const QTimeSpan& other) { + if (&other == this) + return *this; + + d = other.d; + return *this; +} + +/*! + \returns a new QTimeSpan instance initialized to the indicated number of + time units. The default reference date is invalid. + + \note that you can only construct a valid QTimeSpan using the Months or Years + time units if you supply a valid reference date. + + \sa setFromTimeUnit +*/ +QTimeSpan QTimeSpan::fromTimeUnit(Qt::TimeSpanUnit unit, qreal interval, const QDateTime& reference ) +{ + switch (unit){ //note: fall through is intentional! + case Qt::Weeks: + interval *= 7.0; + case Qt::Days: + interval *= 24.0; + case Qt::Hours: + interval *= 60.0; + case Qt::Minutes: + interval *= 60.0; + case Qt::Seconds: + interval *= 1000.0; + case Qt::Milliseconds: + break; + default: + if (reference.isValid()) { + QTimeSpan result(reference); + result.setFromTimeUnit(unit, interval); + return result; + } + Q_ASSERT_X(false, "static constructor", "Can not construct QTimeSpan from Month or Year TimeSpanUnit without a valid reference date."); + return QTimeSpan(); + } + + return QTimeSpan(reference, qint64(interval)); +} + +/*! + \returns the number of the requested units indicated by unit when formatted + as format. + + \sa parts() +*/ +int QTimeSpan::part(Qt::TimeSpanUnit unit, Qt::TimeSpanFormat format) const +{ + if (!format.testFlag(unit)) + return 0; + + if (!hasValidReference()) { + if (unit == Qt::Months || unit == Qt::Years) { + Q_ASSERT_X(false, "part", "Can not calculate Month or Year part without a reference date"); + } + if (format.testFlag(Qt::Months) || format.testFlag(Qt::Years)) { + qWarning() << "Unsetting Qt::Months and Qt::Years flags from format. Not supported without a reference date"; + //should this assert instead? + format&= (Qt::AllUnits ^ (Qt::Months | Qt::Years)); + } + } + + //build up hash with pointers to ints for the units that are set in format, and 0's for those that are not. + QTimeSpanPrivate::TimePartHash partsHash(format); + bool result = partsHash.fill(*this); + + if (!result) { + //what to do? Assert perhaps? + qWarning() << "Result is invalid!"; + return 0; + } + + int val = *(partsHash.value(unit)); + return val; +} + +#define CHECK_INT_LIMIT(interval, unitFactor) if (interval >= (qint64(unitFactor) * qint64(std::numeric_limits::max()) ) ) {qWarning() << "out of range" << unitFactor; return false;} + +/*! + Retreives a breakup of the length of the QTimeSpan in different time units. + + While part() allows you to retreive the value of a single unit for a specific + representation of time, this method allows you to retreive all these values + with a single call. The units that you want to use in the representation of the + time span is defined implicitly by the pointers you pass. Passing a valid pointer + for a time unit will include that unit in the representation, while passing 0 + for that pointer will exclude it. + + The passed integer pointers will be set to the correct value so that together + they represent the whole time span. This function will then return true. + If it is impossible to represent the whole time span in the requested units, + this function returns false. + + The fractionalSmallestUnit qreal pointer can optionally be passed in to + retreive the value for the smallest time unit passed in as a fractional number. + For instance, if your time span contains 4 minutes and 30 seconds, but the + smallest time unit you pass in an integer pointer for is the minute unit, then + the minute integer will be set to 4 and the fractionalSmallestUnit will be set + to 4.5. + + A negative QTimeSpan will result in all the parts of the representation to be + negative, while a positive QTimeSpan will result in an all positive + representation. + + \note Months and years are only valid as units for time spans that have a valid + reference date. Requesting the number of months or years for time spans without + a valid reference date will return false. + + If this function returns false, the value of the passed in pointers is undefined. + + \sa part() +*/ +bool QTimeSpan::parts(int *msecondsPtr, + int *secondsPtr, + int *minutesPtr, + int *hoursPtr, + int *daysPtr, + int *weeksPtr, + int *monthsPtr, + int *yearsPtr, + qreal *fractionalSmallestUnit) const +{ + /* \todo We should probably cache the results of this operation. However, that requires keeping a dirty flag + in the private data store, or a copy of the reference date, interval and last used parts. Is that worth it? + */ + + // Has the user asked for a fractional component? If yes, find which unit it corresponds to. + Qt::TimeSpanUnit smallestUnit = Qt::NoUnit; + if (fractionalSmallestUnit) + { + if (yearsPtr) + smallestUnit = Qt::Years; + if (monthsPtr) + smallestUnit = Qt::Months; + if (weeksPtr) + smallestUnit = Qt::Weeks; + if (daysPtr) + smallestUnit = Qt::Days; + if (hoursPtr) + smallestUnit = Qt::Hours; + if (minutesPtr) + smallestUnit = Qt::Minutes; + if (secondsPtr) + smallestUnit = Qt::Seconds; + if (msecondsPtr) + smallestUnit = Qt::Milliseconds; + } + + QTimeSpan ts(*this); + if (yearsPtr || monthsPtr) { //deal with months and years + //we can not deal with months or years if there is no valid reference date + if (!hasValidReference()) { + qWarning() << "Can not request month or year parts of a QTimeSpan without a valid reference date."; + return false; + } + + int* _years = yearsPtr; + if (!yearsPtr) + _years = new int; + + *_years = 0; + QDate startDate = ts.startDate().date(); + QDate endDate = ts.endDate().date(); + *_years = endDate.year() - startDate.year(); + if (endDate.month() < startDate.month()) { + (*_years)--; + } else if (endDate.month() == startDate.month()) { + if (endDate.day() < startDate.day()) { + (*_years)--; + } + } + + /** \todo Handle fractional years component */ + + int* _months = monthsPtr; + if (!monthsPtr) + _months = new int; + + *_months = endDate.month() - startDate.month(); + if (*_months < 0) + (*_months) += 12; + if (endDate.day() < startDate.day()) { + (*_months)--; + } + int totalMonths = (*_months); + if (!yearsPtr) + totalMonths += (*_years) * 12; + + QDate newStartDate(startDate); + newStartDate = newStartDate.addYears(*_years); + newStartDate = newStartDate.addMonths(*_months); + //qDebug() << "working with new start date" << newStartDate << "and end date" << endDate; + ts = QDateTime(endDate, ts.endDate().time()) - QDateTime(newStartDate, ts.startDate().time()); + *_months = totalMonths; + + /** \todo Handle fractional months component */ + + //clean up temporary variables on the heap + if (!monthsPtr) + delete _months; + if (!yearsPtr) + delete _years; + } + + //from here on, we use ts as the time span! + qint64 intervalLeft = ts.toMSecs(); + qint64 unitFactor; + //qDebug() << "intervalLeft" << intervalLeft; + if (weeksPtr) { + unitFactor = (7 * 24 * 60 * 60 * 1000); + CHECK_INT_LIMIT(intervalLeft, unitFactor) + + *weeksPtr = intervalLeft / unitFactor; + if (smallestUnit == Qt::Weeks) + { + QTimeSpan leftOverTime(referencedDate(), -intervalLeft); + leftOverTime.normalize(); + *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); + } + + if (*weeksPtr != 0) { + intervalLeft = intervalLeft % unitFactor; + } + } + + if (daysPtr) { + unitFactor = (24 * 60 * 60 * 1000); + CHECK_INT_LIMIT(intervalLeft, unitFactor) + + *daysPtr = intervalLeft / unitFactor; + if (smallestUnit == Qt::Days) + { + QTimeSpan leftOverTime(referencedDate(), -intervalLeft); + leftOverTime.normalize(); + *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); + } + + if (*daysPtr != 0 ) { + intervalLeft = intervalLeft % unitFactor; + } + } + + if (hoursPtr) { + unitFactor = (60 * 60 * 1000); + CHECK_INT_LIMIT(intervalLeft, unitFactor) + + *hoursPtr = intervalLeft / unitFactor; + if (smallestUnit == Qt::Hours) + { + QTimeSpan leftOverTime(referencedDate(), -intervalLeft); + leftOverTime.normalize(); + *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); + } + + if (*hoursPtr != 0 ) { + intervalLeft = intervalLeft % unitFactor; + } + } + + if (minutesPtr) { + unitFactor = (60 * 1000); + CHECK_INT_LIMIT(intervalLeft, unitFactor) + + *minutesPtr = intervalLeft / unitFactor; + if (smallestUnit == Qt::Minutes) + { + QTimeSpan leftOverTime(referencedDate(), -intervalLeft); + leftOverTime.normalize(); + *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); + } + + if (*minutesPtr != 0 ) { + intervalLeft = intervalLeft % unitFactor; + } + } + + if (secondsPtr) { + unitFactor = (1000); + CHECK_INT_LIMIT(intervalLeft, unitFactor) + + *secondsPtr = intervalLeft / unitFactor; + if (smallestUnit == Qt::Seconds) + { + QTimeSpan leftOverTime(referencedDate(), -intervalLeft); + leftOverTime.normalize(); + *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); + } + + if (*secondsPtr > 0 ) { + intervalLeft = intervalLeft % unitFactor; + } + } + + if (msecondsPtr) { + unitFactor = 1; + CHECK_INT_LIMIT(intervalLeft, unitFactor) + + *msecondsPtr = intervalLeft; + if (smallestUnit == Qt::Weeks) + { + *fractionalSmallestUnit = qreal(intervalLeft); + } + } + + return true; +} + +/*! + Sets a part of the time span in the given format. + + setPart allows you to adapt the current time span interval unit-by-unit based on + any time format. Where setFromTimeUnit resets the complete time interval, setPart + only sets a specific part in a chosen format. + + \example If you have a time span representing 3 weeks, 2 days, 4 hours, 31 minutes + and 12 seconds, you can change the number of hours to 2 by just calling + span.setPart(Qt::Hours, 2, Qt::Weeks | Qt::Days | Qt::Hours | Qt::Minutes | Qt::Seconds); + + Note that just like with any other function, you can not use the Months and Years + units without using a reference date. +*/ +void QTimeSpan::setPart(Qt::TimeSpanUnit unit, int interval, Qt::TimeSpanFormat format) +{ + if (!format.testFlag(unit)) { + qWarning() << "Can not set a unit that is not part of the format. Ignoring."; + return; + } + + QTimeSpanPrivate::TimePartHash partsHash(format); + bool result = partsHash.fill(*this); + + if (!result) { + qWarning() << "Retreiving parts failed, cannot set parts. Ignoring."; + return; + } + + d->addUnit(this, unit, interval - *(partsHash.value(unit) ) ); +} + +/*! + Returns Qt::TimeSpanUnit representing the order of magnitude of the time span. + That is, the largest unit that can be used to display the time span that + will result in a non-zero value. + + If the QTimeSpan does not have a valid reference date, the largest + possible time unit that will be returned is Qt::Weeks. Otherwise, + the largest possible time unit is Qt::Years. + + \returns Unit representing the order of magnitude of the time span. +*/ +Qt::TimeSpanUnit QTimeSpan::magnitude() +{ + //abs(qint64) doesnt't compile properly with mingw32? + qint64 mag = d->interval; + if (mag < 0) + mag = -mag; + + if (mag < 1000) + return Qt::Milliseconds; + if (mag < (1000LL * 60LL)) + return Qt::Seconds; + if (mag < (1000LL * 60LL * 60LL)) + return Qt::Minutes; + if (mag < (1000LL * 60LL * 60LL * 24LL)) + return Qt::Hours; + if (mag < (1000LL * 60LL * 60LL * 24LL * 7LL)) + return Qt::Days; + + //those the simple cases. The rest is dependent on if there is a reference date + if (hasValidReference()) { + //simple test. If bigger than 366 (not 365!) then we are certain of dealing with years + if (mag > (1000LL * 60LL * 60LL * 24LL * 366LL)) + return Qt::Years; + //we need a more complicated test + int years = 0; + int months = 0; + parts(0, 0, 0, 0, 0, 0, &months, &years); + if (years > 0) + return Qt::Years; + if (months > 0) + return Qt::Months; + } + + return Qt::Weeks; +} + +/*! + \returns true if there is a valid reference date set, false otherwise. +*/ +bool QTimeSpan::hasValidReference() const +{ + return d->reference.isValid(); +} + +/*! + \returns the reference date. Note that the reference date may be invalid. +*/ +QDateTime QTimeSpan::referenceDate() const +{ + return d->reference; +} + +/*! + Sets the reference date. + + If there currently is a reference date, the referenced date will + not be affected. That means that the length of the time span will + change. If there currently is no reference date set, the interval + will not be affected and this function will have the same + effect as moveReferenceDate. + + /sa moveReferenceDate setReferencedDate moveReferencedDate +*/ +void QTimeSpan::setReferenceDate(const QDateTime &referenceDate) +{ + if (d->reference.isValid() && referenceDate.isValid()) { + *this = referencedDate() - referenceDate; + } else { + d->reference = referenceDate; + } +} + +/*! + Moves the time span to align the time spans reference date with the + new reference date. + + Note that the length of the time span will not be modified, so the + referenced date will shift as well. If no reference date was set + before, it is set now and the referenced date will become valid. + + /sa setReferenceDate setReferencedDate moveReferencedDate +*/ +void QTimeSpan::moveReferenceDate(const QDateTime &referenceDate) +{ + d->reference = referenceDate; +} + +/*! + Sets the referenced date. + + If there currently is a reference date, that reference date will + not be affected. This implies that the length of the time span changes. + If there currently is no reference date set, the interval + will not be affected and this function will have the same + effect as moveReferencedDate. + + /sa setReferenceDate moveReferenceDate moveReferencedDate + +*/ +void QTimeSpan::setReferencedDate(const QDateTime &referencedDate) +{ + if (d->reference.isValid()) { + *this = referencedDate - d->reference; + } else { + d->reference = referencedDate.addMSecs(-(d->interval)); + } +} + +/*! + Moves the time span to align the time spans referenced date with the + new referenced date. + + Note that the length of the time span will not be modified, so the + reference date will shift as well. If no reference date was set + before, it is set now. + + /sa setReferenceDate setReferencedDate moveReferencedDate +*/ +void QTimeSpan::moveReferencedDate(const QDateTime &referencedDate) +{ + d->reference = referencedDate.addMSecs(-(d->interval)); +} + +/*! + Returns the referenced date and time. + + The referenced QDateTime is the "other end" of the QTimeSpan from + the reference date. + + An invalid QDateTime will be returned if no valid reference date + has been set. +*/ +QDateTime QTimeSpan::referencedDate() const +{ + if (!(d->reference.isValid())) + return QDateTime(); + + QDateTime dt(d->reference); + dt = dt.addMSecs(d->interval); + return dt; +} + +// Comparison operators +/*! + Returns true if the two compared QTimeSpans have both the same + reference date and the same length. + + Note that two QTimeSpan objects that span the same period, but + where one is positive and the other is negative are not considdered + equal. If you need to compare those, compare the normalized + versions. + + \sa matchesLength +*/ +bool QTimeSpan::operator==(const QTimeSpan &other) const +{ + return ((d->interval == other.d->interval) && + (d->reference == other.d->reference)); +} + +/*! + Returns true if the interval of this QTimeSpan is shorter than + the interval of the other QTimeSpan. +*/ +bool QTimeSpan::operator<(const QTimeSpan &other) const +{ + return d->interval < other.d->interval; +} + +/*! + Returns true if the interval of this QTimeSpan is shorter or equal + than the interval of the other QTimeSpan. +*/ +bool QTimeSpan::operator<=(const QTimeSpan &other) const +{ + return d->interval <= other.d->interval; +} + +/*! + Returns true if the interval of this QTimeSpan is equal + to the interval of the other QTimeSpan. That is, if they have the + same length. + + The default value of normalize is false. If normalize is true, the + absolute values of the interval lengths are compared instead of the + real values. + + \code + QTimeSpan s1 = 2 * QTimeSpan::Day; + QTimeSpan s2 = -2 * QTimeSpan::Day; + qDebug() << s1.matchesLength(s2); //returns false + qDebug() << s1.matchesLength(s2, true); //returns true + \endcode +*/ +bool QTimeSpan::matchesLength(const QTimeSpan &other, bool normalize) const +{ + if (!normalize) { + return d->interval == other.d->interval; + } else { + return qAbs(d->interval) == qAbs(other.d->interval); + } +} + +// Arithmatic operators +/*! + Adds the interval of the other QTimeSpan to the interval of + this QTimeSpan. The reference date of the other QTimeSpan is + ignored. +*/ +QTimeSpan & QTimeSpan::operator+=(const QTimeSpan &other) +{ + d->interval += other.d->interval; + return *this; +} + +/*! + Adds the number of milliseconds to the interval of + this QTimeSpan. The reference date of the QTimeSpan is + not affected. +*/ +QTimeSpan & QTimeSpan::operator+=(qint64 msecs) +{ + d->interval += msecs; + return *this; +} + +/*! + Substracts the interval of the other QTimeSpan from the interval of + this QTimeSpan. The reference date of the other QTimeSpan is + ignored while the reference date of this QTimeSpan is not affected. +*/ +QTimeSpan & QTimeSpan::operator-=(const QTimeSpan &other) +{ + d->interval -= (other.d->interval); + return *this; +} + +/*! + Substracts the number of milliseconds from the interval of + this QTimeSpan. The reference date of the QTimeSpan is + not affected. +*/ +QTimeSpan & QTimeSpan::operator-=(qint64 msecs) +{ + d->interval -= msecs; + return *this; +} + +/*! + Multiplies the interval described by this QTimeSpan by the + given factor. The reference date of the QTimeSpan is not + affected. +*/ +QTimeSpan & QTimeSpan::operator*=(qreal factor) +{ + d->interval *= factor; + return *this; +} + +/*! + Multiplies the interval described by this QTimeSpan by the + given factor. The reference date of the QTimeSpan is not + affected. +*/ +QTimeSpan & QTimeSpan::operator*=(int factor) +{ + d->interval *= factor; + return *this; +} + +/*! + Divides the interval described by this QTimeSpan by the + given factor. The reference date of the QTimeSpan is not + affected. +*/ +QTimeSpan & QTimeSpan::operator/=(qreal factor) +{ + d->interval /= factor; + return *this; +} + +/*! + Divides the interval described by this QTimeSpan by the + given factor. The reference date of the QTimeSpan is not + affected. +*/ +QTimeSpan & QTimeSpan::operator/=(int factor) +{ + d->interval /= factor; + return *this; +} + +/*! + \brief Modifies this QTimeSpan to be the union of two QTimeSpans. + + The union of two QTimeSpans is defined as the minimum QTimeSpan that + encloses both QTimeSpans. The QTimeSpans need not be overlapping. + + \warning Only works if both QTimeSpans have a valid reference date. + \sa operator&=(const QTimeSpan& other) + \sa operator|(const QTimeSpan& other) + \sa united(const QTimeSpan& other) +*/ +QTimeSpan& QTimeSpan::operator|=(const QTimeSpan& other) // Union +{ + Q_ASSERT_X((hasValidReference() && other.hasValidReference()), "assignment-or operator", "Both participating time spans need a valid reference date"); + + //do we need to check for self-assignment? + + QDateTime start = qMin(startDate(), other.startDate()); + QDateTime end = qMax(endDate(), other.endDate()); + + *this = end - start; + return *this; +} + +/*! + \brief Modifies this QTimeSpan to be the intersection of two QTimeSpans. + + The intersection of two QTimeSpans is defined as the maximum QTimeSpan that + both QTimeSpans have in common. If the QTimeSpans don't overlap, a null QTimeSpan + will be returned. The returned QTimeSpan will be positive. + + \warning Only works if both QTimeSpans have a valid reference date. + \sa operator&=(const QTimeSpan& other) + \sa operator&(const QTimeSpan& other) + \sa overlapped(const QTimeSpan& other) +*/ +QTimeSpan& QTimeSpan::operator&=(const QTimeSpan &other) // Intersection +{ + Q_ASSERT_X((hasValidReference() && other.hasValidReference()), "assignment-or operator", "Both participating time spans need a valid reference date"); + + //do we need to check for self-assignment? + + const QTimeSpan* first = this; + const QTimeSpan* last = &other; + if (other.startDate() < startDate()) { + first = &other; + last = this; + } + + //check if there is overlap at all. If not, reset the interval to 0 + if (!(first->endDate() > last->startDate()) ) { + d->interval = 0; + return *this; + } + + *this = qMin(first->endDate(), last->endDate()) - last->startDate(); + return *this; +} + +/*! + Returns true if this QTimeSpan overlaps with the other QTimeSpan. + If one of the QTimeSpans does not have a valid reference date, this + function returns false. +*/ +bool QTimeSpan::overlaps(const QTimeSpan &other) const +{ + if (!hasValidReference() || !other.hasValidReference()) + return false; + + const QTimeSpan* first = this; + const QTimeSpan* last = &other; + if (other.startDate() < startDate()) { + first = &other; + last = this; + } + + return (first->endDate() > last->startDate()); +} + +/*! + \brief Returns a new QTimeSpan that represents the intersection of the two QTimeSpans. + + The intersection of two QTimeSpans is defined as the maximum QTimeSpan that + both QTimeSpans have in common. If the QTimeSpans don't overlap, a null QTimeSpan + will be returned. Any valid returned QTimeSpan will be positive. + + \warning Only works if both QTimeSpans have a valid reference date. Will assert otherwise. + \sa operator&=(const QTimeSpan& other) + \sa operator&(const QTimeSpan& other) + +*/ +QTimeSpan QTimeSpan::overlapped(const QTimeSpan &other) const +{ + Q_ASSERT_X((hasValidReference() && other.hasValidReference()), "assignment-or operator", "Both participating time spans need a valid reference date"); + + const QTimeSpan* first = this; + const QTimeSpan* last = &other; + if (other.startDate() < startDate()) { + first = &other; + last = this; + } + + //check if there is overlap at all. If not, reset the interval to 0 + if (!(first->endDate() >= last->startDate()) ) { + return QTimeSpan(); + } + + return qMin(first->endDate(), last->endDate()) - last->startDate(); +} + +/*! + \brief Returns a new QTimeSpan that represents the union of two QTimeSpans. + + The union of two QTimeSpans is defined as the minimum QTimeSpan that + encloses both QTimeSpans. The QTimeSpans need not be overlapping. + + \warning Only works if both QTimeSpans have a valid reference date. Will assert otherwise. + \sa operator|=(const QTimeSpan& other) + \sa operator|(const QTimeSpan& other) +*/ +QTimeSpan QTimeSpan::united(const QTimeSpan &other) const +{ + Q_ASSERT_X((hasValidReference() && other.hasValidReference()), "assignment-or operator", "Both participating time spans need a valid reference date"); + + QDateTime start = qMin(startDate(), other.startDate()); + QDateTime end = qMax(endDate(), other.endDate()); + + return ( end - start ); +} + +/*! + Determines if the given dateTime lies within the time span. The begin + and end times are taken to be contained in the time span in this function. + + If the time span does not have a valid reference date, this function + returns false. +*/ +bool QTimeSpan::contains(const QDateTime &dateTime) const +{ + if (!hasValidReference()) + return false; + + return ((startDate() <= dateTime) && + (endDate()) >= dateTime); +} + +/*! + Determines if the given date lies within the time span. The begin + and end times are taken to be contained in the time span in this function. + Just like in the QTimeSpan constructors that take a QDate, the time will assumed + to be 00:00:00.000. + + If the time span does not have a valid reference date, this function + returns false. +*/ +bool QTimeSpan::contains(const QDate &date) const +{ + QDateTime dt(date); + return contains(dt); +} + +/*! + Determines if the given time lies within the time span. The begin + and end times are taken to be contained in the time span in this function. + Just like in the QTimeSpan constructors that take a QTime, the date + will be set to today. + + If the time span does not have a valid reference date, this function + returns false. +*/ +bool QTimeSpan::contains(const QTime &time) const +{ + QDateTime dt(QDate::currentDate()); + dt.setTime(time); + return contains(dt); +} + +/*! + Determines if the other QTimeSpan lies within this time span. Another + time span is contained if its start time is the same or later then the + start time of this time span, and its end time is the same or earlier + than the end time of this time span. + + If either time span does not have a valid reference date, this function + returns false. +*/ +bool QTimeSpan::contains(const QTimeSpan &other) const +{ + if (!(hasValidReference() && other.hasValidReference())) + return false; + + return ((startDate() <= other.startDate()) && + (endDate()) >= other.endDate()); +} + +/*! + Returns a new QTimeSpan representing the same time span as this QTimeSpan, but + that is guaranteed to be positive; that is, to have the reference date before or + equal to the referenced date. + + \sa normalize abs + */ +QTimeSpan QTimeSpan::normalized() const +{ + QTimeSpan ts(*this); + ts.normalize(); + return ts; +} + +/*! + Modifies this QTimeSpan to represent the same time span, but + that is guaranteed to be positive; that is, to have the reference date before or + equal to the referenced date. If there is no valid reference date, the interval + will just be made positive. + + \sa normalized abs + */ +void QTimeSpan::normalize() +{ + if (d->interval < 0) { + if (hasValidReference()) { + d->reference = referencedDate(); + } + d->interval = qAbs(d->interval); + } +} + +/*! + Returns a copy of this QTimeSpan that is guaranteed to be positive; that is, + to have the reference date before or equal to the referenced date. The reference + date is not modified. That implies that if there is a reference date, and the + QTimeSpan is negative, the returned QTimeSpan will describe the time span \i after + the reference date instead of the time span \i before. + + If there is no valid reference date, the interval will just be made positive. + + \sa normalize normalized + */ +QTimeSpan QTimeSpan::abs() const +{ + QTimeSpan result(*this); + result.d->interval = qAbs(result.d->interval); + + return result; +} + +/*! + Returns true if the interval is negative. + \sa isNormal + */ +bool QTimeSpan::isNegative() const +{ + return d->interval < 0; +} + +/*! + \fn QTimeSpan::isNormal() const + + Returns true if the interval is normal, that is: not negative. + \sa isNegative + */ + +/*! + Returns the first date of the spanned time period. If there is no valid + reference date, an invalid QDateTime will be returned. + */ +QDateTime QTimeSpan::startDate() const +{ + if (isNegative()) + return referencedDate(); + + return referenceDate(); +} + +/*! + Returns the last date of the spanned time period. If there is no valid + reference date, an invalid QDateTime will be returned. + */ +QDateTime QTimeSpan::endDate() const +{ + if (isNegative()) + return referenceDate(); + + return referencedDate(); +} + +/*! + Returns the duration of the QTimeSpan expressed in milliseconds. This + value may be negative. +*/ +qint64 QTimeSpan::toMSecs() const +{ + return d->interval; +} + +/*! + Returns the duration of the QTimeSpan expressed in the given TimeSpanUnit. This + value may be negative. +*/ +qreal QTimeSpan::toTimeUnit(Qt::TimeSpanUnit unit) const +{ + qreal interval = qreal(d->interval); + switch (unit){ //fall through is intentional + case Qt::Weeks: + interval /= 7.0; + case Qt::Days: + interval /= 24.0; + case Qt::Hours: + interval /= 60.0; + case Qt::Minutes: + interval /= 60.0; + case Qt::Seconds: + interval /= 1000.0; + case Qt::Milliseconds: + break; + default: + Q_ASSERT_X(hasValidReference(), "toTimeUnit", "Can not convert to time units that depend on the reference date (month and year)."); + qreal result(0.0); + int intResult(0); + bool succes(false); + if (unit == Qt::Months) { + succes = parts(0, 0, 0, 0, 0, 0, &intResult, 0, &result); + } else if (unit == Qt::Years) { + succes = parts(0, 0, 0, 0, 0, 0, 0, &intResult, &result); + } + + if (!succes) + return 0.0; + + return result; + } + + return interval; +} + +/*! + Sets the length of this QTimeSpan from the given number of milliseconds. The reference date is not + affected. + */ +void QTimeSpan::setFromMSecs(qint64 msecs) +{ + d->interval = msecs; +} + +/*! + Sets the length of this QTimeSpan from the given number of TimeSpanUnits. The reference date is not + affected. + */ +void QTimeSpan::setFromTimeUnit(Qt::TimeSpanUnit unit, qreal interval) +{ + switch (unit){ + case Qt::Weeks: //fall through of cases is intentional! + interval *= 7.0; + case Qt::Days: + interval *= 24.0; + case Qt::Hours: + interval *= 60.0; + case Qt::Minutes: + interval *= 60.0; + case Qt::Seconds: + interval *= 1000.0; + case Qt::Milliseconds: + break; + case Qt::Months: + setFromMonths(interval); + return; + case Qt::Years: + setFromYears(interval); + return; + default: + Q_ASSERT_X(false, "setFromTimeUnit", "Can not set a QTimeSpan duration from unknown TimeSpanUnit."); + } + + d->interval = qint64(interval); +} + +/*! + Sets the interval of the time span as a number of months. + + \warning This function can only be used if a valid reference date has been set! + + The setFromMonths method deals with fractional months in the following way: first, + the whole number of months is extracted and added to the reference date. The fractional + part of the number of months is then multiplied with the number of days in the month + in which that date falls. If the number of months is negative and adding a whole month + yields a date exactly on a month boundary, the number of days in the month before is + used instead. + That number is used as the number of days and is added to the interval. + + \code + QTimeSpan ts(QDate(2010,01,01)); + ts.setFromMonths(1.5); // ts's referenced date is now februari 14, 0:00:00 + ts.setFromMonths(2.5); // ts's referenced date is now march 15, 0:00:00 + QTimeSpan ts2(QDate(2008,01,01)); //2008 is a leap year! + ts2.setFromMonths(1.5); // ts2's referenced date is now februari 14, 12:00:00 + QTimeSpan ts3(QDate(2008,03,01)); //2008 is a leap year + ts3.setFromMonths(-0.5); // ts3's referenced date is now februari 14: 12:00:00 // + \endcode + */ +void QTimeSpan::setFromMonths(qreal months) +{ + Q_ASSERT_X(hasValidReference(), "setFromMonths", "Can not set interval from time unit month if there is no reference date."); + + int fullMonths = int(months); + qreal fractionalMonth = months - fullMonths; + + QDateTime endDate = d->reference; + endDate = endDate.addMonths(fullMonths); + + int days = d->s_daysPerMonth[endDate.date().month()-1]; + QDateTime measureDate(endDate); + if (fractionalMonth < 0) { + measureDate = measureDate.addMSecs(-1); + } + if (QDate::isLeapYear(measureDate.date().year()) && measureDate.date().month() == 2) { + ++days; //februari has an extra day this year... + } + + QTimeSpan tmp = endDate - d->reference; + qreal fractionalDays = fractionalMonth * days; + d->interval = tmp.toMSecs() + qint64(fractionalDays * 24.0 * 60.0 * 60.0 * 1000.0); +} + +/*! + Sets the interval of the time span as a number of years. + + \warning This function can only be used if a valid reference date has been set! + + The setFromYears method deals with fractional years in the following way: first, + the whole number of years is extracted and added to the reference date. The fractional + part of the number of years is then multiplied with the number of days in the year + in which that date falls. That number is used as the number of days and is added to the + interval. + If the number of years is negative and adding the whole years yields a date exactly on + a year boundary, the number of days in the year before is used instead. + */ +void QTimeSpan::setFromYears(qreal years) +{ + Q_ASSERT_X(hasValidReference(), "setFromYears", "Can not set interval from time unit year if there is no reference date."); + + int fullYears = int(years); + qreal fractionalYear = years - fullYears; + + QDateTime endDate = d->reference; + endDate = endDate.addYears(fullYears); + + qreal days = 365.0; + QDateTime measureDate(endDate); + if (fractionalYear < 0) { + measureDate = measureDate.addMSecs(-1); + } + if (QDate::isLeapYear(measureDate.date().year())) { + days += 1.0; //februari has an extra day this year... + } + + QTimeSpan tmp = endDate - d->reference; + qreal fractionalDays = fractionalYear * days; + d->interval = tmp.toMSecs() + qint64(fractionalDays * 24.0 * 60.0 * 60.0 * 1000.0); +} + +#ifndef QT_NO_DATASTREAM +/*! + Streaming operator. + + This operator allows you to stream a QTimeSpan into a QDataStream. + /sa operator>>(QDataStream &stream, QTimeSpan &span) + */ +QDataStream & operator<<(QDataStream &stream, const QTimeSpan & span) +{ + stream << span.d->reference << span.d->interval; + return stream; +} + +/*! + Streaming operator. + + This operator allows you to stream a QTimeSpan out of a QDataStream. + /sa operator>>(QDataStream &stream, QTimeSpan &span) + */ +QDataStream & operator>>(QDataStream &stream, QTimeSpan &span) +{ + stream >> span.d->reference >> span.d->interval; + return stream; +} +#endif + +/*! + Adds another QTimeSpan to this QTimeSpan. + + The values of the intervals of the QTimeSpans are added up with normal + arithmatic. Negative values will work as expected. + + If the left argument has a reference date, that reference will be kept. + If only the right argument has a reference date, then that reference + date will be used as the new reference date. + + The above can have suprising consequences: + \code + // s1 and s2 are two QTimeSpan objects + QTimeSpan s12 = s1 + s2; + QTimeSpan s21 = s2 + s1; + + if (s12 == s21) { + //may or may not happen, depending on the reference dates of s1 and s2. + } + \endcode +*/ +QTimeSpan operator+(const QTimeSpan &left, const QTimeSpan &right) +{ + QTimeSpan result(left); + result += right; + + // only keep the right reference date if the left argument does not have one + if (!left.hasValidReference() && right.hasValidReference()) { + result.setReferenceDate(right.referenceDate()); + } + + return result; +} + +/*! + Substracts another QTimeSpan from this QTimeSpan. + + The value of the interval of the right QTimeSpan is substracted from the + left QTimeSpan with normal arithmatic. Negative values will work as expected. + + If the left argument has a reference date, that reference will be kept. + If only the right argument has a reference date, then that reference + date will be used as the new reference date. + + \sa operator+ +*/ +QTimeSpan operator-(const QTimeSpan &left, const QTimeSpan &right) +{ + QTimeSpan result(left); + result -= right; + + // only keep the right reference date if the left argument does not have one + if (!left.hasValidReference() && right.hasValidReference()) { + result.setReferenceDate(right.referenceDate()); + } + + return result; + +} + +/*! + Multiply a QTimeSpan by a scalar factor. + + \returns a new QTimeSpan object that has the same reference date as the + left QTimeSpan, but with an interval length that is multiplied by the + right argument. +*/ +QTimeSpan operator*(const QTimeSpan &left, qreal right) +{ + QTimeSpan result(left); + result*=right; + return result; +} + +/*! + Multiply a QTimeSpan by a scalar factor. + + \returns a new QTimeSpan object that has the same reference date as the + left QTimeSpan, but with an interval length that is multiplied by the + right argument. +*/ +QTimeSpan operator*(const QTimeSpan &left, int right) +{ + QTimeSpan result(left); + result*=right; + return result; +} + +/*! + Devide a QTimeSpan by a scalar factor. + + \returns a new QTimeSpan object that has the same reference date as the + left QTimeSpan, but with an interval length that is devided by the + right argument. +*/ +QTimeSpan operator/(const QTimeSpan &left, qreal right) +{ + QTimeSpan result(left); + result/=right; + return result; +} + +/*! + Devide a QTimeSpan by a scalar factor. + + \returns a new QTimeSpan object that has the same reference date as the + left QTimeSpan, but with an interval length that is devided by the + right argument. +*/ +QTimeSpan operator/(const QTimeSpan &left, int right) +{ + QTimeSpan result(left); + result/=right; + return result; +} + +/*! + Devides two QTimeSpans. The devision works on the interval lengths of + the two QTimeSpan objects as you would expect from normal artithmatic. +*/ +qreal operator/(const QTimeSpan &left, const QTimeSpan &right) +{ + return (qreal(left.toMSecs()) / qreal(right.toMSecs())); +} + +/*! + Returns a QTimeSpan object with the same reference date as the right + hand argument, but with a negated interval. Note that unlike with the + normalize() method, this function will result in a QTimeSpan that + describes a different period if the QTimeSpan has a reference date because + the reference date is not modified. +*/ +QTimeSpan operator-(const QTimeSpan &right) // Unary negation +{ + QTimeSpan result(right); + result.setFromMSecs(-result.toMSecs()); + return result; +} + +/*! + \returns the union of the two QTimeSpans. + + \sa united +*/ +QTimeSpan operator|(const QTimeSpan &left, const QTimeSpan &right) // Union +{ + QTimeSpan result(left); + result|=right; + return result; +} + +/*! + \returns the intersection of the two QTimeSpans. + + \sa intersected +*/ +QTimeSpan operator&(const QTimeSpan &left, const QTimeSpan &right) // Intersection +{ + QTimeSpan result(left); + result&=right; + return result; +} + +// Operators that use QTimeSpan and other date/time classes +/*! + Creates a new QTimeSpan object that describes the period between the two + QDateTime objects. The right hand object will be used as the reference date, + so that substracting a date in the past from a date representing now will yield + a positive QTimeSpan. + + \note that while substracting two dates will result in a QTimeSpan describing + the time between those dates, there is no pendant operation for adding two dates. + + Substractions involving an invalid QDateTime, will result in a time span with + an interval length 0. If the right-hand QDateTime is valid, it will still be + used as the reference date. +*/ +QTimeSpan operator-(const QDateTime &left, const QDateTime &right) +{ + QTimeSpan result(right); +#if QT_VERSION >= 0x040700 + if (left.isValid() && right.isValid()) { + result = QTimeSpan(right, right.msecsTo(left)); + } +#else + if (left.isValid() && right.isValid()) { + QDateTime tmpLeft = left.toUTC(); + QDateTime tmpRight = right.toUTC(); + //don't use QDateTime::secsTo, you end up in rounding issues as well as overflow problems! + qint64 interval = 1000LL * 60LL * 60LL * 24LL * qint64(tmpRight.date().daysTo(tmpLeft.date())); + interval += tmpRight.time().msecsTo(tmpLeft.time()); + result.setFromMSecs(interval); + } +#endif + return result; +} + +/*! + Creates a new QTimeSpan object that describes the period between the two + QDate objects. The right hand object will be used as the reference date, + so that substracting a date in the past from a date representing now will yield + a positive QTimeSpan. + + \note that while substracting two dates will result in a QTimeSpan describing + the time between those dates, there is no pendant operation for adding two dates. +*/ +QTimeSpan operator-(const QDate &left, const QDate &right) +{ + QTimeSpan result = QDateTime(left) - QDateTime(right); + return result; +} + +/*! + Creates a new QTimeSpan object that describes the period between the two + QTime objects. The right hand time will be used as the reference time, + so that substracting a time in the past from a time representing now will yield + a positive QTimeSpan. + + \note that that both times will be assumed to be on the current date. + + \note that while substracting two times will result in a QTimeSpan describing + the time between those, there is no pendant operation for adding two times. +*/ +QTimeSpan operator-(const QTime &left, const QTime &right) +{ + return QDateTime(QDate::currentDate(), left) - QDateTime(QDate::currentDate(), right); +} + +/*! + \returns the date described by the left-hand date, shifted by the interval + described in the QTimeSpan. The reference date of the QTimeSpan, if set, is + ignored. + + No rounding takes place. If a QTimeSpan describes 1 day, 23 hours and 59 minutes, + adding that QTimeSpan to a QDate respresenting April 1 will still yield April 2. +*/ +QDate operator+(const QDate &left, const QTimeSpan &right) +{ + QDateTime dt(left); + return (dt + right).date(); +} + +/*! + \returns the date and time described by the left-hand QDateTime, shifted by + the interval described in the QTimeSpan. The reference date of the QTimeSpan, if set, + is ignored. +*/ +QDateTime operator+(const QDateTime &left, const QTimeSpan &right) +{ + QDateTime result(left); + result = result.addMSecs(right.toMSecs()); + return result; +} + +/*! + \returns the time described by the left-hand QTime, shifted by + the interval described in the QTimeSpan. The reference date of the QTimeSpan, if set, + is ignored. + + \note that since QTimeSpan works with dates and times, the time returned will never + be bigger than 23:59:59.999. The time will wrap to the next date. Use QDateTime objects + if you need to keep track of that. +*/ +QTime operator+(const QTime &left, const QTimeSpan &right) +{ + QDateTime dt(QDate::currentDate(), left); + dt = dt.addMSecs(right.toMSecs()); + return dt.time(); +} + +/*! + \returns the date described by the left-hand date, shifted by the negated interval + described in the QTimeSpan. The reference date of the QTimeSpan, if set, is + ignored. + + No rounding takes place. If a QTimeSpan describes 1 day, 23 hours and 59 minutes, + adding that QTimeSpan to a QDate respresenting April 1 will still yield April 2. +*/ +QDate operator-(const QDate &left, const QTimeSpan &right) +{ + QDateTime dt(left); + return (dt - right).date(); +} + +/*! + \returns the date and time described by the left-hand QDateTime, shifted by + the negated interval described in the QTimeSpan. The reference date of the + QTimeSpan, if set, is ignored. +*/ +QDateTime operator-(const QDateTime &left, const QTimeSpan &right) +{ + QDateTime result(left); + result = result.addMSecs( -(right.toMSecs()) ); + return result; +} + +/*! + \returns the time described by the left-hand QTime, shifted by + the negated interval described in the QTimeSpan. The reference date of + the QTimeSpan, if set, is ignored. + + \note that since QTimeSpan works with dates and times, the time returned will never + be bigger than 23:59:59.999. The time will wrap to the next date. Use QDateTimes + if you need to keep track of that. +*/ +QTime operator-(const QTime &left, const QTimeSpan &right) +{ + QDateTime dt(QDate::currentDate(), left); + dt = dt.addMSecs( -(right.toMSecs()) ); + return dt.time(); +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DATESTRING) +/*! + Operator to stream QTimeSpan objects to a debug stream. +*/ +QDebug operator<<(QDebug debug, const QTimeSpan &ts) +{ + debug << "QTimeSpan(Reference Date =" << ts.referenceDate() + << "msecs =" << ts.toMSecs() << ")"; + return debug; +} +#endif + +//String conversions +#ifndef QT_NO_DATESTRING +/*! + \returns an approximate representation of the time span length + + When representing the lenght of a time span, it is often not nessecairy to be + completely accurate. For instance, when dispaying the age of a person, it is + often enough to just state the number of years, or possibly the number of years + and the number of months. Similary, when displaying how long a certain operation + the user of your application started will run, it is useless to display the + number of seconds left if the operation will run for hours more. + + toApproximateString() provides functionality to display the length of the + QTimeSpan in such an approximate way. It will format the time using one or two + neighbouring time units. The first time unit that will be used is the unit + that represents the biggest portion of time in the span. The second time unit + will be the time unit directly under that. The second unit will only be used + if it is not 0, and if the first number is smaller than the indicated + suppresSecondUnitLimit. + + The suppressSecondUnitLimit argument can be used to suppres, for instance, + the number of seconds when the operation will run for more than five minutes + more. The idea is that for an approximate representation of the time length, + it is no longer relevant to display the second unit if the first respresents + a time span that is perhaps an order of magnitude larger already. + + If you set suppressSecondUnitLimit to a negative number, the second unit will + always be displayed, unless no valid unit for it could be found. +*/ +QString QTimeSpan::toApproximateString(int suppresSecondUnitLimit, Qt::TimeSpanFormat format) +{ + if (format==Qt::NoUnit) + return QString(); + + //retreive the time unit to use as the primairy unit + int primairy = -1; + int secondairy = -1; + + Qt::TimeSpanUnit primairyUnit = magnitude(); + while (!format.testFlag(primairyUnit ) && primairyUnit > Qt::NoUnit) { + primairyUnit = Qt::TimeSpanUnit(primairyUnit / 2); + } + + Qt::TimeSpanUnit secondairyUnit = Qt::NoUnit; + if (primairyUnit > 1) { + secondairyUnit = Qt::TimeSpanUnit(primairyUnit / 2); + } else { + primairy = 0; + } + while (!format.testFlag(secondairyUnit) && secondairyUnit > Qt::NoUnit) { + secondairyUnit = Qt::TimeSpanUnit(secondairyUnit / 2); + } + + //build up hash with pointers to ints for the units that are set in format, and 0's for those that are not. + if (primairy < 0) { + QTimeSpanPrivate::TimePartHash partsHash(format); + bool result = partsHash.fill(*this); + + if (!result) { + qDebug() << "false result from parts function"; + return QString(); + } + + primairy = *(partsHash.value(primairyUnit)); + if (secondairyUnit > 0) { + secondairy = *(partsHash.value(secondairyUnit)); + } else { + secondairy = 0; + } + } + + if ((primairy > 0 + && secondairy > 0 + && primairy < suppresSecondUnitLimit) + || (suppresSecondUnitLimit < 0 + && secondairyUnit > Qt::NoUnit) ) + { + //we will display with two units + return d->unitString(primairyUnit, primairy) + QLatin1String(", ") + d->unitString(secondairyUnit, secondairy); + } + + //we will display with only the primairy unit + return d->unitString(primairyUnit, primairy); +} + +/*! + \returns a string representation of the duration of this time span in the requested format + + This function returns a representation of only the length of this time span. If + you need the reference or referenced dates, access those using one of the provided + methods and output them directly. + + The format parameter determines the format of the result string. The duration will be + expressed in the units you use in the format. + + y The number of years + M The number of months + w The number of weeks + d The number of days + h The number of hours + m The number of minutes + s The number of seconds + z The number of milliseconds + + Use multiple letters to force leading zeros. + + \note You can not use years or months if the time span does not have a valid reference + date. + + Characters in the string that don't represent a time unit, are used as literal strings in the + output. Everything between single quotes will always be used as a literal string. This makes + it possible to use the characters used for the time span format also as literal output. To use a + single quote in the output, put two consecutive single quotes within a single quote literal + string block. To just put a single quote in a the output, you need four consequtive single + quotes. +*/ +QString QTimeSpan::toString(const QString &format) const +{ + Qt::TimeSpanFormat tsFormat = Qt::NoUnit; + QList tokenList = d->parseFormatString(format, tsFormat); + + QTimeSpanPrivate::TimePartHash partsHash(tsFormat); + bool result = partsHash.fill(*this); + + if (!result) + return QString(); + + QString formattedString; + foreach(QTimeSpanPrivate::TimeFormatToken token, tokenList) { + if (token.type == 0) { + formattedString.append(token.string); + } else { + Qt::TimeSpanUnit unit(token.type); + formattedString.append (QString("%1").arg(*partsHash.value( unit ), + token.length, + 10, + QChar('0') ) ); + } + } + + return formattedString; +} + +/*! + Returns a time span represented by the string using the format given, or an empty + time span if the string cannot be parsed. + + The optional reference argument will be used as the reference date for the string. + + \note You can only use months or years if you also pass a valid reference. + */ +QTimeSpan QTimeSpan::fromString(const QString &string, const QString &format, const QDateTime &reference) +{ + /* + + There are two possible ways of parsing a string. On the one hand, you could use the + lengths of string literals to determine the positions in the string where you expect + the different parts of the string. On the other hand, you could use the actual contents + of the literals as delimiters to figure out what parts of the string refer to what + unit of time. In that case, the length of the time units would only matter if they are + not surrounded by a string literal. Both seem useful. Perhaps we need two different + modes for this? + + The code here implements the first option. The overloaded version below implements a + more flexible regexp based approach. + */ + + //stage one: parse the format string + QTimeSpan span(reference); + Qt::TimeSpanFormat tsFormat = Qt::NoUnit; + QList tokenList = span.d->parseFormatString(format, tsFormat); + + //prepare the temporaries + QTimeSpanPrivate::TimePartHash partsHash(tsFormat); + QString input(string); + + //extract the values from the input string into our temporary structure + foreach(const QTimeSpanPrivate::TimeFormatToken token, tokenList) { + if (token.type == Qt::NoUnit) { + input = input.remove(0, token.length); + } else { + QString part = input.left(token.length); + input = input.remove(0, token.length); + + bool success(false); + part = part.trimmed(); + int value = part.toInt(&success, 10); + if (!success) { + return QTimeSpan(); + } + *(partsHash.value(token.type)) = value; + } + } + + //construct the time span from the temporary data + //we must set the number of years and months first; for the rest order is not important + if (partsHash.value(Qt::Years)) { + span.d->addUnit(&span, Qt::Years, *(partsHash.value(Qt::Years))); + delete partsHash.value(Qt::Years); + partsHash.insert(Qt::Years, 0); + } + if (partsHash.value(Qt::Months)) { + span.d->addUnit(&span, Qt::Months, *(partsHash.value(Qt::Months))); + delete partsHash.value(Qt::Months); + partsHash.insert(Qt::Months, 0); + } + + //add the rest of the units + QHashIterator it(partsHash); + while (it.hasNext()) { + it.next(); + if (it.value()) { + span.d->addUnit(&span, it.key(), *(it.value())); + qDebug() << "Added unit" << it.key() << "with value" << *(it.value()) << "new value" << span.d->interval; + } + } + + return span; +} + +/*! + Returns a time span represented by the string using the patern given, or an empty + time span if the string cannot be parsed. Each pair of capturing parenthesis can + extract a time unit. The order in which the units appear is given by the list of + arguments unit1 to unit8. Captures for which the corresponding type is set to + Qt::NoUnit will be ignored. + + The reference argument will be used as the reference date for the string. + + \note You can only use months or years if you also pass a valid reference. + */ +QTimeSpan QTimeSpan::fromString(const QString &string, const QRegExp &pattern, const QDateTime &reference, + Qt::TimeSpanUnit unit1, Qt::TimeSpanUnit unit2, Qt::TimeSpanUnit unit3, + Qt::TimeSpanUnit unit4, Qt::TimeSpanUnit unit5, Qt::TimeSpanUnit unit6, + Qt::TimeSpanUnit unit7, Qt::TimeSpanUnit unit8) +{ + if (pattern.indexIn(string) < 0) + return QTimeSpan(); + + QTimeSpanPrivate::TimePartHash partsHash(Qt::NoUnit); + + QList unitList; + unitList << unit1 << unit2 << unit3 << unit4 << unit5 << unit6 << unit7 << unit8; + + for (int i(0); i < qMin(pattern.captureCount(), 8 ); ++i) { + if (unitList.at(i) > Qt::NoUnit) { + partsHash.addUnit(unitList.at(i)); + QString capture = pattern.cap(i + 1); + bool ok(false); + int value = capture.toInt(&ok, 10); + if (!ok) + return QTimeSpan(); + + *(partsHash.value(unitList.at(i))) = value; + } + } + + //create the time span to return + QTimeSpan span(reference); + + //construct the time span from the temporary data + //we must set the number of years and months first; for the rest order is not important + if (partsHash.value(Qt::Years)) { + span.d->addUnit(&span, Qt::Years, *(partsHash.value(Qt::Years))); + delete partsHash.value(Qt::Years); + partsHash.insert(Qt::Years, 0); + } + if (partsHash.value(Qt::Months)) { + span.d->addUnit(&span, Qt::Months, *(partsHash.value(Qt::Months))); + delete partsHash.value(Qt::Months); + partsHash.insert(Qt::Months, 0); + } + + //add the rest of the units + QHashIterator it(partsHash); + while (it.hasNext()) { + it.next(); + if (it.value()) { + span.d->addUnit(&span, it.key(), *(it.value())); + } + } + + return span; +} +#endif + +QT_END_NAMESPACE diff --git a/libraries/shared/src/qtimespan.h b/libraries/shared/src/qtimespan.h new file mode 100644 index 0000000000..03ed69abe3 --- /dev/null +++ b/libraries/shared/src/qtimespan.h @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTIMESPAN_H +#define QTIMESPAN_H + +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +//Move this to qnamespace.h when integrating +namespace Qt +{ + enum TimeSpanUnit { + Milliseconds = 0x0001, + Seconds = 0x0002, + Minutes = 0x0004, + Hours = 0x0008, + Days = 0x0010, + Weeks = 0x0020, + Months = 0x0040, + Years = 0x0080, + DaysAndTime = Days | Hours | Minutes | Seconds, + AllUnits = Milliseconds | DaysAndTime | Months | Years, + NoUnit = 0 + }; + + Q_DECLARE_FLAGS(TimeSpanFormat, TimeSpanUnit) +} +Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::TimeSpanFormat) + +//end of section to move + +class QTimeSpanPrivate; + +class Q_CORE_EXPORT QTimeSpan +{ +public: + QTimeSpan(); + explicit QTimeSpan(qint64 msecs); + explicit QTimeSpan(const QDateTime& reference, qint64 msecs = 0); + explicit QTimeSpan(const QDate& reference, quint64 msecs = 0); + explicit QTimeSpan(const QTime& reference, quint64 msecs = 0); + explicit QTimeSpan(const QDateTime& reference, const QTimeSpan& other); + explicit QTimeSpan(const QDate& reference, const QTimeSpan& other); + explicit QTimeSpan(const QTime& reference, const QTimeSpan& other); + QTimeSpan(const QTimeSpan& other); + + + ~QTimeSpan(); + + // constant time units + static const QTimeSpan Second; + static const QTimeSpan Minute; + static const QTimeSpan Hour; + static const QTimeSpan Day; + static const QTimeSpan Week; + + // status/validity of the time span + bool isEmpty() const; + bool isNull() const; + + // This set of functions operates on a single component of the time span. + inline int msecsPart(Qt::TimeSpanFormat format = Qt::DaysAndTime | Qt::Milliseconds) const {return part(Qt::Milliseconds, format);} + inline int secsPart(Qt::TimeSpanFormat format = Qt::DaysAndTime) const {return part(Qt::Seconds, format);} + inline int minutesPart(Qt::TimeSpanFormat format = Qt::DaysAndTime) const {return part(Qt::Minutes, format);} + inline int hoursPart(Qt::TimeSpanFormat format = Qt::DaysAndTime) const {return part(Qt::Hours, format);} + inline int daysPart(Qt::TimeSpanFormat format = Qt::DaysAndTime) const {return part(Qt::Days, format);} + inline int weeksPart(Qt::TimeSpanFormat format = Qt::DaysAndTime) const {return part(Qt::Weeks, format);} + //int monthsPart(Qt::TimeSpanFormat format) const; + //int yearsPart(Qt::TimeSpanFormat format) const; + int part(Qt::TimeSpanUnit unit, Qt::TimeSpanFormat format = Qt::DaysAndTime) const; + + bool parts(int *msecondsPtr, + int *secondsPtr = 0, + int *minutesPtr = 0, + int *hoursPtr = 0, + int *daysPtr = 0, + int *weeksPtr = 0, + int *monthsPtr = 0, + int *yearsPtr = 0, + qreal *fractionalSmallestUnit = 0) const; + + Qt::TimeSpanUnit magnitude(); + + inline void setMSecsPart(int msecs, Qt::TimeSpanFormat format = Qt::DaysAndTime) {setPart(Qt::Milliseconds, msecs, format);} + inline void setSecsPart(int seconds, Qt::TimeSpanFormat format = Qt::DaysAndTime) {setPart(Qt::Seconds, seconds, format);} + inline void setMinutesPart(int minutes, Qt::TimeSpanFormat format = Qt::DaysAndTime) {setPart(Qt::Minutes, minutes, format);} + inline void setHoursPart(int hours, Qt::TimeSpanFormat format = Qt::DaysAndTime) {setPart(Qt::Hours, hours, format);} + inline void setDaysPart(int days, Qt::TimeSpanFormat format = Qt::DaysAndTime) {setPart(Qt::Days, days, format);} + inline void setWeeksPart(int weeks, Qt::TimeSpanFormat format = Qt::DaysAndTime) {setPart(Qt::Weeks, weeks, format);} + inline void setMonthsPart(int months, Qt::TimeSpanFormat format = Qt::DaysAndTime) {setPart(Qt::Months, months, format);} + inline void setYearsPart(int years, Qt::TimeSpanFormat format = Qt::DaysAndTime) {setPart(Qt::Years, years, format);} + void setPart(Qt::TimeSpanUnit unit, int interval, Qt::TimeSpanFormat format = Qt::DaysAndTime); + + // This set of functions operator on the entire timespan and not + // just a single component of it. + qint64 toMSecs() const; + inline qreal toSecs() const {return toTimeUnit(Qt::Seconds);} + inline qreal toMinutes() const {return toTimeUnit(Qt::Minutes);} + inline qreal toHours() const {return toTimeUnit(Qt::Hours);} + inline qreal toDays() const {return toTimeUnit(Qt::Days);} + inline qreal toWeeks() const {return toTimeUnit(Qt::Weeks);} + inline qreal toMonths() const {return toTimeUnit(Qt::Seconds);} + inline qreal toYears() const {return toTimeUnit(Qt::Seconds);} + qreal toTimeUnit(Qt::TimeSpanUnit unit) const; + + void setFromMSecs(qint64 msecs); + inline void setFromSecs(qreal secs) {setFromTimeUnit(Qt::Seconds, secs);} + inline void setFromMinutes(qreal minutes) {setFromTimeUnit(Qt::Minutes, minutes);} + inline void setFromHours(qreal hours) {setFromTimeUnit(Qt::Hours, hours);} + inline void setFromDays(qreal days) {setFromTimeUnit(Qt::Days, days);} + inline void setFromWeeks(qreal weeks) {setFromTimeUnit(Qt::Weeks, weeks);} + void setFromMonths(qreal months); + void setFromYears(qreal years); + void setFromTimeUnit(Qt::TimeSpanUnit unit, qreal interval); + + // Reference date + bool hasValidReference() const; + QDateTime referenceDate() const; + void setReferenceDate(const QDateTime &referenceDate); + void moveReferenceDate(const QDateTime &referenceDate); + void setReferencedDate(const QDateTime &referencedDate); + void moveReferencedDate(const QDateTime &referencedDate); + + // Referenced date - referenceDate() + *this + QDateTime referencedDate() const; + + // Pretty printing +#ifndef QT_NO_DATESTRING + QString toString(const QString &format) const; + QString toApproximateString(int suppresSecondUnitLimit = 3, + Qt::TimeSpanFormat format = Qt::Seconds | Qt::Minutes | Qt::Hours | Qt::Days | Qt::Weeks); +#endif + + // Assignment operator + QTimeSpan &operator=(const QTimeSpan& other); + + // Comparison operators + bool operator==(const QTimeSpan &other) const; + inline bool operator!=(const QTimeSpan &other) const {return !(operator==(other));} + bool operator<(const QTimeSpan &other) const; + bool operator<=(const QTimeSpan &other) const; + inline bool operator>(const QTimeSpan &other) const {return !(operator<=(other));} + inline bool operator>=(const QTimeSpan &other) const {return !(operator<(other));} + bool matchesLength(const QTimeSpan &other, bool normalize = false) const; + + // Arithmetic operators. Operators that don't change *this are declared as non-members. + QTimeSpan &operator+=(const QTimeSpan &other); + QTimeSpan &operator+=(qint64 msecs); + QTimeSpan &operator-=(const QTimeSpan &other); + QTimeSpan &operator-=(qint64 msecs); + QTimeSpan &operator*=(qreal factor); + QTimeSpan &operator*=(int factor); + QTimeSpan &operator/=(qreal factor); + QTimeSpan &operator/=(int factor); + QTimeSpan &operator|=(const QTimeSpan &other); // Union + QTimeSpan &operator&=(const QTimeSpan &other); // Intersection + + // Ensure the reference date is before the referenced date + QTimeSpan normalized() const; + void normalize(); + QTimeSpan abs() const; + bool isNegative() const; + bool isNormal() const {return !isNegative();} + + // Naturally ordered dates + QDateTime startDate() const; + QDateTime endDate() const; + + // Containment + bool contains(const QDateTime &dateTime) const; + bool contains(const QDate &date) const; + bool contains(const QTime &time) const; + bool contains(const QTimeSpan &other) const; + + bool overlaps(const QTimeSpan &other) const; + QTimeSpan overlapped(const QTimeSpan &other) const; + QTimeSpan united(const QTimeSpan &other) const; + + // Static construction methods +#ifndef QT_NO_DATESTRING + static QTimeSpan fromString(const QString &string, const QString &format, const QDateTime& reference = QDateTime()); + static QTimeSpan fromString(const QString &string, const QRegExp &pattern, const QDateTime& reference, + Qt::TimeSpanUnit unit1, + Qt::TimeSpanUnit unit2 = Qt::NoUnit, Qt::TimeSpanUnit unit3 = Qt::NoUnit, + Qt::TimeSpanUnit unit4 = Qt::NoUnit, Qt::TimeSpanUnit unit5 = Qt::NoUnit, + Qt::TimeSpanUnit unit6 = Qt::NoUnit, Qt::TimeSpanUnit unit7 = Qt::NoUnit, + Qt::TimeSpanUnit unit8 = Qt::NoUnit); +#endif + //static QTimeSpan fromString(const QString &string, Qt::TimeSpanFormat format); + static QTimeSpan fromTimeUnit(Qt::TimeSpanUnit unit, qreal interval, const QDateTime& reference = QDateTime()); + /* + static QTimeSpan fromMSecs(qint64 msecs); + static QTimeSpan fromSecs(qreal secs) {return QTimeSpan::Second * secs;} + static QTimeSpan fromMinutes(qreal minutes) {return QTimeSpan::Minute * minutes;} + static QTimeSpan fromHours(qreal hours) {return QTimeSpan::Hour * hours;} + static QTimeSpan fromDays(qreal days) {return QTimeSpan::Day * days;} + static QTimeSpan fromWeeks(qreal weeks) {return QTimeSpan::Week * weeks;} + */ + +private: +#ifndef QT_NO_DATASTREAM + friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QTimeSpan &); + friend Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QTimeSpan &); +#endif + + QSharedDataPointer d; +}; +Q_DECLARE_TYPEINFO(QTimeSpan, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(QTimeSpan); +Q_DECLARE_METATYPE(Qt::TimeSpanUnit); + +//non-member operators +Q_CORE_EXPORT QTimeSpan operator+(const QTimeSpan &left, const QTimeSpan &right); +Q_CORE_EXPORT QTimeSpan operator-(const QTimeSpan &left, const QTimeSpan &right); +Q_CORE_EXPORT QTimeSpan operator*(const QTimeSpan &left, qreal right);//no problem +Q_CORE_EXPORT QTimeSpan operator*(const QTimeSpan &left, int right);//no problem +inline QTimeSpan operator*(qreal left, const QTimeSpan &right) {return right * left;} // works +inline QTimeSpan operator*(int left, const QTimeSpan &right) {return right * left;} // works +//Q_CORE_EXPORT QTimeSpan operator*(qreal left, const QTimeSpan &right) {return right * left;} //does not work +//Q_CORE_EXPORT QTimeSpan operator*(int left, const QTimeSpan &right) {return right * left;} //does not work +Q_CORE_EXPORT QTimeSpan operator/(const QTimeSpan &left, qreal right); +Q_CORE_EXPORT QTimeSpan operator/(const QTimeSpan &left, int right); +Q_CORE_EXPORT qreal operator/(const QTimeSpan &left, const QTimeSpan &right); +Q_CORE_EXPORT QTimeSpan operator-(const QTimeSpan &right); // Unary negation +Q_CORE_EXPORT QTimeSpan operator|(const QTimeSpan &left, const QTimeSpan &right); // Union +Q_CORE_EXPORT QTimeSpan operator&(const QTimeSpan &left, const QTimeSpan &right); // Intersection + +// Operators that use QTimeSpan and other date/time classes +Q_CORE_EXPORT QTimeSpan operator-(const QDateTime &left, const QDateTime &right); +Q_CORE_EXPORT QTimeSpan operator-(const QDate &left, const QDate &right); +Q_CORE_EXPORT QTimeSpan operator-(const QTime &left, const QTime &right); +Q_CORE_EXPORT QDate operator+(const QDate &left, const QTimeSpan &right); +Q_CORE_EXPORT QDate operator-(const QDate &left, const QTimeSpan &right); +Q_CORE_EXPORT QTime operator+(const QTime &left, const QTimeSpan &right); +Q_CORE_EXPORT QTime operator-(const QTime &left, const QTimeSpan &right); +Q_CORE_EXPORT QDateTime operator+(const QDateTime &left, const QTimeSpan &right); +Q_CORE_EXPORT QDateTime operator-(const QDateTime &left, const QTimeSpan &right); + + +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QTimeSpan &); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QTimeSpan &); +#endif // QT_NO_DATASTREAM + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DATESTRING) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QTimeSpan &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QTIMESPAN_H From 86643803cf0456996ed37114185dc5ca335c285d Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 10 Mar 2014 16:01:53 +0200 Subject: [PATCH 056/109] Moved the connection and configuration of XMPP to the log-in process. --- CMakeLists.txt | 6 --- cmake/modules/FindQxmpp.cmake | 4 +- interface/CMakeLists.txt | 1 + interface/interface_en.ts | 16 +++---- interface/src/Menu.cpp | 15 ++++++- interface/src/Menu.h | 2 + interface/src/XmppClient.cpp | 65 ++++++++++++++++++++++++++++ interface/src/XmppClient.h | 43 +++++++++++++++++++ interface/src/ui/ChatWindow.cpp | 74 ++++++++++++++++++-------------- interface/src/ui/ChatWindow.h | 6 +-- interface/src/ui/FlowLayout.cpp | 2 +- libraries/shared/CMakeLists.txt | 2 +- libraries/shared/src/qtimespan.h | 54 +++++++++++------------ 13 files changed, 208 insertions(+), 82 deletions(-) create mode 100644 interface/src/XmppClient.cpp create mode 100644 interface/src/XmppClient.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b3e1af23e8..33589ddb57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,12 +34,6 @@ if (APPLE) endif (DARWIN_VERSION GREATER 12) endif (APPLE) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") - -find_package(qxmpp REQUIRED) -add_definitions(-DQXMPP_STATIC) -include_directories(SYSTEM ${QXMPP_INCLUDE_DIR}) - # targets not supported on windows if (NOT WIN32) add_subdirectory(animation-server) diff --git a/cmake/modules/FindQxmpp.cmake b/cmake/modules/FindQxmpp.cmake index f50212d63f..13ff1ed855 100644 --- a/cmake/modules/FindQxmpp.cmake +++ b/cmake/modules/FindQxmpp.cmake @@ -29,10 +29,10 @@ else () if (QXMPP_FOUND) if (NOT QXMPP_FIND_QUIETLY) message(STATUS "Found qxmpp: ${QXMPP_LIBRARY}") - endif (NOT QXMPP_FIND_QUIETLY) + endif () else () if (QXMPP_FIND_REQUIRED) message(FATAL_ERROR "Could not find qxmpp") - endif (SIXENSE_FIND_REQUIRED) + endif () endif () endif () \ No newline at end of file diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 6ec0b6f679..2559c00207 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -182,6 +182,7 @@ target_link_libraries( ${TARGET_NAME} "${FACESHIFT_LIBRARIES}" "${ZLIB_LIBRARIES}" + "${QXMPP_LIBRARY}" Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::Network Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKit Qt5::WebKitWidgets Qt5::Xml Qt5::UiTools ) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 8198d97a8f..e5461bc1c2 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -45,7 +45,7 @@ - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 60e93b0cea..2331a1bceb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include "Application.h" @@ -161,7 +162,12 @@ Menu::Menu() : QMenu* toolsMenu = addMenu("Tools"); addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::FstUploader, 0, Application::getInstance(), SLOT(uploadFST())); - addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, 0, this, SLOT(showChat())); + + _chatAction = addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, 0, this, SLOT(showChat())); + const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); + toggleChat(); + connect(&xmppClient, SIGNAL(connected()), this, SLOT(toggleChat())); + connect(&xmppClient, SIGNAL(disconnected()), this, SLOT(toggleChat())); QMenu* viewMenu = addMenu("View"); @@ -1039,6 +1045,13 @@ void Menu::showChat() { _chatWindow->raise(); } +void Menu::toggleChat() { + _chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected()); + if (!_chatAction->isEnabled() && _chatWindow) { + _chatWindow->close(); + } +} + void Menu::audioMuteToggled() { QAction *muteAction = _actionHash.value(MenuOption::MuteAudio); muteAction->setChecked(Application::getInstance()->getAudio()->getMuted()); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cd32a9c8df..380fe2d3b7 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -144,6 +144,7 @@ private slots: void runTests(); void showMetavoxelEditor(); void showChat(); + void toggleChat(); void audioMuteToggled(); private: @@ -204,6 +205,7 @@ private: quint64 _lastAdjust; SimpleMovingAverage _fpsAverage; QAction* _loginAction; + QAction* _chatAction; }; namespace MenuOption { diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp new file mode 100644 index 0000000000..a3716c781e --- /dev/null +++ b/interface/src/XmppClient.cpp @@ -0,0 +1,65 @@ +// +// XmppClient.cpp +// interface +// +// Created by Dimitar Dobrev on 10/3/14 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include "XmppClient.h" + +const QString DEFAULT_XMPP_SERVER = "chat.highfidelity.io"; +const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io"; + +XmppClient::XmppClient() : + _xmppClient(), + _xmppMUCManager() { + AccountManager& accountManager = AccountManager::getInstance(); + connect(&accountManager, SIGNAL(accessTokenChanged()), this, SLOT(connectToServer())); + connect(&accountManager, SIGNAL(logoutComplete()), this, SLOT(disconnectFromServer())); +} + +XmppClient& XmppClient::getInstance() { + static XmppClient sharedInstance; + return sharedInstance; +} + +void XmppClient::xmppConnected() { + _publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM); + _publicChatRoom->setNickName(AccountManager::getInstance().getUsername()); + _publicChatRoom->join(); +} + +void XmppClient::xmppError(QXmppClient::Error error) { + qDebug() << "Error connnecting to XMPP for user " << AccountManager::getInstance().getUsername() << ": " << error; +} + +void XmppClient::connectToServer() { + disconnectFromServer(); + + if (_xmppClient.addExtension(&_xmppMUCManager)) { + connect(&_xmppClient, SIGNAL(connected()), this, SLOT(xmppConnected())); + connect(&_xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(xmppError(QXmppClient::Error))); + } + AccountManager& accountManager = AccountManager::getInstance(); + QString user = accountManager.getUsername(); + const QString& password = accountManager.getXMPPPassword(); + _xmppClient.connectToServer(user + "@" + DEFAULT_XMPP_SERVER, password); +} + +void XmppClient::disconnectFromServer() +{ + if (_xmppClient.isConnected()) { + _xmppClient.disconnectFromServer(); + } +} + +XmppClient::XmppClient(const XmppClient& other) { + Q_UNUSED(other); +} + +void XmppClient::operator =(XmppClient const& other) { + Q_UNUSED(other); +} diff --git a/interface/src/XmppClient.h b/interface/src/XmppClient.h new file mode 100644 index 0000000000..1ebe868424 --- /dev/null +++ b/interface/src/XmppClient.h @@ -0,0 +1,43 @@ +// +// XmppClient.h +// interface +// +// Created by Dimitar Dobrev on 10/3/14 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__XmppClient__ +#define __interface__XmppClient__ + +#include +#include +#include + +/// Generalized threaded processor for handling received inbound packets. +class XmppClient : public QObject { + Q_OBJECT + +public: + static XmppClient& getInstance(); + + QXmppClient& getXMPPClient() { return _xmppClient; } + const QXmppMucRoom* getPublicChatRoom() const { return _publicChatRoom; } + +private slots: + void xmppConnected(); + void xmppError(QXmppClient::Error error); + + void connectToServer(); + void disconnectFromServer(); + +private: + XmppClient(); + XmppClient(XmppClient const& other); // not implemented + void operator=(XmppClient const& other); // not implemented + + QXmppClient _xmppClient; + QXmppMucManager _xmppMUCManager; + QXmppMucRoom* _publicChatRoom; +}; + +#endif // __interface__XmppClient__ diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 4e4b869cda..7bcec5a2a9 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -16,17 +16,13 @@ #include #include "ChatWindow.h" -#include "ui_chatwindow.h" +#include "ui_chatWindow.h" #include "FlowLayout.h" #include "qtimespan.h" -#include -#include #include -#include +#include -const QString DEFAULT_SERVER = "chat.highfidelity.io"; -const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io"; const int NUM_MESSAGES_TO_TIME_STAMP = 20; const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); @@ -42,25 +38,34 @@ ChatWindow::ChatWindow() : ui->messagePlainTextEdit->installEventFilter(this); - ui->numOnlineLabel->hide(); - ui->usersWidget->hide(); - ui->messagesScrollArea->hide(); - ui->messagePlainTextEdit->hide(); - setAttribute(Qt::WA_DeleteOnClose); - _xmppClient.addExtension(&_xmppMUCManager); - connect(&_xmppClient, SIGNAL(connected()), this, SLOT(connected())); - connect(&_xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(error(QXmppClient::Error))); - connect(&_xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage))); - - AccountManager& accountManager = AccountManager::getInstance(); - QString user = accountManager.getUsername(); - const QString& password = accountManager.getXMPPPassword(); - _xmppClient.connectToServer(user + "@" + DEFAULT_SERVER, password); + const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); + if (xmppClient.isConnected()) { + participantsChanged(); + const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); + connect(publicChatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); + ui->connectingToXMPPLabel->hide(); + startTimerForTimeStamps(); + } + else { + ui->numOnlineLabel->hide(); + ui->usersWidget->hide(); + ui->messagesScrollArea->hide(); + ui->messagePlainTextEdit->hide(); + connect(&xmppClient, SIGNAL(connected()), this, SLOT(connected())); + } + connect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage))); } ChatWindow::~ChatWindow() { + const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); + disconnect(&xmppClient, SIGNAL(connected()), this, SLOT(connected())); + disconnect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage))); + + const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); + disconnect(publicChatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); + delete ui; } @@ -75,7 +80,8 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { QString message = ui->messagePlainTextEdit->document()->toPlainText(); if (!message.trimmed().isEmpty()) { - _xmppClient.sendMessage(_chatRoom->jid(), message); + const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); + XmppClient::getInstance().getXMPPClient().sendMessage(publicChatRoom->jid(), message); ui->messagePlainTextEdit->document()->clear(); } return true; @@ -84,7 +90,8 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { } QString ChatWindow::getParticipantName(const QString& participant) { - return participant.right(participant.count() - 1 - _chatRoom->jid().count()); + const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); + return participant.right(participant.count() - 1 - publicChatRoom->jid().count()); } void ChatWindow::addTimeStamp() { @@ -107,22 +114,25 @@ void ChatWindow::addTimeStamp() { numMessagesAfterLastTimeStamp = 0; } -void ChatWindow::connected() { - _chatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM); - connect(_chatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); - _chatRoom->setNickName(AccountManager::getInstance().getUsername()); - _chatRoom->join(); +void ChatWindow::startTimerForTimeStamps() +{ + QTimer* timer = new QTimer(this); + timer->setInterval(10 * 60 * 1000); + connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); + timer->start(); +} +void ChatWindow::connected() { ui->connectingToXMPPLabel->hide(); ui->numOnlineLabel->show(); ui->usersWidget->show(); ui->messagesScrollArea->show(); ui->messagePlainTextEdit->show(); - QTimer* timer = new QTimer(this); - timer->setInterval(10 * 60 * 1000); - connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); - timer->start(); + const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); + connect(publicChatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); + + startTimerForTimeStamps(); } void ChatWindow::timeout() { @@ -136,7 +146,7 @@ void ChatWindow::error(QXmppClient::Error error) { } void ChatWindow::participantsChanged() { - QStringList participants = _chatRoom->participants(); + QStringList participants = XmppClient::getInstance().getPublicChatRoom()->participants(); ui->numOnlineLabel->setText(tr("%1 online now:").arg(participants.count())); while (QLayoutItem* item = ui->usersWidget->layout()->takeAt(0)) { diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h index afa1947268..16f8928d7e 100644 --- a/interface/src/ui/ChatWindow.h +++ b/interface/src/ui/ChatWindow.h @@ -16,7 +16,7 @@ #include #include -#include +#include namespace Ui { class ChatWindow; @@ -34,12 +34,10 @@ protected: private: QString getParticipantName(const QString& participant); + void startTimerForTimeStamps(); void addTimeStamp(); Ui::ChatWindow* ui; - QXmppClient _xmppClient; - QXmppMucManager _xmppMUCManager; - QXmppMucRoom* _chatRoom; int numMessagesAfterLastTimeStamp; QDateTime lastMessageStamp; diff --git a/interface/src/ui/FlowLayout.cpp b/interface/src/ui/FlowLayout.cpp index 5387d9499d..c24ad6ce01 100644 --- a/interface/src/ui/FlowLayout.cpp +++ b/interface/src/ui/FlowLayout.cpp @@ -40,7 +40,7 @@ #include -#include "flowlayout.h" +#include "FlowLayout.h" //! [1] FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 40473c924a..be18a3ba45 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -32,4 +32,4 @@ if (UNIX AND NOT APPLE) target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}") endif (UNIX AND NOT APPLE) -target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Xml "${QXMPP_LIBRARY}") +target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Xml) diff --git a/libraries/shared/src/qtimespan.h b/libraries/shared/src/qtimespan.h index 03ed69abe3..83f9cdbec7 100644 --- a/libraries/shared/src/qtimespan.h +++ b/libraries/shared/src/qtimespan.h @@ -79,7 +79,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::TimeSpanFormat) class QTimeSpanPrivate; -class Q_CORE_EXPORT QTimeSpan +class QTimeSpan { public: QTimeSpan(); @@ -247,8 +247,8 @@ public: private: #ifndef QT_NO_DATASTREAM - friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QTimeSpan &); - friend Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QTimeSpan &); + friend QDataStream &operator<<(QDataStream &, const QTimeSpan &); + friend QDataStream &operator>>(QDataStream &, QTimeSpan &); #endif QSharedDataPointer d; @@ -258,40 +258,40 @@ Q_DECLARE_METATYPE(QTimeSpan); Q_DECLARE_METATYPE(Qt::TimeSpanUnit); //non-member operators -Q_CORE_EXPORT QTimeSpan operator+(const QTimeSpan &left, const QTimeSpan &right); -Q_CORE_EXPORT QTimeSpan operator-(const QTimeSpan &left, const QTimeSpan &right); -Q_CORE_EXPORT QTimeSpan operator*(const QTimeSpan &left, qreal right);//no problem -Q_CORE_EXPORT QTimeSpan operator*(const QTimeSpan &left, int right);//no problem +QTimeSpan operator+(const QTimeSpan &left, const QTimeSpan &right); +QTimeSpan operator-(const QTimeSpan &left, const QTimeSpan &right); +QTimeSpan operator*(const QTimeSpan &left, qreal right);//no problem +QTimeSpan operator*(const QTimeSpan &left, int right);//no problem inline QTimeSpan operator*(qreal left, const QTimeSpan &right) {return right * left;} // works inline QTimeSpan operator*(int left, const QTimeSpan &right) {return right * left;} // works -//Q_CORE_EXPORT QTimeSpan operator*(qreal left, const QTimeSpan &right) {return right * left;} //does not work -//Q_CORE_EXPORT QTimeSpan operator*(int left, const QTimeSpan &right) {return right * left;} //does not work -Q_CORE_EXPORT QTimeSpan operator/(const QTimeSpan &left, qreal right); -Q_CORE_EXPORT QTimeSpan operator/(const QTimeSpan &left, int right); -Q_CORE_EXPORT qreal operator/(const QTimeSpan &left, const QTimeSpan &right); -Q_CORE_EXPORT QTimeSpan operator-(const QTimeSpan &right); // Unary negation -Q_CORE_EXPORT QTimeSpan operator|(const QTimeSpan &left, const QTimeSpan &right); // Union -Q_CORE_EXPORT QTimeSpan operator&(const QTimeSpan &left, const QTimeSpan &right); // Intersection +//QTimeSpan operator*(qreal left, const QTimeSpan &right) {return right * left;} //does not work +//QTimeSpan operator*(int left, const QTimeSpan &right) {return right * left;} //does not work +QTimeSpan operator/(const QTimeSpan &left, qreal right); +QTimeSpan operator/(const QTimeSpan &left, int right); +qreal operator/(const QTimeSpan &left, const QTimeSpan &right); +QTimeSpan operator-(const QTimeSpan &right); // Unary negation +QTimeSpan operator|(const QTimeSpan &left, const QTimeSpan &right); // Union +QTimeSpan operator&(const QTimeSpan &left, const QTimeSpan &right); // Intersection // Operators that use QTimeSpan and other date/time classes -Q_CORE_EXPORT QTimeSpan operator-(const QDateTime &left, const QDateTime &right); -Q_CORE_EXPORT QTimeSpan operator-(const QDate &left, const QDate &right); -Q_CORE_EXPORT QTimeSpan operator-(const QTime &left, const QTime &right); -Q_CORE_EXPORT QDate operator+(const QDate &left, const QTimeSpan &right); -Q_CORE_EXPORT QDate operator-(const QDate &left, const QTimeSpan &right); -Q_CORE_EXPORT QTime operator+(const QTime &left, const QTimeSpan &right); -Q_CORE_EXPORT QTime operator-(const QTime &left, const QTimeSpan &right); -Q_CORE_EXPORT QDateTime operator+(const QDateTime &left, const QTimeSpan &right); -Q_CORE_EXPORT QDateTime operator-(const QDateTime &left, const QTimeSpan &right); +QTimeSpan operator-(const QDateTime &left, const QDateTime &right); +QTimeSpan operator-(const QDate &left, const QDate &right); +QTimeSpan operator-(const QTime &left, const QTime &right); +QDate operator+(const QDate &left, const QTimeSpan &right); +QDate operator-(const QDate &left, const QTimeSpan &right); +QTime operator+(const QTime &left, const QTimeSpan &right); +QTime operator-(const QTime &left, const QTimeSpan &right); +QDateTime operator+(const QDateTime &left, const QTimeSpan &right); +QDateTime operator-(const QDateTime &left, const QTimeSpan &right); #ifndef QT_NO_DATASTREAM -Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QTimeSpan &); -Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QTimeSpan &); +QDataStream &operator<<(QDataStream &, const QTimeSpan &); +QDataStream &operator>>(QDataStream &, QTimeSpan &); #endif // QT_NO_DATASTREAM #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DATESTRING) -Q_CORE_EXPORT QDebug operator<<(QDebug, const QTimeSpan &); +QDebug operator<<(QDebug, const QTimeSpan &); #endif QT_END_NAMESPACE From acb279305345cea9ecd38de908a6a1bb0294fe34 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 11 Mar 2014 02:05:17 +0200 Subject: [PATCH 057/109] Ensured messages are sent to the group chat rather than to individual users. --- interface/interface_en.ts | 8 ++++---- interface/src/ui/ChatWindow.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index e5461bc1c2..5aeb683c8c 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -45,7 +45,7 @@ - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 7bcec5a2a9..3d0c140b0d 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -78,10 +78,14 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { QKeyEvent* keyEvent = static_cast(event); if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { - QString message = ui->messagePlainTextEdit->document()->toPlainText(); - if (!message.trimmed().isEmpty()) { + QString messageText = ui->messagePlainTextEdit->document()->toPlainText().trimmed(); + if (!messageText.isEmpty()) { const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); - XmppClient::getInstance().getXMPPClient().sendMessage(publicChatRoom->jid(), message); + QXmppMessage message; + message.setTo(publicChatRoom->jid()); + message.setType(QXmppMessage::GroupChat); + message.setBody(messageText); + XmppClient::getInstance().getXMPPClient().sendPacket(message); ui->messagePlainTextEdit->document()->clear(); } return true; From 60bf473be7aa86be877cf80eabc4a5fad206467a Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 11 Mar 2014 02:17:24 +0200 Subject: [PATCH 058/109] Improved the adherence to code conventions after a code review. --- interface/interface_en.ts | 2 +- interface/src/XmppClient.cpp | 6 +++--- interface/src/ui/ChatWindow.cpp | 20 +++++++++---------- libraries/shared/CMakeLists.txt | 2 +- .../shared/src/DataServerAccountInfo.cpp | 7 ++----- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 5aeb683c8c..fbbe4752ea 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -76,7 +76,7 @@ - + %1 online now: diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index a3716c781e..de0a47c28a 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -15,7 +15,8 @@ const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io"; XmppClient::XmppClient() : _xmppClient(), - _xmppMUCManager() { + _xmppMUCManager() +{ AccountManager& accountManager = AccountManager::getInstance(); connect(&accountManager, SIGNAL(accessTokenChanged()), this, SLOT(connectToServer())); connect(&accountManager, SIGNAL(logoutComplete()), this, SLOT(disconnectFromServer())); @@ -49,8 +50,7 @@ void XmppClient::connectToServer() { _xmppClient.connectToServer(user + "@" + DEFAULT_XMPP_SERVER, password); } -void XmppClient::disconnectFromServer() -{ +void XmppClient::disconnectFromServer() { if (_xmppClient.isConnected()) { _xmppClient.disconnectFromServer(); } diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 3d0c140b0d..c857ddc8c9 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -15,13 +15,13 @@ #include #include -#include "ChatWindow.h" -#include "ui_chatWindow.h" +#include "Application.h" #include "FlowLayout.h" #include "qtimespan.h" +#include "ui_chatWindow.h" +#include "XmppClient.h" -#include -#include +#include "ChatWindow.h" const int NUM_MESSAGES_TO_TIME_STAMP = 20; @@ -30,7 +30,8 @@ const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); ChatWindow::ChatWindow() : QDialog(Application::getInstance()->getGLWidget(), Qt::Tool), ui(new Ui::ChatWindow), - numMessagesAfterLastTimeStamp(0) { + numMessagesAfterLastTimeStamp(0) +{ ui->setupUi(this); FlowLayout* flowLayout = new FlowLayout(0, 4, 4); @@ -47,8 +48,7 @@ ChatWindow::ChatWindow() : connect(publicChatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); ui->connectingToXMPPLabel->hide(); startTimerForTimeStamps(); - } - else { + } else { ui->numOnlineLabel->hide(); ui->usersWidget->hide(); ui->messagesScrollArea->hide(); @@ -118,8 +118,7 @@ void ChatWindow::addTimeStamp() { numMessagesAfterLastTimeStamp = 0; } -void ChatWindow::startTimerForTimeStamps() -{ +void ChatWindow::startTimerForTimeStamps() { QTimer* timer = new QTimer(this); timer->setInterval(10 * 60 * 1000); connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); @@ -191,8 +190,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { ++numMessagesAfterLastTimeStamp; if (message.stamp().isValid()) { lastMessageStamp = message.stamp().toLocalTime(); - } - else { + } else { lastMessageStamp = QDateTime::currentDateTime(); } } diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index be18a3ba45..3af7272cc1 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -32,4 +32,4 @@ if (UNIX AND NOT APPLE) target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}") endif (UNIX AND NOT APPLE) -target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Xml) +target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets) diff --git a/libraries/shared/src/DataServerAccountInfo.cpp b/libraries/shared/src/DataServerAccountInfo.cpp index 2bcd2dbcd0..1884517515 100644 --- a/libraries/shared/src/DataServerAccountInfo.cpp +++ b/libraries/shared/src/DataServerAccountInfo.cpp @@ -56,12 +56,9 @@ void DataServerAccountInfo::setUsername(const QString& username) { } } -void DataServerAccountInfo::setXMPPPassword(const QString& xmppPassword) -{ +void DataServerAccountInfo::setXMPPPassword(const QString& xmppPassword) { if (_xmppPassword != xmppPassword) { _xmppPassword = xmppPassword; - - qDebug() << "XMPP password changed to " << xmppPassword; } } @@ -73,4 +70,4 @@ QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { in >> info._accessToken >> info._username >> info._xmppPassword; return in; -} \ No newline at end of file +} From ed90e45c9640c0efff77591137dddf7ba032d229 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 11 Mar 2014 15:46:07 +0200 Subject: [PATCH 059/109] Ensured messages are always aligned at the top to user names, and users onine are deleted. --- interface/src/ui/ChatWindow.cpp | 7 +++++-- interface/ui/chatWindow.ui | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index c857ddc8c9..ef379a086c 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -153,6 +153,7 @@ void ChatWindow::participantsChanged() { ui->numOnlineLabel->setText(tr("%1 online now:").arg(participants.count())); while (QLayoutItem* item = ui->usersWidget->layout()->takeAt(0)) { + delete item->widget(); delete item; } foreach (const QString& participant, participants) { @@ -173,13 +174,15 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { font.setBold(true); userLabel->setFont(font); userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - userLabel->setStyleSheet("padding: 4px;"); + userLabel->setStyleSheet("padding: 2px;"); + userLabel->setAlignment(Qt::AlignTop); QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "\\1")); messageLabel->setWordWrap(true); messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); messageLabel->setOpenExternalLinks(true); - messageLabel->setStyleSheet("padding: 4px;"); + messageLabel->setStyleSheet("padding: 2px;"); + messageLabel->setAlignment(Qt::AlignTop); ui->messagesFormLayout->addRow(userLabel, messageLabel); ui->messagesFormLayout->parentWidget()->updateGeometry(); diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 1723e72665..bebfc12674 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -95,7 +95,7 @@ 0 - 4 + 0 4 From e10dacdbfe1ec99662f21987a42c04090db4274c Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 11 Mar 2014 17:50:51 +0200 Subject: [PATCH 060/109] Changed the searcning for qxmpp to include debug libraries as well. --- cmake/modules/FindQxmpp.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindQxmpp.cmake b/cmake/modules/FindQxmpp.cmake index 13ff1ed855..415b184a65 100644 --- a/cmake/modules/FindQxmpp.cmake +++ b/cmake/modules/FindQxmpp.cmake @@ -21,7 +21,7 @@ else () find_path(QXMPP_INCLUDE_DIR QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${QXMPP_SEARCH_DIRS}) - find_library(QXMPP_LIBRARY NAMES qxmpp qxmpp0 PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) + find_library(QXMPP_LIBRARY NAMES qxmpp qxmpp0 qxmpp_d PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIR QXMPP_LIBRARY) From 858e9fa7df9b1376fd0642213f7e93265a8f1b40 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 11 Mar 2014 18:54:39 +0200 Subject: [PATCH 061/109] Ensured sent chat messages are always wholly visible. --- interface/src/ui/ChatWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index ef379a086c..0ebfb3d4dd 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -189,6 +189,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { Application::processEvents(); QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); verticalScrollBar->setSliderPosition(verticalScrollBar->maximum()); + messageLabel->updateGeometry(); ++numMessagesAfterLastTimeStamp; if (message.stamp().isValid()) { From fe2f9f52534729f0aa9d373c0ce05736a45b9415 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 11 Mar 2014 19:26:14 +0200 Subject: [PATCH 062/109] Prevented empty time stamps - when less then a minute has passed - from appearing. --- interface/interface_en.ts | 2 +- interface/src/ui/ChatWindow.cpp | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index fbbe4752ea..ed8a2155e4 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -76,7 +76,7 @@ - + %1 online now: diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 0ebfb3d4dd..09b50ebf46 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -109,13 +109,15 @@ void ChatWindow::addTimeStamp() { } } timeString.chop(1); - QLabel* timeLabel = new QLabel(timeString); - timeLabel->setStyleSheet("color: palette(shadow);" - "background-color: palette(highlight);" - "padding: 4px;"); - timeLabel->setAlignment(Qt::AlignHCenter); - ui->messagesFormLayout->addRow(timeLabel); - numMessagesAfterLastTimeStamp = 0; + if (!timeString.isEmpty()) { + QLabel* timeLabel = new QLabel(timeString); + timeLabel->setStyleSheet("color: palette(shadow);" + "background-color: palette(highlight);" + "padding: 4px;"); + timeLabel->setAlignment(Qt::AlignHCenter); + ui->messagesFormLayout->addRow(timeLabel); + numMessagesAfterLastTimeStamp = 0; + } } void ChatWindow::startTimerForTimeStamps() { From 52c8ccb6981dcde6b01a9e39054b21cbb8505588 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 11 Mar 2014 20:14:31 +0200 Subject: [PATCH 063/109] Added a define to ensure the build works without qxmpp. --- interface/CMakeLists.txt | 14 +++++++++----- interface/src/Menu.cpp | 6 ++++++ interface/src/XmppClient.cpp | 4 ++++ interface/src/XmppClient.h | 4 ++++ interface/src/ui/ChatWindow.cpp | 18 ++++++++++++++---- interface/src/ui/ChatWindow.h | 8 ++++++++ 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 2559c00207..dd4e1dca18 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -67,10 +67,6 @@ endforeach(EXTERNAL_SOURCE_SUBDIR) find_package(Qt5 COMPONENTS Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml UiTools) -find_package(Qxmpp REQUIRED) -add_definitions(-DQXMPP_STATIC) -include_directories(SYSTEM ${QXMPP_INCLUDE_DIR}) - # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) # have qt5 wrap them and generate the appropriate header files @@ -132,6 +128,7 @@ find_package(LibOVR) find_package(Sixense) find_package(Visage) find_package(ZLIB) +find_package(Qxmpp) # include the Sixense library for Razer Hydra if available if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE) @@ -171,6 +168,14 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) target_link_libraries(${TARGET_NAME} "${LIBOVR_LIBRARIES}") endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) +# and with qxmpp for chat +if (QXMPP_FOUND AND NOT DISABLE_QXMPP) + add_definitions(-DHAVE_QXMPP -DQXMPP_STATIC) + include_directories(SYSTEM ${QXMPP_INCLUDE_DIR}) + + target_link_libraries(${TARGET_NAME} "${QXMPP_LIBRARY}") +endif (QXMPP_FOUND AND NOT DISABLE_QXMPP) + # include headers for interface and InterfaceConfig. include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes") @@ -182,7 +187,6 @@ target_link_libraries( ${TARGET_NAME} "${FACESHIFT_LIBRARIES}" "${ZLIB_LIBRARIES}" - "${QXMPP_LIBRARY}" Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::Network Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKit Qt5::WebKitWidgets Qt5::Xml Qt5::UiTools ) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2331a1bceb..5f6c541bc5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -164,10 +164,14 @@ Menu::Menu() : addActionToQMenuAndActionHash(toolsMenu, MenuOption::FstUploader, 0, Application::getInstance(), SLOT(uploadFST())); _chatAction = addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, 0, this, SLOT(showChat())); +#ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); toggleChat(); connect(&xmppClient, SIGNAL(connected()), this, SLOT(toggleChat())); connect(&xmppClient, SIGNAL(disconnected()), this, SLOT(toggleChat())); +#else + _chatAction->setEnabled(false); +#endif QMenu* viewMenu = addMenu("View"); @@ -1046,10 +1050,12 @@ void Menu::showChat() { } void Menu::toggleChat() { +#ifdef HAVE_QXMPP _chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected()); if (!_chatAction->isEnabled() && _chatWindow) { _chatWindow->close(); } +#endif } void Menu::audioMuteToggled() { diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index de0a47c28a..df8cf34874 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -6,6 +6,8 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#ifdef HAVE_QXMPP + #include #include "XmppClient.h" @@ -63,3 +65,5 @@ XmppClient::XmppClient(const XmppClient& other) { void XmppClient::operator =(XmppClient const& other) { Q_UNUSED(other); } + +#endif diff --git a/interface/src/XmppClient.h b/interface/src/XmppClient.h index 1ebe868424..905d6e64fb 100644 --- a/interface/src/XmppClient.h +++ b/interface/src/XmppClient.h @@ -6,6 +6,8 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#ifdef HAVE_QXMPP + #ifndef __interface__XmppClient__ #define __interface__XmppClient__ @@ -41,3 +43,5 @@ private: }; #endif // __interface__XmppClient__ + +#endif diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 09b50ebf46..e7b78a897a 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -40,7 +40,7 @@ ChatWindow::ChatWindow() : ui->messagePlainTextEdit->installEventFilter(this); setAttribute(Qt::WA_DeleteOnClose); - +#ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); if (xmppClient.isConnected()) { participantsChanged(); @@ -56,16 +56,18 @@ ChatWindow::ChatWindow() : connect(&xmppClient, SIGNAL(connected()), this, SLOT(connected())); } connect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage))); +#endif } ChatWindow::~ChatWindow() { +#ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); disconnect(&xmppClient, SIGNAL(connected()), this, SLOT(connected())); disconnect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage))); const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); disconnect(publicChatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); - +#endif delete ui; } @@ -80,12 +82,14 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { QString messageText = ui->messagePlainTextEdit->document()->toPlainText().trimmed(); if (!messageText.isEmpty()) { +#ifdef HAVE_QXMPP const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); QXmppMessage message; message.setTo(publicChatRoom->jid()); message.setType(QXmppMessage::GroupChat); message.setBody(messageText); XmppClient::getInstance().getXMPPClient().sendPacket(message); +#endif ui->messagePlainTextEdit->document()->clear(); } return true; @@ -93,10 +97,12 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { return false; } +#ifdef HAVE_QXMPP QString ChatWindow::getParticipantName(const QString& participant) { const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); return participant.right(participant.count() - 1 - publicChatRoom->jid().count()); } +#endif void ChatWindow::addTimeStamp() { QTimeSpan timePassed = QDateTime::currentDateTime() - lastMessageStamp; @@ -133,10 +139,10 @@ void ChatWindow::connected() { ui->usersWidget->show(); ui->messagesScrollArea->show(); ui->messagePlainTextEdit->show(); - +#ifdef HAVE_QXMPP const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); connect(publicChatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); - +#endif startTimerForTimeStamps(); } @@ -146,6 +152,8 @@ void ChatWindow::timeout() { } } +#ifdef HAVE_QXMPP + void ChatWindow::error(QXmppClient::Error error) { ui->connectingToXMPPLabel->setText(QString::number(error)); } @@ -200,3 +208,5 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { lastMessageStamp = QDateTime::currentDateTime(); } } + +#endif diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h index 16f8928d7e..9250da8933 100644 --- a/interface/src/ui/ChatWindow.h +++ b/interface/src/ui/ChatWindow.h @@ -15,9 +15,13 @@ #include +#ifdef HAVE_QXMPP + #include #include +#endif + namespace Ui { class ChatWindow; } @@ -33,7 +37,9 @@ protected: bool eventFilter(QObject* sender, QEvent* event); private: +#ifdef HAVE_QXMPP QString getParticipantName(const QString& participant); +#endif void startTimerForTimeStamps(); void addTimeStamp(); @@ -44,9 +50,11 @@ private: private slots: void connected(); void timeout(); +#ifdef HAVE_QXMPP void error(QXmppClient::Error error); void participantsChanged(); void messageReceived(const QXmppMessage& message); +#endif }; #endif /* defined(__interface__ChatWindow__) */ From f427f524bfce1d11c5ace7a2da951cf5bc2f4aea Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 12 Mar 2014 00:01:27 +0200 Subject: [PATCH 064/109] Patched the Qt CMake function for auto-translations so that *.ts files are not deleted on clean. --- .../modules/FindQt5LinguistToolsMacros.cmake | 114 ++++++++++++++++++ interface/CMakeLists.txt | 7 +- interface/interface_en.ts | 16 +-- 3 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 cmake/modules/FindQt5LinguistToolsMacros.cmake diff --git a/cmake/modules/FindQt5LinguistToolsMacros.cmake b/cmake/modules/FindQt5LinguistToolsMacros.cmake new file mode 100644 index 0000000000..cd2d1eb74f --- /dev/null +++ b/cmake/modules/FindQt5LinguistToolsMacros.cmake @@ -0,0 +1,114 @@ +#============================================================================= +# Copyright 2005-2011 Kitware, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Kitware, Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + + + +function(QT5_CREATE_TRANSLATION_CUSTOM _qm_files) + set(options) + set(oneValueArgs) + set(multiValueArgs OPTIONS) + + cmake_parse_arguments(_LUPDATE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(_lupdate_files ${_LUPDATE_UNPARSED_ARGUMENTS}) + set(_lupdate_options ${_LUPDATE_OPTIONS}) + + set(_my_sources) + set(_my_tsfiles) + foreach(_file ${_lupdate_files}) + get_filename_component(_ext ${_file} EXT) + get_filename_component(_abs_FILE ${_file} ABSOLUTE) + if(_ext MATCHES "ts") + list(APPEND _my_tsfiles ${_abs_FILE}) + else() + list(APPEND _my_sources ${_abs_FILE}) + endif() + endforeach() + set(_my_temptsfiles) + foreach(_ts_file ${_my_tsfiles}) + if(_my_sources) + # make a list file to call lupdate on, so we don't make our commands too + # long for some systems + get_filename_component(_ts_name ${_ts_file} NAME_WE) + set(_ts_lst_file "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_ts_name}_lst_file") + set(_lst_file_srcs) + foreach(_lst_file_src ${_my_sources}) + set(_lst_file_srcs "${_lst_file_src}\n${_lst_file_srcs}") + endforeach() + + get_directory_property(_inc_DIRS INCLUDE_DIRECTORIES) + foreach(_pro_include ${_inc_DIRS}) + get_filename_component(_abs_include "${_pro_include}" ABSOLUTE) + set(_lst_file_srcs "-I${_pro_include}\n${_lst_file_srcs}") + endforeach() + + file(WRITE ${_ts_lst_file} "${_lst_file_srcs}") + endif() + get_filename_component(_ts_nm ${_ts_file} NAME) + set(_tmpts_file "${CMAKE_CURRENT_BINARY_DIR}/${_ts_nm}") + list(APPEND _my_temptsfiles ${_tmpts_file}) + get_source_file_property(_qm_output_location ${_ts_file} OUTPUT_LOCATION) + add_custom_command( + OUTPUT ${_tmpts_file} + COMMAND ${Qt5_LUPDATE_EXECUTABLE} + ARGS ${_lupdate_options} "@${_ts_lst_file}" -ts ${_ts_file} + COMMAND ${CMAKE_COMMAND} -E copy ${_ts_file} ${_tmpts_file} + DEPENDS ${_my_sources} ${_ts_lst_file} VERBATIM) + if( _qm_output_location ) + set_property(SOURCE ${_tmpts_file} PROPERTY OUTPUT_LOCATION ${_qm_output_location}) + endif() + endforeach() + qt5_add_translation(${_qm_files} ${_my_temptsfiles}) + set(${_qm_files} ${${_qm_files}} PARENT_SCOPE) +endfunction() + + +function(QT5_ADD_TRANSLATION _qm_files) + foreach(_current_FILE ${ARGN}) + get_filename_component(_abs_FILE ${_current_FILE} ABSOLUTE) + get_filename_component(qm ${_abs_FILE} NAME_WE) + get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION) + if(output_location) + file(MAKE_DIRECTORY "${output_location}") + set(qm "${output_location}/${qm}.qm") + else() + set(qm "${CMAKE_CURRENT_BINARY_DIR}/${qm}.qm") + endif() + + add_custom_command(OUTPUT ${qm} + COMMAND ${Qt5_LRELEASE_EXECUTABLE} + ARGS ${_abs_FILE} -qm ${qm} + DEPENDS ${_abs_FILE} VERBATIM + ) + list(APPEND ${_qm_files} ${qm}) + endforeach() + set(${_qm_files} ${${_qm_files}} PARENT_SCOPE) +endfunction() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index dd4e1dca18..d049fc0154 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -10,8 +10,6 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros") set(TARGET_NAME interface) project(${TARGET_NAME}) -find_package(Qt5LinguistTools REQUIRED) - # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") set(FACESHIFT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift") @@ -19,6 +17,9 @@ set(LIBOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/oculus") set(SIXENSE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense") set(VISAGE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/visage") +find_package(Qt5LinguistTools REQUIRED) +find_package(Qt5LinguistToolsMacros) + if (DEFINED ENV{JOB_ID}) set(BUILD_SEQ $ENV{JOB_ID}) else () @@ -77,7 +78,7 @@ set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}") set(QM ${TARGET_NAME}_en.qm) set(TS ${TARGET_NAME}_en.ts) -qt5_create_translation(${QM} ${INTERFACE_SRCS} ${QT_UI_FILES} ${TS}) +qt5_create_translation_custom(${QM} ${INTERFACE_SRCS} ${QT_UI_FILES} ${TS}) if (APPLE) # configure CMake to use a custom Info.plist diff --git a/interface/interface_en.ts b/interface/interface_en.ts index ed8a2155e4..13ed5b968b 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -45,7 +45,7 @@ - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file From 029b25ad29c73fea41c0afde61b57ecf3b370be2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 16:45:15 -0700 Subject: [PATCH 065/109] make domain server less noisy about Agent assignments --- domain-server/src/DomainServer.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index e5979c561f..7e37c9b397 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -544,8 +544,15 @@ void DomainServer::readAvailableDatagrams() { // construct the requested assignment from the packet data Assignment requestAssignment(receivedPacket); - qDebug() << "Received a request for assignment type" << requestAssignment.getType() - << "from" << senderSockAddr; + // Suppress these for Assignment::AgentType to 1 per second... + static quint64 lastMessage = usecTimestampNow(); + quint64 timeNow = usecTimestampNow(); + const quint64 NOISY_TIME_ELAPSED = USECS_PER_SECOND; + if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastMessage) > NOISY_TIME_ELAPSED) { + qDebug() << "Received a request for assignment type" << requestAssignment.getType() + << "from" << senderSockAddr; + lastMessage = timeNow; + } SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); From 00176254424d0f4c75cbe4c6002ba457c821ec8a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 16:48:49 -0700 Subject: [PATCH 066/109] fix unix compiler issue --- libraries/octree/src/OctreeSceneStats.cpp | 4 ++++ libraries/octree/src/OctreeSceneStats.h | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 7d1f3f0cec..ad7f774377 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -18,6 +18,10 @@ #include "OctreeSceneStats.h" +const uint16_t MAX_MISSING_SEQUENCE = 100; /// how many items in our _missingSequenceNumbers before we start to prune them +const uint16_t MAX_MISSING_SEQUENCE_OLD_AGE = 1000; /// age we allow items in _missingSequenceNumbers to be before pruning + + const int samples = 100; OctreeSceneStats::OctreeSceneStats() : _isReadyToSend(false), diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index 25bc5e2c21..249ab376c1 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -257,9 +257,6 @@ private: unsigned long _incomingBytes; unsigned long _incomingWastedBytes; - const uint16_t MAX_MISSING_SEQUENCE = 100; /// how many items in our _missingSequenceNumbers before we start to prune them - const uint16_t MAX_MISSING_SEQUENCE_OLD_AGE = 1000; /// age we allow items in _missingSequenceNumbers to be before pruning - uint16_t _incomingLastSequence; /// last incoming sequence number unsigned int _incomingLikelyLost; /// count of packets likely lost, may be off by _incomingReallyLate count unsigned int _incomingRecovered; /// packets that were late, and we had in our missing list, we consider recovered From 3c24121f5c7953ea9d691f0af6dfe039427b700d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 16:54:03 -0700 Subject: [PATCH 067/109] more noise suppression --- domain-server/src/DomainServer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 7e37c9b397..68601fd007 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -569,8 +569,10 @@ void DomainServer::readAvailableDatagrams() { nodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } else { - qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() - << "from" << senderSockAddr; + if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastMessage) > NOISY_TIME_ELAPSED) { + qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() + << "from" << senderSockAddr; + } } } } From 1d17e750439659084c718f259090c5481d045722 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 16:56:41 -0700 Subject: [PATCH 068/109] more noise suppression --- domain-server/src/DomainServer.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 68601fd007..6410737594 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -547,11 +547,12 @@ void DomainServer::readAvailableDatagrams() { // Suppress these for Assignment::AgentType to 1 per second... static quint64 lastMessage = usecTimestampNow(); quint64 timeNow = usecTimestampNow(); - const quint64 NOISY_TIME_ELAPSED = USECS_PER_SECOND; + const quint64 NOISY_TIME_ELAPSED = 10 * USECS_PER_SECOND; + bool noisyMessage = false; if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastMessage) > NOISY_TIME_ELAPSED) { qDebug() << "Received a request for assignment type" << requestAssignment.getType() << "from" << senderSockAddr; - lastMessage = timeNow; + noisyMessage = true; } SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); @@ -572,8 +573,13 @@ void DomainServer::readAvailableDatagrams() { if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastMessage) > NOISY_TIME_ELAPSED) { qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() << "from" << senderSockAddr; + noisyMessage = true; } } + + if (noisyMessage) { + lastMessage = timeNow; + } } } } From 214866414c6b21eede207a9e87c4d42f81feb062 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 17:09:44 -0700 Subject: [PATCH 069/109] tweak names --- domain-server/src/DomainServer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6410737594..8a95a25e23 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -545,11 +545,11 @@ void DomainServer::readAvailableDatagrams() { Assignment requestAssignment(receivedPacket); // Suppress these for Assignment::AgentType to 1 per second... - static quint64 lastMessage = usecTimestampNow(); + static quint64 lastNoisyMessage = usecTimestampNow(); quint64 timeNow = usecTimestampNow(); - const quint64 NOISY_TIME_ELAPSED = 10 * USECS_PER_SECOND; + const quint64 NOISY_TIME_ELAPSED = 5 * USECS_PER_SECOND; bool noisyMessage = false; - if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastMessage) > NOISY_TIME_ELAPSED) { + if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) { qDebug() << "Received a request for assignment type" << requestAssignment.getType() << "from" << senderSockAddr; noisyMessage = true; @@ -570,7 +570,7 @@ void DomainServer::readAvailableDatagrams() { nodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } else { - if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastMessage) > NOISY_TIME_ELAPSED) { + if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) { qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() << "from" << senderSockAddr; noisyMessage = true; @@ -578,7 +578,7 @@ void DomainServer::readAvailableDatagrams() { } if (noisyMessage) { - lastMessage = timeNow; + lastNoisyMessage = timeNow; } } } From bf9929e64d30840e5b013ce0a271ef243cda09af Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Mar 2014 17:16:49 -0700 Subject: [PATCH 070/109] Since the bucky balls are showing up in the hot spots list when profiling, don't simulate them if they're disabled. --- interface/src/Application.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 032e849f0a..bff223e5e3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1241,8 +1241,10 @@ void Application::idle() { _idleLoopStdev.reset(); } - _buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData()); - + if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) { + _buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData()); + } + // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. idleTimer->start(2); } From aa8c2fc8cb0ed10348dd1cfecd95b7dcd1dc2d74 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 11 Mar 2014 17:26:58 -0700 Subject: [PATCH 071/109] use radians instead of degrees (almost) everywhere --- CMakeLists.txt | 1 + animation-server/src/AnimationServer.cpp | 20 +-- assignment-client/src/audio/AudioMixer.cpp | 4 +- interface/src/Application.cpp | 120 +++++++++--------- interface/src/Application.h | 4 +- interface/src/Audio.cpp | 3 +- interface/src/Camera.cpp | 2 +- interface/src/Camera.h | 2 +- interface/src/Environment.cpp | 4 +- interface/src/Menu.cpp | 4 +- interface/src/MetavoxelSystem.h | 2 +- interface/src/Stars.cpp | 3 +- interface/src/Util.cpp | 48 +++---- interface/src/Util.h | 4 +- interface/src/avatar/Avatar.cpp | 30 ++--- interface/src/avatar/FaceModel.cpp | 9 +- interface/src/avatar/Head.cpp | 6 +- interface/src/avatar/MyAvatar.cpp | 79 ++++++------ interface/src/avatar/MyAvatar.h | 10 +- interface/src/avatar/SkeletonModel.cpp | 7 +- interface/src/devices/Faceshift.cpp | 4 +- interface/src/devices/Faceshift.h | 5 +- interface/src/devices/OculusManager.cpp | 9 +- interface/src/devices/OculusManager.h | 3 + interface/src/devices/SixenseManager.cpp | 2 +- interface/src/devices/TV3DManager.cpp | 4 +- interface/src/renderer/FBXReader.cpp | 21 +-- interface/src/renderer/FBXReader.h | 4 +- interface/src/renderer/GeometryCache.cpp | 6 +- interface/src/renderer/Model.cpp | 12 +- interface/src/starfield/Generator.cpp | 3 +- interface/src/starfield/renderer/Renderer.cpp | 2 +- interface/src/ui/MetavoxelEditor.cpp | 6 +- interface/src/world.h | 4 - libraries/audio/src/Sound.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 8 +- libraries/avatars/src/AvatarData.h | 10 +- libraries/avatars/src/HeadData.cpp | 7 +- libraries/avatars/src/HeadData.h | 24 ++-- libraries/metavoxels/src/MetavoxelData.h | 2 +- libraries/octree/src/ViewFrustum.cpp | 6 +- libraries/octree/src/ViewFrustum.h | 2 +- libraries/script-engine/src/EventTypes.cpp | 5 +- libraries/script-engine/src/EventTypes.h | 2 + libraries/script-engine/src/Quat.cpp | 8 +- libraries/script-engine/src/Quat.h | 6 +- libraries/shared/src/SharedUtil.cpp | 46 +++---- libraries/shared/src/SharedUtil.h | 19 +-- tests/physics/src/CollisionInfoTests.cpp | 4 +- tests/physics/src/PhysicsTestUtil.h | 2 - tests/physics/src/ShapeColliderTests.cpp | 8 +- 51 files changed, 302 insertions(+), 306 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 33589ddb57..4674df40de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ if (WIN32) endif (WIN32) project(hifi) +add_definitions(-DGLM_FORCE_RADIANS) if (WIN32) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp index a013eaaf5c..410ce22f33 100644 --- a/animation-server/src/AnimationServer.cpp +++ b/animation-server/src/AnimationServer.cpp @@ -78,9 +78,9 @@ glm::vec3 bugDirection = glm::vec3(0, 0, 1); const int VOXELS_PER_BUG = 18; glm::vec3 bugPathCenter = glm::vec3(0.25f,0.15f,0.25f); // glm::vec3(BUG_VOXEL_SIZE * 150.0, BUG_VOXEL_SIZE * 30.0, BUG_VOXEL_SIZE * 150.0); float bugPathRadius = 0.2f; //BUG_VOXEL_SIZE * 140.0; -float bugPathTheta = 0.0 * PI_OVER_180; -float bugRotation = 0.0 * PI_OVER_180; -float bugAngleDelta = 0.2 * PI_OVER_180; +float bugPathTheta = 0.0f * RADIANS_PER_DEGREE; +float bugRotation = 0.0f * RADIANS_PER_DEGREE; +float bugAngleDelta = 0.2f * RADIANS_PER_DEGREE; bool moveBugInLine = false; class BugPart { @@ -160,11 +160,11 @@ static void renderMovingBug() { bugPosition.z += (bugDirection.z * BUG_VOXEL_SIZE); // Check boundaries - if (bugPosition.z > 1.0) { - bugDirection.z = -1; + if (bugPosition.z > 1.0f) { + bugDirection.z = -1.f; } if (bugPosition.z < BUG_VOXEL_SIZE) { - bugDirection.z = 1; + bugDirection.z = 1.f; } } else { @@ -174,9 +174,9 @@ static void renderMovingBug() { bugRotation -= bugAngleDelta; // rotate slightly // If we loop past end of circle, just reset back into normal range - if (bugPathTheta > (360.0f * PI_OVER_180)) { - bugPathTheta = 0; - bugRotation = 0; + if (bugPathTheta > TWO_PI) { + bugPathTheta = 0.f; + bugRotation = 0.f; } float x = bugPathCenter.x + bugPathRadius * cos(bugPathTheta); @@ -225,7 +225,7 @@ static void sendVoxelBlinkMessage() { VoxelDetail detail; detail.s = BEACON_SIZE; - glm::vec3 position = glm::vec3(0, 0, detail.s); + glm::vec3 position = glm::vec3(0.f, 0.f, detail.s); detail.x = detail.s * floor(position.x / detail.s); detail.y = detail.s * floor(position.y / detail.s); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 900de817fa..812acc8d70 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -114,7 +114,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f; float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + - (OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f)); + (OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / PI_OVER_TWO)); // multiply the current attenuation coefficient by the calculated off axis coefficient attenuationCoefficient *= offAxisCoefficient; @@ -148,7 +148,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf // figure out the number of samples of delay and the ratio of the amplitude // in the weak channel for audio spatialization - float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource))); + float sinRatio = fabsf(sinf(bearingRelativeAngleToSource)); numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 28285cd7da..b88dee244e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -507,7 +507,7 @@ void Application::paintGL() { float headHeight = _myAvatar->getHead()->calculateAverageEyePosition().y - _myAvatar->getPosition().y; _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale()); _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0)); - _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); + _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); } // Update camera position @@ -744,7 +744,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (!_myAvatar->getDriveKeys(UP)) { _myAvatar->jump(); } - _myAvatar->setDriveKeys(UP, 1); + _myAvatar->setDriveKeys(UP, 1.f); break; case Qt::Key_Asterisk: @@ -752,11 +752,11 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_C: - _myAvatar->setDriveKeys(DOWN, 1); + _myAvatar->setDriveKeys(DOWN, 1.f); break; case Qt::Key_W: - _myAvatar->setDriveKeys(FWD, 1); + _myAvatar->setDriveKeys(FWD, 1.f); break; case Qt::Key_S: @@ -765,7 +765,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } else if (!isShifted && isMeta) { takeSnapshot(); } else { - _myAvatar->setDriveKeys(BACK, 1); + _myAvatar->setDriveKeys(BACK, 1.f); } break; @@ -783,12 +783,12 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Atmosphere); } else { - _myAvatar->setDriveKeys(ROT_LEFT, 1); + _myAvatar->setDriveKeys(ROT_LEFT, 1.f); } break; case Qt::Key_D: - _myAvatar->setDriveKeys(ROT_RIGHT, 1); + _myAvatar->setDriveKeys(ROT_RIGHT, 1.f); break; case Qt::Key_Return: @@ -800,19 +800,19 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Up: - _myAvatar->setDriveKeys(isShifted ? UP : FWD, 1); + _myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.f); break; case Qt::Key_Down: - _myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1); + _myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.f); break; case Qt::Key_Left: - _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1); + _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.f); break; case Qt::Key_Right: - _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1); + _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.f); break; case Qt::Key_I: @@ -946,47 +946,47 @@ void Application::keyReleaseEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_E: - _myAvatar->setDriveKeys(UP, 0); + _myAvatar->setDriveKeys(UP, 0.f); break; case Qt::Key_C: - _myAvatar->setDriveKeys(DOWN, 0); + _myAvatar->setDriveKeys(DOWN, 0.f); break; case Qt::Key_W: - _myAvatar->setDriveKeys(FWD, 0); + _myAvatar->setDriveKeys(FWD, 0.f); break; case Qt::Key_S: - _myAvatar->setDriveKeys(BACK, 0); + _myAvatar->setDriveKeys(BACK, 0.f); break; case Qt::Key_A: - _myAvatar->setDriveKeys(ROT_LEFT, 0); + _myAvatar->setDriveKeys(ROT_LEFT, 0.f); break; case Qt::Key_D: - _myAvatar->setDriveKeys(ROT_RIGHT, 0); + _myAvatar->setDriveKeys(ROT_RIGHT, 0.f); break; case Qt::Key_Up: - _myAvatar->setDriveKeys(FWD, 0); - _myAvatar->setDriveKeys(UP, 0); + _myAvatar->setDriveKeys(FWD, 0.f); + _myAvatar->setDriveKeys(UP, 0.f); break; case Qt::Key_Down: - _myAvatar->setDriveKeys(BACK, 0); - _myAvatar->setDriveKeys(DOWN, 0); + _myAvatar->setDriveKeys(BACK, 0.f); + _myAvatar->setDriveKeys(DOWN, 0.f); break; case Qt::Key_Left: - _myAvatar->setDriveKeys(LEFT, 0); - _myAvatar->setDriveKeys(ROT_LEFT, 0); + _myAvatar->setDriveKeys(LEFT, 0.f); + _myAvatar->setDriveKeys(ROT_LEFT, 0.f); break; case Qt::Key_Right: - _myAvatar->setDriveKeys(RIGHT, 0); - _myAvatar->setDriveKeys(ROT_RIGHT, 0); + _myAvatar->setDriveKeys(RIGHT, 0.f); + _myAvatar->setDriveKeys(ROT_RIGHT, 0.f); break; default: @@ -1488,7 +1488,7 @@ void Application::init() { 3.0f * TREE_SCALE / 2.0f)); _sharedVoxelSystemViewFrustum.setNearClip(TREE_SCALE / 2.0f); _sharedVoxelSystemViewFrustum.setFarClip(3.0f * TREE_SCALE / 2.0f); - _sharedVoxelSystemViewFrustum.setFieldOfView(90); + _sharedVoxelSystemViewFrustum.setFieldOfView(90.f); _sharedVoxelSystemViewFrustum.setOrientation(glm::quat()); _sharedVoxelSystemViewFrustum.calculate(); _sharedVoxelSystem.setViewFrustum(&_sharedVoxelSystemViewFrustum); @@ -2120,7 +2120,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { // We will use these below, from either the camera or head vectors calculated above glm::vec3 position(camera.getPosition()); - float fov = camera.getFieldOfView(); + float fov = camera.getFieldOfView(); // degrees float nearClip = camera.getNearClip(); float farClip = camera.getFarClip(); float aspectRatio = camera.getAspectRatio(); @@ -2133,7 +2133,7 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { // Also make sure it's got the correct lens details from the camera viewFrustum.setAspectRatio(aspectRatio); - viewFrustum.setFieldOfView(fov); + viewFrustum.setFieldOfView(fov); // degrees viewFrustum.setNearClip(nearClip); viewFrustum.setFarClip(farClip); viewFrustum.setEyeOffsetPosition(camera.getEyeOffsetPosition()); @@ -2195,7 +2195,7 @@ void Application::updateShadowMap() { glPushMatrix(); glLoadIdentity(); glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + 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); @@ -2274,7 +2274,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glm::vec3 eyeOffsetPos = whichCamera.getEyeOffsetPosition(); glm::quat eyeOffsetOrient = whichCamera.getEyeOffsetOrientation(); glm::vec3 eyeOffsetAxis = glm::axis(eyeOffsetOrient); - glRotatef(-glm::angle(eyeOffsetOrient), eyeOffsetAxis.x, eyeOffsetAxis.y, eyeOffsetAxis.z); + glRotatef(-glm::degrees(glm::angle(eyeOffsetOrient)), eyeOffsetAxis.x, eyeOffsetAxis.y, eyeOffsetAxis.z); glTranslatef(-eyeOffsetPos.x, -eyeOffsetPos.y, -eyeOffsetPos.z); // transform view according to whichCamera @@ -2283,7 +2283,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glm::quat rotation = whichCamera.getRotation(); glm::vec3 axis = glm::axis(rotation); - glRotatef(-glm::angle(rotation), axis.x, axis.y, axis.z); + 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); @@ -2501,7 +2501,7 @@ void Application::displayOverlay() { (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) ? 80 : 20; - drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 1.0f, 0, frameTimer, WHITE_TEXT); + drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 1.0f, 0.f, frameTimer, WHITE_TEXT); } _overlays.render2D(); @@ -2556,11 +2556,11 @@ void Application::displayStats() { sprintf(framesPerSecond, "Framerate: %3.0f FPS", _fps); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, serverNodes, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, WHITE_TEXT); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarNodes, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, WHITE_TEXT); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, framesPerSecond, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, WHITE_TEXT); if (_statsExpanded) { char packetsPerSecond[30]; @@ -2569,9 +2569,9 @@ void Application::displayStats() { sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)_bytesPerSecond * 8.f / 1000000.f); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, packetsPerSecond, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecond, WHITE_TEXT); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, averageMegabitsPerSecond, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, WHITE_TEXT); } verticalOffset = 0; @@ -2614,7 +2614,7 @@ void Application::displayStats() { "Buffer msecs %.1f", (float) (_audio.getNetworkBufferLengthSamplesPerChannel() + (float) _audio.getJitterBufferSamples()) / (float)_audio.getNetworkSampleRate() * 1000.f); - drawText(30, _glWidget->height() - 22, 0.10f, 0, 2, audioJitter, WHITE_TEXT); + drawText(30, _glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, WHITE_TEXT); char audioPing[30]; @@ -2627,18 +2627,18 @@ void Application::displayStats() { sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, audioPing, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, WHITE_TEXT); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarPing, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, WHITE_TEXT); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, voxelAvgPing, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, WHITE_TEXT); if (_statsExpanded) { char voxelMaxPing[30]; sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, voxelMaxPing, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, WHITE_TEXT); } verticalOffset = 0; @@ -2666,11 +2666,11 @@ void Application::displayStats() { char avatarMixerStats[200]; verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarPosition, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPosition, WHITE_TEXT); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarVelocity, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarVelocity, WHITE_TEXT); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarBodyYaw, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarBodyYaw, WHITE_TEXT); if (_statsExpanded) { SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AvatarMixer); @@ -2683,7 +2683,7 @@ void Application::displayStats() { } verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarMixerStats, WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, WHITE_TEXT); } verticalOffset = 0; @@ -2698,7 +2698,7 @@ void Application::displayStats() { voxelStats.str(""); voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB"; verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); voxelStats.str(""); voxelStats << @@ -2708,14 +2708,14 @@ void Application::displayStats() { voxelStats << " / GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB"; } verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); // Voxel Rendering voxelStats.str(""); voxelStats.precision(4); voxelStats << "Voxel Rendering Slots Max: " << _voxels.getMaxVoxels() / 1000.f << "K"; verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); } voxelStats.str(""); @@ -2723,7 +2723,7 @@ void Application::displayStats() { voxelStats << "Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " << "Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K "; verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); // iterate all the current voxel stats, and list their sending modes, and total voxel counts std::stringstream sendingMode(""); @@ -2767,7 +2767,7 @@ void Application::displayStats() { sendingMode << " "; } verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)sendingMode.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)sendingMode.str().c_str(), WHITE_TEXT); } // Incoming packets @@ -2779,7 +2779,7 @@ void Application::displayStats() { voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData() << " [Recent Max: " << maxString.toLocal8Bit().constData() << "]"; verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); } if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { @@ -2802,7 +2802,7 @@ void Application::displayStats() { voxelStats.str(""); voxelStats << "Server voxels: " << serversTotalString.toLocal8Bit().constData(); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); if (_statsExpanded) { QString serversInternalString = locale.toString((uint)totalInternal); @@ -2813,7 +2813,7 @@ void Application::displayStats() { "Internal: " << serversInternalString.toLocal8Bit().constData() << " " << "Leaves: " << serversLeavesString.toLocal8Bit().constData() << ""; verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); } unsigned long localTotal = VoxelTreeElement::getNodeCount(); @@ -2823,7 +2823,7 @@ void Application::displayStats() { voxelStats.str(""); voxelStats << "Local voxels: " << localTotalString.toLocal8Bit().constData(); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); if (_statsExpanded) { unsigned long localInternal = VoxelTreeElement::getInternalNodeCount(); @@ -2836,7 +2836,7 @@ void Application::displayStats() { "Internal: " << localInternalString.toLocal8Bit().constData() << " " << "Leaves: " << localLeavesString.toLocal8Bit().constData() << ""; verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); } } @@ -2927,17 +2927,17 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { void Application::renderRearViewMirror(const QRect& region, bool billboard) { bool eyeRelativeCamera = false; if (billboard) { - _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); + _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees _mirrorCamera.setDistance(BILLBOARD_DISTANCE * _myAvatar->getScale()); _mirrorCamera.setTargetPosition(_myAvatar->getPosition()); } else if (_rearMirrorTools->getZoomLevel() == BODY) { - _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); + _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); } else { // HEAD zoom level - _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); + _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) { // as a hack until we have a better way of dealing with coordinate precision issues, reposition the @@ -2951,7 +2951,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { } _mirrorCamera.setAspectRatio((float)region.width() / region.height()); - _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); + _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); _mirrorCamera.update(1.0f/_fps); // set the bounds of rear mirror view diff --git a/interface/src/Application.h b/interface/src/Application.h index a93d3d1352..72631666e6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -95,8 +95,8 @@ static const float NODE_KILLED_BLUE = 0.0f; static const QString SNAPSHOT_EXTENSION = ".jpg"; -static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; -static const float BILLBOARD_DISTANCE = 5.0f; +static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees +static const float BILLBOARD_DISTANCE = 5.0f; // meters class Application : public QApplication { Q_OBJECT diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 7f6f1bcb05..c666fcd242 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -656,7 +655,7 @@ void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) { const float MAX_DURATION = 2.f; const float MIN_AUDIBLE_VOLUME = 0.001f; const float NOISE_MAGNITUDE = 0.02f; - float frequency = (_drumSoundFrequency / SAMPLE_RATE) * PI_TIMES_TWO; + float frequency = (_drumSoundFrequency / SAMPLE_RATE) * TWO_PI; if (_drumSoundVolume > 0.f) { for (int i = 0; i < numSamples; i++) { t = (float) _drumSoundSample + (float) i; diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 6cb44f5919..252b3be685 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -81,7 +81,7 @@ void Camera::updateFollowMode(float deltaTime) { _distance = _newDistance; _tightness = _newTightness; } else { - _modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PIE ); + _modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PI ); _upShift = _previousUpShift * (1.0f - _modeShift) + _newUpShift * _modeShift; _distance = _previousDistance * (1.0f - _modeShift) + _newDistance * _modeShift; _tightness = _previousTightness * (1.0f - _modeShift) + _newTightness * _modeShift; diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 81632f4424..9973fd246e 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -89,7 +89,7 @@ private: glm::vec3 _position; glm::vec3 _idealPosition; glm::vec3 _targetPosition; - float _fieldOfView; + float _fieldOfView; // degrees float _aspectRatio; float _nearClip; float _farClip; diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index 9efaa274c0..1f9e23bee1 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -237,8 +237,8 @@ void Environment::renderAtmosphere(Camera& camera, const EnvironmentData& data) program->setUniformValue(locations[INNER_RADIUS_LOCATION], data.getAtmosphereInnerRadius()); program->setUniformValue(locations[KR_ESUN_LOCATION], data.getRayleighScattering() * data.getSunBrightness()); program->setUniformValue(locations[KM_ESUN_LOCATION], data.getMieScattering() * data.getSunBrightness()); - program->setUniformValue(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PIf); - program->setUniformValue(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PIf); + program->setUniformValue(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PI); + program->setUniformValue(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PI); program->setUniformValue(locations[SCALE_LOCATION], 1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())); program->setUniformValue(locations[SCALE_DEPTH_LOCATION], 0.25f); program->setUniformValue(locations[SCALE_OVER_SCALE_DEPTH_LOCATION], diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0674826342..caeb8661b8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -699,8 +699,8 @@ void Menu::editPreferences() { form->addRow("Faceshift Eye Deflection:", faceshiftEyeDeflection); QSpinBox* fieldOfView = new QSpinBox(); - fieldOfView->setMaximum(180); - fieldOfView->setMinimum(1); + fieldOfView->setMaximum(180.f); + fieldOfView->setMinimum(1.f); fieldOfView->setValue(_fieldOfView); form->addRow("Vertical Field of View (Degrees):", fieldOfView); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 4364192009..3658c42f5b 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -139,7 +139,7 @@ public: private slots: void applyTranslation(const glm::vec3& translation); - void applyRotation(const glm::vec3& rotation); + void applyRotation(const glm::vec3& eulerAngles); // eulerAngles are in degrees void applyScale(float scale); void applyURL(const QUrl& url); diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 69340b2926..dcfe726fff 100755 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -30,9 +30,8 @@ bool Stars::setResolution(unsigned k) { } void Stars::render(float fovY, float aspect, float nearZ, float alpha) { - // determine length of screen diagonal from quadrant height and aspect ratio - float quadrantHeight = nearZ * tan(angleConvert(fovY) * 0.5f); + float quadrantHeight = nearZ * tan(RADIANS_PER_DEGREE * fovY * 0.5f); float halfDiagonal = sqrt(quadrantHeight * quadrantHeight * (1.0f + aspect * aspect)); // determine fov angle in respect to the diagonal diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 03b5bed038..45b2c4a616 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -68,19 +68,21 @@ void printVector(glm::vec3 vec) { printf("%4.2f, %4.2f, %4.2f\n", vec.x, vec.y, vec.z); } -// Return the azimuth angle in degrees between two points. +// Return the azimuth angle (in radians) between two points. float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos) { - return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) * 180.0f / PIf; + return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z); } -// Return the angle in degrees between the head and an object in the scene. The value is zero if you are looking right at it. The angle is negative if the object is to your right. +// Return the angle (in radians) between the head and an object in the scene. +// The value is zero if you are looking right at it. +// The angle is negative if the object is to your right. float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw) { - return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) * 180.0f / PIf + render_yaw + head_yaw; + return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) + render_yaw + head_yaw; } -// Helper function returns the positive angle in degrees between two 3D vectors +// Helper function returns the positive angle (in radians) between two 3D vectors float angleBetween(const glm::vec3& v1, const glm::vec3& v2) { - return acos((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2))) * 180.f / PIf; + return acosf((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2))); } // Helper function return the rotation from the first vector onto the second @@ -90,7 +92,7 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return glm::quat(); } glm::vec3 axis; - if (angle > 179.99f) { // 180 degree rotation; must use another axis + if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f)); float axisLength = glm::length(axis); if (axisLength < EPSILON) { // parallel to x; y will work @@ -314,7 +316,7 @@ float widthChar(float scale, int mono, char ch) { return textRenderer(mono)->computeWidth(ch) * (scale / 0.10); } -void drawText(int x, int y, float scale, float rotate, int mono, +void drawText(int x, int y, float scale, float radians, int mono, char const* string, const float* color) { // // Draws text on screen as stroked so it can be resized @@ -322,16 +324,16 @@ void drawText(int x, int y, float scale, float rotate, int mono, glPushMatrix(); glTranslatef(static_cast(x), static_cast(y), 0.0f); glColor3fv(color); - glRotated(rotate,0,0,1); - glScalef(scale / 0.10, scale / 0.10, 1.0); + glRotated(double(radians * DEGREES_PER_RADIAN), 0.0, 0.0, 1.0); + glScalef(scale / 0.1f, scale / 0.1f, 1.f); textRenderer(mono)->draw(0, 0, string); glPopMatrix(); } -void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec, float r, float g, float b) { +void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r, float g, float b) { // - // Draws text on screen as stroked so it can be resized + // Draws vec3 on screen as stroked so it can be resized // char vectext[20]; sprintf(vectext,"%3.1f,%3.1f,%3.1f", vec.x, vec.y, vec.z); @@ -339,10 +341,10 @@ void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, gl glPushMatrix(); glTranslatef(static_cast(x), static_cast(y), 0); glColor3f(r,g,b); - glRotated(180+rotate,0,0,1); - glRotated(180,0,1,0); + glRotated(180.0 + double(radians * DEGREES_PER_RADIAN), 0.0, 0.0, 1.0); + glRotated(180.0, 0.0, 1.0, 0.0); glLineWidth(thick); - glScalef(scale, scale, 1.0); + glScalef(scale, scale, 1.f); len = (int) strlen(vectext); for (i = 0; i < len; i++) { if (!mono) glutStrokeCharacter(GLUT_STROKE_ROMAN, int(vectext[i])); @@ -371,9 +373,9 @@ void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::ve glBegin(GL_LINE_STRIP); for (int i=0; igetCamera()->getRotation(); glm::vec3 chatAxis = glm::axis(chatRotation); - glRotatef(glm::angle(chatRotation), chatAxis.x, chatAxis.y, chatAxis.z); + glRotatef(glm::degrees(glm::angle(chatRotation)), chatAxis.x, chatAxis.y, chatAxis.z); - glColor3f(0, 0.8f, 0); - glRotatef(180, 0, 1, 0); - glRotatef(180, 0, 0, 1); + glColor3f(0.f, 0.8f, 0.f); + glRotatef(180.f, 0.f, 1.f, 0.f); + glRotatef(180.f, 0.f, 0.f, 1.f); glScalef(_scale * CHAT_MESSAGE_SCALE, _scale * CHAT_MESSAGE_SCALE, 1.0f); glDisable(GL_LIGHTING); @@ -288,7 +288,7 @@ void Avatar::render(bool forShadowMap) { _chatMessage[lastIndex] = '\0'; textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str()); _chatMessage[lastIndex] = lastChar; - glColor3f(0, 1, 0); + glColor3f(0.f, 1.f, 0.f); textRenderer(CHAT)->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex); } glEnable(GL_LIGHTING); @@ -301,12 +301,12 @@ void Avatar::render(bool forShadowMap) { glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { glm::quat orientation = getOrientation(); glm::vec3 currentUp = orientation * IDENTITY_UP; - float angle = glm::degrees(acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f))); + float angle = acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f)); if (angle < EPSILON) { return glm::quat(); } glm::vec3 axis; - if (angle > 179.99f) { // 180 degree rotation; must use another axis + if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis axis = orientation * IDENTITY_RIGHT; } else { axis = glm::normalize(glm::cross(currentUp, _worldUpDirection)); @@ -356,9 +356,9 @@ void Avatar::renderBillboard() { // rotate about vertical to face the camera glm::quat rotation = getOrientation(); glm::vec3 cameraVector = glm::inverse(rotation) * (Application::getInstance()->getCamera()->getPosition() - _position); - rotation = rotation * glm::angleAxis(glm::degrees(atan2f(-cameraVector.x, -cameraVector.z)), glm::vec3(0.0f, 1.0f, 0.0f)); + rotation = rotation * glm::angleAxis(atan2f(-cameraVector.x, -cameraVector.z), glm::vec3(0.0f, 1.0f, 0.0f)); glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); // compute the size from the billboard camera parameters and scale float size = _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f)); @@ -404,7 +404,7 @@ void Avatar::renderDisplayName() { // we need "always facing camera": we must remove the camera rotation from the stack glm::quat rotation = Application::getInstance()->getCamera()->getRotation(); glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); // We need to compute the scale factor such as the text remains with fixed size respect to window coordinates // We project a unit vector and check the difference in screen coordinates, to check which is the @@ -662,15 +662,15 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, glm::vec3 perpCos = glm::normalize(glm::cross(axis, perpSin)); perpSin = glm::cross(perpCos, axis); - float anglea = 0.0; - float angleb = 0.0; + float anglea = 0.f; + float angleb = 0.f; for (int i = 0; i < NUM_BODY_CONE_SIDES; i ++) { // the rectangles that comprise the sides of the cone section are // referenced by "a" and "b" in one dimension, and "1", and "2" in the other dimension. anglea = angleb; - angleb = ((float)(i+1) / (float)NUM_BODY_CONE_SIDES) * PIf * 2.0f; + angleb = ((float)(i+1) / (float)NUM_BODY_CONE_SIDES) * TWO_PI; float sa = sinf(anglea); float sb = sinf(angleb); diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 04f1388015..eba8f7bf1a 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -56,9 +56,10 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX glm::mat3 axes = glm::mat3_cast(_rotation); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation))); - state.rotation = glm::angleAxis(-_owningHead->getTweakedRoll(), glm::normalize(inverse * axes[2])) * - glm::angleAxis(_owningHead->getTweakedYaw(), glm::normalize(inverse * axes[1])) * - glm::angleAxis(-_owningHead->getTweakedPitch(), glm::normalize(inverse * axes[0])) * joint.rotation; + state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getTweakedRoll(), glm::normalize(inverse * axes[2])) + * glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getTweakedYaw(), glm::normalize(inverse * axes[1])) + * glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getTweakedPitch(), glm::normalize(inverse * axes[0])) + * joint.rotation; } void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { @@ -69,7 +70,7 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() + _owningHead->getSaccade() - _translation, 1.0f)); glm::quat between = rotationBetween(front, lookAt); - const float MAX_ANGLE = 30.0f; + const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; state.rotation = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * joint.rotation; } diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 74bf983eb1..6c2bad865f 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -183,13 +183,13 @@ void Head::setScale (float scale) { } glm::quat Head::getTweakedOrientation() const { - return _owningAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() ))); + return _owningAvatar->getOrientation() * glm::quat(glm::radians( + glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() ))); } glm::quat Head::getCameraOrientation () const { Avatar* owningAvatar = static_cast(_owningAvatar); - return owningAvatar->getWorldAlignedOrientation() - * glm::quat(glm::radians(glm::vec3(_pitch, 0.f, 0.0f))); + return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_pitch, 0.f, 0.0f))); } glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7108e0d0cc..92552e0a35 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -34,8 +34,8 @@ using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); -const float YAW_MAG = 500.0f; -const float PITCH_MAG = 100.0f; +const float YAW_SPEED = 500.0f; // degrees/sec +const float PITCH_SPEED = 100.0f; // degrees/sec const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions const float COLLISION_BODY_FORCE = 30.0f; // pertains to avatar-to-avatar collisions const float COLLISION_RADIUS_SCALE = 0.125f; @@ -118,12 +118,13 @@ void MyAvatar::update(float deltaTime) { Head* head = getHead(); if (OculusManager::isConnected()) { - float yaw, pitch, roll; + float yaw, pitch, roll; // these angles will be in radians OculusManager::getEulerAngles(yaw, pitch, roll); - head->setYaw(yaw); - head->setPitch(pitch); - head->setRoll(roll); + // but these euler angles are stored in degrees + head->setYaw(yaw * DEGREES_PER_RADIAN); + head->setPitch(pitch * DEGREES_PER_RADIAN); + head->setRoll(roll * DEGREES_PER_RADIAN); } // Get audio loudness data from audio input device @@ -186,7 +187,7 @@ void MyAvatar::simulate(float deltaTime) { float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE; if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) { - radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f)); + radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cosf(0.5f * RADIANS_PER_DEGREE * myCamera->getFieldOfView())); radius *= COLLISION_RADIUS_SCALAR; } @@ -206,11 +207,11 @@ void MyAvatar::simulate(float deltaTime) { // update body yaw by body yaw delta orientation = orientation * glm::quat(glm::radians( - glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime)); + glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime)); // decay body rotation momentum const float BODY_SPIN_FRICTION = 7.5f; - float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime; + float bodySpinMomentum = 1.f - BODY_SPIN_FRICTION * deltaTime; if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } _bodyPitchDelta *= bodySpinMomentum; _bodyYawDelta *= bodySpinMomentum; @@ -340,12 +341,12 @@ void MyAvatar::updateFromGyros(float deltaTime) { bool trackerActive = false; if (faceshift->isActive()) { estimatedPosition = faceshift->getHeadTranslation(); - estimatedRotation = safeEulerAngles(faceshift->getHeadRotation()); + estimatedRotation = DEGREES_PER_RADIAN * safeEulerAngles(faceshift->getHeadRotation()); trackerActive = true; } else if (visage->isActive()) { estimatedPosition = visage->getHeadTranslation(); - estimatedRotation = safeEulerAngles(visage->getHeadRotation()); + estimatedRotation = DEGREES_PER_RADIAN * safeEulerAngles(visage->getHeadRotation()); trackerActive = true; } @@ -492,12 +493,11 @@ void MyAvatar::render(bool forShadowMapOrMirror) { glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z); glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation(); glm::vec3 chatAxis = glm::axis(chatRotation); - glRotatef(glm::angle(chatRotation), chatAxis.x, chatAxis.y, chatAxis.z); + glRotatef(glm::degrees(glm::angle(chatRotation)), chatAxis.x, chatAxis.y, chatAxis.z); - - glColor3f(0, 0.8f, 0); - glRotatef(180, 0, 1, 0); - glRotatef(180, 0, 0, 1); + glColor3f(0.f, 0.8f, 0.f); + glRotatef(180.f, 0.f, 1.f, 0.f); + glRotatef(180.f, 0.f, 0.f, 1.f); glScalef(_scale * CHAT_MESSAGE_SCALE, _scale * CHAT_MESSAGE_SCALE, 1.0f); glDisable(GL_LIGHTING); @@ -513,7 +513,7 @@ void MyAvatar::render(bool forShadowMapOrMirror) { _chatMessage[lastIndex] = '\0'; textRenderer()->draw(-width / 2.0f, 0, _chatMessage.c_str()); _chatMessage[lastIndex] = lastChar; - glColor3f(0, 1, 0); + glColor3f(0.f, 1.f, 0.f); textRenderer()->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex); } glEnable(GL_LIGHTING); @@ -527,7 +527,7 @@ void MyAvatar::renderHeadMouse() const { // TODO? resurrect headMouse stuff? /* // Display small target box at center or head mouse target that can also be used to measure LOD - glColor3f(1.0, 1.0, 1.0); + glColor3f(1.f, 1.f, 1.f); glDisable(GL_LINE_SMOOTH); const int PIXEL_BOX = 16; glBegin(GL_LINES); @@ -549,7 +549,7 @@ void MyAvatar::renderHeadMouse() const { int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE; int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE; - glColor3f(0.0, 1.0, 1.0); + glColor3f(0.f, 1.f, 1.f); glDisable(GL_LINE_SMOOTH); glBegin(GL_LINES); glVertex2f(eyeTargetX - PIXEL_BOX/2, eyeTargetY); @@ -624,15 +624,15 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { // first orbit horizontally glm::quat orientation = getOrientation(); const float ANGULAR_SCALE = 0.5f; - glm::quat rotation = glm::angleAxis(deltaX * -ANGULAR_SCALE, orientation * IDENTITY_UP); + glm::quat rotation = glm::angleAxis(glm::radians(- deltaX * ANGULAR_SCALE), orientation * IDENTITY_UP); setPosition(position + rotation * (getPosition() - position)); orientation = rotation * orientation; setOrientation(orientation); // then vertically float oldPitch = getHead()->getPitch(); - getHead()->setPitch(oldPitch + deltaY * -ANGULAR_SCALE); - rotation = glm::angleAxis(getHead()->getPitch() - oldPitch, orientation * IDENTITY_RIGHT); + getHead()->setPitch(oldPitch - deltaY * ANGULAR_SCALE); + rotation = glm::angleAxis(glm::radians((getHead()->getPitch() - oldPitch)), orientation * IDENTITY_RIGHT); setPosition(position + rotation * (getPosition() - position)); } @@ -737,9 +737,9 @@ void MyAvatar::updateThrust(float deltaTime) { _thrust -= _driveKeys[LEFT] * _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right; _thrust += _driveKeys[UP] * _scale * THRUST_MAG_UP * _thrustMultiplier * deltaTime * up; _thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up; - _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime; - _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime; - getHead()->setPitch(getHead()->getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime); + _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; + _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; + getHead()->setPitch(getHead()->getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); // If thrust keys are being held down, slowly increase thrust to allow reaching great speeds if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { @@ -1037,7 +1037,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { const float CIRCLE_INFLUENCE_SCALE = 2.0f; const float MIN_RADIUS = 0.3f; for (int i = sortedAvatars.size() - 1; i >= 0; i--) { - float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO); + float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / TWO_PI); if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) { sortedAvatars.remove(i); } else { @@ -1048,7 +1048,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { return; } center = sortedAvatars.last().accumulatedCenter; - float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO); + float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / TWO_PI); // compute the average up vector glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; @@ -1069,18 +1069,18 @@ void MyAvatar::updateChatCircle(float deltaTime) { glm::vec3 delta = _position - center; glm::vec3 projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); float myAngle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; - float leftDistance = PI_TIMES_TWO; - float rightDistance = PI_TIMES_TWO; + float leftDistance = TWO_PI; + float rightDistance = TWO_PI; foreach (const SortedAvatar& sortedAvatar, sortedAvatars) { delta = sortedAvatar.avatar->getPosition() - center; projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); float angle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; if (angle < myAngle) { leftDistance = min(myAngle - angle, leftDistance); - rightDistance = min(PI_TIMES_TWO - (myAngle - angle), rightDistance); + rightDistance = min(TWO_PI - (myAngle - angle), rightDistance); } else { - leftDistance = min(PI_TIMES_TWO - (angle - myAngle), leftDistance); + leftDistance = min(TWO_PI - (angle - myAngle), leftDistance); rightDistance = min(angle - myAngle, rightDistance); } } @@ -1126,13 +1126,6 @@ void MyAvatar::setGravity(glm::vec3 gravity) { } } -void MyAvatar::setOrientation(const glm::quat& orientation) { - glm::vec3 eulerAngles = safeEulerAngles(orientation); - _bodyPitch = eulerAngles.x; - _bodyYaw = eulerAngles.y; - _bodyRoll = eulerAngles.z; -} - void MyAvatar::goHome() { qDebug("Going Home!"); setPosition(START_LOCATION); @@ -1169,7 +1162,7 @@ void MyAvatar::updateLocationInDataServer() { if (accountManager.isLoggedIn()) { QString positionString(createByteArray(_position)); - QString orientationString(createByteArray(safeEulerAngles(getOrientation()))); + QString orientationString(createByteArray(glm::radians(safeEulerAngles(getOrientation())))); // construct the json to put the user's location QString locationPutJson = QString() + "{\"address\":{\"position\":\"" @@ -1202,10 +1195,10 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) { NodeList::getInstance()->getDomainInfo().setHostname(domainHostnameString); // orient the user to face the target - glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), - orientationItems[1].toFloat(), - orientationItems[2].toFloat()))) - * glm::angleAxis(180.0f, glm::vec3(0.0f, 1.0f, 0.0f)); + glm::quat newOrientation = glm::quat(glm::vec3(orientationItems[0].toFloat(), + orientationItems[1].toFloat(), + orientationItems[2].toFloat())) + * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); setOrientation(newOrientation); // move the user a couple units away diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f1448edb97..dfc114d952 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -44,10 +44,8 @@ public: void setVelocity(const glm::vec3 velocity) { _velocity = velocity; } void setLeanScale(float scale) { _leanScale = scale; } void setGravity(glm::vec3 gravity); - void setOrientation(const glm::quat& orientation); void setMoveTarget(const glm::vec3 moveTarget); void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } - // getters float getSpeed() const { return _speed; } @@ -55,7 +53,7 @@ public: float getLeanScale() const { return _leanScale; } float getElapsedTimeStopped() const { return _elapsedTimeStopped; } float getElapsedTimeMoving() const { return _elapsedTimeMoving; } - float getAbsoluteHeadYaw() const; + float getAbsoluteHeadYaw() const; // degrees const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } glm::vec3 getGravity() const { return _gravity; } @@ -68,7 +66,7 @@ public: // Set what driving keys are being pressed to control thrust levels void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; - bool getDriveKeys(int key) { return _driveKeys[key]; }; + bool getDriveKeys(int key) { return _driveKeys[key] != 0.f; }; void jump() { _shouldJump = true; }; bool isMyAvatar() { return true; } @@ -101,8 +99,8 @@ public slots: private: bool _mousePressed; - float _bodyPitchDelta; - float _bodyRollDelta; + float _bodyPitchDelta; // degrees + float _bodyRollDelta; // degrees bool _shouldJump; float _driveKeys[MAX_DRIVE_KEYS]; glm::vec3 _gravity; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c57656be58..231e2b4ae0 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -24,7 +24,7 @@ void SkeletonModel::simulate(float deltaTime, bool delayLoad) { return; } setTranslation(_owningAvatar->getPosition()); - setRotation(_owningAvatar->getOrientation() * glm::angleAxis(180.0f, glm::vec3(0.0f, 1.0f, 0.0f))); + setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f))); const float MODEL_SCALE = 0.0006f; setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE); @@ -199,8 +199,9 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const glm::mat3 axes = glm::mat3_cast(_rotation); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation))); - state.rotation = glm::angleAxis(-_owningAvatar->getHead()->getLeanSideways(), glm::normalize(inverse * axes[2])) * - glm::angleAxis(-_owningAvatar->getHead()->getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation; + state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getLeanSideways(), + glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getLeanForward(), + glm::normalize(inverse * axes[0])) * joint.rotation; } void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) { diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index 60839c362d..4fe89ff98b 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -65,8 +65,8 @@ void Faceshift::update() { return; } // get the euler angles relative to the window - glm::vec3 eulers = safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3( - (_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f)))); + glm::vec3 eulers = glm::degrees(safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3( + (_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f))))); // compute and subtract the long term average const float LONG_TERM_AVERAGE_SMOOTHING = 0.999f; diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index d3981fc182..908354ae9d 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -33,6 +33,7 @@ public: const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; } const glm::vec3& getHeadTranslation() const { return _headTranslation; } + // these pitch/yaw angles are in degrees float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; } float getEyeGazeLeftYaw() const { return _eyeGazeLeftYaw; } @@ -96,9 +97,9 @@ private: glm::vec3 _headAngularVelocity; glm::vec3 _headTranslation; + // degrees float _eyeGazeLeftPitch; float _eyeGazeLeftYaw; - float _eyeGazeRightPitch; float _eyeGazeRightYaw; @@ -121,10 +122,12 @@ private: int _jawOpenIndex; + // degrees float _longTermAverageEyePitch; float _longTermAverageEyeYaw; bool _longTermAverageInitialized; + // degrees float _estimatedEyePitch; float _estimatedEyeYaw; }; diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 0f937995b9..c5377ce428 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -27,7 +27,7 @@ int OculusManager::_scaleLocation; int OculusManager::_scaleInLocation; int OculusManager::_hmdWarpParamLocation; bool OculusManager::_isConnected = false; -float OculusManager::_yawOffset = 0; +float OculusManager::_yawOffset = 0.0f; // radians #ifdef HAVE_LIBOVR using namespace OVR; @@ -198,12 +198,7 @@ void OculusManager::updateYawOffset() { void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { #ifdef HAVE_LIBOVR _sensorFusion->GetOrientation().GetEulerAngles(&yaw, &pitch, &roll); - - // convert each angle to degrees - // remove the yaw offset from the returned yaw - yaw = glm::degrees(yaw - _yawOffset); - pitch = glm::degrees(pitch); - roll = glm::degrees(roll); + yaw = yaw - _yawOffset; #endif } diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 3eb22110f5..b3cd400ac8 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -32,6 +32,9 @@ public: static void reset(); + /// param \yaw[out] yaw in radians + /// param \pitch[out] pitch in radians + /// param \roll[out] roll in radians static void getEulerAngles(float& yaw, float& pitch, float& roll); static void updateYawOffset(); diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 5d7e631966..287aa4e5f6 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -89,7 +89,7 @@ void SixenseManager::update(float deltaTime) { // Rotation of Palm glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]); - rotation = glm::angleAxis(180.0f, glm::vec3(0.f, 1.f, 0.f)) * rotation; + rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * rotation; const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f); glm::vec3 newNormal = rotation * PALM_VECTOR; palm->setRawNormal(newNormal); diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index 9325efd77b..07aeea92b4 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -52,7 +52,7 @@ void TV3DManager::setFrustum(Camera& whichCamera) { double nearZ = whichCamera.getNearClip(); // near clipping plane double screenZ = Application::getInstance()->getViewFrustum()->getFocalLength(); // screen projection plane - double top = nearZ * tan(DTR * fovy / 2); //sets top of frustum based on fovy and near clipping plane + double top = nearZ * tan(DTR * fovy / 2.0); //sets top of frustum based on fovy and near clipping plane double right = _aspect * top; // sets right of frustum based on aspect ratio double frustumshift = (IOD / 2) * nearZ / screenZ; @@ -136,4 +136,4 @@ void TV3DManager::display(Camera& whichCamera) { // reset the viewport to how we started glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height()); -} \ No newline at end of file +} diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index c13cc7deab..6e6c9275d9 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -533,8 +533,8 @@ public: glm::quat postRotation; glm::mat4 postTransform; - glm::vec3 rotationMin; - glm::vec3 rotationMax; + glm::vec3 rotationMin; // radians + glm::vec3 rotationMax; // radians }; glm::mat4 getGlobalTransform(const QMultiHash& parentMap, @@ -806,7 +806,7 @@ void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { } glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex); mesh.tangents[firstIndex] += glm::cross(glm::angleAxis( - -glm::degrees(atan2f(-texCoordDelta.t, texCoordDelta.s)), normal) * glm::normalize(bitangent), normal); + - atan2f(-texCoordDelta.t, texCoordDelta.s), normal) * glm::normalize(bitangent), normal); } QVector getIndices(const QVector ids, QVector modelIDs) { @@ -1000,6 +1000,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) jointRightFingertipIDs[index] = getID(object.properties); } glm::vec3 translation; + // NOTE: the euler angles as supplied by the FBX file are in degrees glm::vec3 rotationOffset; glm::vec3 preRotation, rotation, postRotation; glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f); @@ -1054,7 +1055,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (property.properties.at(0) == "RotationMin") { rotationMin = getVec3(property.properties, index); - } else if (property.properties.at(0) == "RotationMax") { + } + // NOTE: these rotation limits are stored in degrees (NOT radians) + else if (property.properties.at(0) == "RotationMax") { rotationMax = getVec3(property.properties, index); } else if (property.properties.at(0) == "RotationMinX") { @@ -1104,10 +1107,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) model.postRotation = glm::quat(glm::radians(postRotation)); model.postTransform = glm::translate(-rotationPivot) * glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); - model.rotationMin = glm::vec3(rotationMinX ? rotationMin.x : -180.0f, - rotationMinY ? rotationMin.y : -180.0f, rotationMinZ ? rotationMin.z : -180.0f); - model.rotationMax = glm::vec3(rotationMaxX ? rotationMax.x : 180.0f, - rotationMaxY ? rotationMax.y : 180.0f, rotationMaxZ ? rotationMax.z : 180.0f); + // NOTE: anbgles from the FBX file are in degrees + // so we convert them to radians for the FBXModel class + model.rotationMin = glm::radians(glm::vec3(rotationMinX ? rotationMin.x : -180.0f, + rotationMinY ? rotationMin.y : -180.0f, rotationMinZ ? rotationMin.z : -180.0f)); + model.rotationMax = glm::radians(glm::vec3(rotationMaxX ? rotationMax.x : 180.0f, + rotationMaxY ? rotationMax.y : 180.0f, rotationMaxZ ? rotationMax.z : 180.0f)); models.insert(getID(object.properties), model); } else if (object.name == "Texture") { diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 3bc80bbf2f..ac4e4bfa14 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -76,8 +76,8 @@ public: glm::quat postRotation; glm::mat4 postTransform; glm::mat4 transform; - glm::vec3 rotationMin; - glm::vec3 rotationMax; + glm::vec3 rotationMin; // radians + glm::vec3 rotationMax; // radians glm::quat inverseDefaultRotation; glm::quat inverseBindRotation; glm::mat4 bindTransform; diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 222edcf46a..0dce47fcfd 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -30,11 +30,11 @@ void GeometryCache::renderHemisphere(int slices, int stacks) { GLfloat* vertexData = new GLfloat[vertices * 3]; GLfloat* vertex = vertexData; for (int i = 0; i < stacks - 1; i++) { - float phi = PIf * 0.5f * i / (stacks - 1); + float phi = PI_OVER_TWO * float(i) / float(stacks - 1); float z = sinf(phi), radius = cosf(phi); for (int j = 0; j < slices; j++) { - float theta = PIf * 2.0f * j / slices; + float theta = TWO_PI * float(j) / float(slices); *(vertex++) = sinf(theta) * radius; *(vertex++) = cosf(theta) * radius; @@ -180,7 +180,7 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) { float y = (float)i / (stacks - 1); for (int j = 0; j <= slices; j++) { - float theta = 3 * PIf / 2 + PIf * j / slices; + float theta = 3.f * PI_OVER_TWO + PI * float(j) / float(slices); //normals *(vertex++) = sinf(theta); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f6cfb08816..4354509037 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -760,15 +760,15 @@ float Model::getLimbLength(int jointIndex) const { void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain) { JointState& state = _jointStates[jointIndex]; const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex]; - if (!constrain || (joint.rotationMin == glm::vec3(-180.0f, -180.0f, -180.0f) && - joint.rotationMax == glm::vec3(180.0f, 180.0f, 180.0f))) { + if (!constrain || (joint.rotationMin == glm::vec3(-PI, -PI, -PI) && + joint.rotationMax == glm::vec3(PI, PI, PI))) { // no constraints state.rotation = state.rotation * glm::inverse(state.combinedRotation) * delta * state.combinedRotation; state.combinedRotation = delta * state.combinedRotation; return; } - glm::quat newRotation = glm::quat(glm::radians(glm::clamp(safeEulerAngles(state.rotation * - glm::inverse(state.combinedRotation) * delta * state.combinedRotation), joint.rotationMin, joint.rotationMax))); + glm::quat newRotation = glm::quat(glm::clamp(safeEulerAngles(state.rotation * + glm::inverse(state.combinedRotation) * delta * state.combinedRotation), joint.rotationMin, joint.rotationMax)); state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation; state.rotation = newRotation; } @@ -789,7 +789,7 @@ void Model::renderCollisionProxies(float alpha) { glTranslatef(position.x, position.y, position.z); const glm::quat& rotation = shape->getRotation(); glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); // draw a grey sphere at shape position glColor4f(0.75f, 0.75f, 0.75f, alpha); @@ -864,7 +864,7 @@ void Model::applyCollision(CollisionInfo& collision) { axis = glm::normalize(axis); glm::vec3 end; getJointPosition(jointIndex, end); - glm::vec3 newEnd = start + glm::angleAxis(glm::degrees(angle), axis) * (end - start); + glm::vec3 newEnd = start + glm::angleAxis(angle, axis) * (end - start); // try to move it setJointPosition(jointIndex, newEnd, -1, true); } diff --git a/interface/src/starfield/Generator.cpp b/interface/src/starfield/Generator.cpp index 72f5e6c859..6065140505 100644 --- a/interface/src/starfield/Generator.cpp +++ b/interface/src/starfield/Generator.cpp @@ -16,6 +16,7 @@ using namespace starfield; const float Generator::STAR_COLORIZATION = 0.1f; +const float PI_OVER_180 = 3.14159265358979f / 180.f; void Generator::computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed) { InputVertices* vertices = & destination; @@ -89,4 +90,4 @@ unsigned Generator::computeStarColor(float colorization) { red = green = blue = 2 + (rand() % 128); } return red | (green << 8) | (blue << 16); -} \ No newline at end of file +} diff --git a/interface/src/starfield/renderer/Renderer.cpp b/interface/src/starfield/renderer/Renderer.cpp index d1a8cbab8d..0be9850338 100755 --- a/interface/src/starfield/renderer/Renderer.cpp +++ b/interface/src/starfield/renderer/Renderer.cpp @@ -308,4 +308,4 @@ bool Renderer::TileSelection::deferred(Renderer::TileSelection::Cursor& cursor) return true; } return false; -} \ No newline at end of file +} diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 0c6c12d868..f2e43588ff 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -138,11 +138,11 @@ glm::quat MetavoxelEditor::getGridRotation() const { return glm::quat(); case GRID_PLANE_XZ: - return glm::angleAxis(-90.0f, glm::vec3(1.0f, 0.0f, 0.0f)); + return glm::angleAxis(-PI_OVER_TWO, glm::vec3(1.0f, 0.0f, 0.0f)); case GRID_PLANE_YZ: default: - return glm::angleAxis(90.0f, glm::vec3(0.0f, 1.0f, 0.0f)); + return glm::angleAxis(PI_OVER_TWO, glm::vec3(0.0f, 1.0f, 0.0f)); } } @@ -275,7 +275,7 @@ void MetavoxelEditor::render() { glm::quat rotation = getGridRotation(); glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); MetavoxelTool* tool = getActiveTool(); if (tool) { diff --git a/interface/src/world.h b/interface/src/world.h index 8d3bd7322e..bc8dab9106 100644 --- a/interface/src/world.h +++ b/interface/src/world.h @@ -9,10 +9,6 @@ #ifndef __interface__world__ #define __interface__world__ -#ifndef PIf -#define PIf 3.14159265f -#endif - const float GRAVITY_EARTH = 9.80665f; const float EDGE_SIZE_GROUND_PLANE = 20.f; diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 83b4d8a79c..91a47b7d2c 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -41,7 +41,7 @@ Sound::Sound(float volume, float frequency, float duration, float decay, QObject int numSamples = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; // we add sounds in chunks of this many samples int chunkStartingSample = 0; - float waveFrequency = (frequency / SAMPLE_RATE) * PI_TIMES_TWO; + float waveFrequency = (frequency / SAMPLE_RATE) * TWO_PI; while (volume > 0.f) { for (int i = 0; i < numSamples; i++) { t = (float)chunkStartingSample + (float)i; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2483e0514b..ea2c2ab590 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -33,9 +33,9 @@ QNetworkAccessManager* AvatarData::networkAccessManager = NULL; AvatarData::AvatarData() : NodeData(), _handPosition(0,0,0), - _bodyYaw(-90.0), - _bodyPitch(0.0), - _bodyRoll(0.0), + _bodyYaw(-90.f), + _bodyPitch(0.0f), + _bodyRoll(0.0f), _targetScale(1.0f), _handState(0), _keyState(NO_KEY_DOWN), @@ -510,7 +510,7 @@ void AvatarData::setClampedTargetScale(float targetScale) { } void AvatarData::setOrientation(const glm::quat& orientation) { - glm::vec3 eulerAngles = safeEulerAngles(orientation); + glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(orientation)); _bodyPitch = eulerAngles.x; _bodyYaw = eulerAngles.y; _bodyRoll = eulerAngles.z; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 925540f899..067fadd8b1 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -108,7 +108,7 @@ public: QByteArray toByteArray(); int parseData(const QByteArray& packet); - // Body Rotation + // Body Rotation (degrees) float getBodyYaw() const { return _bodyYaw; } void setBodyYaw(float bodyYaw) { _bodyYaw = bodyYaw; } float getBodyPitch() const { return _bodyPitch; } @@ -122,7 +122,7 @@ public: glm::quat getHeadOrientation() const { return _headData->getOrientation(); } void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); } - // access to Head().set/getMousePitch + // access to Head().set/getMousePitch (degrees) float getHeadPitch() const { return _headData->getPitch(); } void setHeadPitch(float value) { _headData->setPitch(value); }; @@ -217,9 +217,9 @@ protected: glm::vec3 _handPosition; // Body rotation - float _bodyYaw; - float _bodyPitch; - float _bodyRoll; + float _bodyYaw; // degrees + float _bodyPitch; // degrees + float _bodyRoll; // degrees // Body scale float _targetScale; diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index dac4941d97..a94096d73a 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -17,9 +17,9 @@ HeadData::HeadData(AvatarData* owningAvatar) : _yaw(0.0f), _pitch(0.0f), _roll(0.0f), - _lookAtPosition(0.0f, 0.0f, 0.0f), _leanSideways(0.0f), _leanForward(0.0f), + _lookAtPosition(0.0f, 0.0f, 0.0f), _audioLoudness(0.0f), _isFaceshiftConnected(false), _leftEyeBlink(0.0f), @@ -39,12 +39,11 @@ void HeadData::setOrientation(const glm::quat& orientation) { // rotate body about vertical axis glm::quat bodyOrientation = _owningAvatar->getOrientation(); glm::vec3 newFront = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FRONT); - bodyOrientation = bodyOrientation * glm::angleAxis(glm::degrees(atan2f(-newFront.x, -newFront.z)), - glm::vec3(0.0f, 1.0f, 0.0f)); + bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newFront.x, -newFront.z), glm::vec3(0.0f, 1.0f, 0.0f)); _owningAvatar->setOrientation(bodyOrientation); // the rest goes to the head - glm::vec3 eulers = safeEulerAngles(glm::inverse(bodyOrientation) * orientation); + glm::vec3 eulers = DEGREES_PER_RADIAN * safeEulerAngles(glm::inverse(bodyOrientation) * orientation); _pitch = eulers.x; _yaw = eulers.y; _roll = eulers.z; diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index dfecbd0320..56dc5630d0 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -15,12 +15,13 @@ #include #include -const float MIN_HEAD_YAW = -110; -const float MAX_HEAD_YAW = 110; -const float MIN_HEAD_PITCH = -60; -const float MAX_HEAD_PITCH = 60; -const float MIN_HEAD_ROLL = -50; -const float MAX_HEAD_ROLL = 50; +// degrees +const float MIN_HEAD_YAW = -110.f; +const float MAX_HEAD_YAW = 110.f; +const float MIN_HEAD_PITCH = -60.f; +const float MAX_HEAD_PITCH = 60.f; +const float MIN_HEAD_ROLL = -50.f; +const float MAX_HEAD_ROLL = 50.f; class AvatarData; @@ -29,21 +30,17 @@ public: HeadData(AvatarData* owningAvatar); virtual ~HeadData() { }; + // degrees float getLeanSideways() const { return _leanSideways; } void setLeanSideways(float leanSideways) { _leanSideways = leanSideways; } - float getLeanForward() const { return _leanForward; } void setLeanForward(float leanForward) { _leanForward = leanForward; } - float getYaw() const { return _yaw; } void setYaw(float yaw) { _yaw = glm::clamp(yaw, MIN_HEAD_YAW, MAX_HEAD_YAW); } - float getPitch() const { return _pitch; } void setPitch(float pitch) { _pitch = glm::clamp(pitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); } - float getRoll() const { return _roll; } void setRoll(float roll) { _roll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } - virtual float getTweakedYaw() const { return _yaw; } virtual float getTweakedPitch() const { return _pitch; } virtual float getTweakedRoll() const { return _roll; } @@ -62,6 +59,7 @@ public: float getPupilDilation() const { return _pupilDilation; } void setPupilDilation(float pupilDilation) { _pupilDilation = pupilDilation; } + // degrees void addYaw(float yaw); void addPitch(float pitch); void addRoll(float roll); @@ -73,12 +71,14 @@ public: friend class AvatarData; protected: + // degrees float _yaw; float _pitch; float _roll; - glm::vec3 _lookAtPosition; float _leanSideways; float _leanForward; + + glm::vec3 _lookAtPosition; float _audioLoudness; bool _isFaceshiftConnected; float _leftEyeBlink; diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 85bdd54938..d3946c2677 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -363,7 +363,7 @@ signals: private: glm::vec3 _translation; - glm::vec3 _rotation; + glm::vec3 _rotation; // Euler Angles in degrees float _scale; }; diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index da353a713e..aeffcc4968 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -384,14 +384,14 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const // Compute the angular distance between the two orientations const float ORIENTATION_SIMILAR_ENOUGH = 10.0f; // 10 degrees in any direction glm::quat dQOrientation = _orientation * glm::inverse(compareTo._orientation); - float angleOrientation = compareTo._orientation == _orientation ? 0.0f : glm::angle(dQOrientation); + float angleOrientation = compareTo._orientation == _orientation ? 0.0f : glm::degrees(glm::angle(dQOrientation)); if (isNaN(angleOrientation)) { angleOrientation = 0.0f; } glm::quat dQEyeOffsetOrientation = _eyeOffsetOrientation * glm::inverse(compareTo._eyeOffsetOrientation); float angleEyeOffsetOrientation = compareTo._eyeOffsetOrientation == _eyeOffsetOrientation - ? 0.0f : glm::angle(dQEyeOffsetOrientation); + ? 0.0f : glm::degrees(glm::angle(dQEyeOffsetOrientation)); if (isNaN(angleEyeOffsetOrientation)) { angleOrientation = 0.0f; } @@ -463,7 +463,7 @@ void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearValue, float& farValue, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const { // compute our dimensions the usual way - float hheight = _nearClip * tanf(_fieldOfView * 0.5f * PI_OVER_180); + float hheight = _nearClip * tanf(_fieldOfView * 0.5f * RADIANS_PER_DEGREE); float hwidth = _aspectRatio * hheight; // get our frustum corners in view space diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 36d3346cdb..da4a6997f1 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -120,7 +120,7 @@ private: glm::vec3 _right; // Lens attributes - float _fieldOfView; + float _fieldOfView; // degrees float _aspectRatio; float _nearClip; float _farClip; diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp index d016dc6fc4..9885b8d9dc 100644 --- a/libraries/script-engine/src/EventTypes.cpp +++ b/libraries/script-engine/src/EventTypes.cpp @@ -387,11 +387,10 @@ TouchEvent::TouchEvent(const QTouchEvent& event, const TouchEvent& other) { calculateMetaAttributes(other); } -// returns the degrees between two points (note: 0 degrees is 'east') +// returns the angle (in degrees) between two points (note: 0 degrees is 'east') float angleBetweenPoints(const glm::vec2& a, const glm::vec2& b ) { glm::vec2 length = b - a; - float radian = std::atan2(length.y, length.x); - float angle = radian * 180.0f / PIE; + float angle = DEGREES_PER_RADIAN * std::atan2(length.y, length.x); if (angle < 0) { angle += 360.0f; }; diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h index 5cd31f190c..f5a4fa5c9b 100644 --- a/libraries/script-engine/src/EventTypes.h +++ b/libraries/script-engine/src/EventTypes.h @@ -74,6 +74,8 @@ public: float radius; bool isPinching; bool isPinchOpening; + + // angles are in degrees QVector angles; // angle from center to each point float angle; // the average of the angles float deltaAngle; // the change in average angle from last event diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 78ea73987b..14025f0c67 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -22,8 +22,8 @@ glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) { return q1 * q2; } -glm::quat Quat::fromVec3(const glm::vec3& vec3) { - return glm::quat(vec3); +glm::quat Quat::fromVec3(const glm::vec3& eulerAngles) { + return glm::quat(glm::radians(eulerAngles)); } glm::quat Quat::fromPitchYawRoll(float pitch, float yaw, float roll) { @@ -47,11 +47,11 @@ glm::vec3 Quat::getUp(const glm::quat& orientation) { } glm::vec3 Quat::safeEulerAngles(const glm::quat& orientation) { - return ::safeEulerAngles(orientation); + return glm::degrees(::safeEulerAngles(orientation)); } glm::quat Quat::angleAxis(float angle, const glm::vec3& v) { - return glm::angleAxis(angle, v); + return glm::angleAxis(glm::radians(angle), v); } glm::quat Quat::mix(const glm::quat& q1, const glm::quat& q2, float alpha) { diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index f006374347..3e5f46682c 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -24,13 +24,13 @@ class Quat : public QObject { public slots: glm::quat multiply(const glm::quat& q1, const glm::quat& q2); glm::quat fromVec3(const glm::vec3& vec3); - glm::quat fromPitchYawRoll(float pitch, float yaw, float roll); + glm::quat fromPitchYawRoll(float pitch, float yaw, float roll); // degrees glm::quat inverse(const glm::quat& q); glm::vec3 getFront(const glm::quat& orientation); glm::vec3 getRight(const glm::quat& orientation); glm::vec3 getUp(const glm::quat& orientation); - glm::vec3 safeEulerAngles(const glm::quat& orientation); - glm::quat angleAxis(float angle, const glm::vec3& v); + glm::vec3 safeEulerAngles(const glm::quat& orientation); // degrees + glm::quat angleAxis(float angle, const glm::vec3& v); // degrees glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha); void print(const QString& lable, const glm::quat& q); }; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index f80c56fd0c..70fac3efe4 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -501,27 +501,27 @@ int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm } -int packFloatAngleToTwoByte(unsigned char* buffer, float angle) { - const float ANGLE_CONVERSION_RATIO = (std::numeric_limits::max() / 360.0); +int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) { + const float ANGLE_CONVERSION_RATIO = (std::numeric_limits::max() / 360.f); - uint16_t angleHolder = floorf((angle + 180) * ANGLE_CONVERSION_RATIO); + uint16_t angleHolder = floorf((degrees + 180.f) * ANGLE_CONVERSION_RATIO); memcpy(buffer, &angleHolder, sizeof(uint16_t)); return sizeof(uint16_t); } int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer) { - *destinationPointer = (*byteAnglePointer / (float) std::numeric_limits::max()) * 360.0 - 180; + *destinationPointer = (*byteAnglePointer / (float) std::numeric_limits::max()) * 360.f - 180.f; return sizeof(uint16_t); } int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput) { - const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits::max() / 2.0); + const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits::max() / 2.f); uint16_t quatParts[4]; - quatParts[0] = floorf((quatInput.x + 1.0) * QUAT_PART_CONVERSION_RATIO); - quatParts[1] = floorf((quatInput.y + 1.0) * QUAT_PART_CONVERSION_RATIO); - quatParts[2] = floorf((quatInput.z + 1.0) * QUAT_PART_CONVERSION_RATIO); - quatParts[3] = floorf((quatInput.w + 1.0) * QUAT_PART_CONVERSION_RATIO); + quatParts[0] = floorf((quatInput.x + 1.f) * QUAT_PART_CONVERSION_RATIO); + quatParts[1] = floorf((quatInput.y + 1.f) * QUAT_PART_CONVERSION_RATIO); + quatParts[2] = floorf((quatInput.z + 1.f) * QUAT_PART_CONVERSION_RATIO); + quatParts[3] = floorf((quatInput.w + 1.f) * QUAT_PART_CONVERSION_RATIO); memcpy(buffer, &quatParts, sizeof(quatParts)); return sizeof(quatParts); @@ -531,16 +531,16 @@ int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatO uint16_t quatParts[4]; memcpy(&quatParts, buffer, sizeof(quatParts)); - quatOutput.x = ((quatParts[0] / (float) std::numeric_limits::max()) * 2.0) - 1.0; - quatOutput.y = ((quatParts[1] / (float) std::numeric_limits::max()) * 2.0) - 1.0; - quatOutput.z = ((quatParts[2] / (float) std::numeric_limits::max()) * 2.0) - 1.0; - quatOutput.w = ((quatParts[3] / (float) std::numeric_limits::max()) * 2.0) - 1.0; + quatOutput.x = ((quatParts[0] / (float) std::numeric_limits::max()) * 2.f) - 1.f; + quatOutput.y = ((quatParts[1] / (float) std::numeric_limits::max()) * 2.f) - 1.f; + quatOutput.z = ((quatParts[2] / (float) std::numeric_limits::max()) * 2.f) - 1.f; + quatOutput.w = ((quatParts[3] / (float) std::numeric_limits::max()) * 2.f) - 1.f; return sizeof(quatParts); } -float SMALL_LIMIT = 10.0; -float LARGE_LIMIT = 1000.0; +float SMALL_LIMIT = 10.f; +float LARGE_LIMIT = 1000.f; int packFloatRatioToTwoByte(unsigned char* buffer, float ratio) { // if the ratio is less than 10, then encode it as a positive number scaled from 0 to int16::max() @@ -642,24 +642,24 @@ glm::vec3 safeEulerAngles(const glm::quat& q) { float sy = 2.0f * (q.y * q.w - q.x * q.z); if (sy < 1.0f - EPSILON) { if (sy > -1.0f + EPSILON) { - return glm::degrees(glm::vec3( + return glm::vec3( atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)), asinf(sy), - atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)))); + atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z))); } else { // not a unique solution; x + z = atan2(-m21, m11) - return glm::degrees(glm::vec3( + return glm::vec3( 0.0f, - PIf * -0.5f, - atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); + - PI_OVER_TWO, + atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))); } } else { // not a unique solution; x - z = atan2(-m21, m11) - return glm::degrees(glm::vec3( + return glm::vec3( 0.0f, - PIf * 0.5f, - -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); + PI_OVER_TWO, + -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))); } } diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index b08e0413a6..1695b3b253 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -47,9 +47,13 @@ static const float ZERO = 0.0f; static const float ONE = 1.0f; static const float ONE_HALF = 0.5f; static const float ONE_THIRD = 0.333333f; -static const float PIE = 3.141592f; -static const float PI_TIMES_TWO = 3.141592f * 2.0f; -static const float PI_OVER_180 = 3.141592f / 180.0f; + +static const float PI = 3.14159265358979f; +static const float TWO_PI = 2.f * PI; +static const float PI_OVER_TWO = ONE_HALF * PI; +static const float RADIANS_PER_DEGREE = PI / 180.0f; +static const float DEGREES_PER_RADIAN = 180.0f / PI; + static const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations static const float SQUARE_ROOT_OF_2 = (float)sqrt(2.f); static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.f); @@ -133,8 +137,8 @@ bool isBetween(int64_t value, int64_t max, int64_t min); // These pack/unpack functions are designed to start specific known types in as efficient a manner // as possible. Taking advantage of the known characteristics of the semantic types. -// Angles are known to be between 0 and 360deg, this allows us to encode in 16bits with great accuracy -int packFloatAngleToTwoByte(unsigned char* buffer, float angle); +// Angles are known to be between 0 and 360 degrees, this allows us to encode in 16bits with great accuracy +int packFloatAngleToTwoByte(unsigned char* buffer, float degrees); int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer); // Orientation Quats are known to have 4 normalized components be between -1.0 and 1.0 @@ -164,10 +168,7 @@ int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, flo int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix); int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix); -#ifndef PIf -#define PIf 3.14159265f -#endif - +/// \return vec3 with euler angles in radians glm::vec3 safeEulerAngles(const glm::quat& q); #endif /* defined(__hifi__SharedUtil__) */ diff --git a/tests/physics/src/CollisionInfoTests.cpp b/tests/physics/src/CollisionInfoTests.cpp index 813100944b..43601978ae 100644 --- a/tests/physics/src/CollisionInfoTests.cpp +++ b/tests/physics/src/CollisionInfoTests.cpp @@ -25,7 +25,7 @@ void CollisionInfoTests::rotateThenTranslate() { collision._contactPoint = yAxis; collision._addedVelocity = xAxis + yAxis + zAxis; - glm::quat rotation = glm::angleAxis(rightAngle, zAxis); + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); float distance = 3.f; glm::vec3 translation = distance * yAxis; @@ -64,7 +64,7 @@ void CollisionInfoTests::translateThenRotate() { collision._contactPoint = yAxis; collision._addedVelocity = xAxis + yAxis + zAxis; - glm::quat rotation = glm::angleAxis( -rightAngle, zAxis); + glm::quat rotation = glm::angleAxis( -PI_OVER_TWO, zAxis); float distance = 3.f; glm::vec3 translation = distance * yAxis; diff --git a/tests/physics/src/PhysicsTestUtil.h b/tests/physics/src/PhysicsTestUtil.h index dcbeaed346..c4c7962466 100644 --- a/tests/physics/src/PhysicsTestUtil.h +++ b/tests/physics/src/PhysicsTestUtil.h @@ -18,8 +18,6 @@ const glm::vec3 xAxis(1.f, 0.f, 0.f); const glm::vec3 yAxis(0.f, 1.f, 0.f); const glm::vec3 zAxis(0.f, 0.f, 1.f); -const float rightAngle = 90.f; // degrees - std::ostream& operator<<(std::ostream& s, const glm::vec3& v); std::ostream& operator<<(std::ostream& s, const glm::quat& q); std::ostream& operator<<(std::ostream& s, const glm::mat4& m); diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 314498b1d6..79de9f9175 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -485,7 +485,7 @@ void ShapeColliderTests::capsuleMissesCapsule() { } // rotate B and move it to the side - glm::quat rotation = glm::angleAxis(rightAngle, zAxis); + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); capsuleB.setPosition((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); if (ShapeCollider::shapeShape(&capsuleA, &capsuleB, collisions)) @@ -566,7 +566,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { } { // rotate B and move it to the side - glm::quat rotation = glm::angleAxis(rightAngle, zAxis); + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); capsuleB.setPosition((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); @@ -590,7 +590,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { { // again, but this time check collision details float overlap = 0.1f; - glm::quat rotation = glm::angleAxis(rightAngle, zAxis); + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); glm::vec3 positionB = ((totalRadius + capsuleB.getHalfHeight()) - overlap) * xAxis; capsuleB.setPosition(positionB); @@ -657,7 +657,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { { // collide cylinder wall against cylinder wall float overlap = 0.137f; float shift = 0.317f * halfHeightA; - glm::quat rotation = glm::angleAxis(rightAngle, zAxis); + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); glm::vec3 positionB = (totalRadius - overlap) * zAxis + shift * yAxis; capsuleB.setPosition(positionB); From 6fcb802a9439ed2835c2cf4e3ce710ae551b1299 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 11 Mar 2014 18:01:38 -0700 Subject: [PATCH 072/109] Added preview deplacement + fixed locking issue --- examples/addVoxelOnMouseClickExample.js | 20 +++++++++--------- examples/editVoxels.js | 27 +++++++++++++++++-------- interface/src/ui/LocalVoxelsOverlay.cpp | 4 ++-- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/examples/addVoxelOnMouseClickExample.js b/examples/addVoxelOnMouseClickExample.js index 4450a16c22..244a017ae4 100644 --- a/examples/addVoxelOnMouseClickExample.js +++ b/examples/addVoxelOnMouseClickExample.js @@ -15,16 +15,16 @@ function mousePressEvent(event) { var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Voxels.findRayIntersection(pickRay); if (intersection.intersects) { - + // Note: due to the current C++ "click on voxel" behavior, these values may be the animated color for the voxel - print("clicked on voxel.red/green/blue=" + intersection.voxel.red + ", " + print("clicked on voxel.red/green/blue=" + intersection.voxel.red + ", " + intersection.voxel.green + ", " + intersection.voxel.blue); - print("clicked on voxel.x/y/z/s=" + intersection.voxel.x + ", " + print("clicked on voxel.x/y/z/s=" + intersection.voxel.x + ", " + intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s); print("clicked on face=" + intersection.face); print("clicked on distance=" + intersection.distance); - - var newVoxel = { + + var newVoxel = { x: intersection.voxel.x, y: intersection.voxel.y, z: intersection.voxel.z, @@ -32,7 +32,7 @@ function mousePressEvent(event) { red: 255, green: 0, blue: 255 }; - + if (intersection.face == "MIN_X_FACE") { newVoxel.x -= newVoxel.s; } else if (intersection.face == "MAX_X_FACE") { @@ -46,11 +46,11 @@ function mousePressEvent(event) { } else if (intersection.face == "MAX_Z_FACE") { newVoxel.z += newVoxel.s; } - - print("Voxels.setVoxel("+newVoxel.x + ", " - + newVoxel.y + ", " + newVoxel.z + ", " + newVoxel.s + ", " + + print("Voxels.setVoxel("+newVoxel.x + ", " + + newVoxel.y + ", " + newVoxel.z + ", " + newVoxel.s + ", " + newVoxel.red + ", " + newVoxel.green + ", " + newVoxel.blue + ")" ); - + Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue); } } diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 960840b680..d78efdaad3 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -972,7 +972,6 @@ function mousePressEvent(event) { print("placing import..."); placeImport(); showImport(false); - pasteMode = false; moveTools(); return; } @@ -1177,7 +1176,6 @@ function menuItemEvent(menuItem) { print("importing..."); if (importVoxels()) { showImport(true); - pasteMode = true; } moveTools(); } @@ -1512,14 +1510,27 @@ function checkControllers() { } function update(deltaTime) { - var newWindowDimensions = Controller.getViewportDimensions(); - if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) { - windowDimensions = newWindowDimensions; - moveTools(); - } - if (editToolsOn) { + var newWindowDimensions = Controller.getViewportDimensions(); + if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) { + windowDimensions = newWindowDimensions; + moveTools(); + } + checkControllers(); + + // Move Import Preview + if (isImporting) { + var position = MyAvatar.position; + var forwardVector = Quat.getFront(MyAvatar.orientation); + var targetPosition = Vec3.sum(position, Vec3.multiply(forwardVector, importScale)); + var newPosition = { + x: Math.floor(targetPosition.x / importScale) * importScale, + y: Math.floor(targetPosition.y / importScale) * importScale, + z: Math.floor(targetPosition.z / importScale) * importScale + } + moveImport(newPosition); + } } } diff --git a/interface/src/ui/LocalVoxelsOverlay.cpp b/interface/src/ui/LocalVoxelsOverlay.cpp index 248a5b6733..fde03b85cb 100644 --- a/interface/src/ui/LocalVoxelsOverlay.cpp +++ b/interface/src/ui/LocalVoxelsOverlay.cpp @@ -40,12 +40,12 @@ void LocalVoxelsOverlay::update(float deltatime) { _voxelSystem->init(); } + _tree->lockForWrite(); if (_visible && _voxelCount != _tree->getOctreeElementsCount()) { _voxelCount = _tree->getOctreeElementsCount(); - _tree->lockForWrite(); _voxelSystem->forceRedrawEntireTree(); - _tree->unlock(); } + _tree->unlock(); } void LocalVoxelsOverlay::render() { From 03d7fb147c6913945d0e0f6bda999d93c1e016e2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 11 Mar 2014 18:15:04 -0700 Subject: [PATCH 073/109] Fixed spaces added everywhere --- examples/editVoxels.js | 184 ++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index d78efdaad3..b5702dff47 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -8,10 +8,10 @@ // Captures mouse clicks and edits voxels accordingly. // // click = create a new voxel on this face, same color as old (default color picker state) -// right click or control + click = delete this voxel +// right click or control + click = delete this voxel // shift + click = recolor this voxel // 1 - 8 = pick new color from palette -// 9 = create a new voxel in front of the camera +// 9 = create a new voxel in front of the camera // // Click and drag to create more new voxels in the same direction // @@ -36,19 +36,19 @@ var previewLineWidth = 1.5; var oldMode = Camera.getMode(); -var isAdding = false; -var isExtruding = false; +var isAdding = false; +var isExtruding = false; var isOrbiting = false; var isOrbitingFromTouch = false; var isPanning = false; var isPanningFromTouch = false; var touchPointsToOrbit = 2; // you can change these, but be mindful that on some track pads 2 touch points = right click+drag -var touchPointsToPan = 3; +var touchPointsToPan = 3; var orbitAzimuth = 0.0; var orbitAltitude = 0.0; var orbitCenter = { x: 0, y: 0, z: 0 }; var orbitPosition = { x: 0, y: 0, z: 0 }; -var torsoToEyeVector = { x: 0, y: 0, z: 0 }; +var torsoToEyeVector = { x: 0, y: 0, z: 0 }; var orbitRadius = 0.0; var extrudeDirection = { x: 0, y: 0, z: 0 }; var extrudeScale = 0.0; @@ -60,7 +60,7 @@ var wheelPixelsMoved = 0; var mouseX = 0; -var mouseY = 0; +var mouseY = 0; // Create a table of the different colors you can choose var colors = new Array(); @@ -76,7 +76,7 @@ colors[8] = { red: 31, green: 64, blue: 64 }; var numColors = 9; var whichColor = -1; // Starting color is 'Copy' mode -// Create sounds for adding, deleting, recoloring voxels +// Create sounds for adding, deleting, recoloring voxels var addSound1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+2.raw"); var addSound2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+3.raw"); var addSound3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+4.raw"); @@ -93,7 +93,7 @@ var editToolsOn = true; // starts out off // previewAsVoxel - by default, we will preview adds/deletes/recolors as just 4 lines on the intersecting face. But if you // the preview to show a full voxel then set this to true and the voxel will be displayed for voxel editing -var previewAsVoxel = true; +var previewAsVoxel = false; var voxelPreview = Overlays.addOverlay("cube", { position: { x: 0, y: 0, z: 0}, @@ -104,7 +104,7 @@ var voxelPreview = Overlays.addOverlay("cube", { visible: false, lineWidth: 4 }); - + var linePreviewTop = Overlays.addOverlay("line3d", { position: { x: 0, y: 0, z: 0}, end: { x: 0, y: 0, z: 0}, @@ -228,7 +228,7 @@ var eyedropperTool = Overlays.addOverlay("image", { visible: false, alpha: 0.9 }); - + // This will create a couple of image overlays that make a "slider", we will demonstrate how to trap mouse messages to // move the slider @@ -392,7 +392,7 @@ function calcScaleFromThumb(newThumbX) { // now reset the display accordingly... calcThumbFromScale(pointerVoxelScale); - + // if the user moved the thumb, then they are fixing the voxel scale pointerVoxelScaleSet = true; } @@ -466,27 +466,27 @@ function calculateVoxelFromIntersection(intersection, operation) { var wantDebug = false; if (wantDebug) { - print(">>>>> calculateVoxelFromIntersection().... intersection voxel.red/green/blue=" + intersection.voxel.red + ", " + print(">>>>> calculateVoxelFromIntersection().... intersection voxel.red/green/blue=" + intersection.voxel.red + ", " + intersection.voxel.green + ", " + intersection.voxel.blue); - print(" intersection voxel.x/y/z/s=" + intersection.voxel.x + ", " + print(" intersection voxel.x/y/z/s=" + intersection.voxel.x + ", " + intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s); print(" intersection face=" + intersection.face); print(" intersection distance=" + intersection.distance); - print(" intersection intersection.x/y/z=" + intersection.intersection.x + ", " + print(" intersection intersection.x/y/z=" + intersection.intersection.x + ", " + intersection.intersection.y + ", " + intersection.intersection.z); } - + var voxelSize; if (pointerVoxelScaleSet) { - voxelSize = pointerVoxelScale; + voxelSize = pointerVoxelScale; } else { - voxelSize = intersection.voxel.s; + voxelSize = intersection.voxel.s; } var x; var y; var z; - + // if our "target voxel size" is larger than the voxel we intersected with, then we need to find the closest // ancestor voxel of our target size that contains our intersected voxel. if (voxelSize > intersection.voxel.s) { @@ -527,7 +527,7 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantAddAdjust) { resultVoxel.x -= voxelSize; } - + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; @@ -551,7 +551,7 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantAddAdjust) { resultVoxel.y -= voxelSize; } - + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; @@ -563,7 +563,7 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantAddAdjust) { resultVoxel.y += voxelSize; } - + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust}; resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; @@ -575,7 +575,7 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantAddAdjust) { resultVoxel.z -= voxelSize; } - + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; @@ -594,7 +594,7 @@ function calculateVoxelFromIntersection(intersection, operation) { resultVoxel.topRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; } - + return resultVoxel; } @@ -604,7 +604,7 @@ function showPreviewVoxel() { var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); var intersection = Voxels.findRayIntersection(pickRay); - // if the user hasn't updated the + // if the user hasn't updated the if (!pointerVoxelScaleSet) { calcThumbFromScale(intersection.voxel.s); } @@ -668,10 +668,10 @@ function showPreviewLines() { } var intersection = Voxels.findRayIntersection(pickRay); - + if (intersection.intersects) { - // if the user hasn't updated the + // if the user hasn't updated the if (!pointerVoxelScaleSet) { calcThumbFromScale(intersection.voxel.s); } @@ -788,14 +788,14 @@ function trackKeyReleaseEvent(event) { trackAsOrbitOrPan = false; moveTools(); } - + // on F1 toggle the preview mode between cubes and lines if (event.text == "F1") { previewAsVoxel = !previewAsVoxel; } showPreviewGuides(); - } + } } function startOrbitMode(event) { @@ -804,7 +804,7 @@ function startOrbitMode(event) { var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Voxels.findRayIntersection(pickRay); - // start orbit camera! + // start orbit camera! var cameraPosition = Camera.getPosition(); torsoToEyeVector = Vec3.subtract(cameraPosition, MyAvatar.position); torsoToEyeVector.x = 0.0; @@ -813,12 +813,12 @@ function startOrbitMode(event) { Camera.setMode("independent"); Camera.keepLookingAt(intersection.intersection); // get position for initial azimuth, elevation - orbitCenter = intersection.intersection; + orbitCenter = intersection.intersection; var orbitVector = Vec3.subtract(cameraPosition, orbitCenter); - orbitRadius = Vec3.length(orbitVector); + orbitRadius = Vec3.length(orbitVector); orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x); orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector)); - + //print("startOrbitMode..."); } @@ -826,17 +826,17 @@ function handleOrbitingMove(event) { var cameraOrientation = Camera.getOrientation(); var origEulers = Quat.safeEulerAngles(cameraOrientation); var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation)); - var dx = event.x - mouseX; + var dx = event.x - mouseX; var dy = event.y - mouseY; orbitAzimuth += dx / ORBIT_RATE_AZIMUTH; orbitAltitude += dy / ORBIT_RATE_ALTITUDE; - var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius, + var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius, y:Math.sin(orbitAltitude) * orbitRadius, - z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius }; + z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius }; orbitPosition = Vec3.sum(orbitCenter, orbitVector); Camera.setPosition(orbitPosition); - mouseX = event.x; + mouseX = event.x; mouseY = event.y; //print("handleOrbitingMove..."); } @@ -852,7 +852,7 @@ function endOrbitMode(event) { } function startPanMode(event, intersection) { - // start pan camera! + // start pan camera! print("handle PAN mode!!!"); } @@ -870,14 +870,14 @@ function mousePressEvent(event) { // if our tools are off, then don't do anything if (!editToolsOn) { - return; + return; } - - // Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence. - // but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to + + // Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence. + // but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to // cancel any touch mode, and then let the right-click through if (isOrbitingFromTouch || isPanningFromTouch) { - + // if the user is holding the ALT key AND they are clicking the RIGHT button (or on multi-touch doing a two // finger touch, then we want to let the new panning behavior take over. // if it's any other case we still want to bail @@ -894,15 +894,15 @@ function mousePressEvent(event) { } // let things fall through } else { - return; + return; } } - + // no clicking on overlays while in panning mode if (!trackAsOrbitOrPan) { var clickedOnSomething = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - + // If the user clicked on the thumb, handle the slider logic if (clickedOverlay == thumb) { @@ -959,7 +959,7 @@ function mousePressEvent(event) { return; // no further processing } } - + // TODO: does any of this stuff need to execute if we're panning or orbiting? trackMouseEvent(event); // used by preview support mouseX = event.x; @@ -985,11 +985,11 @@ function mousePressEvent(event) { } if (intersection.intersects) { - // if the user hasn't updated the + // if the user hasn't updated the if (!pointerVoxelScaleSet) { calcThumbFromScale(intersection.voxel.s); } - + // Note: touch and mouse events can cross paths, so we want to ignore any mouse events that would // start a pan or orbit if we're already doing a pan or orbit via touch... if ((event.isAlt || trackAsOrbitOrPan) && !(isOrbitingFromTouch || isPanningFromTouch)) { @@ -1013,14 +1013,14 @@ function mousePressEvent(event) { colors[whichColor].blue = intersection.voxel.blue; moveTools(); } - + } else if (recolorToolSelected || trackAsRecolor) { // Recolor Voxel voxelDetails = calculateVoxelFromIntersection(intersection,"recolor"); // doing this erase then set will make sure we only recolor just the target voxel Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); - Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, + Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue); Audio.playSound(changeColorSound, audioOptions); Overlays.editOverlay(voxelPreview, { visible: false }); @@ -1028,17 +1028,17 @@ function mousePressEvent(event) { // Add voxel on face if (whichColor == -1) { // Copy mode - use clicked voxel color - newColor = { + newColor = { red: intersection.voxel.red, green: intersection.voxel.green, blue: intersection.voxel.blue }; } else { - newColor = { + newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue }; } - + voxelDetails = calculateVoxelFromIntersection(intersection,"add"); Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s, @@ -1048,11 +1048,11 @@ function mousePressEvent(event) { lastVoxelScale = voxelDetails.s; playRandomAddSound(audioOptions); - + Overlays.editOverlay(voxelPreview, { visible: false }); dragStart = { x: event.x, y: event.y }; isAdding = true; - } + } } } @@ -1071,14 +1071,14 @@ function keyPressEvent(event) { Audio.playSound(clickSound, audioOptions); moveTools(); } else if (event.text == "0") { - // Create a brand new 1 meter voxel in front of your avatar - var color = whichColor; + // Create a brand new 1 meter voxel in front of your avatar + var color = whichColor; if (color == -1) color = 0; var newPosition = getNewVoxelPosition(); - var newVoxel = { + var newVoxel = { x: newPosition.x, y: newPosition.y , - z: newPosition.z, + z: newPosition.z, s: NEW_VOXEL_SIZE, red: colors[color].red, green: colors[color].green, @@ -1089,7 +1089,7 @@ function keyPressEvent(event) { playRandomAddSound(audioOptions); } } - + trackKeyPressEvent(event); // used by preview support } @@ -1167,7 +1167,7 @@ function menuItemEvent(menuItem) { Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } } - + if (menuItem == "Export Voxels") { print("export"); Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); @@ -1191,11 +1191,11 @@ function mouseMoveEvent(event) { return; } - // if we're panning or orbiting from touch, ignore these... because our touch takes precedence. + // if we're panning or orbiting from touch, ignore these... because our touch takes precedence. if (isOrbitingFromTouch || isPanningFromTouch) { - return; + return; } - + // double check that we didn't accidentally miss a pan or orbit click request if (trackAsOrbitOrPan && !isPanning && !isOrbiting) { if (event.isLeftButton && !event.isRightButton) { @@ -1217,7 +1217,7 @@ function mouseMoveEvent(event) { thumbX = maxThumbX; } calcScaleFromThumb(thumbX); - + } else if (isOrbiting) { handleOrbitingMove(event); } else if (isPanning) { @@ -1226,8 +1226,8 @@ function mouseMoveEvent(event) { // Watch the drag direction to tell which way to 'extrude' this voxel if (!isExtruding) { var pickRay = Camera.computePickRay(event.x, event.y); - var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x, - y: pickRay.origin.y - lastVoxelPosition.y, + var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x, + y: pickRay.origin.y - lastVoxelPosition.y, z: pickRay.origin.z - lastVoxelPosition.z }; var distance = Vec3.length(lastVoxelDistance); var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance }; @@ -1246,22 +1246,22 @@ function mouseMoveEvent(event) { else if (dy < -lastVoxelScale) extrudeDirection.y = -extrudeScale; else if (dz > lastVoxelScale) extrudeDirection.z = extrudeScale; else if (dz < -lastVoxelScale) extrudeDirection.z = -extrudeScale; - else isExtruding = false; + else isExtruding = false; } else { // We have got an extrusion direction, now look for mouse move beyond threshold to add new voxel - var dx = event.x - mouseX; + var dx = event.x - mouseX; var dy = event.y - mouseY; if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) { lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection); Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); - Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, + Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue); mouseX = event.x; mouseY = event.y; } } } - + // update the add voxel/delete voxel overlay preview trackMouseEvent(event); } @@ -1269,13 +1269,13 @@ function mouseMoveEvent(event) { function mouseReleaseEvent(event) { // if our tools are off, then don't do anything if (!editToolsOn) { - return; + return; } if (isMovingSlider) { isMovingSlider = false; } - + if (isOrbiting) { endOrbitMode(event); isOrbiting = false; @@ -1286,7 +1286,7 @@ function mouseReleaseEvent(event) { isPanning = false; } isAdding = false; - isExtruding = false; + isExtruding = false; } function moveTools() { @@ -1314,7 +1314,7 @@ function moveTools() { if (s == (numColors - 1)) { extraWidth = swatchExtraPadding; } - + Overlays.editOverlay(swatches[s], { x: swatchX, y: swatchesY, @@ -1378,16 +1378,16 @@ function touchBeginEvent(event) { if (!editToolsOn) { return; } - + // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... if (isOrbiting || isPanning) { return; - } - + } + if (event.isAlt || trackAsOrbitOrPan) { if (event.touchPoints == touchPointsToOrbit) { // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... + // come in as 2 then 3 touches... if (isPanningFromTouch) { print("touchBeginEvent... calling endPanMode()"); endPanMode(event); @@ -1397,7 +1397,7 @@ function touchBeginEvent(event) { isOrbitingFromTouch = true; } else if (event.touchPoints == touchPointsToPan) { // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... + // come in as 2 then 3 touches... if (isOrbitingFromTouch) { endOrbitMode(event); isOrbitingFromTouch = false; @@ -1416,11 +1416,11 @@ function touchUpdateEvent(event) { // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... if (isOrbiting || isPanning) { return; - } - + } + if (isOrbitingFromTouch) { // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... + // come in as 2 then 3 touches... if (event.touchPoints == touchPointsToPan) { //print("we now have touchPointsToPan touches... switch to pan..."); endOrbitMode(event); @@ -1434,7 +1434,7 @@ function touchUpdateEvent(event) { if (isPanningFromTouch) { //print("touchUpdateEvent... isPanningFromTouch... event.touchPoints=" + event.touchPoints); // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... + // come in as 2 then 3 touches... if (event.touchPoints == touchPointsToOrbit) { //print("we now have touchPointsToOrbit touches... switch to orbit..."); //print("touchUpdateEvent... calling endPanMode()"); @@ -1457,8 +1457,8 @@ function touchEndEvent(event) { // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... if (isOrbiting || isPanning) { return; - } - + } + if (isOrbitingFromTouch) { endOrbitMode(event); isOrbitingFromTouch = false; @@ -1476,10 +1476,10 @@ var lastFingerDeleteVoxel = { x: -1, y: -1, z: -1}; // off of the build-able are function checkControllers() { var controllersPerPalm = 2; // palm and finger for (var palm = 0; palm < 2; palm++) { - var palmController = palm * controllersPerPalm; - var fingerTipController = palmController + 1; + var palmController = palm * controllersPerPalm; + var fingerTipController = palmController + 1; var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController); - + var BUTTON_COUNT = 6; var BUTTON_BASE = palm * BUTTON_COUNT; var BUTTON_1 = BUTTON_BASE + 1; @@ -1497,7 +1497,7 @@ function checkControllers() { Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE); Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE, newColor.red, newColor.green, newColor.blue); - + lastFingerAddVoxel = fingerTipPosition; } } else if (Controller.isButtonPressed(BUTTON_2)) { @@ -1546,7 +1546,7 @@ function wheelEvent(event) { pointerVoxelScale /= 2.0; if (pointerVoxelScale < MIN_VOXEL_SCALE) { pointerVoxelScale = MIN_VOXEL_SCALE; - } + } } else { pointerVoxelScale *= 2.0; if (pointerVoxelScale > MAX_VOXEL_SCALE) { From f5cdb98efb0d67d8a63a8e2da7ad4d207cacc2c4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 20:32:02 -0700 Subject: [PATCH 074/109] switch ReceivedPacketProcessor to using QWaitCondition --- libraries/shared/src/GenericThread.cpp | 2 ++ libraries/shared/src/GenericThread.h | 2 ++ libraries/shared/src/ReceivedPacketProcessor.cpp | 13 ++++++++++--- libraries/shared/src/ReceivedPacketProcessor.h | 6 ++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index ff371e0f32..59f2426bdb 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -44,6 +44,8 @@ void GenericThread::initialize(bool isThreaded) { void GenericThread::terminate() { if (_isThreaded) { _stopThread = true; + + terminating(); if (_thread) { _thread->wait(); diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 76ceed83cb..1b5b05db5d 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -33,6 +33,8 @@ public: /// Override this function to do whatever your class actually does, return false to exit thread early. virtual bool process() = 0; + virtual void terminating() { }; // lets your subclass know we're terminating, and it should respond appropriately + bool isThreaded() const { return _isThreaded; } public slots: diff --git a/libraries/shared/src/ReceivedPacketProcessor.cpp b/libraries/shared/src/ReceivedPacketProcessor.cpp index b966109903..419e79881f 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.cpp +++ b/libraries/shared/src/ReceivedPacketProcessor.cpp @@ -16,6 +16,10 @@ ReceivedPacketProcessor::ReceivedPacketProcessor() { _dontSleep = false; } +void ReceivedPacketProcessor::terminating() { + _hasPackets.wakeAll(); +} + void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& destinationNode, const QByteArray& packet) { // Make sure our Node and NodeList knows we've heard from this node. destinationNode->setLastHeardMicrostamp(usecTimestampNow()); @@ -24,6 +28,9 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& desti lock(); _packets.push_back(networkPacket); unlock(); + + // Make sure to wake our actual processing thread because we now have packets for it to process. + _hasPackets.wakeAll(); } bool ReceivedPacketProcessor::process() { @@ -31,11 +38,11 @@ bool ReceivedPacketProcessor::process() { // If a derived class handles process sleeping, like the JurisdiciontListener, then it can set // this _dontSleep member and we will honor that request. if (_packets.size() == 0 && !_dontSleep) { - const quint64 RECEIVED_THREAD_SLEEP_INTERVAL = (1000 * 1000)/60; // check at 60fps - usleep(RECEIVED_THREAD_SLEEP_INTERVAL); + _waitingOnPacketsMutex.lock(); + _hasPackets.wait(&_waitingOnPacketsMutex); + _waitingOnPacketsMutex.unlock(); } while (_packets.size() > 0) { - lock(); // lock to make sure nothing changes on us NetworkPacket& packet = _packets.front(); // get the oldest packet NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us diff --git a/libraries/shared/src/ReceivedPacketProcessor.h b/libraries/shared/src/ReceivedPacketProcessor.h index 043dd6c6c4..4088422561 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.h +++ b/libraries/shared/src/ReceivedPacketProcessor.h @@ -11,6 +11,8 @@ #ifndef __shared__ReceivedPacketProcessor__ #define __shared__ReceivedPacketProcessor__ +#include + #include "GenericThread.h" #include "NetworkPacket.h" @@ -43,11 +45,15 @@ protected: /// Implements generic processing behavior for this thread. virtual bool process(); + virtual void terminating(); + bool _dontSleep; private: std::vector _packets; + QWaitCondition _hasPackets; + QMutex _waitingOnPacketsMutex; }; #endif // __shared__PacketReceiver__ From 4d0b762de3fb5f2a0436ee6c73cc71576c4e60bc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 20:33:53 -0700 Subject: [PATCH 075/109] some hacking on full scene debuggin --- .../src/octree/OctreeSendThread.cpp | 18 ++++++++++++++++-- interface/src/Application.cpp | 18 ++++++++++++++++-- interface/src/Application.h | 2 ++ libraries/octree/src/OctreeSceneStats.cpp | 3 +++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 751583960e..7a46fa3722 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -366,8 +366,20 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue } // start tracking our stats - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) - && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); + qDebug() << "----"; + qDebug() << "viewFrustumChanged=" << viewFrustumChanged; + qDebug() << "nodeData->getWantDelta()=" << nodeData->getWantDelta(); + qDebug() << "nodeData->getViewFrustumJustStoppedChanging()=" << nodeData->getViewFrustumJustStoppedChanging(); + qDebug() << "nodeData->hasLodChanged()=" << nodeData->hasLodChanged(); + + + bool isFullScene = ( + (!viewFrustumChanged || !nodeData->getWantDelta()) + && nodeData->getViewFrustumJustStoppedChanging() + ) + || nodeData->hasLodChanged(); + + qDebug() << "isFullScene=" << isFullScene; // If we're starting a full scene, then definitely we want to empty the nodeBag if (isFullScene) { @@ -436,6 +448,8 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); + qDebug() << "SECOND CALC.... isFullScene=" << isFullScene; + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 032e849f0a..f6cdfaa509 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -141,6 +141,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _justStarted(true), _voxelImporter(NULL), _wantToKillLocalVoxels(false), + _viewFrustum(), + _lastQueriedViewFrustum(), + _lastQueriedTime(usecTimestampNow()), _audioScope(256, 200, true), _myAvatar(), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), @@ -1917,8 +1920,19 @@ void Application::updateMyAvatar(float deltaTime) { loadViewFrustum(_myCamera, _viewFrustum); // Update my voxel servers with my current voxel query... - queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions); - queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions); + quint64 now = usecTimestampNow(); + const quint64 TOO_LONG_SINCE_LAST_QUERY = 1 * USECS_PER_SECOND; + + // if we haven't waited long enough and the frustum is similar enough, then surpress this query... + if ((now - _lastQueriedTime) > TOO_LONG_SINCE_LAST_QUERY || !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum)) { + _lastQueriedTime = now; + queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions); + queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions); + _lastQueriedViewFrustum = _viewFrustum; + //qDebug() << ">>>>>>>>>> SENDING query..."; + } else { + //qDebug() << "suppress query..."; + } } void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 234f264447..f56bb62d9f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -377,6 +377,8 @@ private: MetavoxelSystem _metavoxels; ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. + ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels, particles) + quint64 _lastQueriedTime; Oscilloscope _audioScope; diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index ad7f774377..ec073c6ce5 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -167,6 +167,9 @@ void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, OctreeEleme _totalInternal = OctreeElement::getInternalNodeCount(); _totalLeaves = OctreeElement::getLeafNodeCount(); + if (isFullScene) { + qDebug() << "OctreeSceneStats::sceneStarted()... IS FULL SCENE"; + } _isFullScene = isFullScene; _isMoving = isMoving; From 8befefb0542feda2f76bcb7301390b35ea8fe548 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 20:45:46 -0700 Subject: [PATCH 076/109] first cut at making PacketSender use QWaitCondition --- libraries/shared/src/PacketSender.cpp | 17 ++++++++++++++++- libraries/shared/src/PacketSender.h | 6 ++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index 9d819b96d7..52f8759c8b 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -54,6 +54,10 @@ void PacketSender::queuePacketForSending(const SharedNodePointer& destinationNod unlock(); _totalPacketsQueued++; _totalBytesQueued += packet.size(); + + // Make sure to wake our actual processing thread because we now have packets for it to process. + qDebug() << "PacketSender::queuePacketForSending()... wake up, we need to send packets!."; + _hasPackets.wakeAll(); } void PacketSender::setPacketsPerSecond(int packetsPerSecond) { @@ -68,6 +72,10 @@ bool PacketSender::process() { return nonThreadedProcess(); } +void PacketSender::terminating() { + qDebug() << "PacketSender::terminating()... wake up, we need to die."; + _hasPackets.wakeAll(); +} bool PacketSender::threadedProcess() { bool hasSlept = false; @@ -113,7 +121,14 @@ bool PacketSender::threadedProcess() { // we don't want to sleep too long because how ever much we sleep will delay any future unsent // packets that arrive while we're sleeping. So we sleep 1/2 of our target fps interval if (!hasSlept) { - usleep(MINIMAL_SLEEP_INTERVAL); + //usleep(MINIMAL_SLEEP_INTERVAL); + + // wait till we have packets + _waitingOnPacketsMutex.lock(); + qDebug() << "PacketSender::threadedProcess()... waiting on packets to send..."; + _hasPackets.wait(&_waitingOnPacketsMutex); + qDebug() << "PacketSender::threadedProcess()... YIPEEE we're awake..."; + _waitingOnPacketsMutex.unlock(); } return isStillRunning(); diff --git a/libraries/shared/src/PacketSender.h b/libraries/shared/src/PacketSender.h index 7d00e622b9..bd8de9a1b1 100644 --- a/libraries/shared/src/PacketSender.h +++ b/libraries/shared/src/PacketSender.h @@ -11,6 +11,8 @@ #ifndef __shared__PacketSender__ #define __shared__PacketSender__ +#include + #include "GenericThread.h" #include "NetworkPacket.h" #include "NodeList.h" @@ -44,6 +46,7 @@ public: int getPacketsPerSecond() const { return _packetsPerSecond; } virtual bool process(); + virtual void terminating(); /// are there packets waiting in the send queue to be sent bool hasPacketsToSend() const { return _packets.size() > 0; } @@ -113,6 +116,9 @@ private: quint64 _totalPacketsQueued; quint64 _totalBytesQueued; + + QWaitCondition _hasPackets; + QMutex _waitingOnPacketsMutex; }; #endif // __shared__PacketSender__ From 025da315c6293bc41919995397e5c8cc41825164 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 21:11:47 -0700 Subject: [PATCH 077/109] removed dontSleep from JurisdictionListener --- libraries/octree/src/JurisdictionListener.cpp | 2 -- libraries/shared/src/PacketSender.cpp | 4 ---- libraries/shared/src/ReceivedPacketProcessor.cpp | 8 +------- libraries/shared/src/ReceivedPacketProcessor.h | 4 +--- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index 49cdfac741..c280b48c99 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -19,8 +19,6 @@ JurisdictionListener::JurisdictionListener(NodeType_t type) : _nodeType(type), _packetSender(JurisdictionListener::DEFAULT_PACKETS_PER_SECOND) { - ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to - connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled); // tell our NodeList we want to hear about nodes with our node type diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index 52f8759c8b..062976cc37 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -56,7 +56,6 @@ void PacketSender::queuePacketForSending(const SharedNodePointer& destinationNod _totalBytesQueued += packet.size(); // Make sure to wake our actual processing thread because we now have packets for it to process. - qDebug() << "PacketSender::queuePacketForSending()... wake up, we need to send packets!."; _hasPackets.wakeAll(); } @@ -73,7 +72,6 @@ bool PacketSender::process() { } void PacketSender::terminating() { - qDebug() << "PacketSender::terminating()... wake up, we need to die."; _hasPackets.wakeAll(); } @@ -125,9 +123,7 @@ bool PacketSender::threadedProcess() { // wait till we have packets _waitingOnPacketsMutex.lock(); - qDebug() << "PacketSender::threadedProcess()... waiting on packets to send..."; _hasPackets.wait(&_waitingOnPacketsMutex); - qDebug() << "PacketSender::threadedProcess()... YIPEEE we're awake..."; _waitingOnPacketsMutex.unlock(); } diff --git a/libraries/shared/src/ReceivedPacketProcessor.cpp b/libraries/shared/src/ReceivedPacketProcessor.cpp index 419e79881f..df7bfad165 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.cpp +++ b/libraries/shared/src/ReceivedPacketProcessor.cpp @@ -12,10 +12,6 @@ #include "ReceivedPacketProcessor.h" #include "SharedUtil.h" -ReceivedPacketProcessor::ReceivedPacketProcessor() { - _dontSleep = false; -} - void ReceivedPacketProcessor::terminating() { _hasPackets.wakeAll(); } @@ -35,9 +31,7 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& desti bool ReceivedPacketProcessor::process() { - // If a derived class handles process sleeping, like the JurisdiciontListener, then it can set - // this _dontSleep member and we will honor that request. - if (_packets.size() == 0 && !_dontSleep) { + if (_packets.size() == 0) { _waitingOnPacketsMutex.lock(); _hasPackets.wait(&_waitingOnPacketsMutex); _waitingOnPacketsMutex.unlock(); diff --git a/libraries/shared/src/ReceivedPacketProcessor.h b/libraries/shared/src/ReceivedPacketProcessor.h index 4088422561..7c99218753 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.h +++ b/libraries/shared/src/ReceivedPacketProcessor.h @@ -19,7 +19,7 @@ /// Generalized threaded processor for handling received inbound packets. class ReceivedPacketProcessor : public GenericThread { public: - ReceivedPacketProcessor(); + ReceivedPacketProcessor() { } /// Add packet from network receive thread to the processing queue. /// \param sockaddr& senderAddress the address of the sender @@ -47,8 +47,6 @@ protected: virtual void terminating(); - bool _dontSleep; - private: std::vector _packets; From e72d3127bf85b701ed641e348ce9cc4d5f09decf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Mar 2014 21:25:30 -0700 Subject: [PATCH 078/109] fix comment and remove dead code --- libraries/shared/src/PacketSender.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index 062976cc37..9fac115a39 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -115,12 +115,8 @@ bool PacketSender::threadedProcess() { } } - // if threaded and we haven't slept? We want to sleep a little so we don't hog the CPU, but - // we don't want to sleep too long because how ever much we sleep will delay any future unsent - // packets that arrive while we're sleeping. So we sleep 1/2 of our target fps interval + // if threaded and we haven't slept? We want to wait for our consumer to signal us with new packets if (!hasSlept) { - //usleep(MINIMAL_SLEEP_INTERVAL); - // wait till we have packets _waitingOnPacketsMutex.lock(); _hasPackets.wait(&_waitingOnPacketsMutex); From a95fd55172eb0ca1b1157643b56b81041f78a1a5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Mar 2014 01:11:21 -0700 Subject: [PATCH 079/109] suppress sending of extra queries when the client's view hasn't changed --- interface/src/Application.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f6cdfaa509..cf1966c585 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1921,17 +1921,17 @@ void Application::updateMyAvatar(float deltaTime) { // Update my voxel servers with my current voxel query... quint64 now = usecTimestampNow(); - const quint64 TOO_LONG_SINCE_LAST_QUERY = 1 * USECS_PER_SECOND; + quint64 sinceLastQuery = now - _lastQueriedTime; + const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; + bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY; + bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum); - // if we haven't waited long enough and the frustum is similar enough, then surpress this query... - if ((now - _lastQueriedTime) > TOO_LONG_SINCE_LAST_QUERY || !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum)) { + // if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it + if (queryIsDue || viewIsDifferentEnough) { _lastQueriedTime = now; queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions); queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions); _lastQueriedViewFrustum = _viewFrustum; - //qDebug() << ">>>>>>>>>> SENDING query..."; - } else { - //qDebug() << "suppress query..."; } } From 7e6beba4d2ff40759df35f5eebdd1dea7dac49a1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Mar 2014 01:29:41 -0700 Subject: [PATCH 080/109] removed redundant calc of isFullScene --- .../src/octree/OctreeSendThread.cpp | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 7a46fa3722..dcef7d59af 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -241,6 +241,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; + bool isFullScene = false; bool somethingToSend = true; // assume we have something // FOR NOW... node tells us if it wants to receive only view frustum deltas @@ -366,20 +367,8 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue } // start tracking our stats - qDebug() << "----"; - qDebug() << "viewFrustumChanged=" << viewFrustumChanged; - qDebug() << "nodeData->getWantDelta()=" << nodeData->getWantDelta(); - qDebug() << "nodeData->getViewFrustumJustStoppedChanging()=" << nodeData->getViewFrustumJustStoppedChanging(); - qDebug() << "nodeData->hasLodChanged()=" << nodeData->hasLodChanged(); - - - bool isFullScene = ( - (!viewFrustumChanged || !nodeData->getWantDelta()) - && nodeData->getViewFrustumJustStoppedChanging() - ) - || nodeData->hasLodChanged(); - - qDebug() << "isFullScene=" << isFullScene; + isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) + || nodeData->hasLodChanged(); // If we're starting a full scene, then definitely we want to empty the nodeBag if (isFullScene) { @@ -444,12 +433,6 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && - nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - - qDebug() << "SECOND CALC.... isFullScene=" << isFullScene; - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, From 86feabee99ec35becd0ab85e9fffce071988aa3c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Mar 2014 02:22:24 -0700 Subject: [PATCH 081/109] add last scene packets, bytes and pps --- interface/src/ui/OctreeStatsDialog.cpp | 16 +++++++++++++-- libraries/octree/src/OctreeSceneStats.cpp | 25 +++++++++++++---------- libraries/octree/src/OctreeSceneStats.h | 9 ++++++-- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index 92578b6b34..daf5a68587 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -315,13 +315,25 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser const unsigned long USECS_PER_MSEC = 1000; float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; + float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; + float lastFullPackets = stats.getLastFullTotalPackets(); + float lastFullPPS = lastFullPackets; + if (lastFullSendInSeconds > 0) { + lastFullPPS = lastFullPackets / lastFullSendInSeconds; + } QString lastFullEncodeString = locale.toString(lastFullEncode); QString lastFullSendString = locale.toString(lastFullSend); + QString lastFullPacketsString = locale.toString(lastFullPackets); + QString lastFullBytesString = locale.toString((uint)stats.getLastFullTotalBytes()); + QString lastFullPPSString = locale.toString(lastFullPPS); extraDetails << "
" << "Last Full Scene... " << - "Encode Time: " << qPrintable(lastFullEncodeString) << " ms " << - "Send Time: " << qPrintable(lastFullSendString) << " ms "; + "Encode: " << qPrintable(lastFullEncodeString) << " ms " << + "Send: " << qPrintable(lastFullSendString) << " ms " << + "Packets: " << qPrintable(lastFullPacketsString) << " " << + "Bytes: " << qPrintable(lastFullBytesString) << " " << + "Rate: " << qPrintable(lastFullPPSString) << " PPS"; for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index ec073c6ce5..e42fe17df0 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -27,9 +27,12 @@ OctreeSceneStats::OctreeSceneStats() : _isReadyToSend(false), _isStarted(false), _lastFullElapsed(0), + _lastFullTotalEncodeTime(0), + _lastFullTotalPackets(0), + _lastFullTotalBytes(0), + _elapsedAverage(samples), _bitsPerOctreeAverage(samples), - _lastFullTotalEncodeTime(0), _incomingPacket(0), _incomingBytes(0), _incomingWastedBytes(0), @@ -62,8 +65,10 @@ OctreeSceneStats& OctreeSceneStats::operator=(const OctreeSceneStats& other) { void OctreeSceneStats::copyFromOther(const OctreeSceneStats& other) { _totalEncodeTime = other._totalEncodeTime; _elapsed = other._elapsed; - _lastFullTotalEncodeTime = other._lastFullTotalEncodeTime; _lastFullElapsed = other._lastFullElapsed; + _lastFullTotalEncodeTime = other._lastFullTotalEncodeTime; + _lastFullTotalPackets = other._lastFullTotalPackets; + _lastFullTotalBytes = other._lastFullTotalBytes; _encodeStart = other._encodeStart; _packets = other._packets; @@ -167,9 +172,6 @@ void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, OctreeEleme _totalInternal = OctreeElement::getInternalNodeCount(); _totalLeaves = OctreeElement::getLeafNodeCount(); - if (isFullScene) { - qDebug() << "OctreeSceneStats::sceneStarted()... IS FULL SCENE"; - } _isFullScene = isFullScene; _isMoving = isMoving; @@ -511,12 +513,6 @@ int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int a memcpy(&_isFullScene, sourceBuffer, sizeof(_isFullScene)); sourceBuffer += sizeof(_isFullScene); - - if (_isFullScene) { - _lastFullElapsed = _elapsed; - _lastFullTotalEncodeTime = _totalEncodeTime; - } - memcpy(&_isMoving, sourceBuffer, sizeof(_isMoving)); sourceBuffer += sizeof(_isMoving); memcpy(&_packets, sourceBuffer, sizeof(_packets)); @@ -524,6 +520,13 @@ int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int a memcpy(&_bytes, sourceBuffer, sizeof(_bytes)); sourceBuffer += sizeof(_bytes); + if (_isFullScene) { + _lastFullElapsed = _elapsed; + _lastFullTotalEncodeTime = _totalEncodeTime; + _lastFullTotalPackets = _packets; + _lastFullTotalBytes = _bytes; + } + memcpy(&_totalInternal, sourceBuffer, sizeof(_totalInternal)); sourceBuffer += sizeof(_totalInternal); memcpy(&_totalLeaves, sourceBuffer, sizeof(_totalLeaves)); diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index 249ab376c1..2b39b3434c 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -150,8 +150,10 @@ public: unsigned long getTotalEncodeTime() const { return _totalEncodeTime; } unsigned long getElapsedTime() const { return _elapsed; } - unsigned long getLastFullTotalEncodeTime() const { return _lastFullTotalEncodeTime; } unsigned long getLastFullElapsedTime() const { return _lastFullElapsed; } + unsigned long getLastFullTotalEncodeTime() const { return _lastFullTotalEncodeTime; } + unsigned int getLastFullTotalPackets() const { return _lastFullTotalPackets; } + unsigned long getLastFullTotalBytes() const { return _lastFullTotalBytes; } // Used in client implementations to track individual octree packets void trackIncomingOctreePacket(const QByteArray& packet, bool wasStatsPacket, int nodeClockSkewUsec); @@ -181,13 +183,16 @@ private: quint64 _start; quint64 _end; quint64 _elapsed; + quint64 _lastFullElapsed; + quint64 _lastFullTotalEncodeTime; + unsigned int _lastFullTotalPackets; + unsigned long _lastFullTotalBytes; SimpleMovingAverage _elapsedAverage; SimpleMovingAverage _bitsPerOctreeAverage; quint64 _totalEncodeTime; - quint64 _lastFullTotalEncodeTime; quint64 _encodeStart; // scene octree related data From acb4047b54333495cfac5f9f8f072d5ba761ea5c Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 12 Mar 2014 17:12:57 +0200 Subject: [PATCH 082/109] Increased the right margin of sent messages for better visibility. --- interface/interface_en.ts | 14 +++++++------- interface/src/ui/ChatWindow.cpp | 2 +- interface/ui/chatWindow.ui | 5 ++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 13ed5b968b..a8f78cd36e 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -28,19 +28,19 @@ ChatWindow - + Chat - + Connecting to XMPP... - + online now: diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index e7b78a897a..7a3f1ab906 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -191,7 +191,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { messageLabel->setWordWrap(true); messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); messageLabel->setOpenExternalLinks(true); - messageLabel->setStyleSheet("padding: 2px;"); + messageLabel->setStyleSheet("padding: 2px; margin-right: 20px"); messageLabel->setAlignment(Qt::AlignTop); ui->messagesFormLayout->addRow(userLabel, messageLabel); diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index bebfc12674..199d9b42e3 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -81,7 +81,7 @@ 0 0 358 - 328 + 448
@@ -124,6 +124,9 @@ Qt::ScrollBarAlwaysOff + + QAbstractScrollArea::AdjustToContents + From 6173a4d2a79bb1dc969794ab5095cb77261271f5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Mar 2014 10:19:21 -0700 Subject: [PATCH 083/109] fix isFullScene correctly --- assignment-client/src/octree/OctreeSendThread.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index dcef7d59af..ef69771607 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -241,7 +241,9 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; - bool isFullScene = false; + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) + || nodeData->hasLodChanged(); + bool somethingToSend = true; // assume we have something // FOR NOW... node tells us if it wants to receive only view frustum deltas @@ -366,10 +368,6 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue << " Wasted:" << _totalWastedBytes; } - // start tracking our stats - isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) - || nodeData->hasLodChanged(); - // If we're starting a full scene, then definitely we want to empty the nodeBag if (isFullScene) { nodeData->nodeBag.deleteAll(); @@ -383,6 +381,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue } ::startSceneSleepTime = _usleepTime; + // start tracking our stats nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot(), _myServer->getJurisdiction()); // This is the start of "resending" the scene. From 8622f2d548d5e8c942aeac5c31edda3b6c1882be Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Mar 2014 10:20:33 -0700 Subject: [PATCH 084/109] fix comment --- domain-server/src/DomainServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 8a95a25e23..a425052970 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -544,7 +544,7 @@ void DomainServer::readAvailableDatagrams() { // construct the requested assignment from the packet data Assignment requestAssignment(receivedPacket); - // Suppress these for Assignment::AgentType to 1 per second... + // Suppress these for Assignment::AgentType to once per 5 seconds static quint64 lastNoisyMessage = usecTimestampNow(); quint64 timeNow = usecTimestampNow(); const quint64 NOISY_TIME_ELAPSED = 5 * USECS_PER_SECOND; From 1c4051b288081ee12007f90eaecf89c5fd018f1d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 12 Mar 2014 10:35:22 -0700 Subject: [PATCH 085/109] Added importBoundaries --- examples/editVoxels.js | 40 ++++++++++++++++++++++++++++----------- interface/interface_en.ts | 4 ++-- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index b5702dff47..956b6f1d1d 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -283,17 +283,25 @@ var thumbDeltaPerStep = thumbExtents / (pointerVoxelScaleSteps - 1); // Move the following code to a separate file when include will be available. var importTree; var importPreview; +var importBoundaries; var isImporting; var importPosition; var importScale; function initImport() { importPreview = Overlays.addOverlay("localvoxels", { - name: "import", - position: { x: 0, y: 0, z: 0}, - scale: 0, - visible: false - }); + name: "import", + position: { x: 0, y: 0, z: 0}, + scale: 1, + visible: false + }); + importBoundaries = Overlays.addOverlay("cube", { + position: { x: 0, y: 0, z: 0 }, + scale: 1, + color: { red: 128, blue: 128, green: 128 }, + solid: false, + visible: false + }) isImporting = false; importPosition = { x: 0, y: 0, z: 0 }; importScale = 0; @@ -316,8 +324,11 @@ function moveImport(position) { if (0 < position.x && 0 < position.y && 0 < position.z) { importPosition = position; Overlays.editOverlay(importPreview, { - position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } - }); + position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } + }); + Overlays.editOverlay(importBoundaries, { + position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } + }); } } @@ -325,15 +336,21 @@ function rescaleImport(scale) { if (0 < scale) { importScale = scale; Overlays.editOverlay(importPreview, { - scale: importScale - }); + scale: importScale + }); + Overlays.editOverlay(importBoundaries, { + scale: importScale + }); } } function showImport(doShow) { Overlays.editOverlay(importPreview, { - visible: doShow - }); + visible: doShow + }); + Overlays.editOverlay(importBoundaries, { + visible: doShow + }); } function placeImport() { @@ -352,6 +369,7 @@ function cancelImport() { function cleanupImport() { Overlays.deleteOverlay(importPreview); + Overlays.deleteOverlay(importBoundaries); isImporting = false; importPostion = { x: 0, y: 0, z: 0 }; importScale = 0; diff --git a/interface/interface_en.ts b/interface/interface_en.ts index a8f78cd36e..76a5d47101 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) From 4c26f025ac89fce9bfc526131caec6bf06a16003 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 12 Mar 2014 19:58:08 +0200 Subject: [PATCH 086/109] Enabled 'Enter' to open the chat. Deleted the now obsolete ChatEntry. --- interface/interface_en.ts | 16 +++--- interface/src/Application.cpp | 31 +--------- interface/src/Application.h | 4 -- interface/src/Menu.cpp | 6 +- interface/src/ui/ChatEntry.cpp | 102 --------------------------------- interface/src/ui/ChatEntry.h | 35 ----------- 6 files changed, 14 insertions(+), 180 deletions(-) delete mode 100644 interface/src/ui/ChatEntry.cpp delete mode 100644 interface/src/ui/ChatEntry.h diff --git a/interface/interface_en.ts b/interface/interface_en.ts index a8f78cd36e..685c6e3f67 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 032e849f0a..13e3d36fad 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -153,7 +153,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _touchAvgY(0.0f), _isTouchPressed(false), _mousePressed(false), - _chatEntryOn(false), _audio(&_audioScope, STARTUP_JITTER_SAMPLES), _enableProcessVoxelsThread(true), _voxelProcessor(), @@ -698,21 +697,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } if (activeWindow() == _window) { - if (_chatEntryOn) { - if (_chatEntry.keyPressEvent(event)) { - _myAvatar->setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ? - DELETE_KEY_DOWN : INSERT_KEY_DOWN); - _myAvatar->setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); - - } else { - _myAvatar->setChatMessage(_chatEntry.getContents()); - _chatEntry.clear(); - _chatEntryOn = false; - setMenuShortcutsEnabled(true); - } - return; - } - bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier); bool isMeta = event->modifiers().testFlag(Qt::ControlModifier); switch (event->key()) { @@ -793,10 +777,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Return: case Qt::Key_Enter: - _chatEntryOn = true; - _myAvatar->setKeyState(NO_KEY_DOWN); - _myAvatar->setChatMessage(string()); - setMenuShortcutsEnabled(false); + Menu::getInstance()->triggerOption(MenuOption::Chat); break; case Qt::Key_Up: @@ -939,11 +920,6 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (activeWindow() == _window) { - if (_chatEntryOn) { - _myAvatar->setKeyState(NO_KEY_DOWN); - return; - } - switch (event->key()) { case Qt::Key_E: _myAvatar->setDriveKeys(UP, 0); @@ -2483,11 +2459,6 @@ void Application::displayOverlay() { } } - // Show chat entry field - if (_chatEntryOn) { - _chatEntry.render(_glWidget->width(), _glWidget->height()); - } - // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { char frameTimer[10]; diff --git a/interface/src/Application.h b/interface/src/Application.h index 234f264447..70b7347654 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -64,7 +64,6 @@ #include "renderer/TextureCache.h" #include "renderer/VoxelShader.h" #include "ui/BandwidthDialog.h" -#include "ui/ChatEntry.h" #include "ui/OctreeStatsDialog.h" #include "ui/RearMirrorTools.h" #include "ui/LodToolsDialog.h" @@ -426,9 +425,6 @@ private: bool _mousePressed; // true if mouse has been pressed (clear when finished) - ChatEntry _chatEntry; // chat entry field - bool _chatEntryOn; // Whether to show the chat entry - GeometryCache _geometryCache; TextureCache _textureCache; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 5f6c541bc5..f0d4d8c133 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -163,7 +163,11 @@ Menu::Menu() : addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::FstUploader, 0, Application::getInstance(), SLOT(uploadFST())); - _chatAction = addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, 0, this, SLOT(showChat())); + _chatAction = addActionToQMenuAndActionHash(toolsMenu, + MenuOption::Chat, + Qt::Key_Return, + this, + SLOT(showChat())); #ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); toggleChat(); diff --git a/interface/src/ui/ChatEntry.cpp b/interface/src/ui/ChatEntry.cpp deleted file mode 100644 index 470b62542b..0000000000 --- a/interface/src/ui/ChatEntry.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// -// ChatEntry.cpp -// interface -// -// Created by Andrzej Kapolka on 4/24/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. - -#include - -#include "ChatEntry.h" -#include "InterfaceConfig.h" -#include "Util.h" - -using namespace std; - -const int MAX_CONTENT_LENGTH = 80; - -ChatEntry::ChatEntry() : _cursorPos(0) { -} - -void ChatEntry::clear() { - _contents.clear(); - _cursorPos = 0; -} - -bool ChatEntry::keyPressEvent(QKeyEvent* event) { - event->accept(); - switch (event->key()) { - case Qt::Key_Return: - case Qt::Key_Enter: - return false; - - case Qt::Key_Escape: - clear(); - return false; - - case Qt::Key_Backspace: - if (_cursorPos != 0) { - _contents.erase(_cursorPos - 1, 1); - _cursorPos--; - } - return true; - - case Qt::Key_Delete: - if (_cursorPos < (int)_contents.size()) { - _contents.erase(_cursorPos, 1); - } - return true; - - case Qt::Key_Left: - if (_cursorPos != 0) { - _cursorPos--; - } - return true; - - case Qt::Key_Right: - if (_cursorPos != _contents.size()) { - _cursorPos++; - } - return true; - - default: - QString text = event->text(); - if (text.isEmpty()) { - event->ignore(); - return true; - } - if (_contents.size() < MAX_CONTENT_LENGTH) { - _contents.insert(_cursorPos, 1, text.at(0).toLatin1()); - _cursorPos++; - } - return true; - } -} - -const float ALL_WHITE[] = { 1.0f, 1.0f, 1.0f }; - -void ChatEntry::render(int screenWidth, int screenHeight) { - // draw a gray background so that we can actually see what we're typing - int bottom = screenHeight - 150, top = screenHeight - 165; - int left = 20, right = left + 600; - - glColor3f(0.2f, 0.2f, 0.2f); - glBegin(GL_QUADS); - glVertex2f(left - 5, bottom + 7); - glVertex2f(right + 5, bottom + 7); - glVertex2f(right + 5, top - 3); - glVertex2f(left - 5, top - 3); - glEnd(); - - drawText(left, bottom, 0.10f, 0, 2, _contents.c_str(), ALL_WHITE); - - float width = 0; - for (string::iterator it = _contents.begin(), end = it + _cursorPos; it != end; it++) { - width += widthChar(0.10f, 0, *it); - } - glDisable(GL_LINE_SMOOTH); - glBegin(GL_LINE_STRIP); - glVertex2f(left + width, top + 2); - glVertex2f(left + width, bottom + 2); - glEnd(); -} diff --git a/interface/src/ui/ChatEntry.h b/interface/src/ui/ChatEntry.h deleted file mode 100644 index 478641325d..0000000000 --- a/interface/src/ui/ChatEntry.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// ChatEntry.h -// interface -// -// Created by Andrzej Kapolka on 4/24/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__ChatEntry__ -#define __interface__ChatEntry__ - -#include - -class QKeyEvent; - -class ChatEntry { -public: - - ChatEntry(); - - const std::string& getContents() const { return _contents; } - - void clear(); - - bool keyPressEvent(QKeyEvent* event); - - void render(int screenWidth, int screenHeight); - -private: - - std::string _contents; - int _cursorPos; -}; - -#endif /* defined(__interface__ChatEntry__) */ From 4b60c549e77e021b43344bd7246a3c28279761e1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Mar 2014 11:10:52 -0700 Subject: [PATCH 087/109] add LOD to overlay display --- interface/src/Application.cpp | 25 +++++++++++------ interface/src/Menu.cpp | 34 +++++++++++++++++++++++ interface/src/Menu.h | 1 + interface/src/ui/LodToolsDialog.cpp | 42 +++-------------------------- interface/src/ui/LodToolsDialog.h | 2 -- 5 files changed, 56 insertions(+), 48 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cf1966c585..2007b7929c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2786,8 +2786,8 @@ void Application::displayStats() { voxelStats.str(""); QString packetsString = locale.toString((int)voxelPacketsToProcess); QString maxString = locale.toString((int)_recentMaxPackets); - voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData() - << " [Recent Max: " << maxString.toLocal8Bit().constData() << "]"; + voxelStats << "Voxel Packets to Process: " << qPrintable(packetsString) + << " [Recent Max: " << qPrintable(maxString) << "]"; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); } @@ -2810,7 +2810,7 @@ void Application::displayStats() { // Server Voxels voxelStats.str(""); - voxelStats << "Server voxels: " << serversTotalString.toLocal8Bit().constData(); + voxelStats << "Server voxels: " << qPrintable(serversTotalString); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); @@ -2820,8 +2820,8 @@ void Application::displayStats() { voxelStats.str(""); voxelStats << - "Internal: " << serversInternalString.toLocal8Bit().constData() << " " << - "Leaves: " << serversLeavesString.toLocal8Bit().constData() << ""; + "Internal: " << qPrintable(serversInternalString) << " " << + "Leaves: " << qPrintable(serversLeavesString) << ""; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); } @@ -2831,7 +2831,7 @@ void Application::displayStats() { // Local Voxels voxelStats.str(""); - voxelStats << "Local voxels: " << localTotalString.toLocal8Bit().constData(); + voxelStats << "Local voxels: " << qPrintable(localTotalString); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); @@ -2843,8 +2843,17 @@ void Application::displayStats() { voxelStats.str(""); voxelStats << - "Internal: " << localInternalString.toLocal8Bit().constData() << " " << - "Leaves: " << localLeavesString.toLocal8Bit().constData() << ""; + "Internal: " << qPrintable(localInternalString) << " " << + "Leaves: " << qPrintable(localLeavesString) << ""; + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + } + + // LOD Details + if (_statsExpanded) { + voxelStats.str(""); + QString displayLODDetails = Menu::getInstance()->getLODFeedbackText(); + voxelStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 5f6c541bc5..543694f404 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1087,6 +1087,40 @@ void Menu::octreeStatsDetailsClosed() { } } +QString Menu::getLODFeedbackText() { + // determine granularity feedback + int boundaryLevelAdjust = getBoundaryLevelAdjust(); + QString granularityFeedback; + + switch (boundaryLevelAdjust) { + case 0: { + granularityFeedback = QString("at standard granularity."); + } break; + case 1: { + granularityFeedback = QString("at half of standard granularity."); + } break; + case 2: { + granularityFeedback = QString("at a third of standard granularity."); + } break; + default: { + granularityFeedback = QString("at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1); + } break; + } + + // distance feedback + float voxelSizeScale = getVoxelSizeScale(); + float relativeToDefault = voxelSizeScale / DEFAULT_OCTREE_SIZE_SCALE; + QString result; + if (relativeToDefault > 1.01) { + result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback); + } else if (relativeToDefault > 0.99) { + result = QString("the default distance %1").arg(granularityFeedback); + } else { + result = QString("%1 of default %2").arg(relativeToDefault,8,'f',3).arg(granularityFeedback); + } + return result; +} + void Menu::autoAdjustLOD(float currentFPS) { // NOTE: our first ~100 samples at app startup are completely all over the place, and we don't // really want to count them in our average, so we will ignore the real frame rates and stuff diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 380fe2d3b7..00ef51178a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -84,6 +84,7 @@ public: void handleViewFrustumOffsetKeyModifier(int key); // User Tweakable LOD Items + QString getLODFeedbackText(); void autoAdjustLOD(float currentFPS); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index 4cf4a29bf1..564669757c 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -68,7 +68,7 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : const unsigned redish = 0xfff00000; palette.setColor(QPalette::WindowText, QColor::fromRgb(redish)); _feedback->setPalette(palette); - _feedback->setText(getFeedbackText()); + _feedback->setText(Menu::getInstance()->getLODFeedbackText()); const int FEEDBACK_WIDTH = 350; _feedback->setFixedWidth(FEEDBACK_WIDTH); form->addRow("You can see... ", _feedback); @@ -81,40 +81,6 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : this->QDialog::setLayout(form); } -QString LodToolsDialog::getFeedbackText() { - // determine granularity feedback - int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); - QString granularityFeedback; - - switch (boundaryLevelAdjust) { - case 0: { - granularityFeedback = QString("at standard granularity."); - } break; - case 1: { - granularityFeedback = QString("at half of standard granularity."); - } break; - case 2: { - granularityFeedback = QString("at a third of standard granularity."); - } break; - default: { - granularityFeedback = QString("at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1); - } break; - } - - // distance feedback - float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); - float relativeToDefault = voxelSizeScale / DEFAULT_OCTREE_SIZE_SCALE; - QString result; - if (relativeToDefault > 1.01) { - result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback); - } else if (relativeToDefault > 0.99) { - result = QString("the default distance %1").arg(granularityFeedback); - } else { - result = QString("%1 of default %2").arg(relativeToDefault,8,'f',3).arg(granularityFeedback); - } - return result; -} - LodToolsDialog::~LodToolsDialog() { delete _feedback; delete _lodSize; @@ -124,19 +90,19 @@ LodToolsDialog::~LodToolsDialog() { void LodToolsDialog::reloadSliders() { _lodSize->setValue(Menu::getInstance()->getVoxelSizeScale() / TREE_SCALE); _boundaryLevelAdjust->setValue(Menu::getInstance()->getBoundaryLevelAdjust()); - _feedback->setText(getFeedbackText()); + _feedback->setText(Menu::getInstance()->getLODFeedbackText()); } void LodToolsDialog::sizeScaleValueChanged(int value) { float realValue = value * TREE_SCALE; Menu::getInstance()->setVoxelSizeScale(realValue); - _feedback->setText(getFeedbackText()); + _feedback->setText(Menu::getInstance()->getLODFeedbackText()); } void LodToolsDialog::boundaryLevelValueChanged(int value) { Menu::getInstance()->setBoundaryLevelAdjust(value); - _feedback->setText(getFeedbackText()); + _feedback->setText(Menu::getInstance()->getLODFeedbackText()); } void LodToolsDialog::resetClicked(bool checked) { diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index ee96cffd7e..98a5fce5f8 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -36,8 +36,6 @@ protected: void closeEvent(QCloseEvent*); private: - QString getFeedbackText(); - QSlider* _lodSize; QSlider* _boundaryLevelAdjust; QLabel* _feedback; From c3e8d51694d386fab583823bda165643a5325c3b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Mar 2014 12:06:59 -0700 Subject: [PATCH 088/109] orientation stored in dataserver is in degrees --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d2c3c4357a..8136e06085 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1155,7 +1155,7 @@ void MyAvatar::updateLocationInDataServer() { if (accountManager.isLoggedIn()) { QString positionString(createByteArray(_position)); - QString orientationString(createByteArray(glm::radians(safeEulerAngles(getOrientation())))); + QString orientationString(createByteArray(glm::degrees(safeEulerAngles(getOrientation())))); // construct the json to put the user's location QString locationPutJson = QString() + "{\"address\":{\"position\":\"" From e68241d2a9a67a1364d82ca19da95bcc262f2d85 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Mar 2014 12:08:27 -0700 Subject: [PATCH 089/109] using glm::degrees() instead of DEGREES_PER_RADIAN --- interface/src/avatar/MyAvatar.cpp | 4 ++-- libraries/avatars/src/HeadData.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8136e06085..0a2462e656 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -334,12 +334,12 @@ void MyAvatar::updateFromGyros(float deltaTime) { bool trackerActive = false; if (faceshift->isActive()) { estimatedPosition = faceshift->getHeadTranslation(); - estimatedRotation = DEGREES_PER_RADIAN * safeEulerAngles(faceshift->getHeadRotation()); + estimatedRotation = glm::degrees(safeEulerAngles(faceshift->getHeadRotation())); trackerActive = true; } else if (visage->isActive()) { estimatedPosition = visage->getHeadTranslation(); - estimatedRotation = DEGREES_PER_RADIAN * safeEulerAngles(visage->getHeadRotation()); + estimatedRotation = glm::degrees(safeEulerAngles(visage->getHeadRotation())); trackerActive = true; } diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index a94096d73a..cf48aeabfa 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -43,7 +43,7 @@ void HeadData::setOrientation(const glm::quat& orientation) { _owningAvatar->setOrientation(bodyOrientation); // the rest goes to the head - glm::vec3 eulers = DEGREES_PER_RADIAN * safeEulerAngles(glm::inverse(bodyOrientation) * orientation); + glm::vec3 eulers = glm::degrees(safeEulerAngles(glm::inverse(bodyOrientation) * orientation)); _pitch = eulers.x; _yaw = eulers.y; _roll = eulers.z; From 4c7737443eec0e106f76a335ccd8b93281b45d5d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 12:11:30 -0700 Subject: [PATCH 090/109] Fix for OS X warning. --- libraries/metavoxels/src/MetavoxelData.cpp | 6 +++--- libraries/metavoxels/src/MetavoxelData.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 8a6b0bc16a..c31be16c7a 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -228,7 +228,7 @@ public: Spanner* getSpanner() const { return _spanner; } float getDistance() const { return _distance; } - virtual bool visit(Spanner* spanner, float distance); + virtual bool visitSpanner(Spanner* spanner, float distance); private: @@ -243,7 +243,7 @@ FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor( _spanner(NULL) { } -bool FirstRaySpannerIntersectionVisitor::visit(Spanner* spanner, float distance) { +bool FirstRaySpannerIntersectionVisitor::visitSpanner(Spanner* spanner, float distance) { _spanner = spanner; _distance = distance; return false; @@ -938,7 +938,7 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { } qStableSort(spannerDistances); foreach (const SpannerDistance& spannerDistance, spannerDistances) { - if (!visit(spannerDistance.spanner, spannerDistance.distance)) { + if (!visitSpanner(spannerDistance.spanner, spannerDistance.distance)) { return SHORT_CIRCUIT; } } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 7e0a863a3b..040132effa 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -302,7 +302,7 @@ public: /// Visits a spanner that the ray intersects. /// \return true to continue, false to short-circuit the tour - virtual bool visit(Spanner* spanner, float distance) = 0; + virtual bool visitSpanner(Spanner* spanner, float distance) = 0; virtual void prepare(); virtual int visit(MetavoxelInfo& info, float distance); From 9402a5852c79e9b1361afbc3337ddb0150ae9600 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 12 Mar 2014 12:20:43 -0700 Subject: [PATCH 091/109] Fixed review comments --- examples/editVoxels.js | 2 +- interface/interface_en.ts | 8 ++++---- interface/src/Application.cpp | 7 ++++--- interface/src/Application.h | 6 ++++-- interface/src/ClipboardScriptingInterface.cpp | 6 +++--- interface/src/ui/LocalVoxelsOverlay.cpp | 5 ++++- interface/src/ui/Volume3DOverlay.cpp | 3 --- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 956b6f1d1d..31d483d798 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -308,7 +308,7 @@ function initImport() { } function importVoxels() { - if (Clipboard.importVoxels() == 0) { + if (Clipboard.importVoxels()) { isImporting = true; if (importScale <= 0) { importScale = 1; diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 76a5d47101..4aa920945b 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 771f02750d..bfcd7770eb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -140,6 +140,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _fps(120.0f), _justStarted(true), _voxelImporter(NULL), + _importSucceded(false), _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard), _wantToKillLocalVoxels(false), _audioScope(256, 200, true), @@ -1387,7 +1388,7 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { } void Application::importVoxels() { - int result = 1; + _importSucceded = false; if (!_voxelImporter) { _voxelImporter = new VoxelImporter(_window); @@ -1396,7 +1397,7 @@ void Application::importVoxels() { if (!_voxelImporter->exec()) { qDebug() << "[DEBUG] Import succeeded." << endl; - result = 0; + _importSucceded = true; } else { qDebug() << "[DEBUG] Import failed." << endl; if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) { @@ -1408,7 +1409,7 @@ void Application::importVoxels() { // restore the main window's active state _window->activateWindow(); - emit importDone(result); + emit importDone(); } void Application::cutVoxels(const VoxelDetail& sourceVoxel) { diff --git a/interface/src/Application.h b/interface/src/Application.h index c6fc7140eb..637a79c422 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -157,6 +157,7 @@ public: VoxelTree* getVoxelTree() { return _voxels.getTree(); } ParticleTreeRenderer* getParticles() { return &_particles; } MetavoxelSystem* getMetavoxels() { return &_metavoxels; } + bool getImportSucceded() { return _importSucceded; } VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; } VoxelTree* getClipboard() { return &_clipboard; } Environment* getEnvironment() { return &_environment; } @@ -225,8 +226,8 @@ signals: /// Fired when we're rendering in-world interface elements; allows external parties to hook in. void renderingInWorldInterface(); - /// Fired when the import window is closed with a return code. - void importDone(int); + /// Fired when the import window is closed + void importDone(); public slots: void domainChanged(const QString& domainHostname); @@ -368,6 +369,7 @@ private: VoxelSystem _voxels; VoxelTree _clipboard; // if I copy/paste VoxelImporter* _voxelImporter; + bool _importSucceded; VoxelSystem _sharedVoxelSystem; ViewFrustum _sharedVoxelSystemViewFrustum; diff --git a/interface/src/ClipboardScriptingInterface.cpp b/interface/src/ClipboardScriptingInterface.cpp index e8a02b041b..2ddef524aa 100644 --- a/interface/src/ClipboardScriptingInterface.cpp +++ b/interface/src/ClipboardScriptingInterface.cpp @@ -77,11 +77,11 @@ void ClipboardScriptingInterface::exportVoxel(float x, float y, float z, float s bool ClipboardScriptingInterface::importVoxels() { qDebug() << "[DEBUG] Importing ... "; QEventLoop loop; - connect(Application::getInstance(), SIGNAL(importDone(int)), &loop, SLOT(quit())); + connect(Application::getInstance(), SIGNAL(importDone()), &loop, SLOT(quit())); emit readyToImport(); - int returnCode = loop.exec(); + loop.exec(); - return returnCode; + return Application::getInstance()->getImportSucceded(); } void ClipboardScriptingInterface::nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) { diff --git a/interface/src/ui/LocalVoxelsOverlay.cpp b/interface/src/ui/LocalVoxelsOverlay.cpp index fde03b85cb..71ae9ec74e 100644 --- a/interface/src/ui/LocalVoxelsOverlay.cpp +++ b/interface/src/ui/LocalVoxelsOverlay.cpp @@ -61,8 +61,11 @@ void LocalVoxelsOverlay::render() { void LocalVoxelsOverlay::setProperties(const QScriptValue &properties) { Volume3DOverlay::setProperties(properties); + if (properties.property("scale").isValid()) { + setSize(properties.property("scale").toVariant().toFloat()); + } + QScriptValue treeName = properties.property("name"); - // if "end" property was not there, check to see if they included aliases: endPoint, or p2 if (treeName.isValid()) { if ((_treeName = treeName.toString()) == DOMAIN_TREE_NAME) { qDebug() << "addOverlay(): Can't create overlay from domain tree"; diff --git a/interface/src/ui/Volume3DOverlay.cpp b/interface/src/ui/Volume3DOverlay.cpp index a4e6c76a12..dbc1582cc5 100644 --- a/interface/src/ui/Volume3DOverlay.cpp +++ b/interface/src/ui/Volume3DOverlay.cpp @@ -31,9 +31,6 @@ void Volume3DOverlay::setProperties(const QScriptValue& properties) { if (properties.property("size").isValid()) { setSize(properties.property("size").toVariant().toFloat()); } - if (properties.property("scale").isValid()) { - setSize(properties.property("scale").toVariant().toFloat()); - } if (properties.property("isSolid").isValid()) { setIsSolid(properties.property("isSolid").toVariant().toBool()); From 064f784db13d749296510538dbdb0414e3206117 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 12:23:23 -0700 Subject: [PATCH 092/109] More OS X warning fixes. --- .../metavoxels/src/AttributeRegistry.cpp | 26 ++++++++++--------- libraries/metavoxels/src/AttributeRegistry.h | 24 ++++++++--------- libraries/metavoxels/src/MetavoxelData.cpp | 16 ++++++------ 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9ec37acd80..ccb6d3970e 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -155,27 +155,27 @@ Attribute::Attribute(const QString& name) : Attribute::~Attribute() { } -void Attribute::read(MetavoxelData& data, MetavoxelStreamState& state) { +void Attribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { data.createRoot(state.attribute)->read(state); } -void Attribute::write(const MetavoxelNode& root, MetavoxelStreamState& state) { +void Attribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { root.write(state); } -void Attribute::readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { +void Attribute::readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { data.createRoot(state.attribute)->readDelta(reference, state); } -void Attribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { +void Attribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { root.writeDelta(reference, state); } -void Attribute::readSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { +void Attribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { data.getRoot(state.attribute)->readSubdivision(state); } -void Attribute::writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { +void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { root.writeSubdivision(state); } @@ -327,7 +327,7 @@ SpannerSetAttribute::SpannerSetAttribute(const QString& name, const QMetaObject* SharedObjectSetAttribute(name, metaObject) { } -void SpannerSetAttribute::read(MetavoxelData& data, MetavoxelStreamState& state) { +void SpannerSetAttribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { forever { SharedObjectPointer object; state.stream >> object; @@ -338,13 +338,14 @@ void SpannerSetAttribute::read(MetavoxelData& data, MetavoxelStreamState& state) } } -void SpannerSetAttribute::write(const MetavoxelNode& root, MetavoxelStreamState& state) { +void SpannerSetAttribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { Spanner::incrementVisit(); root.writeSpanners(state); state.stream << SharedObjectPointer(); } -void SpannerSetAttribute::readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { +void SpannerSetAttribute::readMetavoxelDelta(MetavoxelData& data, + const MetavoxelNode& reference, MetavoxelStreamState& state) { forever { SharedObjectPointer object; state.stream >> object; @@ -355,13 +356,14 @@ void SpannerSetAttribute::readDelta(MetavoxelData& data, const MetavoxelNode& re } } -void SpannerSetAttribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { +void SpannerSetAttribute::writeMetavoxelDelta(const MetavoxelNode& root, + const MetavoxelNode& reference, MetavoxelStreamState& state) { Spanner::incrementVisit(); root.writeSpannerDelta(reference, state); state.stream << SharedObjectPointer(); } -void SpannerSetAttribute::readSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { +void SpannerSetAttribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { forever { SharedObjectPointer object; state.stream >> object; @@ -372,7 +374,7 @@ void SpannerSetAttribute::readSubdivision(MetavoxelData& data, MetavoxelStreamSt } } -void SpannerSetAttribute::writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { +void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { Spanner::incrementVisit(); root.writeSpannerSubdivision(state); state.stream << SharedObjectPointer(); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 3405f90315..ba19a3b0d2 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -177,14 +177,14 @@ public: virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } - virtual void read(MetavoxelData& data, MetavoxelStreamState& state); - virtual void write(const MetavoxelNode& root, MetavoxelStreamState& state); + virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state); - virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void readSubdivision(MetavoxelData& data, MetavoxelStreamState& state); - virtual void writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); + virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); virtual bool equal(void* first, void* second) const = 0; @@ -355,14 +355,14 @@ public: Q_INVOKABLE SpannerSetAttribute(const QString& name = QString(), const QMetaObject* metaObject = &SharedObject::staticMetaObject); - virtual void read(MetavoxelData& data, MetavoxelStreamState& state); - virtual void write(const MetavoxelNode& root, MetavoxelStreamState& state); + virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state); - virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void readSubdivision(MetavoxelData& data, MetavoxelStreamState& state); - virtual void writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); + virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); }; #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index c31be16c7a..08cd62f30d 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -312,7 +312,7 @@ void MetavoxelData::read(Bitstream& in, const MetavoxelLOD& lod) { break; } MetavoxelStreamState state = { getMinimum(), _size, attribute, in, lod, lod }; - attribute->read(*this, state); + attribute->readMetavoxelRoot(*this, state); } } @@ -321,7 +321,7 @@ void MetavoxelData::write(Bitstream& out, const MetavoxelLOD& lod) const { for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { out << it.key(); MetavoxelStreamState state = { getMinimum(), _size, it.key(), out, lod, lod }; - it.key()->write(*it.value(), state); + it.key()->writeMetavoxelRoot(*it.value(), state); } out << AttributePointer(); } @@ -360,13 +360,13 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD in >> changed; if (changed) { oldRoot->incrementReferenceCount(); - attribute->readDelta(*this, *oldRoot, state); + attribute->readMetavoxelDelta(*this, *oldRoot, state); oldRoot->decrementReferenceCount(attribute); } else { - attribute->readSubdivision(*this, state); + attribute->readMetavoxelSubdivision(*this, state); } } else { - attribute->read(*this, state); + attribute->readMetavoxelRoot(*this, state); } } @@ -415,13 +415,13 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO if (referenceRoot) { if (it.value() == referenceRoot) { out << false; - it.key()->writeSubdivision(*it.value(), state); + it.key()->writeMetavoxelSubdivision(*it.value(), state); } else { out << true; - it.key()->writeDelta(*it.value(), *referenceRoot, state); + it.key()->writeMetavoxelDelta(*it.value(), *referenceRoot, state); } } else { - it.key()->write(*it.value(), state); + it.key()->writeMetavoxelRoot(*it.value(), state); } } } From f1145272eb44900e6d86cf3f9ecb7fea26f6dcf4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 12:29:01 -0700 Subject: [PATCH 093/109] Signed/unsigned fix. --- interface/src/devices/SixenseManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 287aa4e5f6..0482dbf84c 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -60,7 +60,7 @@ void SixenseManager::update(float deltaTime) { // Either find a palm matching the sixense controller, or make a new one PalmData* palm; bool foundHand = false; - for (int j = 0; j < hand->getNumPalms(); j++) { + for (size_t j = 0; j < hand->getNumPalms(); j++) { if (hand->getPalms()[j].getSixenseID() == data.controller_index) { palm = &(hand->getPalms()[j]); foundHand = true; @@ -128,7 +128,7 @@ void SixenseManager::update(float deltaTime) { } // if the controllers haven't been moved in a while, disable - const int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000; + const unsigned int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000; if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) { for (vector::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) { it->setActive(false); From f088912ecfa51bc97fca3d0b6a70a0df38c1abc7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 12:51:43 -0700 Subject: [PATCH 094/109] More GCC warning fixes. --- interface/src/avatar/Avatar.cpp | 3 --- interface/src/avatar/SkeletonModel.cpp | 2 +- interface/src/renderer/FBXReader.cpp | 4 ++-- libraries/metavoxels/src/AttributeRegistry.h | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6cd7523a14..ea7ffc6f2b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -109,9 +109,6 @@ void Avatar::simulate(float deltaTime) { _shouldRenderBillboard = true; } - // copy velocity so we can use it later for acceleration - glm::vec3 oldVelocity = getVelocity(); - getHand()->simulate(deltaTime, false); _skeletonModel.setLODDistance(getLODDistance()); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 4699b360f6..f405358710 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -146,7 +146,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin // rotate palm according to average finger direction float directionLength = glm::length(direction); - const int MIN_ROTATION_FINGERS = 3; + const unsigned int MIN_ROTATION_FINGERS = 3; if (directionLength > EPSILON && palm.getNumFingers() >= MIN_ROTATION_FINGERS) { applyRotationDelta(jointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false); getJointRotation(jointIndex, palmRotation, true); diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index cda14dc807..8bf5583a02 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -66,7 +66,7 @@ template QVariant readBinaryArray(QDataStream& in) { in >> compressedLength; QVector values; - const int DEFLATE_ENCODING = 1; + const unsigned int DEFLATE_ENCODING = 1; if (encoding == DEFLATE_ENCODING) { // preface encoded data with uncompressed length QByteArray compressed(sizeof(quint32) + compressedLength, 0); @@ -163,7 +163,7 @@ FBXNode parseBinaryFBXNode(QDataStream& in) { in >> nameLength; FBXNode node; - const int MIN_VALID_OFFSET = 40; + const unsigned int MIN_VALID_OFFSET = 40; if (endOffset < MIN_VALID_OFFSET || nameLength == 0) { // use a null name to indicate a null node return node; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index ba19a3b0d2..16968da1b2 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -265,7 +265,7 @@ template inline bool SimpleInlineAttribute::merge(vo /// Provides appropriate averaging for RGBA values. class QRgbAttribute : public InlineAttribute { Q_OBJECT - Q_PROPERTY(int defaultValue MEMBER _defaultValue) + Q_PROPERTY(unsigned int defaultValue MEMBER _defaultValue) public: From 6fb313cc435374f5d102836177daac812349bd6f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 13:03:59 -0700 Subject: [PATCH 095/109] Allow streaming uints. --- libraries/metavoxels/src/AttributeRegistry.h | 2 +- libraries/metavoxels/src/Bitstream.cpp | 12 ++++++++++++ libraries/metavoxels/src/Bitstream.h | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 16968da1b2..b6a415bef7 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -265,7 +265,7 @@ template inline bool SimpleInlineAttribute::merge(vo /// Provides appropriate averaging for RGBA values. class QRgbAttribute : public InlineAttribute { Q_OBJECT - Q_PROPERTY(unsigned int defaultValue MEMBER _defaultValue) + Q_PROPERTY(uint defaultValue MEMBER _defaultValue) public: diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 0af44eaae6..f80ef3aa28 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -23,6 +23,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(bool) REGISTER_SIMPLE_TYPE_STREAMER(int) +REGISTER_SIMPLE_TYPE_STREAMER(uint) REGISTER_SIMPLE_TYPE_STREAMER(float) REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) REGISTER_SIMPLE_TYPE_STREAMER(QColor) @@ -238,6 +239,17 @@ Bitstream& Bitstream::operator>>(int& value) { return *this; } +Bitstream& Bitstream::operator<<(uint value) { + return write(&value, 32); +} + +Bitstream& Bitstream::operator>>(uint& value) { + quint32 sizedValue; + read(&sizedValue, 32); + value = sizedValue; + return *this; +} + Bitstream& Bitstream::operator<<(float value) { return write(&value, 32); } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 297d265d97..ca26509d60 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -254,6 +254,9 @@ public: Bitstream& operator<<(int value); Bitstream& operator>>(int& value); + Bitstream& operator<<(uint value); + Bitstream& operator>>(uint& value); + Bitstream& operator<<(float value); Bitstream& operator>>(float& value); From 6caf6b2c311d2950a8ae764bea3b94055a5e204a Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 12 Mar 2014 21:23:35 +0200 Subject: [PATCH 096/109] Filtered received messages, accepting only group ones. --- interface/src/ui/ChatWindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 7a3f1ab906..2fd3d4f9cb 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -179,6 +179,10 @@ void ChatWindow::participantsChanged() { } void ChatWindow::messageReceived(const QXmppMessage& message) { + if (message.type() != QXmppMessage::GroupChat) { + return; + } + QLabel* userLabel = new QLabel(getParticipantName(message.from())); QFont font = userLabel->font(); font.setBold(true); From 800be7eef47af7795f2c7337c2dd99456228f42e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 13:38:16 -0700 Subject: [PATCH 097/109] One more degree conversion. --- interface/src/ui/MetavoxelEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index b1842e0619..eed8a7b6f3 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -387,7 +387,7 @@ void BoxSetTool::render() { glm::quat rotation = _editor->getGridRotation(); glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glm::quat inverseRotation = glm::inverse(rotation); glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); From f85f49caa624a0abcf102d5a6450c5ec60b05ec0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 14:03:22 -0700 Subject: [PATCH 098/109] Might as well update this. --- interface/interface_en.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 685c6e3f67..f700be655c 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) From 5941ad0dd1e791ad03bd5c9a276a72cc16c4d7d3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 12 Mar 2014 14:08:23 -0700 Subject: [PATCH 099/109] Changed lockForWrite() to lockForRead() --- interface/src/ui/LocalVoxelsOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/LocalVoxelsOverlay.cpp b/interface/src/ui/LocalVoxelsOverlay.cpp index 71ae9ec74e..7eaf9ed5c5 100644 --- a/interface/src/ui/LocalVoxelsOverlay.cpp +++ b/interface/src/ui/LocalVoxelsOverlay.cpp @@ -40,7 +40,7 @@ void LocalVoxelsOverlay::update(float deltatime) { _voxelSystem->init(); } - _tree->lockForWrite(); + _tree->lockForRead(); if (_visible && _voxelCount != _tree->getOctreeElementsCount()) { _voxelCount = _tree->getOctreeElementsCount(); _voxelSystem->forceRedrawEntireTree(); From d67381d85ee890e57a70c4c5c9dd7236f2dec158 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 12 Mar 2014 23:45:10 +0200 Subject: [PATCH 100/109] Changed the font used in the chat to Helvetica, with Arial as a fallback. --- interface/interface_en.ts | 14 +++++++------- interface/src/ui/ChatWindow.cpp | 5 +---- interface/ui/chatWindow.ui | 5 ++++- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 685c6e3f67..72299befa4 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -28,19 +28,19 @@ ChatWindow - + Chat - + Connecting to XMPP... - + online now: diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 2fd3d4f9cb..5fc5d56430 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -184,11 +184,8 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { } QLabel* userLabel = new QLabel(getParticipantName(message.from())); - QFont font = userLabel->font(); - font.setBold(true); - userLabel->setFont(font); userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - userLabel->setStyleSheet("padding: 2px;"); + userLabel->setStyleSheet("padding: 2px; font-weight: bold"); userLabel->setAlignment(Qt::AlignTop); QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "\\1")); diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 199d9b42e3..18c9b3814a 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -67,7 +67,7 @@ - margin-top: 12px; + margin-top: 12px; font-family: Helvetica, Arial, sans-serif; Qt::ScrollBarAlwaysOff @@ -121,6 +121,9 @@ 0 + + font-family: Helvetica, Arial, sans-serif; + Qt::ScrollBarAlwaysOff From 889c1358fc100fdd3c51d8bb15f50e2a70a840d0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 14:56:52 -0700 Subject: [PATCH 101/109] Increase the distance at which avatars glow when moving. --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ea7ffc6f2b..4ad7a90d6f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -189,7 +189,7 @@ void Avatar::render(bool forShadowMap) { { // glow when moving in the distance - const float GLOW_DISTANCE = 5.0f; + const float GLOW_DISTANCE = 10.0f; Glower glower(_moving && lengthToTarget > GLOW_DISTANCE && !forShadowMap ? 1.0f : 0.0f); // render body From bab74e9cd4fe0faeea5fdd8bd8df06f70b57c960 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 15:01:09 -0700 Subject: [PATCH 102/109] Push the distance back even more. --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4ad7a90d6f..b7e3d675a5 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -189,7 +189,7 @@ void Avatar::render(bool forShadowMap) { { // glow when moving in the distance - const float GLOW_DISTANCE = 10.0f; + const float GLOW_DISTANCE = 20.0f; Glower glower(_moving && lengthToTarget > GLOW_DISTANCE && !forShadowMap ? 1.0f : 0.0f); // render body From ffe371757343f2edabea23713c839b39ddedc61b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 12 Mar 2014 15:10:33 -0700 Subject: [PATCH 103/109] Enable back face culling on the opaque parts of avatars. --- interface/src/renderer/Model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f657ef01d7..21fdc80858 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -328,6 +328,8 @@ bool Model::render(float alpha) { glDisable(GL_COLOR_MATERIAL); + glEnable(GL_CULL_FACE); + // render opaque meshes with alpha testing glEnable(GL_ALPHA_TEST); @@ -339,8 +341,6 @@ bool Model::render(float alpha) { // render translucent meshes afterwards, with back face culling - glEnable(GL_CULL_FACE); - renderMeshes(alpha, true); glDisable(GL_CULL_FACE); From 6d4d61c38a1db08c2303ffc7f930b3d484e3a403 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 13 Mar 2014 00:51:04 +0200 Subject: [PATCH 104/109] Removed the double border between the sent messages and the entry. --- interface/interface_en.ts | 6 +++--- interface/ui/chatWindow.ui | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 72299befa4..175a1f4525 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -28,19 +28,19 @@ ChatWindow - + Chat - + Connecting to XMPP... - + online now: diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 18c9b3814a..d254efb71a 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -81,7 +81,7 @@ 0 0 358 - 448 + 454 @@ -122,7 +122,10 @@ - font-family: Helvetica, Arial, sans-serif; + border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px; + + + QFrame::NoFrame Qt::ScrollBarAlwaysOff From 3b653ca542ca3313f3594c02182928e583b43d81 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 12 Mar 2014 17:00:12 -0700 Subject: [PATCH 105/109] add a sendAvatarAudioStream option to Agent for mixer load testing, closes #2291 --- assignment-client/src/Agent.cpp | 28 +++++++++++++++++++- assignment-client/src/Agent.h | 7 +++++ libraries/script-engine/src/ScriptEngine.cpp | 27 +++++++++++++------ libraries/script-engine/src/ScriptEngine.h | 11 ++++++-- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 13b1f2bdcf..9d4f07043d 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -25,7 +26,8 @@ Agent::Agent(const QByteArray& packet) : ThreadedAssignment(packet), _voxelEditSender(), - _particleEditSender() + _particleEditSender(), + _avatarAudioStream(NULL) { // be the parent of the script engine so it gets moved when we do _scriptEngine.setParent(this); @@ -34,6 +36,30 @@ Agent::Agent(const QByteArray& packet) : _scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender); } +Agent::~Agent() { + delete _avatarAudioStream; +} + +const int SCRIPT_AUDIO_BUFFER_SAMPLES = (SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000); + +void Agent::setSendAvatarAudioStream(bool sendAvatarAudioStream) { + if (sendAvatarAudioStream) { + // the agentAudioStream number of samples is related to the ScriptEngine callback rate + _avatarAudioStream = new int16_t[SCRIPT_AUDIO_BUFFER_SAMPLES]; + + // fill the _audioStream with zeroes to start + memset(_avatarAudioStream, 0, SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t)); + + _scriptEngine.setNumAvatarAudioBufferSamples(SCRIPT_AUDIO_BUFFER_SAMPLES); + _scriptEngine.setAvatarAudioBuffer(_avatarAudioStream); + } else { + delete _avatarAudioStream; + _avatarAudioStream = NULL; + + _scriptEngine.setAvatarAudioBuffer(NULL); + } +} + void Agent::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 1b41636874..a051f42faf 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -28,12 +28,17 @@ class Agent : public ThreadedAssignment { Q_OBJECT Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) + Q_PROPERTY(bool sendAvatarAudioStream READ isSendingAvatarAudioStream WRITE setSendAvatarAudioStream) public: Agent(const QByteArray& packet); + ~Agent(); void setIsAvatar(bool isAvatar) { QMetaObject::invokeMethod(&_scriptEngine, "setIsAvatar", Q_ARG(bool, isAvatar)); } bool isAvatar() const { return _scriptEngine.isAvatar(); } + void setSendAvatarAudioStream(bool sendAvatarAudioStream); + bool isSendingAvatarAudioStream() const { return (bool) _scriptEngine.sendsAvatarAudioStream(); } + public slots: void run(); void readPendingDatagrams(); @@ -45,6 +50,8 @@ private: ParticleTreeHeadlessViewer _particleViewer; VoxelTreeHeadlessViewer _voxelViewer; + + int16_t* _avatarAudioStream; }; #endif /* defined(__hifi__Agent__) */ diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 04916687fb..9a672317ae 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -28,8 +28,6 @@ #include "LocalVoxels.h" #include "ScriptEngine.h" -const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; - int ScriptEngine::_scriptNumber = 1; VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; @@ -54,6 +52,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co _avatarIdentityTimer(NULL), _avatarBillboardTimer(NULL), _timerFunctionMap(), + _avatarAudioBuffer(NULL), _controllerScriptingInterface(controllerScriptingInterface), _avatarData(NULL), _wantMenuItems(wantMenuItems), @@ -77,9 +76,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co _scriptNumber++; } -ScriptEngine::~ScriptEngine() { -} - void ScriptEngine::setIsAvatar(bool isAvatar) { _isAvatar = isAvatar; @@ -169,8 +165,8 @@ void ScriptEngine::init() { _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); // let the VoxelPacketSender know how frequently we plan to call it - _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); - _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); + _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); + _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); } @@ -228,7 +224,7 @@ void ScriptEngine::run() { qint64 lastUpdate = usecTimestampNow(); while (!_isFinished) { - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); + int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); } @@ -268,6 +264,21 @@ void ScriptEngine::run() { avatarPacket.append(_avatarData->toByteArray()); nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer); + + if (_avatarAudioBuffer && _numAvatarAudioBufferSamples > 0) { + // if have an avatar audio stream then send it out to our audio-mixer + QByteArray audioPacket = byteArrayWithPopulatedHeader(PacketTypeMicrophoneAudioNoEcho); + QDataStream packetStream(&audioPacket, QIODevice::Append); + + // use the orientation and position of this avatar for the source of this audio + packetStream.writeRawData(reinterpret_cast(&_avatarData->getPosition()), sizeof(glm::vec3)); + glm::quat headOrientation = _avatarData->getHeadOrientation(); + packetStream.writeRawData(reinterpret_cast(&headOrientation), sizeof(glm::quat)); + packetStream.writeRawData(reinterpret_cast(_avatarAudioBuffer), + _numAvatarAudioBufferSamples * sizeof(int16_t)); + + nodeList->broadcastToNodes(audioPacket, NodeSet() << NodeType::AudioMixer); + } } qint64 now = usecTimestampNow(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 186524eb7f..9a1d2172dd 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -28,6 +28,8 @@ class ParticlesScriptingInterface; const QString NO_SCRIPT(""); +const unsigned int SCRIPT_DATA_CALLBACK_USECS = roundf((1.0 / 60.0) * 1000 * 1000); + class ScriptEngine : public QObject { Q_OBJECT public: @@ -35,8 +37,6 @@ public: const QString& scriptMenuName = QString(""), AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); - ~ScriptEngine(); - /// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; } @@ -56,6 +56,11 @@ public: void setAvatarData(AvatarData* avatarData, const QString& objectName); + void setAvatarAudioBuffer(int16_t* avatarAudioBuffer) { _avatarAudioBuffer = avatarAudioBuffer; } + bool sendsAvatarAudioStream() const { return (bool) _avatarAudioBuffer; } + void setNumAvatarAudioBufferSamples(int numAvatarAudioBufferSamples) + { _numAvatarAudioBufferSamples = numAvatarAudioBufferSamples; } + void init(); void run(); /// runs continuously until Agent.stop() is called void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller @@ -86,6 +91,8 @@ protected: QTimer* _avatarIdentityTimer; QTimer* _avatarBillboardTimer; QHash _timerFunctionMap; + int16_t* _avatarAudioBuffer; + int _numAvatarAudioBufferSamples; private: void sendAvatarIdentityPacket(); From bd6c42a68505465fd1e4bc9b068dda4733c399bd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 12 Mar 2014 17:32:52 -0700 Subject: [PATCH 106/109] define roundf for use on Win from ScriptEngine --- libraries/script-engine/src/ScriptEngine.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 9a1d2172dd..7dadc17cc6 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -20,14 +20,20 @@ #include -class ParticlesScriptingInterface; - #include "AbstractControllerScriptingInterface.h" #include "Quat.h" #include "Vec3.h" +class ParticlesScriptingInterface; + const QString NO_SCRIPT(""); +#ifdef Q_OS_WIN32 +inline double roundf(double value) { + return (value > 0.0) ? floor(value + 0.5) : ceil(value - 0.5); +} +#endif + const unsigned int SCRIPT_DATA_CALLBACK_USECS = roundf((1.0 / 60.0) * 1000 * 1000); class ScriptEngine : public QObject { From 54d73d4a6458df8041459b410e7cadd2382ebb5a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 12 Mar 2014 17:50:58 -0700 Subject: [PATCH 107/109] use floor since round is missing on windows --- assignment-client/src/Agent.cpp | 2 +- libraries/script-engine/src/ScriptEngine.h | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 9d4f07043d..46f4d233c2 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -40,7 +40,7 @@ Agent::~Agent() { delete _avatarAudioStream; } -const int SCRIPT_AUDIO_BUFFER_SAMPLES = (SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000); +const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000)) + 0.5); void Agent::setSendAvatarAudioStream(bool sendAvatarAudioStream) { if (sendAvatarAudioStream) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 7dadc17cc6..606d0aabf4 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -28,13 +28,7 @@ class ParticlesScriptingInterface; const QString NO_SCRIPT(""); -#ifdef Q_OS_WIN32 -inline double roundf(double value) { - return (value > 0.0) ? floor(value + 0.5) : ceil(value - 0.5); -} -#endif - -const unsigned int SCRIPT_DATA_CALLBACK_USECS = roundf((1.0 / 60.0) * 1000 * 1000); +const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 1000) + 0.5); class ScriptEngine : public QObject { Q_OBJECT From 727903b216fd82b5b47e710c70d653cf4a08980d Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 13 Mar 2014 01:55:42 +0200 Subject: [PATCH 108/109] Changed the chat window to a popup with a custom closing button. --- interface/CMakeLists.txt | 7 ++- interface/interface_en.ts | 16 ++--- interface/resources/images/close.svg | 14 +++++ interface/resources/resources.qrc | 5 ++ interface/src/Menu.cpp | 9 +-- interface/src/ui/ChatWindow.cpp | 2 +- interface/ui/chatWindow.ui | 89 ++++++++++++++++++++++------ 7 files changed, 108 insertions(+), 34 deletions(-) create mode 100644 interface/resources/images/close.svg create mode 100644 interface/resources/resources.qrc diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d049fc0154..1cb0344f86 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -73,8 +73,13 @@ file (GLOB_RECURSE QT_UI_FILES ui/*.ui) # have qt5 wrap them and generate the appropriate header files qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") +# grab the resource files in resources +file (GLOB_RECURSE QT_RESOURCE_FILES resources/*.qrc) +# have qt5 wrap them and generate the appropriate source files +qt5_add_resources(QT_RESOURCES "${QT_RESOURCE_FILES}") + # add them to the interface source files -set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}") +set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}") set(QM ${TARGET_NAME}_en.qm) set(TS ${TARGET_NAME}_en.ts) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 175a1f4525..72e5ee3d3e 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -28,19 +28,19 @@ ChatWindow - + Chat - + Connecting to XMPP... - - + + online now: diff --git a/interface/resources/images/close.svg b/interface/resources/images/close.svg new file mode 100644 index 0000000000..8fe4bf4bdb --- /dev/null +++ b/interface/resources/images/close.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/interface/resources/resources.qrc b/interface/resources/resources.qrc new file mode 100644 index 0000000000..372fa8b1d4 --- /dev/null +++ b/interface/resources/resources.qrc @@ -0,0 +1,5 @@ + + + images/close.svg + + diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f73da0400a..95b94bdcbf 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1039,15 +1039,10 @@ void Menu::showChat() { if (!_chatWindow) { _chatWindow = new ChatWindow(); QMainWindow* mainWindow = Application::getInstance()->getWindow(); - - // the height of the title bar is given by frameGeometry().height() - geometry().height() - // however, frameGeometry() is initialised after showing (Qt queries the OS windowing system) - // on the other hand, moving a window after showing it flickers; so just use some reasonable value - int titleBarHeight = 16; _chatWindow->setGeometry(mainWindow->width() - _chatWindow->width(), - mainWindow->geometry().y() + titleBarHeight, + mainWindow->geometry().y(), _chatWindow->width(), - mainWindow->height() - titleBarHeight); + mainWindow->height()); _chatWindow->show(); } _chatWindow->raise(); diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 5fc5d56430..3a1c26ee35 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -28,7 +28,7 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20; const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); ChatWindow::ChatWindow() : - QDialog(Application::getInstance()->getGLWidget(), Qt::Tool), + QDialog(Application::getInstance()->getGLWidget(), Qt::CustomizeWindowHint), ui(new Ui::ChatWindow), numMessagesAfterLastTimeStamp(0) { diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index d254efb71a..9959c638b2 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -46,20 +46,53 @@ - - - - 0 - 0 - - - - font-weight: bold; color: palette(shadow); margin-bottom: 4px; - - - online now: - - + + + + + + 0 + 0 + + + + font-weight: bold; color: palette(shadow); margin-bottom: 4px; + + + online now: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + Qt::NoFocus + + + + + + + :/images/close.svg:/images/close.svg + + + true + + + + @@ -81,7 +114,7 @@ 0 0 358 - 454 + 452 @@ -133,6 +166,9 @@ QAbstractScrollArea::AdjustToContents + + true + @@ -141,6 +177,25 @@ messagePlainTextEdit messagesScrollArea - - + + + + + + closeButton + clicked() + ChatWindow + accept() + + + 342 + 42 + + + 550 + 42 + + + +
From 442ca145da90a46620eb3495afc541b2aff382e8 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 13 Mar 2014 14:07:21 +0200 Subject: [PATCH 109/109] Hid the chat window rather than close it in order to preserve sent messages. --- interface/interface_en.ts | 8 ++++---- interface/src/Menu.cpp | 2 ++ interface/src/ui/ChatWindow.cpp | 7 +++++++ interface/src/ui/ChatWindow.h | 2 ++ interface/ui/chatWindow.ui | 4 ++-- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 72e5ee3d3e..1d962393d7 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -45,7 +45,7 @@ - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 95b94bdcbf..00ae0a3772 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1043,6 +1043,8 @@ void Menu::showChat() { mainWindow->geometry().y(), _chatWindow->width(), mainWindow->height()); + } + if (!_chatWindow->isVisible()) { _chatWindow->show(); } _chatWindow->raise(); diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 3a1c26ee35..d4f257c0f7 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -50,6 +50,7 @@ ChatWindow::ChatWindow() : startTimerForTimeStamps(); } else { ui->numOnlineLabel->hide(); + ui->closeButton->hide(); ui->usersWidget->hide(); ui->messagesScrollArea->hide(); ui->messagePlainTextEdit->hide(); @@ -71,6 +72,10 @@ ChatWindow::~ChatWindow() { delete ui; } +void ChatWindow::reject() { + hide(); +} + bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { Q_UNUSED(sender); @@ -136,9 +141,11 @@ void ChatWindow::startTimerForTimeStamps() { void ChatWindow::connected() { ui->connectingToXMPPLabel->hide(); ui->numOnlineLabel->show(); + ui->closeButton->show(); ui->usersWidget->show(); ui->messagesScrollArea->show(); ui->messagePlainTextEdit->show(); + ui->messagePlainTextEdit->setFocus(); #ifdef HAVE_QXMPP const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); connect(publicChatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged())); diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h index 9250da8933..57da4da45d 100644 --- a/interface/src/ui/ChatWindow.h +++ b/interface/src/ui/ChatWindow.h @@ -33,6 +33,8 @@ public: ChatWindow(); ~ChatWindow(); + virtual void reject(); + protected: bool eventFilter(QObject* sender, QEvent* event); diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 9959c638b2..ecb4d79c41 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -185,10 +185,10 @@ closeButton clicked() ChatWindow - accept() + hide() - 342 + 390 42