From 57760bca7d1f3de23af220b2a10cdecee747d659 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 8 Jun 2016 16:35:38 -0700
Subject: [PATCH 01/35] sort ShapeInfo data members

---
 libraries/shared/src/ShapeInfo.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h
index c853666d90..ad7a76c6a4 100644
--- a/libraries/shared/src/ShapeInfo.h
+++ b/libraries/shared/src/ShapeInfo.h
@@ -74,12 +74,12 @@ public:
     const DoubleHashKey& getHash() const;
 
 protected:
-    ShapeType _type = SHAPE_TYPE_NONE;
+    QUrl _url; // url for model of convex collision hulls
+    QVector<QVector<glm::vec3>> _points; // points for convex collision hulls
     glm::vec3 _halfExtents = glm::vec3(0.0f);
     glm::vec3 _offset = glm::vec3(0.0f);
     DoubleHashKey _doubleHashKey;
-    QVector<QVector<glm::vec3>> _points; // points for convex collision hulls
-    QUrl _url; // url for model of convex collision hulls
+    ShapeType _type = SHAPE_TYPE_NONE;
 };
 
 #endif // hifi_ShapeInfo_h

From d64729372a1cedc9cfc99464e5b15787bd063650 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Thu, 9 Jun 2016 16:14:49 -0700
Subject: [PATCH 02/35] ShapeInfo name changes

---
 .../src/RenderableModelEntityItem.cpp         | 22 ++++----
 .../src/RenderablePolyVoxEntityItem.cpp       | 18 +++----
 .../src/RenderablePolyVoxEntityItem.h         |  2 +-
 .../physics/src/PhysicalEntitySimulation.cpp  |  2 +-
 libraries/physics/src/ShapeFactory.cpp        |  6 +--
 libraries/shared/src/ShapeInfo.cpp            | 50 +++++++++++--------
 libraries/shared/src/ShapeInfo.h              | 19 ++++---
 tests/physics/src/ShapeManagerTests.cpp       | 10 ++--
 8 files changed, 70 insertions(+), 59 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 7b3b3c3efe..07bcc05572 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -608,8 +608,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         const FBXGeometry& renderGeometry = _model->getFBXGeometry();
         const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry();
 
-        QVector<QVector<glm::vec3>>& points = info.getPoints();
-        points.clear();
+        ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
+        pointCollection.clear();
         uint32_t i = 0;
 
         // the way OBJ files get read, each section under a "g" line is its own meshPart.  We only expect
@@ -619,8 +619,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
             // each meshPart is a convex hull
             foreach (const FBXMeshPart &meshPart, mesh.parts) {
-                points.push_back(QVector<glm::vec3>());
-                QVector<glm::vec3>& pointsInPart = points[i];
+                pointCollection.push_back(QVector<glm::vec3>());
+                ShapeInfo::PointList& pointsInPart = pointCollection[i];
 
                 // run through all the triangles and (uniquely) add each point to the hull
                 uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size();
@@ -664,7 +664,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
 
                 if (pointsInPart.size() == 0) {
                     qCDebug(entitiesrenderer) << "Warning -- meshPart has no faces";
-                    points.pop_back();
+                    pointCollection.pop_back();
                     continue;
                 }
                 ++i;
@@ -681,16 +681,16 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         // multiply each point by scale before handing the point-set off to the physics engine.
         // also determine the extents of the collision model.
         AABox box;
-        for (int i = 0; i < points.size(); i++) {
-            for (int j = 0; j < points[i].size(); j++) {
+        for (int i = 0; i < pointCollection.size(); i++) {
+            for (int j = 0; j < pointCollection[i].size(); j++) {
                 // compensate for registration
-                points[i][j] += _model->getOffset();
+                pointCollection[i][j] += _model->getOffset();
                 // scale so the collision points match the model points
-                points[i][j] *= scale;
+                pointCollection[i][j] *= scale;
                 // this next subtraction is done so we can give info the offset, which will cause
                 // the shape-key to change.
-                points[i][j] -= _model->getOffset();
-                box += points[i][j];
+                pointCollection[i][j] -= _model->getOffset();
+                box += pointCollection[i][j];
             }
         }
 
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
index ad35a1a00c..7d5f227558 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
@@ -1198,7 +1198,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() {
 
     QtConcurrent::run([entity, voxelSurfaceStyle, voxelVolumeSize, mesh] {
         auto polyVoxEntity = std::static_pointer_cast<RenderablePolyVoxEntityItem>(entity);
-        QVector<QVector<glm::vec3>> points;
+        QVector<QVector<glm::vec3>> pointCollection;
         AABox box;
         glm::mat4 vtoM = std::static_pointer_cast<RenderablePolyVoxEntityItem>(entity)->voxelToLocalMatrix();
 
@@ -1241,9 +1241,9 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() {
                 pointsInPart << p3Model;
                 // add next convex hull
                 QVector<glm::vec3> newMeshPoints;
-                points << newMeshPoints;
+                pointCollection << newMeshPoints;
                 // add points to the new convex hull
-                points[i++] << pointsInPart;
+                pointCollection[i++] << pointsInPart;
             }
         } else {
             unsigned int i = 0;
@@ -1299,19 +1299,19 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() {
 
                     // add next convex hull
                     QVector<glm::vec3> newMeshPoints;
-                    points << newMeshPoints;
+                    pointCollection << newMeshPoints;
                     // add points to the new convex hull
-                    points[i++] << pointsInPart;
+                    pointCollection[i++] << pointsInPart;
                 }
             });
         }
-        polyVoxEntity->setCollisionPoints(points, box);
+        polyVoxEntity->setCollisionPoints(pointCollection, box);
     });
 }
 
-void RenderablePolyVoxEntityItem::setCollisionPoints(const QVector<QVector<glm::vec3>> points, AABox box) {
+void RenderablePolyVoxEntityItem::setCollisionPoints(ShapeInfo::PointCollection pointCollection, AABox box) {
     // this catches the payload from computeShapeInfoWorker
-    if (points.isEmpty()) {
+    if (pointCollection.isEmpty()) {
         EntityItem::computeShapeInfo(_shapeInfo);
         return;
     }
@@ -1325,7 +1325,7 @@ void RenderablePolyVoxEntityItem::setCollisionPoints(const QVector<QVector<glm::
                 QString::number(_registrationPoint.y) + "," +
                 QString::number(_registrationPoint.z);
             _shapeInfo.setParams(SHAPE_TYPE_COMPOUND, collisionModelDimensions, shapeKey);
-            _shapeInfo.setConvexHulls(points);
+            _shapeInfo.setPointCollection(pointCollection);
             _meshDirty = false;
         });
 }
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
index e5afb94afa..c46a26deb5 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
@@ -123,7 +123,7 @@ public:
                            std::function<void(int, int, int, uint8_t)> thunk);
 
     void setMesh(model::MeshPointer mesh);
