From 5480c9f5ca0f85349d7a2631209df143034298ed Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Thu, 28 Mar 2019 15:27:49 -0700
Subject: [PATCH 1/9] skinning, skeleton for models, avatar

---
 libraries/fbx/src/GLTFSerializer.cpp | 270 +++++++++++++++++++++++----
 libraries/fbx/src/GLTFSerializer.h   |   2 +
 2 files changed, 232 insertions(+), 40 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index b8d4e53b65..1b6f5767f4 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -711,19 +711,19 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
             node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]);
     } else {
 
-        if (node.defined["rotation"] && node.rotation.size() == 4) {
-            //quat(x,y,z,w) to quat(w,x,y,z)
-            glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
-            tmat = glm::mat4_cast(rotquat) * tmat;
-        }
-
         if (node.defined["scale"] && node.scale.size() == 3) {
             glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]);
             glm::mat4 s = glm::mat4(1.0);
             s = glm::scale(s, scale);
             tmat = s * tmat;
         }
-
+        
+        if (node.defined["rotation"] && node.rotation.size() == 4) {
+            //quat(x,y,z,w) to quat(w,x,y,z)
+            glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
+            tmat = glm::mat4_cast(rotquat) * tmat;
+        }
+        
         if (node.defined["translation"] && node.translation.size() == 3) {
             glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]);
             glm::mat4 t = glm::mat4(1.0);
@@ -734,15 +734,58 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
     return tmat;
 }
 
+std::vector<QVector<float>> GLTFSerializer::getSkinInverseBindMatrices() {
+    std::vector<QVector<float>> inverseBindMatrixValues;
+    for (auto &skin : _file.skins) {
+        GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices];
+        GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView];
+        GLTFBuffer& indicesBuffer = _file.buffers[indicesBufferview.buffer];
+        int accBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0;
+        QVector<float> matrices;
+        addArrayOfType(indicesBuffer.blob, 
+            indicesBufferview.byteOffset + accBoffset, 
+            indicesAccessor.count, 
+            matrices,
+            indicesAccessor.type, 
+            indicesAccessor.componentType);
+        inverseBindMatrixValues.push_back(matrices);
+    }
+    return inverseBindMatrixValues;
+}
+
+QVector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, bool order) {
+    QVector<int> result;
+    result.append(n);
+    int begin = 0;
+    int finish = children.size();
+    if (order) {
+        begin = children.size() - 1;
+        finish = -1;
+    }
+    int index = begin;
+    while (index != finish) {
+        int c = children[index];
+        std::vector<int> nested = _file.nodes[c].children.toStdVector();
+        if (nested.size() != 0) {
+            std::sort(nested.begin(), nested.end());
+            result.append(nodeDFS(c, nested, order));
+        } else {
+            result.append(c);
+        }
+        begin < finish ? index++ : index--;
+    }
+    return result;
+}
+
+
 bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
+    int nodesSize = _file.nodes.size();
 
     //Build dependencies
-    QVector<QVector<int>> nodeDependencies(_file.nodes.size());
+    QVector<QVector<int>> nodeDependencies(nodesSize);
     int nodecount = 0;
-    bool hasChildren = false;
     foreach(auto &node, _file.nodes) {
         //nodes_transforms.push_back(getModelTransform(node));
-        hasChildren |= !node.children.isEmpty();
         foreach(int child, node.children) nodeDependencies[child].push_back(nodecount);
         nodecount++;
     }
@@ -764,26 +807,99 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         
         nodecount++;
     }
-    
-    HFMJoint joint;
-    joint.isSkeletonJoint = true;
-    joint.bindTransformFoundInCluster = false;
-    joint.distanceToParent = 0;
-    joint.parentIndex = -1;
-    hfmModel.joints.resize(_file.nodes.size());
-    hfmModel.jointIndices["x"] = _file.nodes.size();
-    int jointInd = 0;
-    for (auto& node : _file.nodes) {
-        int size = node.transforms.size();
-        if (hasChildren) { size--; }
-        joint.preTransform = glm::mat4(1);
-        for (int i = 0; i < size; i++) {
-            joint.preTransform = node.transforms[i] * joint.preTransform;
-        }
-        joint.name = node.name;
-        hfmModel.joints[jointInd] = joint;
-        jointInd++;
+
+
+    // initialize order in which nodes will be parsed
+    QVector<int> nodeQueue; 
+    int start = 0;
+    int end = nodesSize;
+    if (!_file.scenes[_file.scene].nodes.contains(0)) {
+        end = -1;
+        start = nodesSize - 1;
     }
+    QVector<int> init = _file.scenes[_file.scene].nodes; 
+    std::sort(init.begin(), init.end());
+    int begin = 0; 
+    int finish = init.size();
+    if (start > end)  { 
+        begin = init.size() - 1;
+        finish = -1;
+    }
+    int index = begin;
+    while (index != finish) {
+        int i = init[index];
+        std::vector<int> children = _file.nodes[i].children.toStdVector(); 
+        std::sort(children.begin(), children.end());
+        nodeQueue.append(nodeDFS(i, children, start > end));
+        begin < finish ? index++ : index--;
+    }
+
+
+    // Build joints
+    HFMJoint joint;
+    joint.distanceToParent = 0;
+    hfmModel.jointIndices["x"] = nodesSize;
+    hfmModel.hasSkeletonJoints = false;
+
+    for (int nodeIndex : nodeQueue) {
+        auto& node = _file.nodes[nodeIndex];
+
+        joint.parentIndex = -1;
+        if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) {
+            joint.parentIndex = nodeQueue.indexOf(nodeDependencies[nodeIndex][0]);
+        }
+        joint.transform = node.transforms.first();
+        joint.postTransform = glm::mat4();
+        glm:vec3 scale = extractScale(joint.transform);
+        joint.postTransform[0][0] = scale.x;
+        joint.postTransform[1][1] = scale.y;
+        joint.postTransform[2][2] = scale.z;
+        joint.rotation = glmExtractRotation(joint.transform);
+        joint.translation = extractTranslation(joint.transform);
+          
+        joint.name = node.name;
+        hfmModel.joints.push_back(joint);
+    }
+
+
+    // Build skeleton
+    int matrixIndex = 0;
+    std::vector<QVector<float>> inverseBindValues = getSkinInverseBindMatrices();
+    std::vector<glm::mat4> jointInverseBindTransforms;
+    jointInverseBindTransforms.resize(nodesSize);
+
+    int jointIndex = end;
+    while (jointIndex != start) {
+        start < end ? jointIndex-- : jointIndex++;
+        int jOffset = nodeQueue[jointIndex];
+        auto joint = hfmModel.joints[jointIndex];
+
+        joint.isSkeletonJoint = false;
+        if (!_file.skins.isEmpty()) {
+            hfmModel.hasSkeletonJoints = true;
+            for (int s = 0; s < _file.skins.size(); s++) {
+                auto skin = _file.skins[s];
+                joint.isSkeletonJoint = skin.joints.contains(jOffset);
+
+                if (joint.isSkeletonJoint) {
+                    QVector<float> value = inverseBindValues[s];
+                    int matrixCount = 16 * skin.joints.indexOf(jOffset);
+                    jointInverseBindTransforms[jointIndex] =
+                        glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3],
+                            value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], 
+                            value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11], 
+                            value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]);
+                    matrixIndex++;
+                } else {
+                    jointInverseBindTransforms[jointIndex] = glm::mat4();
+                }
+            }
+            glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex]));
+            hfmModel.bindExtents.addPoint(bindTranslation);
+        }
+        hfmModel.joints[jointIndex] = joint;
+    }
+
 
     //Build materials
     QVector<QString> materialIDs;
@@ -803,23 +919,34 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     }
 
     
-
-    nodecount = 0;
     // Build meshes
-    foreach(auto &node, _file.nodes) {
+    nodecount = 0;
+    int nodeIndex = start;
+    while (nodeIndex != end) {
+        auto& node = _file.nodes[nodeIndex];
 
         if (node.defined["mesh"]) {
             qCDebug(modelformat) << "node_transforms" << node.transforms;
             foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
                 hfmModel.meshes.append(HFMMesh());
                 HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1];
-                HFMCluster cluster;
-                cluster.jointIndex = nodecount;
-                cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0,
-                    0, 1, 0, 0,
-                    0, 0, 1, 0,
-                    0, 0, 0, 1);
-                mesh.clusters.append(cluster);
+                if (!hfmModel.hasSkeletonJoints) {
+                    HFMCluster cluster;
+                    cluster.jointIndex = nodecount;
+                    cluster.inverseBindMatrix = glm::mat4();
+                    cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
+                    mesh.clusters.append(cluster);
+                } else {
+                    int j = start;
+                    while (j != end) {
+                        HFMCluster cluster;
+                        cluster.jointIndex = j;
+                        cluster.inverseBindMatrix = jointInverseBindTransforms[j];
+                        cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
+                        mesh.clusters.append(cluster);
+                        start < end ? j++ : j--;
+                    }
+                }
 
                 HFMMeshPart part = HFMMeshPart();
 
@@ -848,6 +975,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                 }
 
                 QList<QString> keys = primitive.attributes.values.keys();
