diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp
index a567537105..ccb5e1816f 100644
--- a/libraries/model-baker/src/model-baker/Baker.cpp
+++ b/libraries/model-baker/src/model-baker/Baker.cpp
@@ -14,6 +14,7 @@
 #include "BakerTypes.h"
 #include "ModelMath.h"
 #include "ReweightDeformersTask.h"
+#include "CollectShapeVerticesTask.h"
 #include "BuildGraphicsMeshTask.h"
 #include "CalculateMeshNormalsTask.h"
 #include "CalculateMeshTangentsTask.h"
@@ -105,7 +106,7 @@ namespace baker {
 
     class BuildModelTask {
     public:
-        using Input = VaryingSet6<hfm::Model::Pointer, std::vector<hfm::Mesh>, std::vector<hfm::Joint>, QMap<int, glm::quat>, QHash<QString, int>, FlowData>;
+        using Input = VaryingSet7<hfm::Model::Pointer, std::vector<hfm::Mesh>, std::vector<hfm::Joint>, QMap<int, glm::quat>, QHash<QString, int>, FlowData, std::vector<ShapeVertices>>;
         using Output = hfm::Model::Pointer;
         using JobModel = Job::ModelIO<BuildModelTask, Input, Output>;
 
@@ -116,6 +117,9 @@ namespace baker {
             hfmModelOut->jointRotationOffsets = input.get3();
             hfmModelOut->jointIndices = input.get4();
             hfmModelOut->flowData = input.get5();
+            hfmModelOut->shapeVertices = input.get6();
+            // These depend on the ShapeVertices
+            // TODO: Create a task for this rather than calculating it here
             hfmModelOut->computeKdops();
             output = hfmModelOut;
         }
@@ -156,6 +160,9 @@ namespace baker {
             // NOTE: Due to limitations in the current graphics::MeshPointer representation, the output list of ReweightedDeformers is per-mesh. An element is empty if there are no deformers for the mesh of the same index.
             const auto reweightDeformersInputs = ReweightDeformersTask::Input(meshesIn, shapesIn, dynamicTransformsIn, deformersIn).asVarying();
             const auto reweightedDeformers = model.addJob<ReweightDeformersTask>("ReweightDeformers", reweightDeformersInputs);
+            // Shape vertices are included/rejected based on skinning weight, and thus must use the reweighted deformers.
+            const auto collectShapeVerticesInputs = CollectShapeVerticesTask::Input(meshesIn, shapesIn, jointsIn, dynamicTransformsIn, reweightedDeformers).asVarying();
+            const auto shapeVerticesPerJoint = model.addJob<CollectShapeVerticesTask>("CollectShapeVertices", collectShapeVerticesInputs);
 
             // Build the graphics::MeshPointer for each hfm::Mesh
             const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh, shapesIn, dynamicTransformsIn, reweightedDeformers).asVarying();
@@ -191,7 +198,7 @@ namespace baker {
             const auto blendshapesPerMeshOut = model.addJob<BuildBlendshapesTask>("BuildBlendshapes", buildBlendshapesInputs);
             const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying();
             const auto meshesOut = model.addJob<BuildMeshesTask>("BuildMeshes", buildMeshesInputs);
-            const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices, flowData).asVarying();
+            const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices, flowData, shapeVerticesPerJoint).asVarying();
             const auto hfmModelOut = model.addJob<BuildModelTask>("BuildModel", buildModelInputs);
 
             output = Output(hfmModelOut, materialMapping, dracoMeshes, dracoErrors, materialList);
diff --git a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp
new file mode 100644
index 0000000000..755b61b7df
--- /dev/null
+++ b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp
@@ -0,0 +1,92 @@
+//
+//  CollectShapeVerticesTask.h
+//  model-baker/src/model-baker
+//
+//  Created by Sabrina Shanman on 2019/09/27.
+//  Copyright 2019 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "CollectShapeVerticesTask.h"
+
+#include <glm/gtx/transform.hpp>
+
+// Used to track and avoid duplicate shape vertices, as multiple shapes can have the same mesh and dynamicTransform
+class VertexSource {
+public:
+    uint32_t mesh;
+    uint32_t dynamicTransform;
+
+    bool operator==(const VertexSource& other) const {
+        return mesh == other.mesh &&
+            dynamicTransform == other.dynamicTransform;
+    }
+};
+
+void CollectShapeVerticesTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
+    const auto& meshes = input.get0();
+    const auto& shapes = input.get1();
+    const auto& joints = input.get2();
+    const auto& dynamicTransforms = input.get3();
+    const auto& reweightedDeformers = input.get4();
+    auto& shapeVerticesPerJoint = output;
+
+    shapeVerticesPerJoint.reserve(joints.size());
+    std::vector<std::vector<VertexSource>> vertexSourcesPerJoint;
+    vertexSourcesPerJoint.resize(joints.size());
+    for (size_t i = 0; i < shapes.size(); ++i) {
+        const auto& shape = shapes[i];
+        const uint32_t dynamicTransformKey = shape.dynamicTransform;
+        if (dynamicTransformKey == hfm::UNDEFINED_KEY) {
+            continue;
+        }
+
+        VertexSource vertexSource;
+        vertexSource.mesh = shape.mesh;
+        vertexSource.dynamicTransform = dynamicTransformKey;
+
+        const auto& dynamicTransform = dynamicTransforms[dynamicTransformKey];
+        for (size_t j = 0; j < dynamicTransform.clusters.size(); ++j) {
+            const auto& cluster = dynamicTransform.clusters[j];
+            const uint32_t jointIndex = cluster.jointIndex;
+
+            auto& vertexSources = vertexSourcesPerJoint[jointIndex];
+            if (std::find(vertexSources.cbegin(), vertexSources.cend(), vertexSource) == vertexSources.cend()) {
+                vertexSources.push_back(vertexSource);
+                auto& shapeVertices = shapeVerticesPerJoint[jointIndex];
+
+                const uint16_t deformerIndex = dynamicTransform.deformers[j];
+                const auto& mesh = meshes[shape.mesh];
+                const auto& vertices = mesh.vertices;
+                const auto& reweightedDeformer = reweightedDeformers[shape.mesh];
+                const glm::mat4 meshToJoint = cluster.inverseBindMatrix;
+
+                const uint16_t weightsPerVertex = reweightedDeformer.weightsPerVertex;
+                if (weightsPerVertex == 0) {
+                    for (int vertexIndex = 0; vertexIndex < (int)vertices.size(); ++vertexIndex) {
+                        const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertices[vertexIndex]);
+                        shapeVertices.push_back(extractTranslation(vertexTransform));
+                    }
+                } else {
+                    for (int vertexIndex = 0; vertexIndex < (int)vertices.size(); ++vertexIndex) {
+                        for (uint16_t weightIndex = 0; weightIndex < weightsPerVertex; ++weightIndex) {
+                            const size_t index = vertexIndex*4 + weightIndex;
+                            const uint16_t clusterIndex = reweightedDeformer.indices[index];
+                            const uint16_t clusterWeight = reweightedDeformer.weights[index];
+                            // Remember vertices associated with this joint with at least 1/4 weight
+                            const uint16_t EXPANSION_WEIGHT_THRESHOLD = std::numeric_limits<uint16_t>::max() / 4;
+                            if (clusterIndex != j || clusterWeight < EXPANSION_WEIGHT_THRESHOLD) {
+                                continue;
+                            }
+                        
+                            const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertices[vertexIndex]);
+                            shapeVertices.push_back(extractTranslation(vertexTransform));
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h
new file mode 100644
index 0000000000..3111dcadc1
--- /dev/null
+++ b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h
@@ -0,0 +1,30 @@
+//
+//  CollectShapeVerticesTask.h
+//  model-baker/src/model-baker
+//
+//  Created by Sabrina Shanman on 2019/09/27.
+//  Copyright 2019 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_CollectShapeVerticesTask_h
+#define hifi_CollectShapeVerticesTask_h
+
+#include <hfm/HFM.h>
+
+#include "Engine.h"
+#include "BakerTypes.h"
+
+class CollectShapeVerticesTask {
+public:
+    using Input = baker::VaryingSet5<std::vector<hfm::Mesh>, std::vector<hfm::Shape>, std::vector<hfm::Joint>, std::vector<hfm::DynamicTransform>, std::vector<baker::ReweightedDeformers>>;
+    using Output = std::vector<ShapeVertices>;
+    using JobModel = baker::Job::ModelIO<CollectShapeVerticesTask, Input, Output>;
+
+    void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
+};
+
+#endif // hifi_CollectShapeVerticesTask_h
+