-    void setCollisionPoints(const QVector<QVector<glm::vec3>> points, AABox box);
+    void setCollisionPoints(ShapeInfo::PointCollection points, AABox box);
     PolyVox::SimpleVolume<uint8_t>* getVolData() { return _volData; }
 
     uint8_t getVoxelInternal(int x, int y, int z);
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index 6806b3a398..c5134fc027 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -217,7 +217,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
         } else if (entity->isReadyToComputeShape()) {
             ShapeInfo shapeInfo;
             entity->computeShapeInfo(shapeInfo);
-            int numPoints = shapeInfo.getMaxNumPoints();
+            int numPoints = shapeInfo.getLargestSubshapePointCount();
             if (numPoints > MAX_HULL_POINTS) {
                 qWarning() << "convex hull with" << numPoints
                     << "points for entity" << entity->getName()
diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp
index d667d1075d..a23ef97007 100644
--- a/libraries/physics/src/ShapeFactory.cpp
+++ b/libraries/physics/src/ShapeFactory.cpp
@@ -179,15 +179,15 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
         }
         break;
         case SHAPE_TYPE_COMPOUND: {
-            const QVector<QVector<glm::vec3>>& points = info.getPoints();
+            const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
             uint32_t numSubShapes = info.getNumSubShapes();
             if (numSubShapes == 1) {
-                shape = createConvexHull(info.getPoints()[0]);
+                shape = createConvexHull(pointCollection[0]);
             } else {
                 auto compound = new btCompoundShape();
                 btTransform trans;
                 trans.setIdentity();
-                foreach (QVector<glm::vec3> hullPoints, points) {
+                foreach (const ShapeInfo::PointList& hullPoints, pointCollection) {
                     btConvexHullShape* hull = createConvexHull(hullPoints);
                     compound->addChildShape (trans, hull);
                 }
diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp
index 9c1e5c3816..cd0cb6fe8a 100644
--- a/libraries/shared/src/ShapeInfo.cpp
+++ b/libraries/shared/src/ShapeInfo.cpp
@@ -16,9 +16,13 @@
 #include "NumericalConstants.h" // for MILLIMETERS_PER_METER
 
 void ShapeInfo::clear() {
-    _type = SHAPE_TYPE_NONE;
-    _halfExtents = _offset = glm::vec3(0.0f);
+    _url.clear();
+    _pointCollection.clear();
+    _triangleIndices.clear();
+    _halfExtents = glm::vec3(0.0f);
+    _offset = glm::vec3(0.0f);
     _doubleHashKey.clear();
+    _type = SHAPE_TYPE_NONE;
 }
 
 void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
@@ -61,9 +65,9 @@ void ShapeInfo::setSphere(float radius) {
     _doubleHashKey.clear();
 }
 
-void ShapeInfo::setConvexHulls(const QVector<QVector<glm::vec3>>& points) {
-    _points = points;
-    _type = (_points.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
+void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
+    _pointCollection = pointCollection;
+    _type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
     _doubleHashKey.clear();
 }
 
@@ -83,15 +87,15 @@ uint32_t ShapeInfo::getNumSubShapes() const {
     if (_type == SHAPE_TYPE_NONE) {
         return 0;
     } else if (_type == SHAPE_TYPE_COMPOUND) {
-        return _points.size();
+        return _pointCollection.size();
     }
     return 1;
 }
 
-int ShapeInfo::getMaxNumPoints() const {
+int ShapeInfo::getLargestSubshapePointCount() const {
     int numPoints = 0;
-    for (int i = 0; i < _points.size(); ++i) {
-        int n = _points[i].size();
+    for (int i = 0; i < _pointCollection.size(); ++i) {
+        int n = _pointCollection[i].size();
         if (n > numPoints) {
             numPoints = n;
         }
@@ -187,23 +191,23 @@ const DoubleHashKey& ShapeInfo::getHash() const {
         // TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
         uint32_t primeIndex = 0;
         key.computeHash((uint32_t)_type, primeIndex++);
-    
-        // compute hash1 
+
+        // compute hash1
         uint32_t hash = key.getHash();
         for (int j = 0; j < 3; ++j) {
             // NOTE: 0.49f is used to bump the float up almost half a millimeter
             // so the cast to int produces a round() effect rather than a floor()
             hash ^= DoubleHashKey::hashFunction(
-                    (uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), 
+                    (uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f),
                     primeIndex++);
             if (useOffset) {
                 hash ^= DoubleHashKey::hashFunction(
-                        (uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f), 
+                        (uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f),
                         primeIndex++);
             }
         }
         key.setHash(hash);
-    
+
         // compute hash2
         hash = key.getHash2();
         for (int j = 0; j < 3; ++j) {
@@ -224,14 +228,16 @@ const DoubleHashKey& ShapeInfo::getHash() const {
         }
         key.setHash2(hash);
 
-        QString url = _url.toString();
-        if (!url.isEmpty()) {
-            // fold the urlHash into both parts
-            QByteArray baUrl = url.toLocal8Bit();
-            const char *cUrl = baUrl.data();
-            uint32_t urlHash = qChecksum(cUrl, baUrl.count());
-            key.setHash(key.getHash() ^ urlHash);
-            key.setHash2(key.getHash2() ^ urlHash);
+        if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_MESH) {
+            QString url = _url.toString();
+            if (!url.isEmpty()) {
+                // fold the urlHash into both parts
+                QByteArray baUrl = url.toLocal8Bit();
+                const char *cUrl = baUrl.data();
+                uint32_t urlHash = qChecksum(cUrl, baUrl.count());
+                key.setHash(key.getHash() ^ urlHash);
+                key.setHash2(key.getHash2() ^ urlHash);
+            }
         }
     }
     return _doubleHashKey;
diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h
index ad7a76c6a4..7f178bb53a 100644
--- a/libraries/shared/src/ShapeInfo.h
+++ b/libraries/shared/src/ShapeInfo.h
@@ -38,18 +38,23 @@ enum ShapeType {
     SHAPE_TYPE_CYLINDER_X,
     SHAPE_TYPE_CYLINDER_Y,
     SHAPE_TYPE_CYLINDER_Z,
-    SHAPE_TYPE_STATIC_MESH
+    SHAPE_TYPE_MESH
 };
 
 class ShapeInfo {
 
 public:
+
+    using PointList = QVector<glm::vec3>;
+    using PointCollection = QVector<PointList>;
+    using TriangleIndices = QVector<uint32_t>;
+
     void clear();
 
     void setParams(ShapeType type, const glm::vec3& halfExtents, QString url="");
     void setBox(const glm::vec3& halfExtents);
     void setSphere(float radius);
-    void setConvexHulls(const QVector<QVector<glm::vec3>>& points);
+    void setPointCollection(const PointCollection& pointCollection);
     void setCapsuleY(float radius, float halfHeight);
     void setOffset(const glm::vec3& offset);
 
@@ -58,12 +63,11 @@ public:
     const glm::vec3& getHalfExtents() const { return _halfExtents; }
     const glm::vec3& getOffset() const { return _offset; }
 
-    QVector<QVector<glm::vec3>>& getPoints() { return _points; }
-    const QVector<QVector<glm::vec3>>& getPoints() const { return _points; }
+    PointCollection& getPointCollection() { return _pointCollection; }
+    const PointCollection& getPointCollection() const { return _pointCollection; }
     uint32_t getNumSubShapes() const;
 
-    void appendToPoints (const QVector<glm::vec3>& newPoints) { _points << newPoints; }
-    int getMaxNumPoints() const;
+    int getLargestSubshapePointCount() const;
 
     float computeVolume() const;
 
@@ -75,7 +79,8 @@ public:
 
 protected:
     QUrl _url; // url for model of convex collision hulls
-    QVector<QVector<glm::vec3>> _points; // points for convex collision hulls
+    PointCollection _pointCollection;
+    TriangleIndices _triangleIndices;
     glm::vec3 _halfExtents = glm::vec3(0.0f);
     glm::vec3 _offset = glm::vec3(0.0f);
     DoubleHashKey _doubleHashKey;
diff --git a/tests/physics/src/ShapeManagerTests.cpp b/tests/physics/src/ShapeManagerTests.cpp
index 66ac9d0c4a..c8805132fa 100644
--- a/tests/physics/src/ShapeManagerTests.cpp
+++ b/tests/physics/src/ShapeManagerTests.cpp
@@ -194,23 +194,23 @@ void ShapeManagerTests::addCompoundShape() {
     int numHullPoints = tetrahedron.size();
 
     // compute the points of the hulls
-    QVector< QVector<glm::vec3> > hulls;
+    ShapeInfo::PointCollection pointCollection;
     int numHulls = 5;
     glm::vec3 offsetNormal(1.0f, 0.0f, 0.0f);
     for (int i = 0; i < numHulls; ++i) {
         glm::vec3 offset = (float)(i - numHulls/2) * offsetNormal;
-        QVector<glm::vec3> hull;
+        ShapeInfo::PointList pointList;
         float radius = (float)(i + 1);
         for (int j = 0; j < numHullPoints; ++j) {
             glm::vec3 point = radius * tetrahedron[j] + offset;
-            hull.push_back(point);
+            pointList.push_back(point);
         }
-        hulls.push_back(hull);
+        pointCollection.push_back(pointList);
     }
 
     // create the ShapeInfo
     ShapeInfo info;
-    info.setConvexHulls(hulls);
+    info.setPointCollection(hulls);
 
     // create the shape
     ShapeManager shapeManager;

From 5d5dc2837b09984a9d7bcfb324cea0d632812f36 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 14 Jun 2016 13:58:53 -0700
Subject: [PATCH 03/35] fix comment

---
 libraries/render-utils/src/Model.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index ded1184c24..0470a238fc 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -1002,7 +1002,7 @@ void Model::scaleToFit() {
     Extents modelMeshExtents = getUnscaledMeshExtents();
 
     // size is our "target size in world space"
-    // we need to set our model scale so that the extents of the mesh, fit in a cube that size...
+    // we need to set our model scale so that the extents of the mesh, fit in a box that size...
     glm::vec3 meshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum;
     glm::vec3 rescaleDimensions = _scaleToFitDimensions / meshDimensions;
     setScaleInternal(rescaleDimensions);

From 13bb174b8b6e84e4fb6ac735dfb6cfc6dec1dee8 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 14 Jun 2016 14:16:49 -0700
Subject: [PATCH 04/35] small change to commented out debug code

---
 libraries/physics/src/PhysicalEntitySimulation.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index c5134fc027..cdf33a6edb 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -231,7 +231,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
                 result.push_back(motionState);
                 entityItr = _entitiesToAddToPhysics.erase(entityItr);
             } else {
-                //qDebug() << "Warning!  Failed to generate new shape for entity." << entity->getName();
+                //qWarning() << "Failed to generate new shape for entity." << entity->getName();
                 ++entityItr;
             }
         } else {

From 9fc77ccfa2f048c61b956b5cd64796097c32a5ea Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 14 Jun 2016 14:19:45 -0700
Subject: [PATCH 05/35] use reference to avoid big copy

---
 libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
index 7d5f227558..eb6db2874f 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
@@ -1207,7 +1207,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() {
             // pull each triangle in the mesh into a polyhedron which can be collided with
             unsigned int i = 0;
 
-            const gpu::BufferView vertexBufferView = mesh->getVertexBuffer();
+            const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer();
             const gpu::BufferView& indexBufferView = mesh->getIndexBuffer();
 
             gpu::BufferView::Iterator<const uint32_t> it = indexBufferView.cbegin<uint32_t>();

From a519b77ae7f2ca513bbc6f8456b4dd144dcbe56b Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 14 Jun 2016 14:22:28 -0700
Subject: [PATCH 06/35] add SHAPE_TYPE_MESH and build mesh shapes

---
 .../src/RenderableModelEntityItem.cpp         | 102 +++++++++++++++++-
 libraries/physics/src/ShapeFactory.cpp        |  97 ++++++++++++++++-
 libraries/physics/src/ShapeFactory.h          |  15 ++-
 libraries/physics/src/ShapeManager.cpp        |  18 ++--
 libraries/shared/src/ShapeInfo.cpp            |  23 ++--
 libraries/shared/src/ShapeInfo.h              |  12 ++-
 6 files changed, 235 insertions(+), 32 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 07bcc05572..e7991eb638 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -599,13 +599,13 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
 
 void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
     ShapeType type = getShapeType();
+    glm::vec3 dimensions = getDimensions();
     if (type == SHAPE_TYPE_COMPOUND) {
         updateModelBounds();
 
         // should never fall in here when collision model not fully loaded
         // hence we assert that all geometries exist and are loaded
         assert(_model->isLoaded() && _model->isCollisionLoaded());
-        const FBXGeometry& renderGeometry = _model->getFBXGeometry();
         const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry();
 
         ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
@@ -677,7 +677,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         // to the visual model and apply them to the collision model (without regard for the
         // collision model's extents).
 
-        glm::vec3 scale = getDimensions() / renderGeometry.getUnscaledMeshExtents().size();
+        const FBXGeometry& renderGeometry = _model->getFBXGeometry();
+        glm::vec3 scale = dimensions / renderGeometry.getUnscaledMeshExtents().size();
         // multiply each point by scale before handing the point-set off to the physics engine.
         // also determine the extents of the collision model.
         AABox box;
@@ -697,9 +698,104 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         glm::vec3 collisionModelDimensions = box.getDimensions();
         info.setParams(type, collisionModelDimensions, _compoundShapeURL);
         info.setOffset(_model->getOffset());
+    } else if (type == SHAPE_TYPE_MESH) {
+        updateModelBounds();
+
+        // should never fall in here when collision model not fully loaded
+        assert(_model->isLoaded());
+
+        ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
+        pointCollection.clear();
+
+        ShapeInfo::PointList points;
+        ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
+        auto& meshes = _model->getGeometry()->getGeometry()->getMeshes();
+
+        glm::vec3 modelOffset = _model->getOffset();
+        for (auto& mesh : meshes) {
+            const gpu::BufferView& vertices = mesh->getVertexBuffer();
+            const gpu::BufferView& indices = mesh->getIndexBuffer();
+            const gpu::BufferView& parts = mesh->getPartBuffer();
+
+            // copy points
+            uint32_t meshIndexOffset = (uint32_t)points.size();
+            gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertices.cbegin<const glm::vec3>();
+            points.reserve(points.size() + vertices.getNumElements());
+            Extents extents;
+            while (vertexItr != vertices.cend<const glm::vec3>()) {
+                points.push_back(*vertexItr);
+                extents.addPoint(*vertexItr);
+                ++vertexItr;
+            }
+
+            // scale points and shift by modelOffset
+            glm::vec3 extentsSize = extents.size();
+            glm::vec3 scale = dimensions / extents.size();
+            for (int i = 0; i < 3; ++i) {
+                if (extentsSize[i] < 1.0e-6f) {
+                    scale[i] = 1.0f;
+                }
+            }
+            for (int i = 0; i < points.size(); ++i) {
+                points[i] = (points[i] * scale) + modelOffset;
+            }
+
+            // copy triangleIndices
+            triangleIndices.reserve(triangleIndices.size() + indices.getNumElements());
+            gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
+            while (partItr != parts.cend<const model::Mesh::Part>()) {
+
+                if (partItr->_topology == model::Mesh::TRIANGLES) {
+                    assert(partItr->_numIndices % 3 == 0);
+                    auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
+                    auto indexEnd = indexItr + partItr->_numIndices;
+                    while (indexItr != indexEnd) {
+                        triangleIndices.push_back(*indexItr + meshIndexOffset);
+                        ++indexItr;
+                    }
+                } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) {
+                    assert(partItr->_numIndices > 2);
+                    uint32_t approxNumIndices = 3 * partItr->_numIndices;
+                    if (approxNumIndices > (uint32_t)(triangleIndices.capacity() - triangleIndices.size())) {
+                        // we underestimated the final size of triangleIndices so we pre-emptively expand it
+                        triangleIndices.reserve(triangleIndices.size() + approxNumIndices);
+                    }
+
+                    auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
+                    auto indexEnd = indexItr + (partItr->_numIndices - 2);
+
+                    // first triangle uses the first three indices
+                    triangleIndices.push_back(*indexItr + meshIndexOffset);
+                    triangleIndices.push_back(*(++indexItr) + meshIndexOffset);
+                    triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
+
+                    // the rest use previous and next index
+                    uint32_t triangleCount = 1;
+                    while (indexItr != indexEnd) {
+                        if (triangleCount % 2 == 0) {
+                            // even triangles use first two indices in order
+                            triangleIndices.push_back(*(indexItr) + meshIndexOffset);
+                            triangleIndices.push_back(*(++indexItr) + meshIndexOffset); // yes pre-increment
+                        } else {
+                            // odd triangles swap order of first two indices
+                            triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
+                            triangleIndices.push_back(*(indexItr++) + meshIndexOffset); // yes post-increment
+                        }
+                        triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
+                        ++triangleCount;
+                    }
+                } else if (partItr->_topology == model::Mesh::QUADS) {
+                    // TODO: support model::Mesh::QUADS
+                }
+                // TODO? support model::Mesh::QUAD_STRIP?
+                ++partItr;
+            }
+        }
+        pointCollection.push_back(points);
+        info.setParams(SHAPE_TYPE_MESH, 0.5f * dimensions, _modelURL);
     } else {
         ModelEntityItem::computeShapeInfo(info);
-        info.setParams(type, 0.5f * getDimensions());
+        info.setParams(type, 0.5f * dimensions);
         adjustShapeInfoByRegistration(info);
     }
 }
diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp
index a23ef97007..4d5f56853d 100644
--- a/libraries/physics/src/ShapeFactory.cpp
+++ b/libraries/physics/src/ShapeFactory.cpp
@@ -67,7 +67,8 @@ static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = {
 };
 
 
-btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& points) {
+// util method
+btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) {
     assert(points.size() > 0);
 
     btConvexHullShape* hull = new btConvexHullShape();
@@ -158,6 +159,84 @@ btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& poin
     return hull;
 }
 
+// util method
+btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) {
+    assert(info.getType() == SHAPE_TYPE_MESH); // should only get here for mesh shapes
+
+    const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
+    assert(pointCollection.size() == 1); // should only have one mesh
+
+    const ShapeInfo::PointList& pointList = pointCollection[0];
+    assert(pointList.size() > 2); // should have at least one triangle's worth of points
+
+    const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
+    assert(triangleIndices.size() > 2); // should have at least one triangle's worth of indices
+
+    // allocate mesh buffers
+    btIndexedMesh mesh;
+    int32_t numIndices = triangleIndices.size();
+    const int32_t VERTICES_PER_TRIANGLE = 3;
+    mesh.m_numTriangles = numIndices / VERTICES_PER_TRIANGLE;
+    if (numIndices < INT16_MAX) {
+        // small number of points so we can use 16-bit indices
+        mesh.m_triangleIndexBase = new unsigned char[sizeof(int16_t) * (size_t)numIndices];
+        mesh.m_indexType = PHY_SHORT;
+        mesh.m_triangleIndexStride = VERTICES_PER_TRIANGLE * sizeof(int16_t);
+    } else {
+        mesh.m_triangleIndexBase = new unsigned char[sizeof(int32_t) * (size_t)numIndices];
+        mesh.m_indexType = PHY_INTEGER;
+        mesh.m_triangleIndexStride = VERTICES_PER_TRIANGLE * sizeof(int32_t);
+    }
+    mesh.m_numVertices = pointList.size();
+    mesh.m_vertexBase = new unsigned char[VERTICES_PER_TRIANGLE * sizeof(btScalar) * (size_t)mesh.m_numVertices];
+    mesh.m_vertexStride = VERTICES_PER_TRIANGLE * sizeof(btScalar);
+    mesh.m_vertexType = PHY_FLOAT;
+
+    // copy data into buffers
+    btScalar* vertexData = static_cast<btScalar*>((void*)(mesh.m_vertexBase));
+    for (int32_t i = 0; i < mesh.m_numVertices; ++i) {
+        int32_t j = i * VERTICES_PER_TRIANGLE;
+        const glm::vec3& point = pointList[i];
+        vertexData[j] = point.x;
+        vertexData[j + 1] = point.y;
+        vertexData[j + 2] = point.z;
+    }
+    if (numIndices < INT16_MAX) {
+        int16_t* indices = static_cast<int16_t*>((void*)(mesh.m_triangleIndexBase));
+        for (int32_t i = 0; i < numIndices; ++i) {
+            indices[i] = triangleIndices[i];
+        }
+    } else {
+        int32_t* indices = static_cast<int32_t*>((void*)(mesh.m_triangleIndexBase));
+        for (int32_t i = 0; i < numIndices; ++i) {
+            indices[i] = triangleIndices[i];
+        }
+    }
+
+    // store buffers in a new dataArray and return the pointer
+    // (external StaticMeshShape will own all of the data that was allocated here)
+    btTriangleIndexVertexArray* dataArray = new btTriangleIndexVertexArray;
+    dataArray->addIndexedMesh(mesh, mesh.m_indexType);
+    return dataArray;
+}
+
+// util method
+void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) {
+    assert(dataArray);
+    IndexedMeshArray& meshes = dataArray->getIndexedMeshArray();
+    for (int32_t i = 0; i < meshes.size(); ++i) {
+        btIndexedMesh mesh = meshes[i];
+        mesh.m_numTriangles = 0;
+        delete [] mesh.m_triangleIndexBase;
+        mesh.m_triangleIndexBase = nullptr;
+        mesh.m_numVertices = 0;
+        delete [] mesh.m_vertexBase;
+        mesh.m_vertexBase = nullptr;
+    }
+    meshes.clear();
+    delete dataArray;
+}
+
 btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
     btCollisionShape* shape = NULL;
     int type = info.getType();
@@ -195,6 +274,11 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
             }
         }
         break;
+        case SHAPE_TYPE_MESH: {
+            btTriangleIndexVertexArray* dataArray = createStaticMeshArray(info);
+            shape = new StaticMeshShape(dataArray);
+        }
+        break;
     }
     if (shape) {
         if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) {
@@ -228,3 +312,14 @@ void ShapeFactory::deleteShape(btCollisionShape* shape) {
     }
     delete shape;
 }
+
+// the dataArray must be created before we create the StaticMeshShape
+ShapeFactory::StaticMeshShape::StaticMeshShape(btTriangleIndexVertexArray* dataArray)
+:   btBvhTriangleMeshShape(dataArray, true), _dataArray(dataArray) {
+    assert(dataArray);
+}
+
+ShapeFactory::StaticMeshShape::~StaticMeshShape() {
+    deleteStaticMeshArray(_dataArray);
+    _dataArray = nullptr;
+}
diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h
index 1ba2bdb619..6202612eb9 100644
--- a/libraries/physics/src/ShapeFactory.h
+++ b/libraries/physics/src/ShapeFactory.h
@@ -20,9 +20,22 @@
 // translates between ShapeInfo and btShape
 
 namespace ShapeFactory {
-    btConvexHullShape* createConvexHull(const QVector<glm::vec3>& points);
     btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
     void deleteShape(btCollisionShape* shape);
+
+    //btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info);
+    //void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray);
+
+    class StaticMeshShape : public btBvhTriangleMeshShape {
+    public:
+        StaticMeshShape() = delete;
+        StaticMeshShape(btTriangleIndexVertexArray* dataArray);
+        ~StaticMeshShape();
+
+    private:
+        // the StaticMeshShape owns its vertex/index data
+        btTriangleIndexVertexArray* _dataArray;
+    };
 };
 
 #endif // hifi_ShapeFactory_h
diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp
index 4231d1eb60..4fa660239c 100644
--- a/libraries/physics/src/ShapeManager.cpp
+++ b/libraries/physics/src/ShapeManager.cpp
@@ -32,15 +32,13 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
     if (info.getType() == SHAPE_TYPE_NONE) {
         return NULL;
     }
-    if (info.getType() != SHAPE_TYPE_COMPOUND) {
-        // Very small or large non-compound objects are not supported.
-        float diagonal = 4.0f * glm::length2(info.getHalfExtents());
-        const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
-        if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED) {
-            // qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
-            return NULL;
-        }
+    const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
+    if (4.0f * glm::length2(info.getHalfExtents()) < MIN_SHAPE_DIAGONAL_SQUARED) {
+        // tiny shapes are not supported
+        // qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
+        return NULL;
     }
+
     DoubleHashKey key = info.getHash();
     ShapeReference* shapeRef = _shapeMap.find(key);
     if (shapeRef) {
@@ -66,8 +64,8 @@ bool ShapeManager::releaseShapeByKey(const DoubleHashKey& key) {
             shapeRef->refCount--;
             if (shapeRef->refCount == 0) {
                 _pendingGarbage.push_back(key);
-                const int MAX_GARBAGE_CAPACITY = 255;
-                if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) {
+                const int MAX_SHAPE_GARBAGE_CAPACITY = 255;
+                if (_pendingGarbage.size() > MAX_SHAPE_GARBAGE_CAPACITY) {
                     collectGarbage();
                 }
             }
diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp
index cd0cb6fe8a..ed1a76ef99 100644
--- a/libraries/shared/src/ShapeInfo.cpp
+++ b/libraries/shared/src/ShapeInfo.cpp
@@ -41,9 +41,9 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
             break;
         }
         case SHAPE_TYPE_COMPOUND:
+        case SHAPE_TYPE_MESH:
             _url = QUrl(url);
-            _halfExtents = halfExtents;
-            break;
+            // yes, fall through
         default:
             _halfExtents = halfExtents;
             break;
@@ -182,18 +182,15 @@ const DoubleHashKey& ShapeInfo::getHash() const {
     // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
     if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
         bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET;
-        // The key is not yet cached therefore we must compute it!  To this end we bypass the const-ness
-        // of this method by grabbing a non-const pointer to "this" and a non-const reference to _doubleHashKey.
-        ShapeInfo* thisPtr = const_cast<ShapeInfo*>(this);
-        DoubleHashKey& key = thisPtr->_doubleHashKey;
+        // The key is not yet cached therefore we must compute it.
 
         // compute hash1
         // TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
         uint32_t primeIndex = 0;
-        key.computeHash((uint32_t)_type, primeIndex++);
+        _doubleHashKey.computeHash((uint32_t)_type, primeIndex++);
 
         // compute hash1
-        uint32_t hash = key.getHash();
+        uint32_t hash = _doubleHashKey.getHash();
         for (int j = 0; j < 3; ++j) {
             // NOTE: 0.49f is used to bump the float up almost half a millimeter
             // so the cast to int produces a round() effect rather than a floor()
@@ -206,10 +203,10 @@ const DoubleHashKey& ShapeInfo::getHash() const {
                         primeIndex++);
             }
         }
-        key.setHash(hash);
+        _doubleHashKey.setHash(hash);
 
         // compute hash2
-        hash = key.getHash2();
+        hash = _doubleHashKey.getHash2();
         for (int j = 0; j < 3; ++j) {
             // NOTE: 0.49f is used to bump the float up almost half a millimeter
             // so the cast to int produces a round() effect rather than a floor()
@@ -226,7 +223,7 @@ const DoubleHashKey& ShapeInfo::getHash() const {
             hash += ~(floatHash << 10);
             hash = (hash << 16) | (hash >> 16);
         }
-        key.setHash2(hash);
+        _doubleHashKey.setHash2(hash);
 
         if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_MESH) {
             QString url = _url.toString();
@@ -235,8 +232,8 @@ const DoubleHashKey& ShapeInfo::getHash() const {
                 QByteArray baUrl = url.toLocal8Bit();
                 const char *cUrl = baUrl.data();
                 uint32_t urlHash = qChecksum(cUrl, baUrl.count());
-                key.setHash(key.getHash() ^ urlHash);
-                key.setHash2(key.getHash2() ^ urlHash);
+                _doubleHashKey.setHash(_doubleHashKey.getHash() ^ urlHash);
+                _doubleHashKey.setHash2(_doubleHashKey.getHash2() ^ urlHash);
             }
         }
     }
diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h
index 7f178bb53a..794f31a987 100644
--- a/libraries/shared/src/ShapeInfo.h
+++ b/libraries/shared/src/ShapeInfo.h
@@ -30,14 +30,15 @@ enum ShapeType {
     SHAPE_TYPE_NONE,
     SHAPE_TYPE_BOX,
     SHAPE_TYPE_SPHERE,
-    SHAPE_TYPE_PLANE,
-    SHAPE_TYPE_COMPOUND,
     SHAPE_TYPE_CAPSULE_X,
     SHAPE_TYPE_CAPSULE_Y,
     SHAPE_TYPE_CAPSULE_Z,
     SHAPE_TYPE_CYLINDER_X,
     SHAPE_TYPE_CYLINDER_Y,
     SHAPE_TYPE_CYLINDER_Z,
+    SHAPE_TYPE_HULL,
+    SHAPE_TYPE_PLANE,
+    SHAPE_TYPE_COMPOUND,
     SHAPE_TYPE_MESH
 };
 
@@ -62,10 +63,13 @@ public:
 
     const glm::vec3& getHalfExtents() const { return _halfExtents; }
     const glm::vec3& getOffset() const { return _offset; }
+    uint32_t getNumSubShapes() const;
 
     PointCollection& getPointCollection() { return _pointCollection; }
     const PointCollection& getPointCollection() const { return _pointCollection; }
-    uint32_t getNumSubShapes() const;
+
+    TriangleIndices& getTriangleIndices() { return _triangleIndices; }
+    const TriangleIndices& getTriangleIndices() const { return _triangleIndices; }
 
     int getLargestSubshapePointCount() const;
 
@@ -83,7 +87,7 @@ protected:
     TriangleIndices _triangleIndices;
     glm::vec3 _halfExtents = glm::vec3(0.0f);
     glm::vec3 _offset = glm::vec3(0.0f);
-    DoubleHashKey _doubleHashKey;
+    mutable DoubleHashKey _doubleHashKey;
     ShapeType _type = SHAPE_TYPE_NONE;
 };
 

From f41fb30aceb4f6c9a66013397fa899c17c84ad11 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 14 Jun 2016 14:59:52 -0700
Subject: [PATCH 07/35] add "Static Mesh" option to edit.js

---
 .../entities/src/EntityItemProperties.cpp     | 23 +++++++++++++++----
 scripts/system/html/entityProperties.html     |  1 +
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 89bf9f1a21..c521962976 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -88,8 +88,21 @@ void EntityItemProperties::setLastEdited(quint64 usecTime) {
     _lastEdited = usecTime > _created ? usecTime : _created;
 }
 
-const char* shapeTypeNames[] = {"none", "box", "sphere", "plane", "compound", "capsule-x",
-    "capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z"};
+const char* shapeTypeNames[] = {
+    "none",
+    "box",
+    "sphere",
+    "capsule-x",
+    "capsule-y",
+    "capsule-z",
+    "cylinder-x",
+    "cylinder-y",
+    "cylinder-z",
+    "hull",
+    "plane",
+    "compound",
+    "static-mesh"
+};
 
 QHash<QString, ShapeType> stringToShapeTypeLookup;
 
@@ -101,14 +114,16 @@ void buildStringToShapeTypeLookup() {
     addShapeType(SHAPE_TYPE_NONE);
     addShapeType(SHAPE_TYPE_BOX);
     addShapeType(SHAPE_TYPE_SPHERE);
-    addShapeType(SHAPE_TYPE_PLANE);
-    addShapeType(SHAPE_TYPE_COMPOUND);
     addShapeType(SHAPE_TYPE_CAPSULE_X);
     addShapeType(SHAPE_TYPE_CAPSULE_Y);
     addShapeType(SHAPE_TYPE_CAPSULE_Z);
     addShapeType(SHAPE_TYPE_CYLINDER_X);
     addShapeType(SHAPE_TYPE_CYLINDER_Y);
     addShapeType(SHAPE_TYPE_CYLINDER_Z);
+    addShapeType(SHAPE_TYPE_HULL);
+    addShapeType(SHAPE_TYPE_PLANE);
+    addShapeType(SHAPE_TYPE_COMPOUND);
+    addShapeType(SHAPE_TYPE_MESH);
 }
 
 QString getCollisionGroupAsString(uint8_t group) {
diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html
index 0af199ef56..121e38c340 100644
--- a/scripts/system/html/entityProperties.html
+++ b/scripts/system/html/entityProperties.html
@@ -1646,6 +1646,7 @@
                 <option value="box">Box</option>
                 <option value="sphere">Sphere</option>
                 <option value="compound">Compound</option>
+                <option value="static-mesh">Static Mesh</option>
             </select>
         </div>
         <div class="model-group model-section zone-section property url ">

From 2f6e5ab2ee3bfcf30b1b98194e2ca2b60d39c849 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 14 Jun 2016 15:26:13 -0700
Subject: [PATCH 08/35] remove fall-through in switch/case logic

---
 libraries/shared/src/ShapeInfo.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp
index ed1a76ef99..af81a0f96b 100644
--- a/libraries/shared/src/ShapeInfo.cpp
+++ b/libraries/shared/src/ShapeInfo.cpp
@@ -27,12 +27,12 @@ void ShapeInfo::clear() {
 
 void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
     _type = type;
+    _halfExtents = halfExtents;
     switch(type) {
         case SHAPE_TYPE_NONE:
             _halfExtents = glm::vec3(0.0f);
             break;
         case SHAPE_TYPE_BOX:
-            _halfExtents = halfExtents;
             break;
         case SHAPE_TYPE_SPHERE: {
             // sphere radius is max of halfExtents
@@ -43,9 +43,8 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
         case SHAPE_TYPE_COMPOUND:
         case SHAPE_TYPE_MESH:
             _url = QUrl(url);
-            // yes, fall through
+            break;
         default:
-            _halfExtents = halfExtents;
             break;
     }
     _doubleHashKey.clear();

From d1752211e6181c29a4814151f29d10256e31e032 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 14 Jun 2016 15:52:41 -0700
Subject: [PATCH 09/35] make implicit casts explict

---
 libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index e7991eb638..73c3c2e50c 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -720,7 +720,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
             // copy points
             uint32_t meshIndexOffset = (uint32_t)points.size();
             gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertices.cbegin<const glm::vec3>();
-            points.reserve(points.size() + vertices.getNumElements());
+            points.reserve((int32_t)((gpu::Size)points.size() + vertices.getNumElements()));
             Extents extents;
             while (vertexItr != vertices.cend<const glm::vec3>()) {
                 points.push_back(*vertexItr);
@@ -741,7 +741,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
             }
 
             // copy triangleIndices
-            triangleIndices.reserve(triangleIndices.size() + indices.getNumElements());
+            triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements()));
             gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
             while (partItr != parts.cend<const model::Mesh::Part>()) {
 

From ab3548cac089189db0f4bf38c15b98f8872e4cf4 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 15 Jun 2016 14:56:57 -0700
Subject: [PATCH 10/35] fix crash

---
 libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 73c3c2e50c..8f0c9f0a02 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -593,6 +593,8 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
 
         // the model is still being downloaded.
         return false;
+    } else if (type == SHAPE_TYPE_MESH) {
+        return (_model && _model->isLoaded());
     }
     return true;
 }

From f444b70fdc6d018c2e634eaa90e06a6e9d80c123 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 15 Jun 2016 18:15:22 -0700
Subject: [PATCH 11/35] fix collision shape for scaled models

---
 .../src/RenderableModelEntityItem.cpp                  | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 8f0c9f0a02..c01ad8b92a 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -713,7 +713,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
         auto& meshes = _model->getGeometry()->getGeometry()->getMeshes();
 
-        glm::vec3 modelOffset = _model->getOffset();
+        glm::vec3 scaledModelOffset = _model->getOffset() * _model->getScale();
         for (auto& mesh : meshes) {
             const gpu::BufferView& vertices = mesh->getVertexBuffer();
             const gpu::BufferView& indices = mesh->getIndexBuffer();
@@ -730,16 +730,16 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
                 ++vertexItr;
             }
 
-            // scale points and shift by modelOffset
+            // scale and shift
             glm::vec3 extentsSize = extents.size();
-            glm::vec3 scale = dimensions / extents.size();
+            glm::vec3 scaleToFit = dimensions / extents.size();
             for (int i = 0; i < 3; ++i) {
                 if (extentsSize[i] < 1.0e-6f) {
-                    scale[i] = 1.0f;
+                    scaleToFit[i] = 1.0f;
                 }
             }
             for (int i = 0; i < points.size(); ++i) {
-                points[i] = (points[i] * scale) + modelOffset;
+                points[i] = (points[i] * scaleToFit) + scaledModelOffset;
             }
 
             // copy triangleIndices

From c0c77e90272a74afeea6556013468a12afe89d39 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Thu, 16 Jun 2016 09:26:33 -0700
Subject: [PATCH 12/35] optimize/cleanup compound/mesh shape computation

---
 .../src/RenderableModelEntityItem.cpp         | 61 +++++++++++--------
 1 file changed, 34 insertions(+), 27 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index c01ad8b92a..13d757e9b4 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -618,6 +618,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case.
         const uint32_t TRIANGLE_STRIDE = 3;
         const uint32_t QUAD_STRIDE = 4;
+        Extents extents;
         foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
             // each meshPart is a convex hull
             foreach (const FBXMeshPart &meshPart, mesh.parts) {
@@ -631,14 +632,18 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
                     glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]];
                     glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]];
                     glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[j + 2]];