+                QVector<uint16_t> clusterJoints;
+                QVector<float> clusterWeights;
 
                 foreach(auto &key, keys) {
                     int accessorIdx = primitive.attributes.values[key];
@@ -949,8 +1078,68 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                         for (int n = 0; n < texcoords.size(); n = n + 2) {
                             mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
                         }
+                    } else if (key == "JOINTS_0") {
+                        QVector<uint16_t> joints;
+                        success = addArrayOfType(buffer.blob,
+                            bufferview.byteOffset + accBoffset,
+                            accessor.count,
+                            joints,
+                            accessor.type, 
+                            accessor.componentType);
+                        if (!success) {
+                            qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url;
+                            continue;
+                        }
+                        for (int n = 0; n < joints.size(); n++) {
+                            clusterJoints.push_back(joints[n]);
+                        }
+                    } else if (key == "WEIGHTS_0") {
+                        QVector<float> weights;
+                        success = addArrayOfType(buffer.blob, 
+                            bufferview.byteOffset + accBoffset, 
+                            accessor.count, 
+                            weights,
+                            accessor.type, 
+                            accessor.componentType);
+                        if (!success) {
+                            qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url;
+                            continue;
+                        }
+                        for (int n = 0; n < weights.size(); n++) {
+                            clusterWeights.push_back(weights[n]);
+                        }
+                    }
+                }
+            
+                // adapted from FBXSerializer.cpp
+                if (hfmModel.hasSkeletonJoints) {
+                    int numClusterIndices = clusterJoints.size();
+                    const int WEIGHTS_PER_VERTEX = 4;
+                    const float ALMOST_HALF = 0.499f;
+                    int numVertices = mesh.vertices.size();
+                    mesh.clusterIndices.fill(0, numClusterIndices);
+                    mesh.clusterWeights.fill(0, numClusterIndices);
+
+                    for (int c = 0; c < clusterJoints.size(); c++) {
+                        mesh.clusterIndices[c] = _file.skins[node.skin].joints[clusterJoints[c]];
                     }
 
+                    // normalize and compress to 16-bits
+                    for (int i = 0; i < numVertices; ++i) {
+                        int j = i * WEIGHTS_PER_VERTEX;
+
+                        float totalWeight = 0.0f;
+                        for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
+                            totalWeight += clusterWeights[k];
+                        }
+                        const float ALMOST_HALF = 0.499f;
+                        if (totalWeight > 0.0f) {
+                            float weightScalingFactor = (float)(UINT16_MAX) / totalWeight;
+                            for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
+                                mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF);
+                            }
+                        }
+                    }
                 }
 
                 if (primitive.defined["material"]) {
@@ -959,7 +1148,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                 mesh.parts.push_back(part);
 
                 // populate the texture coordinates if they don't exist
-                if (mesh.texCoords.size() == 0) {
+                if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) {
                     for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0));
                 }
                 mesh.meshExtents.reset();
@@ -973,6 +1162,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
             
         }
         nodecount++;
+        start < end ? nodeIndex++ : nodeIndex--;
     }
 
     
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index 05dc526f79..331f5937ed 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -712,6 +712,8 @@ private:
     hifi::ByteArray _glbBinary;
 
     glm::mat4 getModelTransform(const GLTFNode& node);
+    std::vector<QVector<float>> getSkinInverseBindMatrices();
+    QVector<int> nodeDFS(int n, std::vector<int>& children, bool order);
 
     bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
     bool parseGLTF(const hifi::ByteArray& data);

From b283bb303d83fb9bf460216ffc99aedfa46ee1ee Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Thu, 28 Mar 2019 17:55:51 -0700
Subject: [PATCH 2/9] jenkins warning fixes

---
 libraries/fbx/src/GLTFSerializer.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 1b6f5767f4..cf8d40cfc1 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -757,9 +757,9 @@ QVector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, bool ord
     QVector<int> result;
     result.append(n);
     int begin = 0;
-    int finish = children.size();
+    int finish = (int)children.size();
     if (order) {
-        begin = children.size() - 1;
+        begin = (int)children.size() - 1;
         finish = -1;
     }
     int index = begin;
@@ -850,7 +850,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         }
         joint.transform = node.transforms.first();
         joint.postTransform = glm::mat4();
-        glm:vec3 scale = extractScale(joint.transform);
+        glm::vec3 scale = extractScale(joint.transform);
         joint.postTransform[0][0] = scale.x;
         joint.postTransform[1][1] = scale.y;
         joint.postTransform[2][2] = scale.z;
@@ -1132,7 +1132,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                         for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
                             totalWeight += clusterWeights[k];
                         }
-                        const float ALMOST_HALF = 0.499f;
                         if (totalWeight > 0.0f) {
                             float weightScalingFactor = (float)(UINT16_MAX) / totalWeight;
                             for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {

From 269b910d24a8423ec2e73048bf40829c0115bc31 Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Fri, 29 Mar 2019 18:25:17 -0700
Subject: [PATCH 3/9] update variables, loops

---
 libraries/fbx/src/GLTFSerializer.cpp | 105 ++++++++++++++-------------
 libraries/fbx/src/GLTFSerializer.h   |   4 +-
 2 files changed, 56 insertions(+), 53 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index cf8d40cfc1..d29c09992f 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -734,8 +734,8 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
     return tmat;
 }
 
-std::vector<QVector<float>> GLTFSerializer::getSkinInverseBindMatrices() {
-    std::vector<QVector<float>> inverseBindMatrixValues;
+std::vector<std::vector<float>> GLTFSerializer::getSkinInverseBindMatrices() {
+    std::vector<std::vector<float>> inverseBindMatrixValues;
     for (auto &skin : _file.skins) {
         GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices];
         GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView];
@@ -748,14 +748,14 @@ std::vector<QVector<float>> GLTFSerializer::getSkinInverseBindMatrices() {
             matrices,
             indicesAccessor.type, 
             indicesAccessor.componentType);
-        inverseBindMatrixValues.push_back(matrices);
+        inverseBindMatrixValues.push_back(matrices.toStdVector());
     }
     return inverseBindMatrixValues;
 }
 
-QVector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, bool order) {
-    QVector<int> result;
-    result.append(n);
+std::vector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, bool order) {
+    std::vector<int> result;
+    result.push_back(n);
     int begin = 0;
     int finish = (int)children.size();
     if (order) {
@@ -768,9 +768,11 @@ QVector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, bool ord
         std::vector<int> nested = _file.nodes[c].children.toStdVector();
         if (nested.size() != 0) {
             std::sort(nested.begin(), nested.end());
-            result.append(nodeDFS(c, nested, order));
+            for (int n : nodeDFS(c, nested, order)) {
+                result.push_back(n);
+            }
         } else {
-            result.append(c);
+            result.push_back(c);
         }
         begin < finish ? index++ : index--;
     }
@@ -779,10 +781,10 @@ QVector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, bool ord
 
 
 bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
-    int nodesSize = _file.nodes.size();
+    int numNodes = _file.nodes.size();
 
     //Build dependencies
-    QVector<QVector<int>> nodeDependencies(nodesSize);
+    QVector<QVector<int>> nodeDependencies(numNodes);
     int nodecount = 0;
     foreach(auto &node, _file.nodes) {
         //nodes_transforms.push_back(getModelTransform(node));
@@ -810,35 +812,40 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
 
     // initialize order in which nodes will be parsed
-    QVector<int> nodeQueue; 
-    int start = 0;
-    int end = nodesSize;
+    QVector<int> nodeQueue;
+    nodeQueue.reserve(numNodes);
+    int rootNode = 0;
+    int finalNode = numNodes;
     if (!_file.scenes[_file.scene].nodes.contains(0)) {
-        end = -1;
-        start = nodesSize - 1;
+        rootNode = numNodes - 1;
+        finalNode = -1;
     }
-    QVector<int> init = _file.scenes[_file.scene].nodes; 
-    std::sort(init.begin(), init.end());
-    int begin = 0; 
-    int finish = init.size();
-    if (start > end)  { 
-        begin = init.size() - 1;
-        finish = -1;
+    bool rootAtStartOfList = rootNode < finalNode;
+    int nodeListStride = 1;
+    if (!rootAtStartOfList) { nodeListStride = -1; }
+
+    QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; 
+    std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
+    int sceneRootNode = 0; 
+    int sceneFinalNode = initialSceneNodes.size();
+    if (!rootAtStartOfList) { 
+        sceneRootNode = initialSceneNodes.size() - 1;
+        sceneFinalNode = -1;
     }
-    int index = begin;
-    while (index != finish) {
-        int i = init[index];
+    for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) {
+        int i = initialSceneNodes[index];
         std::vector<int> children = _file.nodes[i].children.toStdVector(); 
         std::sort(children.begin(), children.end());
-        nodeQueue.append(nodeDFS(i, children, start > end));
-        begin < finish ? index++ : index--;
+        for (int n : nodeDFS(i, children, !rootAtStartOfList)) {
+            nodeQueue.append(n);
+        }
     }
 
 
     // Build joints
     HFMJoint joint;
     joint.distanceToParent = 0;
-    hfmModel.jointIndices["x"] = nodesSize;
+    hfmModel.jointIndices["x"] = numNodes;
     hfmModel.hasSkeletonJoints = false;
 
     for (int nodeIndex : nodeQueue) {
@@ -858,46 +865,46 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         joint.translation = extractTranslation(joint.transform);
           
         joint.name = node.name;
+        joint.isSkeletonJoint = false;
         hfmModel.joints.push_back(joint);
     }
 
 
     // Build skeleton
-    int matrixIndex = 0;
-    std::vector<QVector<float>> inverseBindValues = getSkinInverseBindMatrices();
     std::vector<glm::mat4> jointInverseBindTransforms;
-    jointInverseBindTransforms.resize(nodesSize);
+    if (!_file.skins.isEmpty()) {
+        int matrixIndex = 0;
+        std::vector<std::vector<float>> inverseBindValues = getSkinInverseBindMatrices();
+        jointInverseBindTransforms.resize(numNodes);
 
-    int jointIndex = end;
-    while (jointIndex != start) {
-        start < end ? jointIndex-- : jointIndex++;
-        int jOffset = nodeQueue[jointIndex];
-        auto joint = hfmModel.joints[jointIndex];
+        int jointIndex = finalNode;
+        while (jointIndex != rootNode) {
+            rootAtStartOfList ? jointIndex-- : jointIndex++;
+            int jOffset = nodeQueue[jointIndex];
+            auto joint = hfmModel.joints[jointIndex];
 
-        joint.isSkeletonJoint = false;
-        if (!_file.skins.isEmpty()) {
             hfmModel.hasSkeletonJoints = true;
             for (int s = 0; s < _file.skins.size(); s++) {
                 auto skin = _file.skins[s];
                 joint.isSkeletonJoint = skin.joints.contains(jOffset);
 
                 if (joint.isSkeletonJoint) {
-                    QVector<float> value = inverseBindValues[s];
+                    std::vector<float> value = inverseBindValues[s];
                     int matrixCount = 16 * skin.joints.indexOf(jOffset);
                     jointInverseBindTransforms[jointIndex] =
-                        glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3],
+                        glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], 
                             value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], 
-                            value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11], 
+                            value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11],
                             value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]);
                     matrixIndex++;
                 } else {
                     jointInverseBindTransforms[jointIndex] = glm::mat4();
                 }
+                glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex]));
+                hfmModel.bindExtents.addPoint(bindTranslation);
             }
