diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp
index 01897ee5e9..9b80041bcf 100644
--- a/libraries/baking/src/FBXBaker.cpp
+++ b/libraries/baking/src/FBXBaker.cpp
@@ -142,6 +142,27 @@ void FBXBaker::rewriteAndBakeSceneModels(const QVector<hfm::Mesh>& meshes, const
                 } else if (object->name == "Texture" || object->name == "Video") {
                     // this is an embedded texture, we need to remove it from the FBX
                     object = rootChild.children.erase(object);
+                } else if (object->name == "Material") {
+                    for (FBXNode& materialChild : object->children) {
+                        if (materialChild.name == "Properties60" || materialChild.name == "Properties70") {
+                            // This is a properties node
+                            // Remove the material texture scale because that is now included in the material JSON
+                            // Texture nodes are removed, so their texture scale is effectively gone already
+                            static const QVariant MAYA_UV_SCALE = hifi::ByteArray("Maya|uv_scale");
+                            static const QVariant MAYA_UV_OFFSET = hifi::ByteArray("Maya|uv_offset");
+                            for (int i = 0; i < materialChild.children.size(); i++) {
+                                const auto& prop = materialChild.children[i];
+                                const auto& propertyName = prop.properties.at(0);
+                                if (propertyName == MAYA_UV_SCALE ||
+                                    propertyName == MAYA_UV_OFFSET) {
+                                    materialChild.children.removeAt(i);
+                                    --i;
+                                }
+                            }
+                        }
+                    }
+
+                    object++;
                 } else {
                     object++;
                 }
diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h
index d2d330167d..6d1b9d83d2 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h
+++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h
@@ -59,8 +59,8 @@ namespace scriptable {
      * @property {string} occlusionMap
      * @property {string} lightmapMap
      * @property {string} scatteringMap
-     * @property {string} texCoordTransform0
-     * @property {string} texCoordTransform1
+     * @property {Mat4|string} texCoordTransform0
+     * @property {Mat4|string} texCoordTransform1
      * @property {string} lightmapParams
      * @property {string} materialParams
      * @property {boolean} defaultFallthrough
@@ -93,6 +93,7 @@ namespace scriptable {
         QString occlusionMap;
         QString lightmapMap;
         QString scatteringMap;
+        std::array<glm::mat4, graphics::Material::NUM_TEXCOORD_TRANSFORMS> texCoordTransforms;
 
         bool defaultFallthrough;
         std::unordered_map<uint, bool> propertyFallthroughs; // not actually exposed to script
diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp
index 3bd4af601c..4182b52309 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp
+++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp
@@ -470,9 +470,13 @@ namespace scriptable {
         // These need to be implemented, but set the fallthrough for now
         if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) {
             obj.setProperty("texCoordTransform0", FALLTHROUGH);
+        } else {
+            obj.setProperty("texCoordTransform0", mat4toScriptValue(engine, material.texCoordTransforms[0]));
         }
         if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) {
             obj.setProperty("texCoordTransform1", FALLTHROUGH);
+        } else {
+            obj.setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1]));
         }
         if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) {
             obj.setProperty("lightmapParams", FALLTHROUGH);
diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
index caee4ceb2a..8825a26bfe 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
+++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
@@ -119,6 +119,10 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint
         if (map && map->getTextureSource()) {
             scatteringMap = map->getTextureSource()->getUrl().toString();
         }
+
+        for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) {
+            texCoordTransforms[i] = material->getTexCoordTransform(i);
+        }
     }
 }
 
diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h
index 80b247bed0..330feaa61c 100755
--- a/libraries/graphics/src/graphics/Material.h
+++ b/libraries/graphics/src/graphics/Material.h
@@ -324,6 +324,7 @@ public:
     void setModel(const std::string& model) { _model = model; }
 
     glm::mat4 getTexCoordTransform(uint i) const { return _texcoordTransforms[i]; }
+    void setTexCoordTransform(uint i, const glm::mat4& mat4) { _texcoordTransforms[i] = mat4; }
     glm::vec2 getLightmapParams() const { return _lightmapParams; }
     glm::vec2 getMaterialParams() const { return _materialParams; }
 
diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp
index 5a5f4ab54b..9d6fc06d01 100644
--- a/libraries/material-networking/src/material-networking/MaterialCache.cpp
+++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp
@@ -177,6 +177,9 @@ std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource
         material->setModel(modelString);
     }
 
+    std::array<bool, graphics::Material::NUM_TEXCOORD_TRANSFORMS> hasTexcoordTransform;
+    std::array<glm::mat4, graphics::Material::NUM_TEXCOORD_TRANSFORMS> texcoordTransforms;
+
     if (modelString == HIFI_PBR) {
         const QString FALLTHROUGH("fallthrough");
         for (auto& key : materialJSON.keys()) {
@@ -372,8 +375,12 @@ std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource
                     if (valueString == FALLTHROUGH) {
                         material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM0);
                     }
+                } else if (value.isObject()) {
+                    auto valueVariant = value.toVariant();
+                    glm::mat4 transform = mat4FromVariant(valueVariant);
+                    hasTexcoordTransform[0] = true;
+                    texcoordTransforms[0] = transform;
                 }