+
                     if (!pointsInPart.contains(p0)) {
                         pointsInPart << p0;
+                        extents.addPoint(p0);
                     }
                     if (!pointsInPart.contains(p1)) {
                         pointsInPart << p1;
+                        extents.addPoint(p1);
                     }
                     if (!pointsInPart.contains(p2)) {
                         pointsInPart << p2;
+                        extents.addPoint(p2);
                     }
                 }
 
@@ -652,15 +657,19 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
                     glm::vec3 p3 = mesh.vertices[meshPart.quadIndices[j + 3]];
                     if (!pointsInPart.contains(p0)) {
                         pointsInPart << p0;
+                        extents.addPoint(p0);
                     }
                     if (!pointsInPart.contains(p1)) {
                         pointsInPart << p1;
+                        extents.addPoint(p1);
                     }
                     if (!pointsInPart.contains(p2)) {
                         pointsInPart << p2;
+                        extents.addPoint(p2);
                     }
                     if (!pointsInPart.contains(p3)) {
                         pointsInPart << p3;
+                        extents.addPoint(p3);
                     }
                 }
 
@@ -673,33 +682,30 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
             }
         }
 
+        glm::vec3 extentsSize = extents.size();
+        glm::vec3 scaleToFit = dimensions / extentsSize;
+        for (int i = 0; i < 3; ++i) {
+            if (extentsSize[i] < 1.0e-6f) {
+                scaleToFit[i] = 1.0f;
+            }
+        }
+
         // We expect that the collision model will have the same units and will be displaced
         // from its origin in the same way the visual model is.  The visual model has
         // been centered and probably scaled.  We take the scaling and offset which were applied
         // to the visual model and apply them to the collision model (without regard for the
         // collision model's extents).
 
-        const FBXGeometry& renderGeometry = _model->getFBXGeometry();
-        glm::vec3 scale = dimensions / renderGeometry.getUnscaledMeshExtents().size();
         // multiply each point by scale before handing the point-set off to the physics engine.
         // also determine the extents of the collision model.
-        AABox box;
+        glm::vec3 scaledModelOffset = _model->getOffset() * _model->getScale();
         for (int i = 0; i < pointCollection.size(); i++) {
             for (int j = 0; j < pointCollection[i].size(); j++) {
-                // compensate for registration
-                pointCollection[i][j] += _model->getOffset();
-                // scale so the collision points match the model points
-                pointCollection[i][j] *= scale;
-                // this next subtraction is done so we can give info the offset, which will cause
-                // the shape-key to change.
-                pointCollection[i][j] -= _model->getOffset();
-                box += pointCollection[i][j];
+                pointCollection[i][j] = (pointCollection[i][j] * scaleToFit) + scaledModelOffset;
             }
         }
 