-            glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex]));
-            hfmModel.bindExtents.addPoint(bindTranslation);
+            hfmModel.joints[jointIndex] = joint;
         }
-        hfmModel.joints[jointIndex] = joint;
     }
 
 
@@ -921,8 +928,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     
     // Build meshes
     nodecount = 0;
-    int nodeIndex = start;
-    while (nodeIndex != end) {
+    for (int nodeIndex = rootNode; nodeIndex != finalNode; nodeIndex += nodeListStride) {
         auto& node = _file.nodes[nodeIndex];
 
         if (node.defined["mesh"]) {
@@ -937,14 +943,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
                     mesh.clusters.append(cluster);
                 } else {
-                    int j = start;
-                    while (j != end) {
+                    for (int j = rootNode; j != finalNode; j += nodeListStride) {
                         HFMCluster cluster;
                         cluster.jointIndex = j;
                         cluster.inverseBindMatrix = jointInverseBindTransforms[j];
                         cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
                         mesh.clusters.append(cluster);
-                        start < end ? j++ : j--;
                     }
                 }
 
@@ -1148,7 +1152,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
                 // populate the texture coordinates if they don't exist
                 if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) {
-                    for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0));
+                    for (int i = 0; i < part.triangleIndices.size(); i++) { mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); }
                 }
                 mesh.meshExtents.reset();
                 foreach(const glm::vec3& vertex, mesh.vertices) {
@@ -1161,7 +1165,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
             
         }
         nodecount++;
-        start < end ? nodeIndex++ : nodeIndex--;
     }
 
     
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index 331f5937ed..e383e6581c 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -712,8 +712,8 @@ private:
     hifi::ByteArray _glbBinary;
 
     glm::mat4 getModelTransform(const GLTFNode& node);
-    std::vector<QVector<float>> getSkinInverseBindMatrices();
-    QVector<int> nodeDFS(int n, std::vector<int>& children, bool order);
+    std::vector<std::vector<float>> getSkinInverseBindMatrices();
+    std::vector<int> nodeDFS(int n, std::vector<int>& children, bool order);
 
     bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
     bool parseGLTF(const hifi::ByteArray& data);

From 2920fdc966b93e0e50f498168a50a25454d99883 Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Mon, 1 Apr 2019 21:09:14 +0100
Subject: [PATCH 4/9] variables, cluster size

---
 libraries/fbx/src/GLTFSerializer.cpp | 33 ++++++++++++----------------
 libraries/fbx/src/GLTFSerializer.h   |  2 +-
 2 files changed, 15 insertions(+), 20 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index d29c09992f..1192952b9e 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -753,28 +753,26 @@ std::vector<std::vector<float>> GLTFSerializer::getSkinInverseBindMatrices() {
     return inverseBindMatrixValues;
 }
 
-std::vector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, bool order) {
+std::vector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, int stride) {
     std::vector<int> result;
     result.push_back(n);
-    int begin = 0;
-    int finish = (int)children.size();
-    if (order) {
-        begin = (int)children.size() - 1;
-        finish = -1;
+    int rootDFS = 0;
+    int finalDFS = (int)children.size();
+    if (stride == -1) {
+        rootDFS = (int)children.size() - 1;
+        finalDFS = -1;
     }
-    int index = begin;
-    while (index != finish) {
+    for (int index = rootDFS; index != finalDFS; index += stride) {
         int c = children[index];
         std::vector<int> nested = _file.nodes[c].children.toStdVector();
         if (nested.size() != 0) {
             std::sort(nested.begin(), nested.end());
-            for (int n : nodeDFS(c, nested, order)) {
+            for (int n : nodeDFS(c, nested, stride)) {
                 result.push_back(n);
             }
         } else {
             result.push_back(c);
         }
-        begin < finish ? index++ : index--;
     }
     return result;
 }
@@ -836,7 +834,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         int i = initialSceneNodes[index];
         std::vector<int> children = _file.nodes[i].children.toStdVector(); 
         std::sort(children.begin(), children.end());
-        for (int n : nodeDFS(i, children, !rootAtStartOfList)) {
+        for (int n : nodeDFS(i, children, nodeListStride)) {
             nodeQueue.append(n);
         }
     }
@@ -856,14 +854,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
             joint.parentIndex = nodeQueue.indexOf(nodeDependencies[nodeIndex][0]);
         }
         joint.transform = node.transforms.first();
-        joint.postTransform = glm::mat4();
-        glm::vec3 scale = extractScale(joint.transform);
-        joint.postTransform[0][0] = scale.x;
-        joint.postTransform[1][1] = scale.y;
-        joint.postTransform[2][2] = scale.z;
-        joint.rotation = glmExtractRotation(joint.transform);
         joint.translation = extractTranslation(joint.transform);
-          
+        joint.rotation = glmExtractRotation(joint.transform);
+        glm::vec3 scale = extractScale(joint.transform);
+        joint.postTransform = glm::scale(glm::mat4(), scale);        
+
         joint.name = node.name;
         joint.isSkeletonJoint = false;
         hfmModel.joints.push_back(joint);
@@ -1121,7 +1116,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     const int WEIGHTS_PER_VERTEX = 4;
                     const float ALMOST_HALF = 0.499f;
                     int numVertices = mesh.vertices.size();
-                    mesh.clusterIndices.fill(0, numClusterIndices);
+                    mesh.clusterIndices.fill(mesh.clusters.size() - 1, numClusterIndices);
                     mesh.clusterWeights.fill(0, numClusterIndices);
 
                     for (int c = 0; c < clusterJoints.size(); c++) {
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index e383e6581c..af91a1753d 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -713,7 +713,7 @@ private:
 
     glm::mat4 getModelTransform(const GLTFNode& node);
     std::vector<std::vector<float>> getSkinInverseBindMatrices();
-    std::vector<int> nodeDFS(int n, std::vector<int>& children, bool order);
+    std::vector<int> nodeDFS(int n, std::vector<int>& children, int stride);
 
     bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
     bool parseGLTF(const hifi::ByteArray& data);

From 8439019c4eef997a05fe768aad22da3b83b65c6b Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Tue, 2 Apr 2019 19:56:17 +0100
Subject: [PATCH 5/9] update use of vectors

---
 libraries/fbx/src/GLTFSerializer.cpp | 43 ++++++++++++----------------
 libraries/fbx/src/GLTFSerializer.h   |  4 +--
 2 files changed, 21 insertions(+), 26 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 1192952b9e..84609be250 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -734,8 +734,7 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
     return tmat;
 }
 
-std::vector<std::vector<float>> GLTFSerializer::getSkinInverseBindMatrices() {
-    std::vector<std::vector<float>> inverseBindMatrixValues;
+void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues) {
     for (auto &skin : _file.skins) {
         GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices];
         GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView];
@@ -750,31 +749,28 @@ std::vector<std::vector<float>> GLTFSerializer::getSkinInverseBindMatrices() {
             indicesAccessor.componentType);
         inverseBindMatrixValues.push_back(matrices.toStdVector());
     }
-    return inverseBindMatrixValues;
 }
 
