diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp
index 770cd5aa3c5a71f5f6ee4022507d6602315cd7e3..c46fa8dcf3c73b44807f0e2b15e1605bddc58a47 100644
--- a/indra/llmeshoptimizer/llmeshoptimizer.cpp
+++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp
@@ -26,18 +26,24 @@
 
 #include "llmeshoptimizer.h"
 
-#include "linden_common.h"
-
 #include "meshoptimizer.h"
-#include "llmath.h"
 
 
 LLMeshOptimizer::LLMeshOptimizer()
 {
-
+    // Todo: Looks like for memory management, we can add allocator and deallocator callbacks
+    // Should be one time
+    // meshopt_setAllocator(allocate, deallocate);
 }
 
 LLMeshOptimizer::~LLMeshOptimizer()
 {
 
 }
+
+//static
+U32 LLMeshOptimizer::simplifyModel()
+{
+    LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL;
+    return 0;
+}
diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h
index edce4edf0560373b140d81d3793d694e94485d5b..d53ec2e24c1023aa30d0962b81577a2438c3817d 100644
--- a/indra/llmeshoptimizer/llmeshoptimizer.h
+++ b/indra/llmeshoptimizer/llmeshoptimizer.h
@@ -26,11 +26,16 @@
 #ifndef LLMESHOPTIMIZER_H
 #define LLMESHOPTIMIZER_H
 
+#include "linden_common.h"
+
 class LLMeshOptimizer
 {
 public:
     LLMeshOptimizer();
     ~LLMeshOptimizer();
+
+    // returns state
+    static U32 simplifyModel();
 private:
 };
 
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index d9edd4dc30b16b6a7f3f03431c70e92c7b267edf..d8f3021fb2677730c7aad483b529c450eea31cf6 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -722,7 +722,20 @@ void LLFloaterModelPreview::onAutoFillCommit(LLUICtrl* ctrl, void* userdata)
 
 void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
 {
-	mModelPreview->onLODParamCommit(lod, enforce_tri_limit);
+    LLComboBox* lod_source_combo = getChild<LLComboBox>("lod_source_" + lod_name[lod]);
+    S32 mode = lod_source_combo->getCurrentIndex();
+    switch (mode)
+    {
+    case LLModelPreview::GENERATE:
+        mModelPreview->onLODGenerateParamCommit(lod, enforce_tri_limit);
+        break;
+    case LLModelPreview::MESH_OPTIMIZER:
+        mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit);
+        break;
+    default:
+        LL_ERRS() << "Only supposed to be called to generate models" << LL_ENDL;
+        break;
+    }
 
 	//refresh LoDs that reference this one
 	for (S32 i = lod - 1; i >= 0; --i)
@@ -1721,7 +1734,9 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)
 	refresh();
 
 	LLComboBox* lod_source_combo = getChild<LLComboBox>("lod_source_" + lod_name[lod]);
-	if (lod_source_combo->getCurrentIndex() == LLModelPreview::GENERATE)
+    S32 index = lod_source_combo->getCurrentIndex();
+	if (index == LLModelPreview::GENERATE
+        || index == LLModelPreview::MESH_OPTIMIZER)
 	{ //rebuild LoD to update triangle counts
 		onLODParamCommit(lod, true);
 	}
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index a9e80ab5da107dcd7ea1ae10c3edaf030561c50a..a6eef74cdc442cbe8fa3ec9cd5bb8e3836c3a95e 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -41,6 +41,7 @@
 #include "lliconctrl.h"
 #include "llmatrix4a.h"
 #include "llmeshrepository.h"
+#include "llmeshoptimizer.h"
 #include "llrender.h"
 #include "llsdutil_math.h"
 #include "llskinningutil.h"
@@ -1339,7 +1340,7 @@ void LLModelPreview::restoreNormals()
     updateStatusMessages();
 }
 
-void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit)
+void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit)
 {
     // Allow LoD from -1 to LLModel::LOD_PHYSICS
     if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1)
@@ -1424,7 +1425,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
         mRequestedLoDMode[which_lod] = lod_mode;
     }
 