-        glm::vec3 collisionModelDimensions = box.getDimensions();
-        info.setParams(type, collisionModelDimensions, _compoundShapeURL);
-        info.setOffset(_model->getOffset());
+        info.setParams(type, dimensions, _compoundShapeURL);
     } else if (type == SHAPE_TYPE_MESH) {
         updateModelBounds();
 
@@ -713,6 +719,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
         auto& meshes = _model->getGeometry()->getGeometry()->getMeshes();
 
+        Extents extents;
         glm::vec3 scaledModelOffset = _model->getOffset() * _model->getScale();
         for (auto& mesh : meshes) {
             const gpu::BufferView& vertices = mesh->getVertexBuffer();
@@ -723,25 +730,12 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
             uint32_t meshIndexOffset = (uint32_t)points.size();
             gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertices.cbegin<const glm::vec3>();
             points.reserve((int32_t)((gpu::Size)points.size() + vertices.getNumElements()));
-            Extents extents;
             while (vertexItr != vertices.cend<const glm::vec3>()) {
                 points.push_back(*vertexItr);
                 extents.addPoint(*vertexItr);
                 ++vertexItr;
             }
 
-            // scale and shift
-            glm::vec3 extentsSize = extents.size();
-            glm::vec3 scaleToFit = dimensions / extents.size();
-            for (int i = 0; i < 3; ++i) {
-                if (extentsSize[i] < 1.0e-6f) {
-                    scaleToFit[i] = 1.0f;
-                }
-            }
-            for (int i = 0; i < points.size(); ++i) {
-                points[i] = (points[i] * scaleToFit) + scaledModelOffset;
-            }
-
             // copy triangleIndices
             triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements()));
             gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
@@ -793,6 +787,19 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
                 ++partItr;
             }
         }
+
+        // scale and shift
+        glm::vec3 extentsSize = extents.size();
+        glm::vec3 scaleToFit = dimensions / extentsSize;
+        for (int i = 0; i < 3; ++i) {
+            if (extentsSize[i] < 1.0e-6f) {
+                scaleToFit[i] = 1.0f;
+            }
+        }
+        for (int i = 0; i < points.size(); ++i) {
+            points[i] = (points[i] * scaleToFit) + scaledModelOffset;
+        }
+
         pointCollection.push_back(points);
         info.setParams(SHAPE_TYPE_MESH, 0.5f * dimensions, _modelURL);
     } else {

From 5484b6fbb7e9a545007275a2da0129000c285529 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Thu, 16 Jun 2016 12:15:47 -0700
Subject: [PATCH 13/35] more correct extraction from triangle strips

---
 .../src/RenderableModelEntityItem.cpp         | 29 ++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 13d757e9b4..1adbd4fe1a 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -761,24 +761,27 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
                     auto indexEnd = indexItr + (partItr->_numIndices - 2);
 
                     // first triangle uses the first three indices
-                    triangleIndices.push_back(*indexItr + meshIndexOffset);
-                    triangleIndices.push_back(*(++indexItr) + meshIndexOffset);
-                    triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
+                    triangleIndices.push_back(*(indexItr++) + meshIndexOffset);
+                    triangleIndices.push_back(*(indexItr++) + meshIndexOffset);
+                    triangleIndices.push_back(*(indexItr++) + meshIndexOffset);
 
                     // the rest use previous and next index
                     uint32_t triangleCount = 1;
                     while (indexItr != indexEnd) {
-                        if (triangleCount % 2 == 0) {
-                            // even triangles use first two indices in order
-                            triangleIndices.push_back(*(indexItr) + meshIndexOffset);
-                            triangleIndices.push_back(*(++indexItr) + meshIndexOffset); // yes pre-increment
-                        } else {
-                            // odd triangles swap order of first two indices
-                            triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
-                            triangleIndices.push_back(*(indexItr++) + meshIndexOffset); // yes post-increment
+                        if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) {
+                            if (triangleCount % 2 == 0) {
+                                // even triangles use first two indices in order
+                                triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset);
+                                triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset);
+                            } else {
+                                // odd triangles swap order of first two indices
+                                triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset);
+                                triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset);
+                            }
+                            triangleIndices.push_back(*indexItr + meshIndexOffset);
+                            ++triangleCount;
                         }
-                        triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
-                        ++triangleCount;
+                        ++indexItr;
                     }
                 } else if (partItr->_topology == model::Mesh::QUADS) {
                     // TODO: support model::Mesh::QUADS

From f22a5613bd1b7a2ba0b86de3424e92edda0bd4e7 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Mon, 20 Jun 2016 17:27:39 -0700
Subject: [PATCH 14/35] fix static mesh for model with per-mesh transforms

---
 .../src/RenderableModelEntityItem.cpp         | 30 ++++++++++++++-----
 1 file changed, 23 insertions(+), 7 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 1adbd4fe1a..7b1dddfcab 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -707,6 +707,22 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
 
         info.setParams(type, dimensions, _compoundShapeURL);
     } else if (type == SHAPE_TYPE_MESH) {
+        // compute meshPart local transforms
+        QVector<glm::mat4> localTransforms;
+        const FBXGeometry& geometry = _model->getFBXGeometry();
+        int numberOfMeshes = geometry.meshes.size();
+        for (int i = 0; i < numberOfMeshes; i++) {
+            const FBXMesh& mesh = geometry.meshes.at(i);
+            if (mesh.clusters.size() > 0) {
+                const FBXCluster& cluster = mesh.clusters.at(0);
+                auto jointMatrix = _model->getRig()->getJointTransform(cluster.jointIndex);
+                localTransforms.push_back(jointMatrix * cluster.inverseBindMatrix);
+            } else {
+                glm::mat4 identity;
+                localTransforms.push_back(identity);
+            }
+        }
+
         updateModelBounds();
 
         // should never fall in here when collision model not fully loaded
@@ -720,19 +736,21 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         auto& meshes = _model->getGeometry()->getGeometry()->getMeshes();
 
         Extents extents;
-        glm::vec3 scaledModelOffset = _model->getOffset() * _model->getScale();
+        int meshCount = 0;
         for (auto& mesh : meshes) {
             const gpu::BufferView& vertices = mesh->getVertexBuffer();
             const gpu::BufferView& indices = mesh->getIndexBuffer();
             const gpu::BufferView& parts = mesh->getPartBuffer();
 
             // copy points
+            const glm::mat4& localTransform = localTransforms[meshCount];
             uint32_t meshIndexOffset = (uint32_t)points.size();
             gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertices.cbegin<const glm::vec3>();
             points.reserve((int32_t)((gpu::Size)points.size() + vertices.getNumElements()));
             while (vertexItr != vertices.cend<const glm::vec3>()) {
-                points.push_back(*vertexItr);
-                extents.addPoint(*vertexItr);
+                glm::vec3 point = extractTranslation(localTransform * glm::translate(*vertexItr));
+                points.push_back(point);
+                extents.addPoint(point);
                 ++vertexItr;
             }
 
@@ -783,12 +801,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
                         }
                         ++indexItr;
                     }
-                } else if (partItr->_topology == model::Mesh::QUADS) {
-                    // TODO: support model::Mesh::QUADS
                 }
-                // TODO? support model::Mesh::QUAD_STRIP?
                 ++partItr;
             }
+            ++meshCount;
         }
 
         // scale and shift
@@ -800,7 +816,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
             }
         }
         for (int i = 0; i < points.size(); ++i) {
-            points[i] = (points[i] * scaleToFit) + scaledModelOffset;
+            points[i] = (points[i] * scaleToFit);
         }
 
         pointCollection.push_back(points);

From 937bd0c1bea53ed3cd3954b91f138a8cce46f16c Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 21 Jun 2016 13:54:21 -0700
Subject: [PATCH 15/35] SHAPE_TYPE_MESH --> SHAPE_TYPE_STATIC_MESH

---
 .../entities-renderer/src/RenderableModelEntityItem.cpp     | 6 +++---
 libraries/entities/src/EntityItemProperties.cpp             | 2 +-
 libraries/physics/src/ShapeFactory.cpp                      | 4 ++--
 libraries/shared/src/ShapeInfo.cpp                          | 4 ++--
 libraries/shared/src/ShapeInfo.h                            | 2 +-
 5 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 7b1dddfcab..ab91edd294 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -593,7 +593,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
 
         // the model is still being downloaded.
         return false;
-    } else if (type == SHAPE_TYPE_MESH) {
+    } else if (type == SHAPE_TYPE_STATIC_MESH) {
         return (_model && _model->isLoaded());
     }
     return true;
@@ -706,7 +706,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         }
 
         info.setParams(type, dimensions, _compoundShapeURL);
-    } else if (type == SHAPE_TYPE_MESH) {
+    } else if (type == SHAPE_TYPE_STATIC_MESH) {
         // compute meshPart local transforms
         QVector<glm::mat4> localTransforms;
         const FBXGeometry& geometry = _model->getFBXGeometry();
@@ -820,7 +820,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         }
 
         pointCollection.push_back(points);
-        info.setParams(SHAPE_TYPE_MESH, 0.5f * dimensions, _modelURL);
+        info.setParams(SHAPE_TYPE_STATIC_MESH, 0.5f * dimensions, _modelURL);
     } else {
         ModelEntityItem::computeShapeInfo(info);
         info.setParams(type, 0.5f * dimensions);
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index c521962976..a62f4b182a 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -123,7 +123,7 @@ void buildStringToShapeTypeLookup() {
     addShapeType(SHAPE_TYPE_HULL);
     addShapeType(SHAPE_TYPE_PLANE);
     addShapeType(SHAPE_TYPE_COMPOUND);
-    addShapeType(SHAPE_TYPE_MESH);
+    addShapeType(SHAPE_TYPE_STATIC_MESH);
 }
 
 QString getCollisionGroupAsString(uint8_t group) {
diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp
index 4d5f56853d..3afc170a8c 100644
--- a/libraries/physics/src/ShapeFactory.cpp
+++ b/libraries/physics/src/ShapeFactory.cpp
@@ -161,7 +161,7 @@ btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) {
 
 // util method
 btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) {
-    assert(info.getType() == SHAPE_TYPE_MESH); // should only get here for mesh shapes
+    assert(info.getType() == SHAPE_TYPE_STATIC_MESH); // should only get here for mesh shapes
 
     const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
     assert(pointCollection.size() == 1); // should only have one mesh
@@ -274,7 +274,7 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
             }
         }
         break;
-        case SHAPE_TYPE_MESH: {
+        case SHAPE_TYPE_STATIC_MESH: {
             btTriangleIndexVertexArray* dataArray = createStaticMeshArray(info);
             shape = new StaticMeshShape(dataArray);
         }
diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp
index af81a0f96b..e0f4cc18b2 100644
--- a/libraries/shared/src/ShapeInfo.cpp
+++ b/libraries/shared/src/ShapeInfo.cpp
@@ -41,7 +41,7 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
             break;
         }
         case SHAPE_TYPE_COMPOUND:
-        case SHAPE_TYPE_MESH:
+        case SHAPE_TYPE_STATIC_MESH:
             _url = QUrl(url);
             break;
         default:
@@ -224,7 +224,7 @@ const DoubleHashKey& ShapeInfo::getHash() const {
         }
         _doubleHashKey.setHash2(hash);
 
-        if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_MESH) {
+        if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_STATIC_MESH) {
             QString url = _url.toString();
             if (!url.isEmpty()) {
                 // fold the urlHash into both parts
diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h
index 794f31a987..96132a4b23 100644
--- a/libraries/shared/src/ShapeInfo.h
+++ b/libraries/shared/src/ShapeInfo.h
@@ -39,7 +39,7 @@ enum ShapeType {
     SHAPE_TYPE_HULL,
     SHAPE_TYPE_PLANE,
     SHAPE_TYPE_COMPOUND,
-    SHAPE_TYPE_MESH
+    SHAPE_TYPE_STATIC_MESH
 };
 
 class ShapeInfo {

From 4c9ec7ca432bfac30bf84c0bf829845d111c6191 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 21 Jun 2016 13:55:11 -0700
Subject: [PATCH 16/35] protect physics against bad entity properties

---
 libraries/physics/src/EntityMotionState.cpp |  5 +++
 libraries/physics/src/ObjectMotionState.cpp | 48 +++++++++++----------
 2 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index 8f22c576f0..e0d355911a 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -152,6 +152,11 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
     }
     assert(entityTreeIsLocked());
 
+    if (_entity->getShapeType() == SHAPE_TYPE_STATIC_MESH
+        || (_body && _body->getCollisionShape()->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE)) {
+        return MOTION_TYPE_STATIC;
+    }
+
     if (_entity->getDynamic()) {
         if (!_entity->getParentID().isNull()) {
             // if something would have been dynamic but is a child of something else, force it to be kinematic, instead.
diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp
index de435e80da..f915121718 100644
--- a/libraries/physics/src/ObjectMotionState.cpp
+++ b/libraries/physics/src/ObjectMotionState.cpp
@@ -203,35 +203,37 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
         }
     }
 
-    if (flags & Simulation::DIRTY_LINEAR_VELOCITY) {
-        btVector3 newLinearVelocity = glmToBullet(getObjectLinearVelocity());
-        if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
-            float delta = (newLinearVelocity - _body->getLinearVelocity()).length();
-            if (delta > ACTIVATION_LINEAR_VELOCITY_DELTA) {
-                flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
+    if (_body->getCollisionShape()->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE) {
+        if (flags & Simulation::DIRTY_LINEAR_VELOCITY) {
+            btVector3 newLinearVelocity = glmToBullet(getObjectLinearVelocity());
+            if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
+                float delta = (newLinearVelocity - _body->getLinearVelocity()).length();
+                if (delta > ACTIVATION_LINEAR_VELOCITY_DELTA) {
+                    flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
+                }
             }
-        }
-        _body->setLinearVelocity(newLinearVelocity);
+            _body->setLinearVelocity(newLinearVelocity);
 
-        btVector3 newGravity = glmToBullet(getObjectGravity());
-        if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
-            float delta = (newGravity - _body->getGravity()).length();
-            if (delta > ACTIVATION_GRAVITY_DELTA ||
-                    (delta > 0.0f && _body->getGravity().length2() == 0.0f)) {
-                flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
+            btVector3 newGravity = glmToBullet(getObjectGravity());
+            if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
+                float delta = (newGravity - _body->getGravity()).length();
+                if (delta > ACTIVATION_GRAVITY_DELTA ||
+                        (delta > 0.0f && _body->getGravity().length2() == 0.0f)) {
+                    flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
+                }
             }
+            _body->setGravity(newGravity);
         }
-        _body->setGravity(newGravity);
-    }
-    if (flags & Simulation::DIRTY_ANGULAR_VELOCITY) {
-        btVector3 newAngularVelocity = glmToBullet(getObjectAngularVelocity());
-        if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
-            float delta = (newAngularVelocity - _body->getAngularVelocity()).length();
-            if (delta > ACTIVATION_ANGULAR_VELOCITY_DELTA) {
-                flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
+        if (flags & Simulation::DIRTY_ANGULAR_VELOCITY) {
+            btVector3 newAngularVelocity = glmToBullet(getObjectAngularVelocity());
+            if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
+                float delta = (newAngularVelocity - _body->getAngularVelocity()).length();
+                if (delta > ACTIVATION_ANGULAR_VELOCITY_DELTA) {
+                    flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
+                }
             }
+            _body->setAngularVelocity(newAngularVelocity);
         }
-        _body->setAngularVelocity(newAngularVelocity);
     }
 
     if (flags & Simulation::DIRTY_MATERIAL) {

From 702e83ba6a8a9d6db90eb4c40caf81caa1a005c4 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 21 Jun 2016 14:03:21 -0700
Subject: [PATCH 17/35] prevent incompatible entity properties combos

---
 libraries/entities/src/EntityItem.cpp | 50 ++++++++++++++++++---------
 1 file changed, 34 insertions(+), 16 deletions(-)

diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 64b6a2c655..2abb9f12e2 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -1602,14 +1602,20 @@ void EntityItem::updateMass(float mass) {
 void EntityItem::updateVelocity(const glm::vec3& value) {
     glm::vec3 velocity = getLocalVelocity();
     if (velocity != value) {
-        const float MIN_LINEAR_SPEED = 0.001f;
-        if (glm::length(value) < MIN_LINEAR_SPEED) {
-            velocity = ENTITY_ITEM_ZERO_VEC3;
+        if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
+            if (velocity != Vectors::ZERO) {
+                setLocalVelocity(Vectors::ZERO);
+            }
         } else {
-            velocity = value;
+            const float MIN_LINEAR_SPEED = 0.001f;
+            if (glm::length(value) < MIN_LINEAR_SPEED) {
+                velocity = ENTITY_ITEM_ZERO_VEC3;
+            } else {
+                velocity = value;
+            }
+            setLocalVelocity(velocity);
+            _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
         }
-        setLocalVelocity(velocity);
-        _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
     }
 }
 
@@ -1630,22 +1636,30 @@ void EntityItem::updateDamping(float value) {
 
 void EntityItem::updateGravity(const glm::vec3& value) {
     if (_gravity != value) {
-        _gravity = value;
-        _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
+        if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
+            _gravity = Vectors::ZERO;
+        } else {
+            _gravity = value;
+            _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
+        }
     }
 }
 
 void EntityItem::updateAngularVelocity(const glm::vec3& value) {
     glm::vec3 angularVelocity = getLocalAngularVelocity();
     if (angularVelocity != value) {
-        const float MIN_ANGULAR_SPEED = 0.0002f;
-        if (glm::length(value) < MIN_ANGULAR_SPEED) {
-            angularVelocity = ENTITY_ITEM_ZERO_VEC3;
+        if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
+            setLocalAngularVelocity(Vectors::ZERO);
         } else {
-            angularVelocity = value;
+            const float MIN_ANGULAR_SPEED = 0.0002f;
+            if (glm::length(value) < MIN_ANGULAR_SPEED) {
+                angularVelocity = ENTITY_ITEM_ZERO_VEC3;
+            } else {
+                angularVelocity = value;
+            }
+            setLocalAngularVelocity(angularVelocity);
+            _dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY;
         }
-        setLocalAngularVelocity(angularVelocity);
-        _dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY;
     }
 }
 
@@ -1680,8 +1694,12 @@ void EntityItem::updateCollisionMask(uint8_t value) {
 
 void EntityItem::updateDynamic(bool value) {
     if (_dynamic != value) {
-        _dynamic = value;
-        _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
+        if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
+            _dynamic = false;
+        } else {
+            _dynamic = value;
+            _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
+        }
     }
 }
 

From e88b264864807d7f8518ded3c447e8fe2f1fb64e Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 22 Jun 2016 09:30:44 -0700
Subject: [PATCH 18/35] revert compound hull shape generation

---
 .../src/RenderableModelEntityItem.cpp         | 25 ++++---------------
 1 file changed, 5 insertions(+), 20 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index ab91edd294..366e365107 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -618,7 +618,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
         // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case.
         const uint32_t TRIANGLE_STRIDE = 3;
         const uint32_t QUAD_STRIDE = 4;
-        Extents extents;
         foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
             // each meshPart is a convex hull
             foreach (const FBXMeshPart &meshPart, mesh.parts) {
@@ -632,18 +631,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
                     glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]];
                     glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]];
                     glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[j + 2]];