-std::vector<int> GLTFSerializer::nodeDFS(int n, std::vector<int>& children, int stride) {
-    std::vector<int> result;
-    result.push_back(n);
-    int rootDFS = 0;
-    int finalDFS = (int)children.size();
+void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result) {
+    int startingIndex = 0;
+    int finalIndex = (int)children.size();
     if (stride == -1) {
-        rootDFS = (int)children.size() - 1;
-        finalDFS = -1;
+        startingIndex = (int)children.size() - 1;
+        finalIndex = -1;
     }
-    for (int index = rootDFS; index != finalDFS; index += stride) {
+    for (int index = startingIndex; index != finalIndex; index += stride) {
         int c = children[index];
+        result.push_back(c);
         std::vector<int> nested = _file.nodes[c].children.toStdVector();
         if (nested.size() != 0) {
             std::sort(nested.begin(), nested.end());
-            for (int n : nodeDFS(c, nested, stride)) {
-                result.push_back(n);
+            for (int r : nested) {
+                if (result.end() == std::find(result.begin(), result.end(), r)) {
+                    getNodeQueueByDepthFirstChildren(nested, stride, result);
+                }
             }
-        } else {
-            result.push_back(c);
-        }
+        } 
     }
-    return result;
 }
 
 
@@ -810,7 +806,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
 
     // initialize order in which nodes will be parsed
-    QVector<int> nodeQueue;
+    std::vector<int> nodeQueue;
     nodeQueue.reserve(numNodes);
     int rootNode = 0;
     int finalNode = numNodes;
@@ -832,14 +828,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     }
     for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) {
         int i = initialSceneNodes[index];
+        nodeQueue.push_back(i);
         std::vector<int> children = _file.nodes[i].children.toStdVector(); 
         std::sort(children.begin(), children.end());
-        for (int n : nodeDFS(i, children, nodeListStride)) {
-            nodeQueue.append(n);
-        }
+        getNodeQueueByDepthFirstChildren(children, nodeListStride, nodeQueue);
     }
 
-
     // Build joints
     HFMJoint joint;
     joint.distanceToParent = 0;
@@ -851,7 +845,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
         joint.parentIndex = -1;
         if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) {
-            joint.parentIndex = nodeQueue.indexOf(nodeDependencies[nodeIndex][0]);
+            joint.parentIndex = std::distance(nodeQueue.begin(), std::find(nodeQueue.begin(), nodeQueue.end(), nodeDependencies[nodeIndex][0]));
         }
         joint.transform = node.transforms.first();
         joint.translation = extractTranslation(joint.transform);
@@ -869,7 +863,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     std::vector<glm::mat4> jointInverseBindTransforms;
     if (!_file.skins.isEmpty()) {
         int matrixIndex = 0;
-        std::vector<std::vector<float>> inverseBindValues = getSkinInverseBindMatrices();
+        std::vector<std::vector<float>> inverseBindValues;
+        getSkinInverseBindMatrices(inverseBindValues);
         jointInverseBindTransforms.resize(numNodes);
 
         int jointIndex = finalNode;
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index af91a1753d..d9c477bd99 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -712,8 +712,8 @@ private:
     hifi::ByteArray _glbBinary;
 
     glm::mat4 getModelTransform(const GLTFNode& node);
-    std::vector<std::vector<float>> getSkinInverseBindMatrices();
-    std::vector<int> nodeDFS(int n, std::vector<int>& children, int stride);
+    void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues);
+    void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result);
 
     bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
     bool parseGLTF(const hifi::ByteArray& data);

From 14352452793f4e9d1b477c86176276b9e4fdbbcf Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Tue, 2 Apr 2019 21:18:16 +0100
Subject: [PATCH 6/9] root cluster

---
 libraries/fbx/src/GLTFSerializer.cpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 84609be250..5e4716c56d 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -861,11 +861,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
     // Build skeleton
     std::vector<glm::mat4> jointInverseBindTransforms;
+    jointInverseBindTransforms.resize(numNodes);
     if (!_file.skins.isEmpty()) {
         int matrixIndex = 0;
         std::vector<std::vector<float>> inverseBindValues;
         getSkinInverseBindMatrices(inverseBindValues);
-        jointInverseBindTransforms.resize(numNodes);
 
         int jointIndex = finalNode;
         while (jointIndex != rootNode) {
@@ -941,6 +941,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                         mesh.clusters.append(cluster);
                     }
                 }
+                HFMCluster root; 
+                root.jointIndex = rootNode;
+                if (root.jointIndex == -1) { root.jointIndex = 0; }
+                root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
+                root.inverseBindTransform = Transform(root.inverseBindMatrix);
+                mesh.clusters.append(root);
 
                 HFMMeshPart part = HFMMeshPart();
 
@@ -1131,6 +1137,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                             for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
                                 mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF);
                             }
+                        } else {
+                            mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
                         }
                     }
                 }

From fcc7e9af3048b23f551bec322ec1595fab8ff16f Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Wed, 3 Apr 2019 00:09:36 +0100
Subject: [PATCH 7/9] minor syntax fix

---
 libraries/fbx/src/GLTFSerializer.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 5e4716c56d..1699722215 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -863,7 +863,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     std::vector<glm::mat4> jointInverseBindTransforms;
     jointInverseBindTransforms.resize(numNodes);
     if (!_file.skins.isEmpty()) {
-        int matrixIndex = 0;
         std::vector<std::vector<float>> inverseBindValues;
         getSkinInverseBindMatrices(inverseBindValues);
 
@@ -886,7 +885,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                             value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], 
                             value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11],
                             value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]);
-                    matrixIndex++;
                 } else {
                     jointInverseBindTransforms[jointIndex] = glm::mat4();
                 }
@@ -943,7 +941,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                 }
                 HFMCluster root; 
                 root.jointIndex = rootNode;
-                if (root.jointIndex == -1) { root.jointIndex = 0; }
+                if (root.jointIndex == -1) { 
+                    root.jointIndex = 0; 
+                }
                 root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
                 root.inverseBindTransform = Transform(root.inverseBindMatrix);
                 mesh.clusters.append(root);

From ff3e5bbc6129762c1ce0fa18fb3cbad3e759d75a Mon Sep 17 00:00:00 2001
From: danteruiz <danteruiz102@gmail.com>
Date: Tue, 2 Apr 2019 16:20:10 -0700
Subject: [PATCH 8/9] make sure to delete avatar when thier is no reason

---
 interface/src/avatar/AvatarManager.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 38bdd061c0..7a64edae11 100755
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -497,7 +497,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
     // it might not fire until after we create a new instance for the same remote avatar, which creates a race
     // on the creation of entities for that avatar instance and the deletion of entities for this instance
     avatar->removeAvatarEntitiesFromTree();
-    if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
+    if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble || removalReason == KillAvatarReason::NoReason) {
         emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID());
         emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
 

From 44fc0d21dbc6542fcb56e26c86f02bed059d6e23 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Wed, 3 Apr 2019 13:43:39 -0700
Subject: [PATCH 9/9] Revert "Remove _compositeFramebuffer from display
 plugins"

This reverts commit cb311408c68f2d465a80d76e0fdfe979cde96e13.
---
 .../Basic2DWindowOpenGLDisplayPlugin.cpp      |   6 +-
 .../Basic2DWindowOpenGLDisplayPlugin.h        |   2 +-
 .../display-plugins/OpenGLDisplayPlugin.cpp   | 175 ++++++++++--------
 .../src/display-plugins/OpenGLDisplayPlugin.h |  28 +--
 .../hmd/DebugHmdDisplayPlugin.h               |   2 +-
 .../display-plugins/hmd/HmdDisplayPlugin.cpp  |  64 ++++---
 .../display-plugins/hmd/HmdDisplayPlugin.h    |  11 +-
 .../stereo/InterleavedStereoDisplayPlugin.cpp |   4 +-
 .../stereo/InterleavedStereoDisplayPlugin.h   |   2 +-
 .../src/OculusMobileDisplayPlugin.cpp         |  10 +-
 .../src/OculusMobileDisplayPlugin.h           |   4 +-
 .../plugins/src/plugins/DisplayPlugin.cpp     |  12 +-
 libraries/plugins/src/plugins/DisplayPlugin.h |   8 +-
 .../render-utils/src/RenderCommonTask.cpp     |   4 +-
 libraries/render/src/render/Args.h            |   2 +-
 plugins/oculus/src/OculusDebugDisplayPlugin.h |   2 +-
 plugins/oculus/src/OculusDisplayPlugin.cpp    |  28 +--
 plugins/oculus/src/OculusDisplayPlugin.h      |   2 +-
 .../src/OculusLegacyDisplayPlugin.cpp         |   4 +-
 .../src/OculusLegacyDisplayPlugin.h           |   2 +-
 plugins/openvr/src/OpenVrDisplayPlugin.cpp    |  16 +-
 plugins/openvr/src/OpenVrDisplayPlugin.h      |   4 +-
 22 files changed, 211 insertions(+), 181 deletions(-)

diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
index f55f5919f5..9828a8beda 100644
--- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
@@ -109,7 +109,7 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
     return Parent::internalActivate();
 }
 
-void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPointer& compositeFramebuffer) {
+void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() {
 #if defined(Q_OS_ANDROID)
     auto& virtualPadManager = VirtualPad::Manager::instance();
     if(virtualPadManager.getLeftVirtualPad()->isShown()) {
@@ -121,7 +121,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPoin
 
         render([&](gpu::Batch& batch) {
             batch.enableStereo(false);
-            batch.setFramebuffer(compositeFramebuffer);
+            batch.setFramebuffer(_compositeFramebuffer);
             batch.resetViewTransform();
             batch.setProjectionTransform(mat4());
             batch.setPipeline(_cursorPipeline);
@@ -140,7 +140,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPoin
         });
     }
 #endif
-    Parent::compositeExtra(compositeFramebuffer);
+    Parent::compositeExtra();
 }
 
 static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60;
diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h
index d4c321a571..cc304c19c2 100644
--- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h
@@ -33,7 +33,7 @@ public:
 
     virtual bool isThrottled() const override;
 
-    virtual void compositeExtra(const gpu::FramebufferPointer&) override;
+    virtual void compositeExtra() override;
 
     virtual void pluginUpdate() override {};
 
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index 5bc84acc6a..c536e6b6e2 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -379,6 +379,14 @@ void OpenGLDisplayPlugin::customizeContext() {
         scissorState->setDepthTest(gpu::State::DepthTest(false));
         scissorState->setScissorEnable(true);
 
+        {
+#ifdef Q_OS_ANDROID
+            gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB);
+#else
+            gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
+#endif 
+           _simplePipeline = gpu::Pipeline::create(program, scissorState);
+        }
         {
 #ifdef Q_OS_ANDROID
             gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB);
@@ -388,59 +396,29 @@ void OpenGLDisplayPlugin::customizeContext() {
             _presentPipeline = gpu::Pipeline::create(program, scissorState);
         }
 
-        
-        // HUD operator
         {
-            gpu::PipelinePointer hudPipeline;
-            {
-                gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
-                hudPipeline = gpu::Pipeline::create(program, blendState);
-            }
-
-            gpu::PipelinePointer hudMirrorPipeline;
-            {
-                gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX);
-                hudMirrorPipeline = gpu::Pipeline::create(program, blendState);
-            }
-
-
-            _hudOperator = [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer& compositeFramebuffer, bool mirror) {
-                auto hudStereo = isStereo();
-                auto hudCompositeFramebufferSize = compositeFramebuffer->getSize();
-                std::array<glm::ivec4, 2> hudEyeViewports;
-                for_each_eye([&](Eye eye) {
-                    hudEyeViewports[eye] = eyeViewport(eye);
-                });
-                if (hudPipeline && hudTexture) {
-                    batch.enableStereo(false);
-                    batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline);
-                    batch.setResourceTexture(0, hudTexture);
-                    if (hudStereo) {
-                        for_each_eye([&](Eye eye) {
-                            batch.setViewportTransform(hudEyeViewports[eye]);
-                            batch.draw(gpu::TRIANGLE_STRIP, 4);
-                        });
-                    } else {
-                        batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize));
-                        batch.draw(gpu::TRIANGLE_STRIP, 4);
-                    }
-                }
-            };
-
+            gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
+            _hudPipeline = gpu::Pipeline::create(program, blendState);
+        }
+        {
+            gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX);
+            _mirrorHUDPipeline = gpu::Pipeline::create(program, blendState);
         }
-
 
         {
             gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTransformedTexture);
             _cursorPipeline = gpu::Pipeline::create(program, blendState);
         }
     }
+    updateCompositeFramebuffer();
 }
 
 void OpenGLDisplayPlugin::uncustomizeContext() {
     _presentPipeline.reset();
     _cursorPipeline.reset();
-    _hudOperator = DEFAULT_HUD_OPERATOR;
+    _hudPipeline.reset();
+    _mirrorHUDPipeline.reset();
+    _compositeFramebuffer.reset();
     withPresentThreadLock([&] {
         _currentFrame.reset();
         _lastFrame = nullptr;
@@ -532,16 +510,24 @@ void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const {
     });
 }
 
+void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) {
+    renderFromTexture(batch, texture, viewport, scissor, nullptr);
+}
 
-void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) {
+void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) {
+    auto fbo = gpu::FramebufferPointer();
     batch.enableStereo(false);
     batch.resetViewTransform();
-    batch.setFramebuffer(destFbo);
+    batch.setFramebuffer(fbo);
     batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
     batch.setStateScissorRect(scissor);
     batch.setViewportTransform(viewport);
     batch.setResourceTexture(0, texture);
+#ifndef USE_GLES
     batch.setPipeline(_presentPipeline);
+#else
+    batch.setPipeline(_simplePipeline);
+#endif
     batch.draw(gpu::TRIANGLE_STRIP, 4);
     if (copyFbo) {
         gpu::Vec4i copyFboRect(0, 0, copyFbo->getWidth(), copyFbo->getHeight());
@@ -567,7 +553,7 @@ void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::Textur
         batch.setViewportTransform(copyFboRect);
         batch.setStateScissorRect(copyFboRect);
         batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, {0.0f, 0.0f, 0.0f, 1.0f});
-        batch.blit(destFbo, sourceRect, copyFbo, copyRect);
+        batch.blit(fbo, sourceRect, copyFbo, copyRect);
     }
 }
 
@@ -595,14 +581,41 @@ void OpenGLDisplayPlugin::updateFrameData() {
     });
 }
 
-void OpenGLDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) {
+std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> OpenGLDisplayPlugin::getHUDOperator() {
+    auto hudPipeline = _hudPipeline;
+    auto hudMirrorPipeline = _mirrorHUDPipeline;
+    auto hudStereo = isStereo();
+    auto hudCompositeFramebufferSize = _compositeFramebuffer->getSize();
+    std::array<glm::ivec4, 2> hudEyeViewports;
+    for_each_eye([&](Eye eye) {
+        hudEyeViewports[eye] = eyeViewport(eye);
+    });
+    return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) {
+        if (hudPipeline && hudTexture) {
+            batch.enableStereo(false);
+            batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline);
+            batch.setResourceTexture(0, hudTexture);
+            if (hudStereo) {
+                for_each_eye([&](Eye eye) {
+                    batch.setViewportTransform(hudEyeViewports[eye]);
+                    batch.draw(gpu::TRIANGLE_STRIP, 4);
+                });
+            } else {
+                batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize));
+                batch.draw(gpu::TRIANGLE_STRIP, 4);
+            }
+        }
+    };
+}
+
+void OpenGLDisplayPlugin::compositePointer() {
     auto& cursorManager = Cursor::Manager::instance();
     const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
     auto cursorTransform = DependencyManager::get<CompositorHelper>()->getReticleTransform(glm::mat4());
     render([&](gpu::Batch& batch) {
         batch.enableStereo(false);
         batch.setProjectionTransform(mat4());
-        batch.setFramebuffer(compositeFramebuffer);
+        batch.setFramebuffer(_compositeFramebuffer);
         batch.setPipeline(_cursorPipeline);
         batch.setResourceTexture(0, cursorData.texture);
         batch.resetViewTransform();
@@ -613,13 +626,34 @@ void OpenGLDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compos
                 batch.draw(gpu::TRIANGLE_STRIP, 4);
             });
         } else {
-            batch.setViewportTransform(ivec4(uvec2(0), compositeFramebuffer->getSize()));
+            batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize()));
             batch.draw(gpu::TRIANGLE_STRIP, 4);
         }
     });
 }
 
-void OpenGLDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) {
+void OpenGLDisplayPlugin::compositeScene() {
+    render([&](gpu::Batch& batch) {
+        batch.enableStereo(false);
+        batch.setFramebuffer(_compositeFramebuffer);
+        batch.setViewportTransform(ivec4(uvec2(), _compositeFramebuffer->getSize()));
+        batch.setStateScissorRect(ivec4(uvec2(), _compositeFramebuffer->getSize()));
+        batch.resetViewTransform();
+        batch.setProjectionTransform(mat4());
+        batch.setPipeline(_simplePipeline);
+        batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
+        batch.draw(gpu::TRIANGLE_STRIP, 4);
+    });
+}
+
+void OpenGLDisplayPlugin::compositeLayers() {
+    updateCompositeFramebuffer();
+
+    {
+        PROFILE_RANGE_EX(render_detail, "compositeScene", 0xff0077ff, (uint64_t)presentCount())
+        compositeScene();
+    }
+
 #ifdef HIFI_ENABLE_NSIGHT_DEBUG
     if (false) // do not draw the HUD if running nsight debug
 #endif
@@ -633,35 +667,23 @@ void OpenGLDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& composi
 
     {
         PROFILE_RANGE_EX(render_detail, "compositeExtra", 0xff0077ff, (uint64_t)presentCount())
-        compositeExtra(compositeFramebuffer);
+        compositeExtra();
     }
 
     // Draw the pointer last so it's on top of everything
     auto compositorHelper = DependencyManager::get<CompositorHelper>();
     if (compositorHelper->getReticleVisible()) {
         PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount())
-            compositePointer(compositeFramebuffer);
+            compositePointer();
     }
 }
 