-    if (lod_mode == 0)
+    if (lod_mode == LIMIT_TRIANGLES)
     {
         lod_mode = GLOD_TRIANGLE_BUDGET;
 
@@ -2957,7 +2958,7 @@ BOOL LLModelPreview::render()
     {
         genBuffers(-1, skin_weight);
         //genBuffers(3);
-        //genLODs();
+        //genGlodLODs();
     }
 
     if (!mModel[mPreviewLOD].empty())
@@ -3544,7 +3545,7 @@ bool LLModelPreview::lodQueryCallback()
         {
             S32 lod = preview->mLodsQuery.back();
             preview->mLodsQuery.pop_back();
-            preview->genLODs(lod);
+            preview->genGlodLODs(lod);
 
             if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH))
             {
@@ -3559,12 +3560,202 @@ bool LLModelPreview::lodQueryCallback()
     return true;
 }
 
-void LLModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
+void LLModelPreview::onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit)
 {
     if (!mLODFrozen)
     {
-        genLODs(lod, 3, enforce_tri_limit);
+        genGlodLODs(lod, 3, enforce_tri_limit);
         refresh();
     }
 }
 
+void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit)
+{
+    if (mLODFrozen)
+    {
+        return;
+    }
+
+    // Allow LoD from -1 to LLModel::LOD_PHYSICS
+    if (lod < -1 || lod > LLModel::NUM_LODS - 1)
+    {
+        std::ostringstream out;
+        out << "Invalid level of detail: " << lod;
+        LL_WARNS() << out.str() << LL_ENDL;
+        LLFloaterModelPreview::addStringToLog(out, false);
+        assert(lod >= -1 && lod < LLModel::NUM_LODS);
+        return;
+    }
+
+    if (mBaseModel.empty())
+    {
+        return;
+    }
+
+
+    U32 triangle_count = 0;
+    U32 instanced_triangle_count = 0;
+
+    //get the triangle count for the whole scene
+    for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter)
+    {
+        for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance)
+        {
+            LLModel* mdl = instance->mModel;
+            if (mdl)
+            {
+                instanced_triangle_count += mdl->getNumTriangles();
+            }
+        }
+    }
+
+    //get the triangle count for the non-instanced set of models
+    for (U32 i = 0; i < mBaseModel.size(); ++i)
+    {
+        triangle_count += mBaseModel[i]->getNumTriangles();
+    }
+
+    //get ratio of uninstanced triangles to instanced triangles
+    F32 triangle_ratio = (F32)triangle_count / (F32)instanced_triangle_count;
+    U32 base_triangle_count = triangle_count;
+    U32 lod_mode = 0;
+    F32 lod_error_threshold = 0;
+
+
+    // Urgh...
+    // TODO: add interface to mFMP to get error treshold or let mFMP write one into LLModelPreview
+    // We should not be accesing views from other class!
+    LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[lod]);
+    if (iface)
+    {
+        lod_mode = iface->getFirstSelectedIndex();
+    }
+
+    lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[lod]).asReal();
+
+    if (lod != -1)
+    {
+        mRequestedLoDMode[lod] = lod_mode;
+    }
+
+    S32 limit = -1;
+    if (lod_mode == LIMIT_TRIANGLES)
+    {
+        limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[lod]).asInteger();
+        //convert from "scene wide" to "non-instanced" triangle limit
+        limit = (S32)((F32)limit*triangle_ratio);
+    }
+
+    // Glod regenerates vertex buffer at this stage
+
+    // Build models
+
+    S32 start = LLModel::LOD_HIGH;
+    S32 end = 0;
+    S32 decimation = 3;
+
+    if (lod != -1)
+    {
+        start  = lod;
+        end = lod;
+    }
+
+    mMaxTriangleLimit = base_triangle_count;
+
+    for (S32 lod = start; lod >= end; --lod)
+    {
+        if (lod == -1)
+        {
+            if (lod < start)
+            {
+                triangle_count /= decimation;
+            }
+        }
+        else
+        {
+            if (enforce_tri_limit)
+            {
+                triangle_count = limit;
+            }
+            else
+            {
+                for (S32 j = LLModel::LOD_HIGH; j > lod; --j)
+                {
+                    triangle_count /= decimation;
+                }
+            }
+        }
+
+        mModel[lod].clear();
+        mModel[lod].resize(mBaseModel.size());
+        mVertexBuffer[lod].clear();
+
+        mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio);
+        mRequestedErrorThreshold[lod] = lod_error_threshold;
+
+        for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx)
+        {
+            LLModel* base = mBaseModel[mdl_idx];
+            
+            LLVolumeParams volume_params;
+            volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+            mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f);
+
+            std::string name = base->mLabel + getLodSuffix(lod);
+
+            mModel[lod][mdl_idx]->mLabel = name;
+            mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID;
+            //mModel[lod][mdl_idx]->setNumVolumeFaces(   );
+
+            LLModel* target_model = mModel[lod][mdl_idx];
+
+
+            //
+            LLMeshOptimizer::simplifyModel(/*Some params*/);
+
+            //blind copy skin weights and just take closest skin weight to point on
+            //decimated mesh for now (auto-generating LODs with skin weights is still a bit
+            //of an open problem).
+            target_model->mPosition = base->mPosition;
+            target_model->mSkinWeights = base->mSkinWeights;
+            target_model->mSkinInfo = base->mSkinInfo;
+            //copy material list
+            target_model->mMaterialList = base->mMaterialList;
+
+            if (!validate_model(target_model))
+            {
+                LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL;
+            }
+        }
+
+        //rebuild scene based on mBaseScene
+        mScene[lod].clear();
+        mScene[lod] = mBaseScene;
+
+        for (U32 i = 0; i < mBaseModel.size(); ++i)
+        {
+            LLModel* mdl = mBaseModel[i];
+            LLModel* target = mModel[lod][i];
+            if (target)
+            {
+                for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter)
+                {
+                    for (U32 j = 0; j < iter->second.size(); ++j)
+                    {
+                        if (iter->second[j].mModel == mdl)
+                        {
+                            iter->second[j].mModel = target;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    mResourceCost = calcResourceCost();
+
+
+    LLMeshOptimizer::simplifyModel();
+    refresh();
+}
+
diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h
index 3664a27a72487d8c5faae31fd8ac7854fe9dd559..df66d79b7b4dcdb4642b73685ce4ae95434cd096 100644
--- a/indra/newview/llmodelpreview.h
+++ b/indra/newview/llmodelpreview.h
@@ -125,9 +125,16 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
     {
         LOD_FROM_FILE = 0,
         GENERATE,
+        MESH_OPTIMIZER,
         USE_LOD_ABOVE,
     } eLoDMode;
 
+    typedef enum
+    {
+        LIMIT_TRIANGLES = 0,
+        LIMIT_ERROR_TRESHOLD,
+    } eLoDLimit;
+
 public:
     // Todo: model preview shouldn't need floater dependency, it
     // should just expose data to floater, not control flaoter like it does
@@ -155,7 +162,7 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
     void loadModelCallback(S32 lod);
     bool lodsReady() { return !mGenLOD && mLodsQuery.empty(); }
     void queryLODs() { mGenLOD = true; };
-    void genLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false);
+    void genGlodLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false);
     void generateNormals();
     void restoreNormals();
     U32 calcResourceCost();
@@ -166,7 +173,8 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
     void updateStatusMessages();
     void updateLodControls(S32 lod);
     void clearGLODGroup();
-    void onLODParamCommit(S32 lod, bool enforce_tri_limit);
+    void onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit);
+    void onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit);
     void addEmptyFace(LLModel* pTarget);
 
     const bool getModelPivot(void) const { return mHasPivot; }
diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml
index 7f863756eb92147f52494c17999bca9f4fce907a..96bda42316b6b51989f2da5ecab7c11783731e5a 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -176,6 +176,10 @@
                  name="Generate"
                  label="Generate"
                  value="Generate" />
+                <item
+                 name="MeshOpt"
+                 label="MeshOpt"
+                 value="MeshOpt" />
             </combo_box>
             <line_editor
              follows="left|top"
@@ -305,6 +309,10 @@
                  name="Generate"
                  label="Generate"
                  value="Generate" />
+                <item
+                 name="MeshOpt"
+                 label="MeshOpt"
+                 value="MeshOpt" />
                 <item
                  name="Use LoD above"
                  label="Use LoD above"
@@ -438,6 +446,10 @@
                  name="Generate"
                  label="Generate"
                  value="Generate" />
+                <item
+                 name="MeshOpt"
+                 label="MeshOpt"
+                 value="MeshOpt" />
                 <item
                  name="Use LoD above"
                  label="Use LoD above"
@@ -571,6 +583,10 @@
                  name="Generate"
                  label="Generate"
                  value="Generate" />
+                <item
+                 name="MeshOpt"
+                 label="MeshOpt"
+                 value="MeshOpt" />
                 <item
                  name="Use LoD above"
                  label="Use LoD above"