-
                     if (!pointsInPart.contains(p0)) {
                         pointsInPart << p0;
-                        extents.addPoint(p0);
                     }
                     if (!pointsInPart.contains(p1)) {
                         pointsInPart << p1;
-                        extents.addPoint(p1);
                     }
                     if (!pointsInPart.contains(p2)) {
                         pointsInPart << p2;
-                        extents.addPoint(p2);
                     }
                 }
 
@@ -657,19 +652,15 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
                     glm::vec3 p3 = mesh.vertices[meshPart.quadIndices[j + 3]];
                     if (!pointsInPart.contains(p0)) {
                         pointsInPart << p0;
-                        extents.addPoint(p0);
                     }
                     if (!pointsInPart.contains(p1)) {
                         pointsInPart << p1;
-                        extents.addPoint(p1);
                     }
                     if (!pointsInPart.contains(p2)) {
                         pointsInPart << p2;
-                        extents.addPoint(p2);
                     }
                     if (!pointsInPart.contains(p3)) {
                         pointsInPart << p3;
-                        extents.addPoint(p3);
                     }
                 }
 
@@ -682,29 +673,23 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
             }
         }
 
-        glm::vec3 extentsSize = extents.size();
-        glm::vec3 scaleToFit = dimensions / extentsSize;
-        for (int i = 0; i < 3; ++i) {
-            if (extentsSize[i] < 1.0e-6f) {
-                scaleToFit[i] = 1.0f;
-            }
-        }
-
         // We expect that the collision model will have the same units and will be displaced
         // from its origin in the same way the visual model is.  The visual model has
         // been centered and probably scaled.  We take the scaling and offset which were applied
         // to the visual model and apply them to the collision model (without regard for the
         // collision model's extents).
 
+        glm::vec3 scaleToFit = dimensions / _model->getFBXGeometry().getUnscaledMeshExtents().size();
         // multiply each point by scale before handing the point-set off to the physics engine.
         // also determine the extents of the collision model.
-        glm::vec3 scaledModelOffset = _model->getOffset() * _model->getScale();
         for (int i = 0; i < pointCollection.size(); i++) {
             for (int j = 0; j < pointCollection[i].size(); j++) {
-                pointCollection[i][j] = (pointCollection[i][j] * scaleToFit) + scaledModelOffset;
+                // compensate for registration
+                pointCollection[i][j] += _model->getOffset();
+                // scale so the collision points match the model points
+                pointCollection[i][j] *= scaleToFit;
             }
         }
-
         info.setParams(type, dimensions, _compoundShapeURL);
     } else if (type == SHAPE_TYPE_STATIC_MESH) {
         // compute meshPart local transforms

From b6b73af2b487b10e9cabe85299f498ef0b683df2 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Fri, 17 Jun 2016 14:27:45 -0700
Subject: [PATCH 19/35] Clean up domain-server setup

---
 domain-server/src/DomainServer.cpp | 70 +++++++++++++-----------------
 domain-server/src/DomainServer.h   |  2 +-
 2 files changed, 32 insertions(+), 40 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 223cab61da..a4b57226ed 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -104,23 +104,25 @@ DomainServer::DomainServer(int argc, char* argv[]) :
     connect(&_settingsManager, &DomainServerSettingsManager::updateNodePermissions,
             &_gatekeeper, &DomainGatekeeper::updateNodePermissions);
 
-    if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth()) {
-        // we either read a certificate and private key or were not passed one
-        // and completed login or did not need to
-
-        qDebug() << "Setting up LimitedNodeList and assignments.";
-        setupNodeListAndAssignments();
-
-        // setup automatic networking settings with data server
-        setupAutomaticNetworking();
-
-        // preload some user public keys so they can connect on first request
-        _gatekeeper.preloadAllowedUserPublicKeys();
-
-        optionallyGetTemporaryName(args);
+    // if we were given a certificate/private key or oauth credentials they must succeed
+    if (!(optionallyReadX509KeyAndCertificate() && optionallySetupOAuth())) {
+        return;
     }
 
+    qDebug() << "Setting up domain-server";
+    setupNodeListAndAssignments();
+    setupAutomaticNetworking();
+    _gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request
+
     _metadata = new DomainMetadata(this);
+
+    // check for the temporary name parameter
+    const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
+    if (args.contains(GET_TEMPORARY_NAME_SWITCH)) {
+        getTemporaryName();
+    }
+
+    qDebug() << "domain-server" << nullptr << "is running";
 }
 
 DomainServer::~DomainServer() {
@@ -233,34 +235,25 @@ bool DomainServer::optionallySetupOAuth() {
 
 static const QString METAVERSE_DOMAIN_ID_KEY_PATH = "metaverse.id";
 
-void DomainServer::optionallyGetTemporaryName(const QStringList& arguments) {
-    // check for the temporary name parameter
-    const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
+void DomainServer::getTemporaryName(bool force) {
+    // check if we already have a domain ID
+    const QVariant* idValueVariant = valueForKeyPath(_settingsManager.getSettingsMap(), METAVERSE_DOMAIN_ID_KEY_PATH);
 
-    if (arguments.contains(GET_TEMPORARY_NAME_SWITCH)) {
-
-        // make sure we don't already have a domain ID
-        const QVariant* idValueVariant = valueForKeyPath(_settingsManager.getSettingsMap(), METAVERSE_DOMAIN_ID_KEY_PATH);
-        if (idValueVariant) {
-            qWarning() << "Temporary domain name requested but a domain ID is already present in domain-server settings."
-                << "Will not request temporary name.";
+    if (idValueVariant) {
+        qWarning() << "Temporary domain name requested but a domain ID is already present in domain-server settings.";
+        if (force) {
+            qWarning() << "Temporary domain name will be requested to replace it.";
+        } else {
+            qWarning() << "Temporary domain name will not be requested.";
             return;
         }
-
-        // we've been asked to grab a temporary name from the API
-        // so fire off that request now
-        auto accountManager = DependencyManager::get<AccountManager>();
-
-        // get callbacks for temporary domain result
-        JSONCallbackParameters callbackParameters;
-        callbackParameters.jsonCallbackReceiver = this;
-        callbackParameters.jsonCallbackMethod = "handleTempDomainSuccess";
-        callbackParameters.errorCallbackReceiver = this;
-        callbackParameters.errorCallbackMethod = "handleTempDomainError";
-
-        accountManager->sendRequest("/api/v1/domains/temporary", AccountManagerAuth::None,
-                                    QNetworkAccessManager::PostOperation, callbackParameters);
     }
+
+    // request a temporary name from the metaverse
+    auto accountManager = DependencyManager::get<AccountManager>();
+    JSONCallbackParameters callbackParameters { this, "handleTempDomainSuccess", this, "handleTempDomainError" };
+    accountManager->sendRequest("/api/v1/domains/temporary", AccountManagerAuth::None,
+                                QNetworkAccessManager::PostOperation, callbackParameters);
 }
 
 void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) {
@@ -333,7 +326,6 @@ bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
 
 
 void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
-
     const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
 
     QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index c742dbc9b3..8a25591605 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -100,7 +100,7 @@ private:
     bool optionallySetupOAuth();
     bool optionallyReadX509KeyAndCertificate();
 
-    void optionallyGetTemporaryName(const QStringList& arguments);
+    void getTemporaryName(bool force = false);
 
     static bool packetVersionMatch(const udt::Packet& packet);
 

From 3a36b0a2e5307094c106c73d4ddfb3cb5a289838 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Fri, 17 Jun 2016 18:31:23 -0700
Subject: [PATCH 20/35] add temporary domain info to account info

---
 libraries/networking/src/AccountManager.cpp        |  5 +++++
 libraries/networking/src/AccountManager.h          |  3 +++
 libraries/networking/src/DataServerAccountInfo.cpp | 13 ++++++++++---
 libraries/networking/src/DataServerAccountInfo.h   |  8 +++++++-
 4 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index 26b3801ec1..ac6d0cd4a0 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -471,6 +471,11 @@ void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken)
     persistAccountToFile();
 }
 
+void AccountManager::setTemporaryDomain(const QUuid& domainID, const QString& key) {
+    _accountInfo.setTemporaryDomain(domainID, key);
+    persistAccountToFile();
+}
+
 void AccountManager::requestAccessToken(const QString& login, const QString& password) {
 
     QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h
index 4803d2625f..dc3315eb45 100644
--- a/libraries/networking/src/AccountManager.h
+++ b/libraries/networking/src/AccountManager.h
@@ -88,6 +88,9 @@ public:
 
     void setSessionID(const QUuid& sessionID) { _sessionID = sessionID; }
 
+    void setTemporaryDomain(const QUuid& domainID, const QString& key);
+    const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); }
+
 public slots:
     void requestAccessToken(const QString& login, const QString& password);
 
diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp
index 211bfdccfa..6c6f3eb90c 100644
--- a/libraries/networking/src/DataServerAccountInfo.cpp
+++ b/libraries/networking/src/DataServerAccountInfo.cpp
@@ -25,6 +25,8 @@
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #endif
 
+const QString DataServerAccountInfo::EMPTY_KEY = QString();
+
 DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) : QObject() {
     _accessToken = otherInfo._accessToken;
     _username = otherInfo._username;
@@ -33,6 +35,8 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI
     _walletID = otherInfo._walletID;
     _privateKey = otherInfo._privateKey;
     _domainID = otherInfo._domainID;
+    _temporaryDomainID = otherInfo._temporaryDomainID;
+    _temporaryDomainApiKey = otherInfo._temporaryDomainApiKey;
 }
 
 DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) {
@@ -51,6 +55,8 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
     swap(_walletID, otherInfo._walletID);
     swap(_privateKey, otherInfo._privateKey);
     swap(_domainID, otherInfo._domainID);
+    swap(_temporaryDomainID, otherInfo._temporaryDomainID);
+    swap(_temporaryDomainApiKey, otherInfo._temporaryDomainApiKey);
 }
 
 void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) {
@@ -145,13 +151,14 @@ QByteArray DataServerAccountInfo::signPlaintext(const QByteArray& plaintext) {
 
 QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
     out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey
-        << info._walletID << info._privateKey << info._domainID;
-
+        << info._walletID << info._privateKey << info._domainID
+        << info._temporaryDomainID << info._temporaryDomainApiKey;
     return out;
 }
 
 QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) {
     in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey
-        >> info._walletID >> info._privateKey >> info._domainID;
+        >> info._walletID >> info._privateKey >> info._domainID
+        >> info._temporaryDomainID >> info._temporaryDomainApiKey;
     return in;
 }
diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h
index 6ee726efde..8cb416cf34 100644
--- a/libraries/networking/src/DataServerAccountInfo.h
+++ b/libraries/networking/src/DataServerAccountInfo.h
@@ -22,6 +22,7 @@ const float SATOSHIS_PER_CREDIT = 100000000.0f;
 
 class DataServerAccountInfo : public QObject {
     Q_OBJECT
+    const static QString EMPTY_KEY;
 public:
     DataServerAccountInfo() {};
     DataServerAccountInfo(const DataServerAccountInfo& otherInfo);
@@ -52,6 +53,9 @@ public:
     void setDomainID(const QUuid& domainID) { _domainID = domainID; }
     const QUuid& getDomainID() const { return _domainID; }
 
+    void setTemporaryDomain(const QUuid& domainID, const QString& key) { _temporaryDomainID = domainID; _temporaryDomainApiKey = key; }
+    const QString& getTemporaryDomainKey(const QUuid& domainID) { return domainID == _temporaryDomainID ? _temporaryDomainApiKey : EMPTY_KEY; }
+
     bool hasProfile() const;
 
     void setProfileInfoFromJSON(const QJsonObject& jsonObject);
@@ -67,7 +71,9 @@ private:
     QString _xmppPassword;
     QString _discourseApiKey;
     QUuid _walletID;
-    QUuid _domainID; // if this holds account info for a domain, this holds the ID of that domain
+    QUuid _domainID;
+    QUuid _temporaryDomainID;
+    QString _temporaryDomainApiKey;
     QByteArray _privateKey;
 
 };

From eebf8e91c6bdcca2308e64b75a4b8ee97b1b970b Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Fri, 17 Jun 2016 19:02:27 -0700
Subject: [PATCH 21/35] add api_key to domains/public_key updates

---
 libraries/networking/src/AccountManager.cpp | 27 +++++++++++++++------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index ac6d0cd4a0..38f33da6ce 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -652,22 +652,33 @@ void AccountManager::processGeneratedKeypair() {
         const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key";
 
         QString uploadPath;
-        if (keypairGenerator->getDomainID().isNull()) {
+        const auto& domainID = keypairGenerator->getDomainID();
+        if (domainID.isNull()) {
             uploadPath = USER_PUBLIC_KEY_UPDATE_PATH;
         } else {
-            uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(keypairGenerator->getDomainID()));
+            uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID));
         }
 
         // setup a multipart upload to send up the public key
         QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
 
-        QHttpPart keyPart;
-        keyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
-        keyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
-                          QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
-        keyPart.setBody(keypairGenerator->getPublicKey());
+        QHttpPart publicKeyPart;
+        publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
 