-void OpenGLDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
+void OpenGLDisplayPlugin::internalPresent() {
     render([&](gpu::Batch& batch) {
         // Note: _displayTexture must currently be the same size as the display.
         uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels();
         auto viewport = ivec4(uvec2(0),  dims);
-
-        gpu::TexturePointer finalTexture;
-        if (_displayTexture) {
-            finalTexture = _displayTexture;
-        } else if (compositeFramebuffer) {
-            finalTexture = compositeFramebuffer->getRenderBuffer(0);
-        } else {
-            qCWarning(displayPlugins) << "No valid texture for output";
-        }
-            
-        if (finalTexture) {
-            renderFromTexture(batch, finalTexture, viewport, viewport);
-        } 
+        renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport);
      });
     swapBuffers();
     _presentRate.increment();
@@ -678,7 +700,7 @@ void OpenGLDisplayPlugin::present() {
     }
     incrementPresentCount();
 
-    if (_currentFrame && _currentFrame->framebuffer) {
+    if (_currentFrame) {
         auto correction = getViewCorrection();
         getGLBackend()->setCameraCorrection(correction, _prevRenderView);
         _prevRenderView = correction * _currentFrame->view;
@@ -698,18 +720,18 @@ void OpenGLDisplayPlugin::present() {
         // Write all layers to a local framebuffer
         {
             PROFILE_RANGE_EX(render, "composite", 0xff00ffff, frameId)
-            compositeLayers(_currentFrame->framebuffer);
+            compositeLayers();
         }
 
         // Take the composite framebuffer and send it to the output device
         {
             PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId)
-            internalPresent(_currentFrame->framebuffer);
+            internalPresent();
         }
 
         gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory());
     } else if (alwaysPresent()) {
-        internalPresent(nullptr);
+        internalPresent();
     }
     _movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent));
 }
@@ -766,12 +788,7 @@ bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) {
 }
 
 QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
-    if (!_currentFrame || !_currentFrame->framebuffer) {
-        return QImage();
-    }
-
-    auto compositeFramebuffer = _currentFrame->framebuffer;
-    auto size = compositeFramebuffer->getSize();
+    auto size = _compositeFramebuffer->getSize();
     if (isHmd()) {
         size.x /= 2;
     }
@@ -789,7 +806,7 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
     auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
     QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32);
     withOtherThreadContext([&] {
-        glBackend->downloadFramebuffer(compositeFramebuffer, ivec4(corner, bestSize), screenshot);
+        glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot);
     });
     return screenshot.mirrored(false, true);
 }
@@ -841,7 +858,7 @@ bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
 }
 
 ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
-    auto vpSize = glm::uvec2(getRecommendedRenderSize());
+    uvec2 vpSize = _compositeFramebuffer->getSize();
     vpSize.x /= 2;
     uvec2 vpPos;
     if (eye == Eye::Right) {
@@ -874,6 +891,14 @@ void OpenGLDisplayPlugin::render(std::function<void(gpu::Batch& batch)> f) {
 OpenGLDisplayPlugin::~OpenGLDisplayPlugin() {
 }
 
+void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
+    auto renderSize = glm::uvec2(getRecommendedRenderSize());
+    if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
+        _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
+       // _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_SRGBA_32, renderSize.x, renderSize.y));
+    }
+}
+
 void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) {
 #if !defined(USE_GLES)
     auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
index 3c48e8fc48..49a38ecb4c 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
@@ -94,10 +94,14 @@ protected:
     // is not populated
     virtual bool alwaysPresent() const { return false; }
 
+    void updateCompositeFramebuffer();
+
     virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; }
-    virtual void compositeLayers(const gpu::FramebufferPointer&);
-    virtual void compositePointer(const gpu::FramebufferPointer&);
-    virtual void compositeExtra(const gpu::FramebufferPointer&) {};
+    virtual void compositeLayers();
+    virtual void compositeScene();
+    virtual std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> getHUDOperator();
+    virtual void compositePointer();
+    virtual void compositeExtra() {};
 
     // These functions must only be called on the presentation thread
     virtual void customizeContext();
@@ -112,10 +116,10 @@ protected:
     virtual void deactivateSession() {}
 
     // Plugin specific functionality to send the composed scene to the output window or device
-    virtual void internalPresent(const gpu::FramebufferPointer&);
+    virtual void internalPresent();
 
-    
-    void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo = nullptr, const gpu::FramebufferPointer& copyFbo = nullptr);
+    void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& fbo);
+    void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor);
     virtual void updateFrameData();
     virtual glm::mat4 getViewCorrection() { return glm::mat4(); }
 
@@ -138,8 +142,14 @@ protected:
     gpu::FramePointer _currentFrame;
     gpu::Frame* _lastFrame { nullptr };
     mat4 _prevRenderView;
+    gpu::FramebufferPointer _compositeFramebuffer;
+    gpu::PipelinePointer _hudPipeline;
+    gpu::PipelinePointer _mirrorHUDPipeline;
+    gpu::ShaderPointer _mirrorHUDPS;
+    gpu::PipelinePointer _simplePipeline;
+    gpu::PipelinePointer _presentPipeline;
     gpu::PipelinePointer _cursorPipeline;
-    gpu::TexturePointer _displayTexture;
+    gpu::TexturePointer _displayTexture{};
     float _compositeHUDAlpha { 1.0f };
 
     struct CursorData {
@@ -175,9 +185,5 @@ protected:
     // be serialized through this mutex
     mutable Mutex _presentMutex;
     float _hudAlpha{ 1.0f };
-
-private:
-    gpu::PipelinePointer _presentPipeline;
-
 };
 
diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h
index 95592cc490..f2b1f36419 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h
@@ -24,7 +24,7 @@ public:
 
 protected:
     void updatePresentPose() override;
-    void hmdPresent(const gpu::FramebufferPointer&) override {}
+    void hmdPresent() override {}
     bool isHmdMounted() const override { return true; }
     bool internalActivate() override;
 private:
diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
index a515232b3f..321bcc3fd2 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
@@ -114,23 +114,20 @@ void HmdDisplayPlugin::internalDeactivate() {
 
 void HmdDisplayPlugin::customizeContext() {
     Parent::customizeContext();
-    _hudOperator = _hudRenderer.build();
+    _hudRenderer.build();
 }
 
 void HmdDisplayPlugin::uncustomizeContext() {
     // This stops the weirdness where if the preview was disabled, on switching back to 2D,
     // the vsync was stuck in the disabled state.  No idea why that happens though.
     _disablePreview = false;
-    if (_currentFrame && _currentFrame->framebuffer) {
-        render([&](gpu::Batch& batch) {
-            batch.enableStereo(false);
-            batch.resetViewTransform();
-            batch.setFramebuffer(_currentFrame->framebuffer);
-            batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
-        });
-
-    }
-    _hudRenderer = {};
+    render([&](gpu::Batch& batch) {
+        batch.enableStereo(false);
+        batch.resetViewTransform();
+        batch.setFramebuffer(_compositeFramebuffer);
+        batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
+    });
+    _hudRenderer = HUDRenderer();
     _previewTexture.reset();
     Parent::uncustomizeContext();
 }
@@ -177,11 +174,11 @@ float HmdDisplayPlugin::getLeftCenterPixel() const {
     return leftCenterPixel;
 }
 
-void HmdDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
+void HmdDisplayPlugin::internalPresent() {
     PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
 
     // Composite together the scene, hud and mouse cursor
-    hmdPresent(compositeFramebuffer);
+    hmdPresent();
 
     if (_displayTexture) {
         // Note: _displayTexture must currently be the same size as the display.
@@ -263,7 +260,7 @@ void HmdDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeF
 
                 viewport.z *= 2;
             }
-            renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, scissor, nullptr, fbo);
+            renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor, fbo);
         });
         swapBuffers();
 
@@ -348,7 +345,7 @@ glm::mat4 HmdDisplayPlugin::getViewCorrection() {
     }
 }
 
-DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
+void HmdDisplayPlugin::HUDRenderer::build() {
     vertices = std::make_shared<gpu::Buffer>();
     indices = std::make_shared<gpu::Buffer>();
 
@@ -383,7 +380,7 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
     indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
 
     // Compute indices order
-    std::vector<GLushort> indexData;
+    std::vector<GLushort> indices;
     for (int i = 0; i < stacks - 1; i++) {
         for (int j = 0; j < slices - 1; j++) {
             GLushort bottomLeftIndex = i * slices + j;
@@ -391,21 +388,24 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
             GLushort topLeftIndex = bottomLeftIndex + slices;
             GLushort topRightIndex = topLeftIndex + 1;
             // FIXME make a z-order curve for better vertex cache locality
-            indexData.push_back(topLeftIndex);
-            indexData.push_back(bottomLeftIndex);
-            indexData.push_back(topRightIndex);
+            indices.push_back(topLeftIndex);
+            indices.push_back(bottomLeftIndex);
+            indices.push_back(topRightIndex);
 
-            indexData.push_back(topRightIndex);
-            indexData.push_back(bottomLeftIndex);
-            indexData.push_back(bottomRightIndex);
+            indices.push_back(topRightIndex);
+            indices.push_back(bottomLeftIndex);
+            indices.push_back(bottomRightIndex);
         }
     }
-    indices->append(indexData);
+    this->indices->append(indices);
     format = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
     format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
     format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
     uniformsBuffer = std::make_shared<gpu::Buffer>(sizeof(Uniforms), nullptr);
+    updatePipeline();
+}
 
+void HmdDisplayPlugin::HUDRenderer::updatePipeline() {
     if (!pipeline) {
         auto program = gpu::Shader::createProgram(shader::render_utils::program::hmd_ui);
         gpu::StatePointer state = gpu::StatePointer(new gpu::State());
@@ -416,6 +416,10 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
 
         pipeline = gpu::Pipeline::create(program, state);
     }
+}
+
+std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> HmdDisplayPlugin::HUDRenderer::render(HmdDisplayPlugin& plugin) {
+    updatePipeline();
 
     auto hudPipeline = pipeline;
     auto hudFormat = format;
@@ -424,9 +428,9 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
     auto hudUniformBuffer = uniformsBuffer;
     auto hudUniforms = uniforms;
     auto hudIndexCount = indexCount;
-    return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer&, const bool mirror) {
-        if (pipeline && hudTexture) {
-            batch.setPipeline(pipeline);
+    return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) {
+        if (hudPipeline && hudTexture) {
+            batch.setPipeline(hudPipeline);
 
             batch.setInputFormat(hudFormat);
             gpu::BufferView posView(hudVertices, VERTEX_OFFSET, hudVertices->getSize(), VERTEX_STRIDE, hudFormat->getAttributes().at(gpu::Stream::POSITION)._element);
@@ -450,7 +454,7 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
     };
 }
 
-void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) {
+void HmdDisplayPlugin::compositePointer() {
     auto& cursorManager = Cursor::Manager::instance();
     const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
     auto compositorHelper = DependencyManager::get<CompositorHelper>();
@@ -459,7 +463,7 @@ void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& composite
     render([&](gpu::Batch& batch) {
         // FIXME use standard gpu stereo rendering for this.
         batch.enableStereo(false);
-        batch.setFramebuffer(compositeFramebuffer);
+        batch.setFramebuffer(_compositeFramebuffer);
         batch.setPipeline(_cursorPipeline);
         batch.setResourceTexture(0, cursorData.texture);
         batch.resetViewTransform();
@@ -474,6 +478,10 @@ void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& composite
     });
 }
 
+std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> HmdDisplayPlugin::getHUDOperator() {
+    return _hudRenderer.render(*this);
+}
+
 HmdDisplayPlugin::~HmdDisplayPlugin() {
 }
 
diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h
index 6755c5b7e0..d8c0ce8e1d 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h
@@ -53,15 +53,16 @@ signals:
     void hmdVisibleChanged(bool visible);
 
 protected:
-    virtual void hmdPresent(const gpu::FramebufferPointer&) = 0;
+    virtual void hmdPresent() = 0;
     virtual bool isHmdMounted() const = 0;
     virtual void postPreview() {};
     virtual void updatePresentPose();
 
     bool internalActivate() override;
     void internalDeactivate() override;
-    void compositePointer(const gpu::FramebufferPointer&) override;
-    void internalPresent(const gpu::FramebufferPointer&) override;
+    std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> getHUDOperator() override;
+    void compositePointer() override;
+    void internalPresent() override;
     void customizeContext() override;
     void uncustomizeContext() override;
     void updateFrameData() override;
@@ -119,6 +120,8 @@ private:
         static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) };
         static const int VERTEX_STRIDE { sizeof(Vertex) };
 
-        HUDOperator build();
+        void build();
+        void updatePipeline();
+        std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> render(HmdDisplayPlugin& plugin);
     } _hudRenderer;
 };
diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp
index 69aa7fc344..0ae0f9b1b6 100644
--- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp
@@ -37,13 +37,13 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
     return result;
 }
 
-void InterleavedStereoDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
+void InterleavedStereoDisplayPlugin::internalPresent() {
     render([&](gpu::Batch& batch) {
         batch.enableStereo(false);
         batch.resetViewTransform();
         batch.setFramebuffer(gpu::FramebufferPointer());
         batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
-        batch.setResourceTexture(0, compositeFramebuffer->getRenderBuffer(0));
+        batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
         batch.setPipeline(_interleavedPresentPipeline);
         batch.draw(gpu::TRIANGLE_STRIP, 4);
     });
diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h
index 52dfa8f402..debd340f24 100644
--- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h
@@ -21,7 +21,7 @@ protected:
     // initialize OpenGL context settings needed by the plugin
     void customizeContext() override;
     void uncustomizeContext() override;
-    void internalPresent(const gpu::FramebufferPointer&) override;
+    void internalPresent() override;
 
 private:
     static const QString NAME;
diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp
index 12a9b12adc..9809d02866 100644
--- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp
+++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp
@@ -245,7 +245,7 @@ void OculusMobileDisplayPlugin::updatePresentPose() {
     });
 }
 