-                // TODO: implement texCoordTransform0
             } else if (key == "texCoordTransform1") {
                 auto value = materialJSON.value(key);
                 if (value.isString()) {
@@ -381,8 +388,12 @@ std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource
                     if (valueString == FALLTHROUGH) {
                         material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM1);
                     }
+                } else if (value.isObject()) {
+                    auto valueVariant = value.toVariant();
+                    glm::mat4 transform = mat4FromVariant(valueVariant);
+                    hasTexcoordTransform[1] = true;
+                    texcoordTransforms[1] = transform;
                 }
-                // TODO: implement texCoordTransform1
             } else if (key == "lightmapParams") {
                 auto value = materialJSON.value(key);
                 if (value.isString()) {
@@ -409,6 +420,14 @@ std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource
             }
         }
     }
+
+    // Do this after the texture maps are defined, so it overrides the default transforms
+    for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) {
+        if (hasTexcoordTransform[i]) {
+            material->setTexCoordTransform(i, texcoordTransforms[i]);
+        }
+    }
+
     return std::pair<std::string, std::shared_ptr<NetworkMaterial>>(name, material);
 }
 
diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp
index 597e537d8d..e453b7bff4 100644
--- a/libraries/shared/src/RegisteredMetaTypes.cpp
+++ b/libraries/shared/src/RegisteredMetaTypes.cpp
@@ -636,6 +636,79 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4) {
     mat4[3][3] = object.property("r3c3").toVariant().toFloat();
 }
 
+QVariant mat4ToVariant(const glm::mat4& mat4) {
+    if (mat4 != mat4) {
+        // NaN
+        return QVariant();
+    }
+    QVariantMap object;
+
+    object["r0c0"] = mat4[0][0];
+    object["r1c0"] = mat4[0][1];
+    object["r2c0"] = mat4[0][2];
+    object["r3c0"] = mat4[0][3];
+    object["r0c1"] = mat4[1][0];
+    object["r1c1"] = mat4[1][1];
+    object["r2c1"] = mat4[1][2];
+    object["r3c1"] = mat4[1][3];
+    object["r0c2"] = mat4[2][0];
+    object["r1c2"] = mat4[2][1];
+    object["r2c2"] = mat4[2][2];
+    object["r3c2"] = mat4[2][3];
+    object["r0c3"] = mat4[3][0];
+    object["r1c3"] = mat4[3][1];
+    object["r2c3"] = mat4[3][2];
+    object["r3c3"] = mat4[3][3];
+
+    return object;
+}
+
+glm::mat4 mat4FromVariant(const QVariant& object, bool& valid) {
+    glm::mat4 mat4;
+    valid = false;
+    if (!object.isValid() || object.isNull()) {
+        return mat4;
+    } else {
+        const static auto getElement = [](const QVariantMap& map, const char * key, float& value, bool& everyConversionValid) {
+            auto variantValue = map[key];
+            if (variantValue.canConvert<float>()) {
+                value = variantValue.toFloat();
+            } else {
+                everyConversionValid = false;
+            }
+        };
+
+        auto map = object.toMap();
+        bool everyConversionValid = true;
+
+        getElement(map, "r0c0", mat4[0][0], everyConversionValid);
+        getElement(map, "r1c0", mat4[0][1], everyConversionValid);
+        getElement(map, "r2c0", mat4[0][2], everyConversionValid);
+        getElement(map, "r3c0", mat4[0][3], everyConversionValid);
+        getElement(map, "r0c1", mat4[1][0], everyConversionValid);
+        getElement(map, "r1c1", mat4[1][1], everyConversionValid);
+        getElement(map, "r2c1", mat4[1][2], everyConversionValid);
+        getElement(map, "r3c1", mat4[1][3], everyConversionValid);
+        getElement(map, "r0c2", mat4[2][0], everyConversionValid);
+        getElement(map, "r1c2", mat4[2][1], everyConversionValid);
+        getElement(map, "r2c2", mat4[2][2], everyConversionValid);
+        getElement(map, "r3c2", mat4[2][3], everyConversionValid);
+        getElement(map, "r0c3", mat4[3][0], everyConversionValid);
+        getElement(map, "r1c3", mat4[3][1], everyConversionValid);
+        getElement(map, "r2c3", mat4[3][2], everyConversionValid);
+        getElement(map, "r3c3", mat4[3][3], everyConversionValid);
+
+        if (everyConversionValid) {
+            valid = true;
+        }
+    }
+}
+
+glm::mat4 mat4FromVariant(const QVariant& object) {
+    bool valid = false;
+    return mat4FromVariant(object, valid);
+}
+
 QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector<glm::vec3>& vector) {
     QScriptValue array = engine->newArray();
     for (int i = 0; i < vector.size(); i++) {
diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h
index ea2c5b8354..96c64f7384 100644
--- a/libraries/shared/src/RegisteredMetaTypes.h
+++ b/libraries/shared/src/RegisteredMetaTypes.h
@@ -67,6 +67,10 @@ void registerMetaTypes(QScriptEngine* engine);
 QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4);
 void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4);
 
+QVariant mat4ToVariant(const glm::mat4& mat4);
+glm::mat4 mat4FromVariant(const QVariant& object, bool& valid);
+glm::mat4 mat4FromVariant(const QVariant& object);
+
 /**jsdoc
 * A 2-dimensional vector.
 *