-        requestMultiPart->append(keyPart);
+        publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
+                          QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
+        publicKeyPart.setBody(keypairGenerator->getPublicKey());
+        requestMultiPart->append(publicKeyPart);
+
+        if (!domainID.isNull()) {
+            const auto& key = getTemporaryDomainKey(domainID);
+            QHttpPart apiKeyPart;
+            publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
+            apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
+                              QVariant("form-data; name=\"api_key\""));
+            apiKeyPart.setBody(key.toUtf8());
+            requestMultiPart->append(apiKeyPart);
+        }
 
         // setup callback parameters so we know once the keypair upload has succeeded or failed
         JSONCallbackParameters callbackParameters;

From a6115cba6e981f17477edd98a0b6695770cc5c19 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Fri, 17 Jun 2016 19:04:03 -0700
Subject: [PATCH 22/35] update temporary domains to use api_key

---
 domain-server/src/DomainServer.cpp | 149 +++++++++++++++++++----------
 domain-server/src/DomainServer.h   |  10 +-
 2 files changed, 105 insertions(+), 54 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index a4b57226ed..df1697af28 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -76,6 +76,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
     setApplicationVersion(BuildInfo::VERSION);
     QSettings::setDefaultFormat(QSettings::IniFormat);
 
+    qDebug() << "Setting up domain-server";
+
     // make sure we have a fresh AccountManager instance
     // (need this since domain-server can restart itself and maintain static variables)
     DependencyManager::set<AccountManager>();
@@ -109,20 +111,26 @@ DomainServer::DomainServer(int argc, char* argv[]) :
         return;
     }
 
-    qDebug() << "Setting up domain-server";
-    setupNodeListAndAssignments();
-    setupAutomaticNetworking();
-    _gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request
-
-    _metadata = new DomainMetadata(this);
-
     // check for the temporary name parameter
     const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
     if (args.contains(GET_TEMPORARY_NAME_SWITCH)) {
         getTemporaryName();
     }
 
-    qDebug() << "domain-server" << nullptr << "is running";
+    setupNodeListAndAssignments();
+    setupAutomaticNetworking();
+    if (!getID().isNull()) {
+        setupHeartbeatToMetaverse();
+        // send the first heartbeat immediately
+        sendHeartbeatToMetaverse();
+    }
+
+    _gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request
+
+    _metadata = new DomainMetadata(this);
+
+
+    qDebug() << "domain-server is running";
 }
 
 DomainServer::~DomainServer() {
@@ -150,6 +158,10 @@ void DomainServer::restart() {
     exit(DomainServer::EXIT_CODE_REBOOT);
 }
 
+const QUuid& DomainServer::getID() {
+    return DependencyManager::get<LimitedNodeList>()->getSessionUUID();
+}
+
 bool DomainServer::optionallyReadX509KeyAndCertificate() {
     const QString X509_CERTIFICATE_OPTION = "cert";
     const QString X509_PRIVATE_KEY_OPTION = "key";
@@ -264,11 +276,13 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) {
     static const QString DOMAIN_KEY = "domain";
     static const QString ID_KEY = "id";
     static const QString NAME_KEY = "name";
+    static const QString KEY_KEY = "api_key";
 
     auto domainObject = jsonObject[DATA_KEY].toObject()[DOMAIN_KEY].toObject();
     if (!domainObject.isEmpty()) {
         auto id = domainObject[ID_KEY].toString();
         auto name = domainObject[NAME_KEY].toString();
+        auto key = domainObject[KEY_KEY].toString();
 
         qInfo() << "Received new temporary domain name" << name;
         qDebug() << "The temporary domain ID is" << id;
@@ -284,9 +298,13 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) {
         // change our domain ID immediately
         DependencyManager::get<LimitedNodeList>()->setSessionUUID(QUuid { id });
 
-        // change our automatic networking settings so that we're communicating with the ICE server
-        setupICEHeartbeatForFullNetworking();
+        // store the new token to the account info
+        auto accountManager = DependencyManager::get<AccountManager>();
+        accountManager->setTemporaryDomain(id, key);
 
+        // update our heartbeats to use the correct id
+        setupICEHeartbeatForFullNetworking();
+        setupHeartbeatToMetaverse();
     } else {
         qWarning() << "There were problems parsing the API response containing a temporary domain name. Please try again"
             << "via domain-server relaunch or from the domain-server settings.";
@@ -325,7 +343,7 @@ bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
 }
 
 
-void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
+void DomainServer::setupNodeListAndAssignments() {
     const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
 
     QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
@@ -450,29 +468,20 @@ bool DomainServer::resetAccountManagerAccessToken() {
 }
 
 void DomainServer::setupAutomaticNetworking() {
-    auto nodeList = DependencyManager::get<LimitedNodeList>();
-
+    qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting;
     _automaticNetworkingSetting =
         _settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
 
+    auto nodeList = DependencyManager::get<LimitedNodeList>();
+    const QUuid& domainID = getID();
+
     if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
         setupICEHeartbeatForFullNetworking();
     }
 
-    _hasAccessToken = resetAccountManagerAccessToken();
-
-    if (!_hasAccessToken) {
-        qDebug() << "Will not send heartbeat to Metaverse API without an access token.";
-        qDebug() << "If this is not a temporary domain add an access token to your config file or via the web interface.";
-
-        return;
-    }
-
     if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
         _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
 
-        const QUuid& domainID = nodeList->getSessionUUID();
-
         if (!domainID.isNull()) {
             qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID"
                 << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
@@ -484,9 +493,6 @@ void DomainServer::setupAutomaticNetworking() {
 
                 // have the LNL enable public socket updating via STUN
                 nodeList->startSTUNPublicSocketUpdate();
-            } else {
-                // send our heartbeat to data server so it knows what our network settings are
-                sendHeartbeatToMetaverse();
             }
         } else {
             qDebug() << "Cannot enable domain-server automatic networking without a domain ID."
@@ -494,18 +500,20 @@ void DomainServer::setupAutomaticNetworking() {
 
             return;
         }
-    } else {
-        sendHeartbeatToMetaverse();
     }
+}
 
-    qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting;
-
-    // no matter the auto networking settings we should heartbeat to the data-server every 15s
+void DomainServer::setupHeartbeatToMetaverse() {
+    // heartbeat to the data-server every 15s
     const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000;
 
-    QTimer* dataHeartbeatTimer = new QTimer(this);
-    connect(dataHeartbeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartbeatToMetaverse()));
-    dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS);
+    if (!_metaverseHeartbeatTimer) {
+        // setup a timer to heartbeat with the metaverse-server
+        _metaverseHeartbeatTimer = new QTimer { this };
+        connect(_metaverseHeartbeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartbeatToMetaverse()));
+        // do not send a heartbeat immediately - this avoids flooding if the heartbeat fails with a 401
+        _metaverseHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS);
+    }
 }
 
 void DomainServer::setupICEHeartbeatForFullNetworking() {
@@ -524,22 +532,21 @@ void DomainServer::setupICEHeartbeatForFullNetworking() {
     limitedNodeList->startSTUNPublicSocketUpdate();
 
     // to send ICE heartbeats we'd better have a private key locally with an uploaded public key
-    auto accountManager = DependencyManager::get<AccountManager>();
-    auto domainID = accountManager->getAccountInfo().getDomainID();
-
     // if we have an access token and we don't have a private key or the current domain ID has changed
     // we should generate a new keypair
-    if (!accountManager->getAccountInfo().hasPrivateKey() || domainID != limitedNodeList->getSessionUUID()) {
-        accountManager->generateNewDomainKeypair(limitedNodeList->getSessionUUID());
+    auto accountManager = DependencyManager::get<AccountManager>();
+    if (!accountManager->getAccountInfo().hasPrivateKey() || accountManager->getAccountInfo().getDomainID() != getID()) {
+        accountManager->generateNewDomainKeypair(getID());
     }
 
     // hookup to the signal from account manager that tells us when keypair is available
     connect(accountManager.data(), &AccountManager::newKeypair, this, &DomainServer::handleKeypairChange);
 
     if (!_iceHeartbeatTimer) {
-        // setup a timer to heartbeat with the ice-server every so often
+        // setup a timer to heartbeat with the ice-server
         _iceHeartbeatTimer = new QTimer { this };
         connect(_iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToIceServer);
+        sendHeartbeatToIceServer();
         _iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
     }
 }
@@ -1067,9 +1074,6 @@ void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr)
 }
 
 void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
-    auto nodeList = DependencyManager::get<LimitedNodeList>();
-    const QUuid& domainID = nodeList->getSessionUUID();
-
     // Setup the domain object to send to the data server
     QJsonObject domainObject;
 
@@ -1088,6 +1092,13 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
     NodePermissions anonymousPermissions = _settingsManager.getPermissionsForName(NodePermissions::standardNameAnonymous);
     domainObject[RESTRICTED_ACCESS_FLAG] = !anonymousPermissions.canConnectToDomain;
 
+    const auto& temporaryDomainKey = DependencyManager::get<AccountManager>()->getTemporaryDomainKey(getID());
+    if (!temporaryDomainKey.isEmpty()) {
+        // add the temporary domain token
+        const QString KEY_KEY = "api_key";
+        domainObject[KEY_KEY] = temporaryDomainKey;
+    }
+
     if (_metadata) {
         // Add the metadata to the heartbeat
         static const QString DOMAIN_HEARTBEAT_KEY = "heartbeat";
@@ -1097,18 +1108,47 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
     QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(domainObject).toJson(QJsonDocument::Compact)));
 
     static const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
-    DependencyManager::get<AccountManager>()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
-                                              AccountManagerAuth::Required,
+    DependencyManager::get<AccountManager>()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())),
+                                              AccountManagerAuth::Optional,
                                               QNetworkAccessManager::PutOperation,
-                                              JSONCallbackParameters(),
+                                              JSONCallbackParameters(nullptr, QString(), this, "handleMetaverseHeartbeatError"),
                                               domainUpdateJSON.toUtf8());
 }
 
+void DomainServer::handleMetaverseHeartbeatError(QNetworkReply& requestReply) {
+    if (!_metaverseHeartbeatTimer) {
+        // avoid rehandling errors from the same issue
+        return;
+    }
+
+    // if we have a temporary domain with a bad token, we will get a 401
+    if (requestReply.error() == QNetworkReply::NetworkError::AuthenticationRequiredError) {
+        static const QString DATA_KEY = "data";
+        static const QString TOKEN_KEY = "api_key";
+
+        QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
+        auto tokenFailure = jsonObject[DATA_KEY].toObject()[TOKEN_KEY];
+
+        if (!tokenFailure.isNull()) {
+            qWarning() << "Temporary domain name lacks a valid API key, and is being reset.";
+
+            // halt heartbeats until we have a token
+            _metaverseHeartbeatTimer->deleteLater();
+            _metaverseHeartbeatTimer = nullptr;
+
+            // give up eventually to avoid flooding traffic
+            static const int MAX_ATTEMPTS = 5;
+            static int attempt = 0;
+            if (++attempt < MAX_ATTEMPTS) {
+                // get a new temporary name and token
+                getTemporaryName(true);
+            }
+        }
+    }
+}
+
 void DomainServer::sendICEServerAddressToMetaverseAPI() {
     if (!_iceServerSocket.isNull()) {
-        auto nodeList = DependencyManager::get<LimitedNodeList>();
-        const QUuid& domainID = nodeList->getSessionUUID();
-
         const QString ICE_SERVER_ADDRESS = "ice_server_address";
 
         QJsonObject domainObject;
@@ -1116,6 +1156,13 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
         // we're using full automatic networking and we have a current ice-server socket, use that now
         domainObject[ICE_SERVER_ADDRESS] = _iceServerSocket.getAddress().toString();
 
+        const auto& temporaryDomainKey = DependencyManager::get<AccountManager>()->getTemporaryDomainKey(getID());
+        if (!temporaryDomainKey.isEmpty()) {
+            // add the temporary domain token
+            const QString KEY_KEY = "api_key";
+            domainObject[KEY_KEY] = temporaryDomainKey;
+        }
+
         QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson()));
 
         // make sure we hear about failure so we can retry
@@ -1127,7 +1174,7 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
 
         static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address";
 
-        DependencyManager::get<AccountManager>()->sendRequest(DOMAIN_ICE_ADDRESS_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
+        DependencyManager::get<AccountManager>()->sendRequest(DOMAIN_ICE_ADDRESS_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())),
                                                   AccountManagerAuth::Optional,
                                                   QNetworkAccessManager::PutOperation,
                                                   callbackParameters,
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 8a25591605..138cb9ca2d 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -80,6 +80,8 @@ private slots:
     void handleTempDomainSuccess(QNetworkReply& requestReply);
     void handleTempDomainError(QNetworkReply& requestReply);
 
+    void handleMetaverseHeartbeatError(QNetworkReply& requestReply);
+
     void queuedQuit(QString quitMessage, int exitCode);
 
     void handleKeypairChange();
@@ -96,7 +98,9 @@ signals:
     void userDisconnected();
     
 private:
-    void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
+    const QUuid& getID();
+
+    void setupNodeListAndAssignments();
     bool optionallySetupOAuth();
     bool optionallyReadX509KeyAndCertificate();
 
@@ -108,6 +112,7 @@ private:
 
     void setupAutomaticNetworking();
     void setupICEHeartbeatForFullNetworking();
+    void setupHeartbeatToMetaverse();
     void sendHeartbeatToMetaverse(const QString& networkAddress);
 
     void randomizeICEServerAddress(bool shouldTriggerHostLookup);
@@ -178,6 +183,7 @@ private:
     // These will be parented to this, they are not dangling
     DomainMetadata* _metadata { nullptr };
     QTimer* _iceHeartbeatTimer { nullptr };
+    QTimer* _metaverseHeartbeatTimer { nullptr };
 
     QList<QHostAddress> _iceServerAddresses;
     QSet<QHostAddress> _failedIceServerAddresses;
@@ -186,8 +192,6 @@ private:
     int _numHeartbeatDenials { 0 };
     bool _connectedToICEServer { false };
 
-    bool _hasAccessToken { false };
-
     friend class DomainGatekeeper;
     friend class DomainMetadata;
 };

From 4a30d549add061e408c95d692e2098ef6eb70207 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Fri, 17 Jun 2016 19:26:22 -0700
Subject: [PATCH 23/35] force temp domain reset on 404 too (401 already)

---
 domain-server/src/DomainServer.cpp | 51 +++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 19 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index df1697af28..192c0d26e6 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -1121,29 +1121,42 @@ void DomainServer::handleMetaverseHeartbeatError(QNetworkReply& requestReply) {
         return;
     }
 