-void OculusMobileDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compsiteFramebuffer) {
+void OculusMobileDisplayPlugin::internalPresent() {
     VrHandler::pollTask();
 
     if (!vrActive()) {
@@ -253,12 +253,8 @@ void OculusMobileDisplayPlugin::internalPresent(const gpu::FramebufferPointer& c
         return;
     }
 
-    GLuint sourceTexture = 0;
-    glm::uvec2 sourceSize;
-    if (compsiteFramebuffer) {
-        sourceTexture = getGLBackend()->getTextureID(compsiteFramebuffer->getRenderBuffer(0));
-        sourceSize = { compsiteFramebuffer->getWidth(), compsiteFramebuffer->getHeight() };
-    }
+    auto sourceTexture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
+    glm::uvec2 sourceSize{ _compositeFramebuffer->getWidth(), _compositeFramebuffer->getHeight() };
     VrHandler::presentFrame(sourceTexture, sourceSize, presentTracking);
     _presentRate.increment();
 }
diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h
index b5f7aa57b0..a98989655e 100644
--- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h
+++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h
@@ -54,8 +54,8 @@ protected:
     void uncustomizeContext() override;
 
     void updatePresentPose() override;
-    void internalPresent(const gpu::FramebufferPointer&) override;
-    void hmdPresent(const gpu::FramebufferPointer&) override { throw std::runtime_error("Unused"); }
+    void internalPresent() override;
+    void hmdPresent() override { throw std::runtime_error("Unused"); }
     bool isHmdMounted() const override;
     bool alwaysPresent() const override { return true; }
 
diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp
index 71db87557c..47503e8f85 100644
--- a/libraries/plugins/src/plugins/DisplayPlugin.cpp
+++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp
@@ -2,12 +2,6 @@
 
 #include <NumericalConstants.h>
 
-
-const DisplayPlugin::HUDOperator DisplayPlugin::DEFAULT_HUD_OPERATOR{ std::function<void(gpu::Batch&, const gpu::TexturePointer&, const gpu::FramebufferPointer&, bool mirror)>() };
-
-DisplayPlugin::DisplayPlugin() : _hudOperator{ DEFAULT_HUD_OPERATOR } {
-}
-
 int64_t DisplayPlugin::getPaintDelayUsecs() const {
     std::lock_guard<std::mutex> lock(_paintDelayMutex);
     return _paintDelayTimer.isValid() ? _paintDelayTimer.nsecsElapsed() / NSECS_PER_USEC : 0;
@@ -41,8 +35,8 @@ void DisplayPlugin::waitForPresent() {
     }
 }
 
-std::function<void(gpu::Batch&, const gpu::TexturePointer&, const gpu::FramebufferPointer& compositeFramebuffer, bool mirror)> DisplayPlugin::getHUDOperator() {
-    HUDOperator hudOperator;
+std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> DisplayPlugin::getHUDOperator() {
+    std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> hudOperator;
     {
         QMutexLocker locker(&_presentMutex);
         hudOperator = _hudOperator;
@@ -54,5 +48,3 @@ glm::mat4 HmdDisplay::getEyeToHeadTransform(Eye eye) const {
     static const glm::mat4 xform;
     return xform;
 }
-
-
diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h
index 9194fde3ac..aa52e57c3f 100644
--- a/libraries/plugins/src/plugins/DisplayPlugin.h
+++ b/libraries/plugins/src/plugins/DisplayPlugin.h
@@ -121,8 +121,6 @@ class DisplayPlugin : public Plugin, public HmdDisplay {
     Q_OBJECT
     using Parent = Plugin;
 public:
-    DisplayPlugin();
-
     virtual int getRequiredThreadCount() const { return 0; }
     virtual bool isHmd() const { return false; }
     virtual int getHmdScreen() const { return -1; }
@@ -216,8 +214,7 @@ public:
     void waitForPresent();
     float getAveragePresentTime() { return _movingAveragePresent.average / (float)USECS_PER_MSEC; } // in msec
 
-    using HUDOperator = std::function<void(gpu::Batch&, const gpu::TexturePointer&, const gpu::FramebufferPointer&, bool mirror)>;
-    virtual HUDOperator getHUDOperator() final;
+    std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> getHUDOperator();
 
     static const QString& MENU_PATH();
 
@@ -234,8 +231,7 @@ protected:
 
     gpu::ContextPointer _gpuContext;
 
-    static const HUDOperator DEFAULT_HUD_OPERATOR;
-    HUDOperator _hudOperator;
+    std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> _hudOperator { std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)>() };
 
     MovingAverage<float, 10> _movingAveragePresent;
 
diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp
index e021465ff3..b1a62625b2 100644
--- a/libraries/render-utils/src/RenderCommonTask.cpp
+++ b/libraries/render-utils/src/RenderCommonTask.cpp
@@ -126,8 +126,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra
         if (inputs) {
             batch.setFramebuffer(inputs);
         }
-        if (renderContext->args->_hudOperator && renderContext->args->_blitFramebuffer) {
-            renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_blitFramebuffer, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE);
+        if (renderContext->args->_hudOperator) {
+            renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE);
         }
     });
 #endif
diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h
index 8b2fff68c6..b5c98e3428 100644
--- a/libraries/render/src/render/Args.h
+++ b/libraries/render/src/render/Args.h
@@ -131,7 +131,7 @@ namespace render {
         render::ScenePointer _scene;
         int8_t _cameraMode { -1 };
 
-        std::function<void(gpu::Batch&, const gpu::TexturePointer&, const gpu::FramebufferPointer&, bool mirror)> _hudOperator;
+        std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> _hudOperator;
         gpu::TexturePointer _hudTexture;
     };
 
diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.h b/plugins/oculus/src/OculusDebugDisplayPlugin.h
index 690a488b34..ec05cd92e2 100644
--- a/plugins/oculus/src/OculusDebugDisplayPlugin.h
+++ b/plugins/oculus/src/OculusDebugDisplayPlugin.h
@@ -16,7 +16,7 @@ public:
     bool isSupported() const override;
 
 protected:
-    void hmdPresent(const gpu::FramebufferPointer&) override {}
+    void hmdPresent() override {}
     bool isHmdMounted() const override { return true; }
 
 private:
diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp
index 48440ac80f..df01591639 100644
--- a/plugins/oculus/src/OculusDisplayPlugin.cpp
+++ b/plugins/oculus/src/OculusDisplayPlugin.cpp
@@ -108,16 +108,13 @@ void OculusDisplayPlugin::customizeContext() {
 }
 
 void OculusDisplayPlugin::uncustomizeContext() {
-
 #if 0
-    if (_currentFrame && _currentFrame->framebuffer) {
-        // Present a final black frame to the HMD
-        _currentFrame->framebuffer->Bound(FramebufferTarget::Draw, [] {
-            Context::ClearColor(0, 0, 0, 1);
-            Context::Clear().ColorBuffer();
-        });
-        hmdPresent();
-    }
+    // Present a final black frame to the HMD
+    _compositeFramebuffer->Bound(FramebufferTarget::Draw, [] {
+        Context::ClearColor(0, 0, 0, 1);
+        Context::Clear().ColorBuffer();
+    });
+    hmdPresent();
 #endif
 
     ovr_DestroyTextureSwapChain(_session, _textureSwapChain);
@@ -130,7 +127,7 @@ void OculusDisplayPlugin::uncustomizeContext() {
 static const uint64_t FRAME_BUDGET = (11 * USECS_PER_MSEC);
 static const uint64_t FRAME_OVER_BUDGET = (15 * USECS_PER_MSEC);
 
-void OculusDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
+void OculusDisplayPlugin::hmdPresent() {
     static uint64_t lastSubmitEnd = 0;
 
     if (!_customized) {
@@ -160,8 +157,15 @@ void OculusDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFra
         auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer);
         glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0);
         render([&](gpu::Batch& batch) {
-            auto viewport = ivec4(uvec2(), _outputFramebuffer->getSize());
-            renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, viewport, _outputFramebuffer);
+            batch.enableStereo(false);
+            batch.setFramebuffer(_outputFramebuffer);
+            batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize()));
+            batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize()));
+            batch.resetViewTransform();
+            batch.setProjectionTransform(mat4());
+            batch.setPipeline(_presentPipeline);
+            batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
+            batch.draw(gpu::TRIANGLE_STRIP, 4);
         });
         glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0);
     }
diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h
index a0126d2e58..9209fd373e 100644
--- a/plugins/oculus/src/OculusDisplayPlugin.h
+++ b/plugins/oculus/src/OculusDisplayPlugin.h
@@ -28,7 +28,7 @@ protected:
     QThread::Priority getPresentPriority() override { return QThread::TimeCriticalPriority; }
 
     bool internalActivate() override;
-    void hmdPresent(const gpu::FramebufferPointer&) override;
+    void hmdPresent() override;
     bool isHmdMounted() const override;
     void customizeContext() override;
     void uncustomizeContext() override;
diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp
index a928887866..e6b555443f 100644
--- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp
+++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp
@@ -237,7 +237,7 @@ void OculusLegacyDisplayPlugin::uncustomizeContext() {
     Parent::uncustomizeContext();
 }
 
-void OculusLegacyDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
+void OculusLegacyDisplayPlugin::hmdPresent() {
     if (!_hswDismissed) {
         ovrHSWDisplayState hswState;
         ovrHmd_GetHSWDisplayState(_hmd, &hswState);
@@ -252,7 +252,7 @@ void OculusLegacyDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compos
     memset(eyePoses, 0, sizeof(ovrPosef) * 2);
     eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation;
     
-    GLint texture = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0));
+    GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
     auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
     glFlush();
     if (_hmdWindow->makeCurrent()) {
diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h
index 241d626f0c..36bdd1c792 100644
--- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h
+++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h
@@ -39,7 +39,7 @@ protected:
 
     void customizeContext() override;
     void uncustomizeContext() override;
-    void hmdPresent(const gpu::FramebufferPointer&) override;
+    void hmdPresent() override;
     bool isHmdMounted() const override { return true; }
 
 private:
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
index 3d22268472..11d941dcd0 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
@@ -511,13 +511,13 @@ void OpenVrDisplayPlugin::customizeContext() {
     Parent::customizeContext();
 
     if (_threadedSubmit) {
-//        _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0);
+        _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0);
         for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) {
-//            if (0 != i) {
+            if (0 != i) {
                 _compositeInfos[i].texture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x,
                                                                               _renderTargetSize.y, gpu::Texture::SINGLE_MIP,
                                                                               gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT));
-//            }
+            }
             _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture);
         }
         _submitThread->_canvas = _submitCanvas;
@@ -613,17 +613,17 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
     return Parent::beginFrameRender(frameIndex);
 }
 
-void OpenVrDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) {
+void OpenVrDisplayPlugin::compositeLayers() {
     if (_threadedSubmit) {
         ++_renderingIndex;
         _renderingIndex %= COMPOSITING_BUFFER_SIZE;
 
         auto& newComposite = _compositeInfos[_renderingIndex];
         newComposite.pose = _currentPresentFrameInfo.presentPose;
-        compositeFramebuffer->setRenderBuffer(0, newComposite.texture);
+        _compositeFramebuffer->setRenderBuffer(0, newComposite.texture);
     }
 
-    Parent::compositeLayers(compositeFramebuffer);
+    Parent::compositeLayers();
 
     if (_threadedSubmit) {
         auto& newComposite = _compositeInfos[_renderingIndex];
@@ -645,13 +645,13 @@ void OpenVrDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& composi
     }
 }
 
-void OpenVrDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
+void OpenVrDisplayPlugin::hmdPresent() {
     PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex)
 
     if (_threadedSubmit) {
         _submitThread->waitForPresent();
     } else {
-        GLuint glTexId = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0));
+        GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
         vr::Texture_t vrTexture{ (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto };
         vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT);
         vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT);
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h
index 923a0f7a8f..265f328920 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.h
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.h
@@ -72,8 +72,8 @@ protected:
     void internalDeactivate() override;
     void updatePresentPose() override;
 
-    void compositeLayers(const gpu::FramebufferPointer&) override;
-    void hmdPresent(const gpu::FramebufferPointer&) override;
+    void compositeLayers() override;
+    void hmdPresent() override;
     bool isHmdMounted() const override;
     void postPreview() override;