-    // if we have a temporary domain with a bad token, we will get a 401
-    if (requestReply.error() == QNetworkReply::NetworkError::AuthenticationRequiredError) {
-        static const QString DATA_KEY = "data";
-        static const QString TOKEN_KEY = "api_key";
+    // check if we need to force a new temporary domain name
+    switch (requestReply.error()) {
+        // if we have a temporary domain with a bad token, we get a 401
+        case QNetworkReply::NetworkError::AuthenticationRequiredError: {
+            static const QString DATA_KEY = "data";
+            static const QString TOKEN_KEY = "api_key";
 
-        QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
-        auto tokenFailure = jsonObject[DATA_KEY].toObject()[TOKEN_KEY];
+            QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
+            auto tokenFailure = jsonObject[DATA_KEY].toObject()[TOKEN_KEY];
 
-        if (!tokenFailure.isNull()) {
-            qWarning() << "Temporary domain name lacks a valid API key, and is being reset.";
-
-            // halt heartbeats until we have a token
-            _metaverseHeartbeatTimer->deleteLater();
-            _metaverseHeartbeatTimer = nullptr;
-
-            // give up eventually to avoid flooding traffic
-            static const int MAX_ATTEMPTS = 5;
-            static int attempt = 0;
-            if (++attempt < MAX_ATTEMPTS) {
-                // get a new temporary name and token
-                getTemporaryName(true);
+            if (!tokenFailure.isNull()) {
+                qWarning() << "Temporary domain name lacks a valid API key, and is being reset.";
             }
+            break;
         }
+        // if the domain does not (or no longer) exists, we get a 404
+        case QNetworkReply::NetworkError::ContentNotFoundError:
+            qWarning() << "Domain not found, getting a new temporary domain.";
+            break;
+        // otherwise, we erred on something else, and should not force a temporary domain
+        default:
+            return;
+    }
+
+    // halt heartbeats until we have a token
+    _metaverseHeartbeatTimer->deleteLater();
+    _metaverseHeartbeatTimer = nullptr;
+
+    // give up eventually to avoid flooding traffic
+    static const int MAX_ATTEMPTS = 5;
+    static int attempt = 0;
+    if (++attempt < MAX_ATTEMPTS) {
+        // get a new temporary name and token
+        getTemporaryName(true);
+    } else {
+        qWarning() << "Already attempted too many temporary domain requests. Please set a domain ID manually or restart.";
     }
 }
 

From 8009d23f7007fbf311ab80f593346d4cf5658764 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 22 Jun 2016 15:20:45 -0700
Subject: [PATCH 24/35] more STATIC_MESH and dynamic overlap prevention

---
 libraries/entities/src/EntityItem.cpp         | 12 ++--
 libraries/entities/src/EntityItem.h           |  4 +-
 libraries/entities/src/ModelEntityItem.cpp    | 57 ++++++++++++-------
 libraries/entities/src/ModelEntityItem.h      |  6 +-
 .../entities/src/ParticleEffectEntityItem.cpp |  6 +-
 .../entities/src/ParticleEffectEntityItem.h   |  4 +-
 libraries/entities/src/ZoneEntityItem.cpp     |  4 +-
 libraries/entities/src/ZoneEntityItem.h       |  6 +-
 8 files changed, 61 insertions(+), 38 deletions(-)

diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 2abb9f12e2..f0a4d40860 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -1693,9 +1693,13 @@ void EntityItem::updateCollisionMask(uint8_t value) {
 }
 
 void EntityItem::updateDynamic(bool value) {
-    if (_dynamic != value) {
-        if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
-            _dynamic = false;
+    if (getDynamic() != value) {
+        // dynamic and STATIC_MESH are incompatible so we check for that case
+        if (value && getShapeType() == SHAPE_TYPE_STATIC_MESH) {
+            if (_dynamic) {
+                _dynamic = false;
+                _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
+            }
         } else {
             _dynamic = value;
             _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
@@ -1749,7 +1753,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
         group = BULLET_COLLISION_GROUP_COLLISIONLESS;
         mask = 0;
     } else {
-        if (_dynamic) {
+        if (getDynamic()) {
             group = BULLET_COLLISION_GROUP_DYNAMIC;
         } else if (isMovingRelativeToParent() || hasActions()) {
             group = BULLET_COLLISION_GROUP_KINEMATIC;
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 4a691462ab..9fa13690f1 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -283,7 +283,7 @@ public:
 
     void computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const;
 
-    bool getDynamic() const { return _dynamic; }
+    bool getDynamic() const { return SHAPE_TYPE_STATIC_MESH == getShapeType() ? false : _dynamic; }
     void setDynamic(bool value) { _dynamic = value; }
 
     virtual bool shouldBePhysical() const { return false; }
@@ -348,7 +348,7 @@ public:
     void updateDynamic(bool value);
     void updateLifetime(float value);
     void updateCreated(uint64_t value);
-    virtual void updateShapeType(ShapeType type) { /* do nothing */ }
+    virtual void setShapeType(ShapeType type) { /* do nothing */ }
 
     uint32_t getDirtyFlags() const { return _dirtyFlags; }
     void clearDirtyFlags(uint32_t mask = 0xffffffff) { _dirtyFlags &= ~mask; }
diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp
index 40faf2c3c3..8e925b2f79 100644
--- a/libraries/entities/src/ModelEntityItem.cpp
+++ b/libraries/entities/src/ModelEntityItem.cpp
@@ -77,7 +77,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointRotationsSet, setJointRotationsSet);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointRotations, setJointRotations);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslationsSet, setJointTranslationsSet);
@@ -145,7 +145,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
         dataAt += bytesFromAnimation;
     }
 
-    READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
+    READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
 
     if (animationPropertiesChanged) {
         _dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
@@ -257,37 +257,54 @@ void ModelEntityItem::debugDump() const {
     qCDebug(entities) << "    compound shape URL:" << getCompoundShapeURL();
 }
 
-void ModelEntityItem::updateShapeType(ShapeType type) {
-    // BEGIN_TEMPORARY_WORKAROUND
-    // we have allowed inconsistent ShapeType's to be stored in SVO files in the past (this was a bug)
-    // but we are now enforcing the entity properties to be consistent.  To make the possible we're
-    // introducing a temporary workaround: we will ignore ShapeType updates that conflict with the
-    // _compoundShapeURL.
-    if (hasCompoundShapeURL()) {
-        type = SHAPE_TYPE_COMPOUND;
-    }
-    // END_TEMPORARY_WORKAROUND
-
+void ModelEntityItem::setShapeType(ShapeType type) {
     if (type != _shapeType) {
+        if (type == SHAPE_TYPE_STATIC_MESH && _dynamic) {
+            // dynamic and STATIC_MESH are incompatible
+            // since the shape is being set here we clear the dynamic bit
+            _dynamic = false;
+            _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
+        }
         _shapeType = type;
         _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
     }
 }
 
-// virtual
 ShapeType ModelEntityItem::getShapeType() const {
-    if (_shapeType == SHAPE_TYPE_COMPOUND) {
-        return hasCompoundShapeURL() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
-    } else {
-        return _shapeType;
+    return computeTrueShapeType();
+}
+
+ShapeType ModelEntityItem::computeTrueShapeType() const {
+    ShapeType type = _shapeType;
+    if (type == SHAPE_TYPE_STATIC_MESH && _dynamic) {
+        // dynamic is incompatible with STATIC_MESH
+        // shouldn't fall in here but just in case --> fall back to COMPOUND
+        type = SHAPE_TYPE_COMPOUND;
+    }
+    if (type == SHAPE_TYPE_COMPOUND && !hasCompoundShapeURL()) {
+        // no compoundURL set --> fall back to NONE
+        type = SHAPE_TYPE_NONE;
+    }
+    return type;
+}
+
+void ModelEntityItem::setModelURL(const QString& url) {
+    if (_modelURL != url) {
+        _modelURL = url;
+        _parsedModelURL = QUrl(url);
+        if (_shapeType == SHAPE_TYPE_STATIC_MESH) {
+            _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
+        }
     }
 }
 
 void ModelEntityItem::setCompoundShapeURL(const QString& url) {
     if (_compoundShapeURL != url) {
+        ShapeType oldType = computeTrueShapeType();
         _compoundShapeURL = url;
-        _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
-        _shapeType = _compoundShapeURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND;
+        if (oldType != computeTrueShapeType()) {
+            _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
+        }
     }
 }
 
diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h
index 29730bf4df..7b7edaf945 100644
--- a/libraries/entities/src/ModelEntityItem.h
+++ b/libraries/entities/src/ModelEntityItem.h
@@ -50,9 +50,10 @@ public:
     virtual bool needsToCallUpdate() const;
     virtual void debugDump() const;
 
-    void updateShapeType(ShapeType type);
+    void setShapeType(ShapeType type);
     virtual ShapeType getShapeType() const;
 
+
     // TODO: Move these to subclasses, or other appropriate abstraction
     // getters/setters applicable to models and particles
 
@@ -76,7 +77,7 @@ public:
     }
 
     // model related properties
-    virtual void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); }
+    virtual void setModelURL(const QString& url);
     virtual void setCompoundShapeURL(const QString& url);
 
     // Animation related items...
@@ -130,6 +131,7 @@ public:
 
 private:
     void setAnimationSettings(const QString& value); // only called for old bitstream format
+    ShapeType computeTrueShapeType() const;
 
 protected:
     // these are used:
diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp
index a7bd0038e6..c501737146 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.cpp
+++ b/libraries/entities/src/ParticleEffectEntityItem.cpp
@@ -342,7 +342,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
 
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifespan, setLifespan);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(isEmitting, setIsEmitting);
@@ -406,7 +406,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
         READ_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, bool, setIsEmitting);
     }
 
-    READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
+    READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
     READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles);
     READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan);
     READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate);
@@ -584,7 +584,7 @@ void ParticleEffectEntityItem::debugDump() const {
     qCDebug(entities) << "          getLastEdited:" << debugTime(getLastEdited(), now);
 }
 
-void ParticleEffectEntityItem::updateShapeType(ShapeType type) {
+void ParticleEffectEntityItem::setShapeType(ShapeType type) {
     if (type != _shapeType) {
         _shapeType = type;
         _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h
index 4538a1bb43..777f3b6cf6 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.h
+++ b/libraries/entities/src/ParticleEffectEntityItem.h
@@ -95,8 +95,8 @@ public:
     void setAlphaSpread(float alphaSpread);
     float getAlphaSpread() const { return _alphaSpread; }
 
-    void updateShapeType(ShapeType type);
-    virtual ShapeType getShapeType() const { return _shapeType; }
+    void setShapeType(ShapeType type) override;
+    virtual ShapeType getShapeType() const override { return _shapeType; }
 
     virtual void debugDump() const;
 
diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp
index a28b8210c2..0b99d0377f 100644
--- a/libraries/entities/src/ZoneEntityItem.cpp
+++ b/libraries/entities/src/ZoneEntityItem.cpp
@@ -73,7 +73,7 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) {
     
     bool somethingChangedInStage = _stageProperties.setProperties(properties);
 
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundMode, setBackgroundMode);
 
@@ -117,7 +117,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
     bytesRead += bytesFromStage;
     dataAt += bytesFromStage;
 
-    READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
+    READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
     READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
     READ_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);
 
diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h
index 56968aa9c9..599e70f83e 100644
--- a/libraries/entities/src/ZoneEntityItem.h
+++ b/libraries/entities/src/ZoneEntityItem.h
@@ -54,9 +54,9 @@ public:
     static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; }
     static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; }
     
-    virtual bool isReadyToComputeShape() { return false; }
-    void updateShapeType(ShapeType type) { _shapeType = type; }
-    virtual ShapeType getShapeType() const;
+    virtual bool isReadyToComputeShape() override { return false; }
+    void setShapeType(ShapeType type) override { _shapeType = type; }
+    virtual ShapeType getShapeType() const override;
     
     virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); }
     const QString getCompoundShapeURL() const { return _compoundShapeURL; }

From e5b89ebd17aaa10fcb7253c10fcaa4ea0576d9ae Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 22 Jun 2016 15:33:44 -0700
Subject: [PATCH 25/35] bump version number

---
 libraries/networking/src/udt/PacketHeaders.cpp | 2 +-
 libraries/networking/src/udt/PacketHeaders.h   | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 6ca50420f3..c74b10820d 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
         case PacketType::EntityAdd:
         case PacketType::EntityEdit:
         case PacketType::EntityData:
-            return VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS;
+            return VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH;
         case PacketType::AvatarIdentity:
         case PacketType::AvatarData:
         case PacketType::BulkAvatarData:
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index ae54450fee..e484a06502 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -180,6 +180,7 @@ const PacketVersion VERSION_LIGHT_HAS_FALLOFF_RADIUS = 57;
 const PacketVersion VERSION_ENTITIES_NO_FLY_ZONES = 58;
 const PacketVersion VERSION_ENTITIES_MORE_SHAPES = 59;
 const PacketVersion VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS = 60;
+const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61;
 
 enum class AvatarMixerPacketVersion : PacketVersion {
     TranslationSupport = 17,

From f495f5e3b56173d15977159300db9802fa46843d Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 22 Jun 2016 16:05:55 -0700
Subject: [PATCH 26/35] removed mac hydra support

---
 plugins/hifiSixense/src/SixenseManager.cpp | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp
index 03028249a3..48d13a8eaf 100644
--- a/plugins/hifiSixense/src/SixenseManager.cpp
+++ b/plugins/hifiSixense/src/SixenseManager.cpp
@@ -66,14 +66,8 @@ const QString SHOW_DEBUG_RAW = "Debug Draw Raw Data";
 const QString SHOW_DEBUG_CALIBRATED = "Debug Draw Calibrated Data";
 
 bool SixenseManager::isSupported() const {
-#ifdef HAVE_SIXENSE
-
-#if defined(Q_OS_OSX)
-    return QSysInfo::macVersion() <= QSysInfo::MV_MAVERICKS;
-#else
+#if defined(HAVE_SIXENSE) && !defined(Q_OS_OSX)
     return true;
-#endif
-
 #else
     return false;
 #endif

From a9ed0b1c8386c530e08ef9e23845ca21580f05f7 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 22 Jun 2016 16:56:50 -0700
Subject: [PATCH 27/35] completely destroyed sixense on macs

---
 cmake/externals/sixense/CMakeLists.txt        |  25 +--
 cmake/macros/TargetSixense.cmake              |  12 +-
 plugins/hifiSixense/src/SixenseManager.cpp    |   2 +
 plugins/hifiSixense/src/SixenseSupportOSX.cpp | 155 ------------------
 4 files changed, 10 insertions(+), 184 deletions(-)
 delete mode 100644 plugins/hifiSixense/src/SixenseSupportOSX.cpp

diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt
index 16f2850449..bd0d042c0b 100644
--- a/cmake/externals/sixense/CMakeLists.txt
+++ b/cmake/externals/sixense/CMakeLists.txt
@@ -57,30 +57,7 @@ if (WIN32)
 
 elseif(APPLE)
 
-    set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
-    set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${SOURCE_DIR}/lib/osx_x64/debug_dll/libsixensed_x64.dylib CACHE TYPE INTERNAL)
-  
-    set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64")
-    ExternalProject_Add_Step(
-      ${EXTERNAL_NAME}
-      change-install-name-release
-      COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
-      COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR}/release_dll -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
-      DEPENDEES install
-      WORKING_DIRECTORY <SOURCE_DIR>
-      LOG 1
-    )
-
-    set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64")
-    ExternalProject_Add_Step(
-      ${EXTERNAL_NAME}
-      change-install-name-debug
-      COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
-      COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR}/debug_dll -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
-      DEPENDEES install
-      WORKING_DIRECTORY <SOURCE_DIR>
-      LOG 1
-    )
+    # We no longer support Sixense on Macs due to bugs in the Sixense DLL
     
 elseif(NOT ANDROID)
 
diff --git a/cmake/macros/TargetSixense.cmake b/cmake/macros/TargetSixense.cmake
index 6fd9cede1f..28128d8b79 100644
--- a/cmake/macros/TargetSixense.cmake
+++ b/cmake/macros/TargetSixense.cmake
@@ -6,9 +6,11 @@
 #  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 #
 macro(TARGET_SIXENSE)
-    add_dependency_external_projects(sixense)
-    find_package(Sixense REQUIRED)
-    target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
-    target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
-    add_definitions(-DHAVE_SIXENSE)
+	if(NOT APPLE)
+	    add_dependency_external_projects(sixense)
+	    find_package(Sixense REQUIRED)
+	    target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
+	    target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
+	    add_definitions(-DHAVE_SIXENSE)
+	endif()
 endmacro()
diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp
index 48d13a8eaf..ade643ec72 100644
--- a/plugins/hifiSixense/src/SixenseManager.cpp
+++ b/plugins/hifiSixense/src/SixenseManager.cpp
@@ -131,6 +131,7 @@ void SixenseManager::setSixenseFilter(bool filter) {
 void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
     BAIL_IF_NOT_LOADED
 
+#ifdef HAVE_SIXENSE
     static bool sixenseHasBeenConnected { false };
     if (!sixenseHasBeenConnected && sixenseIsBaseConnected(0)) {
         sixenseHasBeenConnected = true;
@@ -146,6 +147,7 @@ void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibr
         _container->requestReset();
         _inputDevice->_requestReset = false;
     }
+#endif
 }
 
 void SixenseManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
diff --git a/plugins/hifiSixense/src/SixenseSupportOSX.cpp b/plugins/hifiSixense/src/SixenseSupportOSX.cpp
deleted file mode 100644
index fce2ea023b..0000000000
--- a/plugins/hifiSixense/src/SixenseSupportOSX.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-//
-//  SixenseSupportOSX.cpp
-//  libraries/input-plugins/src/input-plugins
-//
-//  Created by Clement on 10/20/15.
-//  Copyright 2015 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-// Mock implementation of sixense.h to hide dynamic linking on OS X
-#if defined(__APPLE__) && defined(HAVE_SIXENSE)
-#include <type_traits>
-
-#include <sixense.h>
-
-#include <QtCore/QCoreApplication>
-#include <QtCore/QLibrary>
-#include <QtCore/QDebug>
-
-#ifndef SIXENSE_LIB_FILENAME
-#define SIXENSE_LIB_FILENAME QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64"
-#endif
-
-using Library = std::unique_ptr<QLibrary>;
-static Library SIXENSE;
-
-struct Callable {
-    template<typename... Args>
-    int operator() (Args&&... args){
-        return reinterpret_cast<int(*)(Args...)>(function)(std::forward<Args>(args)...);
-    }
-    QFunctionPointer function;
-};
-
-Callable resolve(const Library& library, const char* name) {
-    Q_ASSERT_X(library && library->isLoaded(), __FUNCTION__, "Sixense library not loaded");
-    auto function = library->resolve(name);
-    Q_ASSERT_X(function, __FUNCTION__, std::string("Could not resolve ").append(name).c_str());
-    return Callable { function };
-}
-#define FORWARD resolve(SIXENSE, __FUNCTION__)
-
-
-void loadSixense() {
-    Q_ASSERT_X(!(SIXENSE && SIXENSE->isLoaded()), __FUNCTION__, "Sixense library already loaded");
-    SIXENSE.reset(new QLibrary(SIXENSE_LIB_FILENAME));
-    Q_CHECK_PTR(SIXENSE);
-    
-    if (SIXENSE->load()){
-        qDebug() << "Loaded sixense library for hydra support -" << SIXENSE->fileName();
-    } else {
-        qDebug() << "Sixense library at" << SIXENSE->fileName() << "failed to load:" << SIXENSE->errorString();
-        qDebug() << "Continuing without hydra support.";
-    }
-}
-void unloadSixense() {
-    SIXENSE->unload();
-}
-
-
-// sixense.h wrapper for OSX dynamic linking
-int sixenseInit() {
-    loadSixense();
-    if (!SIXENSE || !SIXENSE->isLoaded()) {
-        return SIXENSE_FAILURE;
-    }
-    return FORWARD();
-}
-int sixenseExit() {
-    auto returnCode = FORWARD();
-    unloadSixense();
-    return returnCode;
-}
-
-int sixenseGetMaxBases() {
-    return FORWARD();
-}
-int sixenseSetActiveBase(int i) {
-    return FORWARD(i);
-}
-int sixenseIsBaseConnected(int i) {
-    return FORWARD(i);
-}
-
-int sixenseGetMaxControllers() {
-    return FORWARD();
-}
-int sixenseIsControllerEnabled(int which) {
-    return FORWARD(which);
-}
-int sixenseGetNumActiveControllers() {
-    return FORWARD();
-}
-
-int sixenseGetHistorySize() {
-    return FORWARD();
-}
-
-int sixenseGetData(int which, int index_back, sixenseControllerData* data) {
-    return FORWARD(which, index_back, data);
-}
-int sixenseGetAllData(int index_back, sixenseAllControllerData* data) {
-    return FORWARD(index_back, data);
-}
-int sixenseGetNewestData(int which, sixenseControllerData* data) {
-    return FORWARD(which, data);
-}
-int sixenseGetAllNewestData(sixenseAllControllerData* data) {
-    return FORWARD(data);
-}
-
-int sixenseSetHemisphereTrackingMode(int which_controller, int state) {
-    return FORWARD(which_controller, state);
-}
-int sixenseGetHemisphereTrackingMode(int which_controller, int* state) {
-    return FORWARD(which_controller, state);
-}
-int sixenseAutoEnableHemisphereTracking(int which_controller) {
-    return FORWARD(which_controller);
-}
-
-int sixenseSetHighPriorityBindingEnabled(int on_or_off) {
-    return FORWARD(on_or_off);
-}
-int sixenseGetHighPriorityBindingEnabled(int* on_or_off) {
-    return FORWARD(on_or_off);
-}
-
-int sixenseTriggerVibration(int controller_id, int duration_100ms, int pattern_id) {
-    return FORWARD(controller_id, duration_100ms, pattern_id);
-}
-
-int sixenseSetFilterEnabled(int on_or_off) {
-    return FORWARD(on_or_off);
-}
-int sixenseGetFilterEnabled(int* on_or_off) {
-    return FORWARD(on_or_off);
-}
-
-int sixenseSetFilterParams(float near_range, float near_val, float far_range, float far_val) {
-    return FORWARD(near_range, near_val, far_range, far_val);
-}
-int sixenseGetFilterParams(float* near_range, float* near_val, float* far_range, float* far_val) {
-    return FORWARD(near_range, near_val, far_range, far_val);
-}
-
-int sixenseSetBaseColor(unsigned char red, unsigned char green, unsigned char blue) {
-    return FORWARD(red, green, blue);
-}
-int sixenseGetBaseColor(unsigned char* red, unsigned char* green, unsigned char* blue) {
-    return FORWARD(red, green, blue);
-}
-#endif

From 645ae3c21e96fe299ceac3c2499b218b9fc87213 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 22 Jun 2016 17:02:02 -0700
Subject: [PATCH 28/35] remove tabs

---
 cmake/macros/TargetSixense.cmake | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/cmake/macros/TargetSixense.cmake b/cmake/macros/TargetSixense.cmake
index 28128d8b79..07dcfe67e4 100644
--- a/cmake/macros/TargetSixense.cmake
+++ b/cmake/macros/TargetSixense.cmake
@@ -6,11 +6,11 @@
 #  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 #
 macro(TARGET_SIXENSE)
-	if(NOT APPLE)
-	    add_dependency_external_projects(sixense)
-	    find_package(Sixense REQUIRED)
-	    target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
-	    target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
-	    add_definitions(-DHAVE_SIXENSE)
-	endif()
+    if(NOT APPLE)
+        add_dependency_external_projects(sixense)
+        find_package(Sixense REQUIRED)
+        target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
+        target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
+        add_definitions(-DHAVE_SIXENSE)
+    endif()
 endmacro()

From 1d8d1240cad863ab853123b874d78d677ce26e07 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Thu, 23 Jun 2016 10:38:46 -0700
Subject: [PATCH 29/35] remove override to avoid new Jenkins OSX warnings

---
 libraries/entities/src/ParticleEffectEntityItem.h | 4 ++--
 libraries/entities/src/ZoneEntityItem.h           | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h
index 777f3b6cf6..9ddda62c8b 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.h
+++ b/libraries/entities/src/ParticleEffectEntityItem.h
@@ -95,8 +95,8 @@ public:
     void setAlphaSpread(float alphaSpread);
     float getAlphaSpread() const { return _alphaSpread; }
 
-    void setShapeType(ShapeType type) override;
-    virtual ShapeType getShapeType() const override { return _shapeType; }
+    void setShapeType(ShapeType type);
+    virtual ShapeType getShapeType() const { return _shapeType; }
 
     virtual void debugDump() const;
 
diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h
index 599e70f83e..f0f2a91d63 100644
--- a/libraries/entities/src/ZoneEntityItem.h
+++ b/libraries/entities/src/ZoneEntityItem.h
@@ -54,9 +54,9 @@ public:
     static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; }
     static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; }
     
-    virtual bool isReadyToComputeShape() override { return false; }
-    void setShapeType(ShapeType type) override { _shapeType = type; }
-    virtual ShapeType getShapeType() const override;
+    virtual bool isReadyToComputeShape() { return false; }
+    void setShapeType(ShapeType type) { _shapeType = type; }
+    virtual ShapeType getShapeType() const;
     
     virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); }
     const QString getCompoundShapeURL() const { return _compoundShapeURL; }

From 1de1c632afbb4f6f124108d32265e2aa3ec25c53 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Thu, 23 Jun 2016 17:16:42 -0700
Subject: [PATCH 30/35] use private_description instead of description for
 domain settings

---
 domain-server/resources/web/settings/js/settings.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js
index c2cb2ecb80..4188e86548 100644
--- a/domain-server/resources/web/settings/js/settings.js
+++ b/domain-server/resources/web/settings/js/settings.js
@@ -555,7 +555,7 @@ function createNewDomainID(description, justConnected) {
   // get the JSON object ready that we'll use to create a new domain
   var domainJSON = {
     "domain": {
-       "description": description
+       "private_description": description
     },
     "access_token": $(Settings.ACCESS_TOKEN_SELECTOR).val()
   }
@@ -748,8 +748,8 @@ function chooseFromHighFidelityDomains(clickedButton) {
         _.each(data.data.domains, function(domain){
           var domainString = "";
 
-          if (domain.description) {
-            domainString += '"' + domain.description + '" - ';
+          if (domain.private_description) {
+            domainString += '"' + domain.private_description + '" - ';
           }
 
           domainString += domain.id;

From 13310923c4440ec6b0a97dbf89627dc63fe832ad Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Thu, 23 Jun 2016 19:38:23 -0700
Subject: [PATCH 31/35] reset to temp domain after logout

---
 .../resources/web/settings/js/settings.js     |  2 ++
 domain-server/src/DomainServer.cpp            | 19 ++++++++++---------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js
index 4188e86548..4f153d6190 100644
--- a/domain-server/resources/web/settings/js/settings.js
+++ b/domain-server/resources/web/settings/js/settings.js
@@ -457,6 +457,8 @@ function disonnectHighFidelityAccount() {
   }, function(){
     // we need to post to settings to clear the access-token
     $(Settings.ACCESS_TOKEN_SELECTOR).val('').change();
+    // reset the domain id to get a new temporary name
+    $(Settings.DOMAIN_ID_SELECTOR).val('').change();
     saveSettings();
   });
 }
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 192c0d26e6..ca0d7728dd 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -111,12 +111,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
         return;
     }
 
-    // check for the temporary name parameter
-    const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
-    if (args.contains(GET_TEMPORARY_NAME_SWITCH)) {
-        getTemporaryName();
-    }
-
     setupNodeListAndAssignments();
     setupAutomaticNetworking();
     if (!getID().isNull()) {
@@ -125,6 +119,12 @@ DomainServer::DomainServer(int argc, char* argv[]) :
         sendHeartbeatToMetaverse();
     }
 
+    // check for the temporary name parameter
+    const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
+    if (args.contains(GET_TEMPORARY_NAME_SWITCH)) {
+        getTemporaryName();
+    }
+
     _gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request
 
     _metadata = new DomainMetadata(this);
@@ -251,12 +251,13 @@ void DomainServer::getTemporaryName(bool force) {
     // check if we already have a domain ID
     const QVariant* idValueVariant = valueForKeyPath(_settingsManager.getSettingsMap(), METAVERSE_DOMAIN_ID_KEY_PATH);
 
+    qInfo() << "Requesting temporary domain name";
     if (idValueVariant) {
-        qWarning() << "Temporary domain name requested but a domain ID is already present in domain-server settings.";
+        qDebug() << "A domain ID is already present in domain-server settings:" << idValueVariant->toString();
         if (force) {
-            qWarning() << "Temporary domain name will be requested to replace it.";
+            qDebug() << "Requesting temporary domain name to replace current ID:" << getID();
         } else {
-            qWarning() << "Temporary domain name will not be requested.";
+            qInfo() << "Abandoning request of temporary domain name.";
             return;
         }
     }

From b1b378a91feb062e160cf794002e59c13e5ed11b Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Thu, 23 Jun 2016 19:38:41 -0700
Subject: [PATCH 32/35] add back access token to domain-server

---
 domain-server/src/DomainServer.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index ca0d7728dd..6e5c1fd361 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -470,6 +470,9 @@ bool DomainServer::resetAccountManagerAccessToken() {
 
 void DomainServer::setupAutomaticNetworking() {
     qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting;
+
+    resetAccountManagerAccessToken();
+
     _automaticNetworkingSetting =
         _settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
 

From 1fcd7aa0c44d714fa4505b9b97fdad698ed9a674 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Thu, 23 Jun 2016 19:54:34 -0700
Subject: [PATCH 33/35] add build version to heartbeat

---
 domain-server/src/DomainServer.cpp | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 223cab61da..d421c6554b 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -1081,6 +1081,11 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
     // Setup the domain object to send to the data server
     QJsonObject domainObject;
 
+    // add the version
+    static const QString VERSION_KEY = "version";
+    domainObject[VERSION_KEY] = BuildInfo::VERSION;
+
+    // add networking
     if (!networkAddress.isEmpty()) {
         static const QString PUBLIC_NETWORK_ADDRESS_KEY = "network_address";
         domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress;
@@ -1089,10 +1094,10 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
     static const QString AUTOMATIC_NETWORKING_KEY = "automatic_networking";
     domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting;
 
-    // add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings
-    const QString RESTRICTED_ACCESS_FLAG = "restricted";
 
-    // consider the domain to have restricted access if "anonymous" connections can't connect to the domain.
+    // add access level for anonymous connections
+    // consider the domain to be "restricted" if anonymous connections are disallowed
+    static const QString RESTRICTED_ACCESS_FLAG = "restricted";
     NodePermissions anonymousPermissions = _settingsManager.getPermissionsForName(NodePermissions::standardNameAnonymous);
     domainObject[RESTRICTED_ACCESS_FLAG] = !anonymousPermissions.canConnectToDomain;
 

From a095da31fd8c1422058da1de0622554124f4f56c Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Fri, 24 Jun 2016 11:48:55 -0700
Subject: [PATCH 34/35] Fix initial visibility of QML windows from scripts

---
 libraries/ui/src/QmlWindowClass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp
index b8834f0549..c0eba4abf3 100644
--- a/libraries/ui/src/QmlWindowClass.cpp
+++ b/libraries/ui/src/QmlWindowClass.cpp
@@ -118,7 +118,7 @@ void QmlWindowClass::initQml(QVariantMap properties) {
             }
 
             bool visible = !properties.contains(VISIBILE_PROPERTY) || properties[VISIBILE_PROPERTY].toBool();
-            object->setProperty(VISIBILE_PROPERTY, visible);
+            object->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible);
             object->setProperty(SOURCE_PROPERTY, _source);
 
             // Forward messages received from QML on to the script

From 8bf72f28da84d177e67e920fa1e3f1b243a587b6 Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault" <tony@highfidelity.io>
Date: Fri, 24 Jun 2016 12:06:02 -0700
Subject: [PATCH 35/35] Fix for grab script search ray length

---
 scripts/system/controllers/handControllerGrab.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js
index 7706132c58..93d2269584 100644
--- a/scripts/system/controllers/handControllerGrab.js
+++ b/scripts/system/controllers/handControllerGrab.js
@@ -1173,10 +1173,12 @@ function MyController(hand) {
         }
 
         var rayPickInfo = this.calcRayPickInfo(this.hand);
-        this.intersectionDistance = rayPickInfo.distance;
         if (rayPickInfo.entityID) {
             candidateEntities.push(rayPickInfo.entityID);
             this.entityPropertyCache.addEntity(rayPickInfo.entityID);
+            this.intersectionDistance = rayPickInfo.distance;
+        } else {
+            this.intersectionDistance = 0;
         }
 
         var grabbableEntities = candidateEntities.filter(function (entity) {