From 5b066ebb7a629c4126b175020712f84cc5800233 Mon Sep 17 00:00:00 2001
From: Mnikolenko ProductEngine <mnikolenko@productengine.com>
Date: Wed, 19 Dec 2018 18:39:59 +0200
Subject: SL-10155 FIXED [MAC] Mesh upload tries to upload folder instead of
 opening it

---
 indra/newview/llfilepicker.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 0f22b6200f..b6fd70452e 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -101,6 +101,8 @@ LLFilePicker::LLFilePicker()
 	mOFN.lpfnHook = NULL;
 	mOFN.lpTemplateName = NULL;
 	mFilesW[0] = '\0';
+#elif LL_DARWIN
+	mPickOptions = 0;
 #endif
 
 }
-- 
cgit v1.2.3


From 3b932c27b7b1009ef404da5a7e0613d9cd3e7bf5 Mon Sep 17 00:00:00 2001
From: AndreyL ProductEngine <alihatskiy@productengine.com>
Date: Thu, 3 Jan 2019 00:35:38 +0200
Subject: SL-10288 Mesh uploader changes from Firestorm

---
 doc/contributions.txt                              |   2 +
 indra/newview/app_settings/settings.xml            | 199 +++++++-
 .../shaders/class1/objects/previewV.glsl           |   8 +-
 indra/newview/lldynamictexture.cpp                 |  30 +-
 indra/newview/llfloatermodelpreview.cpp            | 527 ++++++++++++++-------
 indra/newview/llfloatermodelpreview.h              |   2 +
 indra/newview/pipeline.cpp                         |   6 +-
 .../skins/default/xui/en/floater_model_preview.xml | 369 ++++++++++-----
 8 files changed, 844 insertions(+), 299 deletions(-)

diff --git a/doc/contributions.txt b/doc/contributions.txt
index a09b6aff43..d2b1b6f134 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -256,6 +256,8 @@ Benja Kepler
 	VWR-746
 Benjamin Bigdipper
 Beth Walcher
+Beq Janus
+	SL-10288
 Bezilon Kasei
 Biancaluce Robbiani
 	CT-225
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 3ad8b6cded..a58fa2d7a0 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -6628,8 +6628,189 @@
     <key>Value</key>
     <integer>600</integer>
   </map>
-  <key>MigrateCacheDirectory</key>
-    <map>
+  <key>MeshPreviewCanvasColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Canvas colour for the Mesh uploader</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>0.169</real>
+      <real>0.169</real>
+      <real>0.169</real>
+      <real>1.0</real>
+    </array>
+  </map>
+  <key>MeshPreviewEdgeColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Edge colour for the Mesh uploader preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>0.4</real>
+      <real>0.4</real>
+      <real>0.4</real>
+      <real>1.0</real>
+    </array>
+  </map>
+  <key>MeshPreviewBaseColor</key>
+  <map>
+    <key>Comment</key>
+    <string>base diffuse colour for the Mesh uploader</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+    </array>
+  </map>
+  <key>MeshPreviewBrightnessColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Brightness modifier</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color3</string>
+    <key>Value</key>
+    <array>
+      <real>0.9</real>
+      <real>0.9</real>
+      <real>0.9</real>
+    </array>
+  </map>
+  <key>MeshPreviewEdgeWidth</key>
+  <map>
+    <key>Comment</key>
+    <string>line thickness used when display edges is selected in mesh preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+      <real>1.0</real>
+  </map>
+  <key>MeshPreviewPhysicsEdgeColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Edge colour for the Mesh uploader physics preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>0.0</real>
+      <real>0.25</real>
+      <real>0.5</real>
+      <real>0.25</real>
+    </array>
+  </map>
+  <key>MeshPreviewPhysicsFillColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Fill colour for the Mesh uploader physics preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>0.0</real>
+      <real>0.5</real>
+      <real>1.0</real>
+      <real>0.5</real>
+    </array>
+  </map>
+  <key>MeshPreviewPhysicsEdgeWidth</key>
+  <map>
+    <key>Comment</key>
+    <string>line thickness used when display physics is selected in mesh preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>1.0</real>
+  </map>
+  <key>MeshPreviewDegenerateEdgeColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Edge colour for the Mesh uploader Degenerate preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>0.0</real>
+      <real>0.0</real>
+      <real>1.0</real>
+    </array>
+  </map>
+  <key>MeshPreviewDegenerateFillColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Fill colour for the Mesh uploader Degenerate preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>0.0</real>
+      <real>0.0</real>
+      <real>0.5</real>
+    </array>
+  </map>
+  <key>MeshPreviewDegenerateEdgeWidth</key>
+  <map>
+    <key>Comment</key>
+    <string>line thickness used when display Degenerate is selected in mesh preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>3.0</real>
+  </map>
+  <key>MeshPreviewDegeneratePointSize</key>
+  <map>
+    <key>Comment</key>
+    <string>Large point size used to highlight degenerate triangle vertices in Mesh preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>8.0</real>
+  </map>
+  <key>MeshPreviewZoomLimit</key>
+  <map>
+    <key>Comment</key>
+    <string>Maximum Zoom level in preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>10.0</real>
+  </map>
+  <map>
       <key>Comment</key>
       <string>Check for old version of disk cache to migrate to current location</string>
       <key>Persist</key>
@@ -7868,7 +8049,17 @@
       <key>Value</key>
 	  <integer>13</integer>
     </map>
-
+  <key>PreviewRenderSize</key>  
+  <map>
+    <key>Comment</key>
+    <string>Resolution of the image rendered for the mesh upload preview</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>S32</string>
+    <key>Value</key>
+    <integer>1024</integer>
+  </map>
   <key>PreviewAmbientColor</key>
   <map>
     <key>Comment</key>
@@ -7885,8 +8076,6 @@
       <real>1.0</real>
     </array>
   </map>
-
-
   <key>PreviewDiffuse0</key>
   <map>
     <key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
index 7f3f84398b..3424613e94 100644
--- a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
@@ -91,8 +91,10 @@ void main()
 
 	// Collect normal lights (need to be divided by two, as we later multiply by 2)
 	col.rgb += light_diffuse[1].rgb * calcDirectionalLight(norm, light_position[1].xyz);
-	col.rgb += light_diffuse[2].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[2], light_direction[2], light_attenuation[2].x, light_attenuation[2].z);
-	col.rgb += light_diffuse[3].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[3], light_direction[3], light_attenuation[3].x, light_attenuation[3].z);
-		
+//	col.rgb += light_diffuse[2].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[2], light_direction[2], light_attenuation[2].x, light_attenuation[2].z);
+	col.rgb += light_diffuse[2].rgb * calcDirectionalLight(norm, light_position[2].xyz);
+//	col.rgb += light_diffuse[3].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[3], light_direction[3], light_attenuation[3].x, light_attenuation[3].z);
+	col.rgb += light_diffuse[3].rgb * calcDirectionalLight(norm, light_position[3].xyz);
+	col /= 2.0;
 	vertex_color = col*color;
 }
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index fa9a0712fa..af6977d3cd 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -125,11 +125,17 @@ BOOL LLViewerDynamicTexture::render()
 //-----------------------------------------------------------------------------
 void LLViewerDynamicTexture::preRender(BOOL clear_depth)
 {
-	//only images up to 512x512 are supported
-	llassert(mFullHeight <= 512);
-	llassert(mFullWidth <= 512);
+	// <FS:Beq> changes to support higher resolution rendering in the preview
+	////only images up to 512x512 are supported
+	//llassert(mFullHeight <= 512);
+	//llassert(mFullWidth <= 512);
+	gPipeline.allocatePhysicsBuffer();
+	llassert(mFullWidth <= static_cast<S32>(gPipeline.mPhysicsDisplay.getWidth()));
+	llassert(mFullHeight <= static_cast<S32>(gPipeline.mPhysicsDisplay.getHeight()));
 
-	if (gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI)
+//	if (gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI)
+	if (gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsATI)
+// </FS:Beq>
 	{ //using offscreen render target, just use the bottom left corner
 		mOrigin.set(0, 0);
 	}
@@ -215,14 +221,15 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
 	{
 		return TRUE;
 	}
-
-	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI;
-
+	// <FS:Beq> changes to support higher resolution rendering in the preview
+	//	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI;
+	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsATI;
 	if (use_fbo)
 	{
-		gPipeline.mWaterDis.bindTarget();
+//		gPipeline.mWaterDis.bindTarget();
+		gPipeline.mPhysicsDisplay.bindTarget();
 	}
-
+	// </FS:Beq>
 	LLGLSLShader::bindNoShader();
 	LLVertexBuffer::unbind();
 	
@@ -258,7 +265,10 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
 
 	if (use_fbo)
 	{
-		gPipeline.mWaterDis.flush();
+		// <FS:Beq> changes to support higher resolution rendering in the preview
+		// gPipeline.mWaterDis.flush();
+		gPipeline.mPhysicsDisplay.flush();
+		// </FS:Beq>
 	}
 
 	return ret;
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 268c646719..78d97753d3 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -427,8 +427,11 @@ void LLFloaterModelPreview::initModelPreview()
 	{
 		delete mModelPreview;
 	}
-
-	mModelPreview = new LLModelPreview(512, 512, this );
+	// <FS:Beq> mesh uploader changes to allow higher resolution render
+	//	mModelPreview = new LLModelPreview(512, 512, this);
+	auto size = gSavedSettings.getS32("PreviewRenderSize");
+	mModelPreview = new LLModelPreview(size, size, this );
+	// </FS:Beq>
 	mModelPreview->setPreviewTarget(16.f);
 	mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5));
 	mModelPreview->setModelUpdatedCallback(boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this, _1));
@@ -438,8 +441,16 @@ void LLFloaterModelPreview::onViewOptionChecked(LLUICtrl* ctrl)
 {
 	if (mModelPreview)
 	{
-		mModelPreview->mViewOption[ctrl->getName()] = !mModelPreview->mViewOption[ctrl->getName()];
-		
+		// <FS:Beq> only show explode when phsyics is on
+		//		mModelPreview->mViewOption[ctrl->getName()] = !mModelPreview->mViewOption[ctrl->getName()];
+		auto name = ctrl->getName();
+		mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name];
+		if (name == "show_physics")
+		{
+			auto enabled = mModelPreview->mViewOption[name];
+			childSetEnabled("physics_explode", enabled);
+			childSetVisible("physics_explode", enabled);
+		}
 		mModelPreview->refresh();
 	}
 }
@@ -653,6 +664,43 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
 	}
 }
 
+// <FS:Beq> extracted method to simplify changes in layout
+void LLFloaterModelPreview::draw3dPreview()
+{
+	gGL.color3f(1.f, 1.f, 1.f);
+
+	gGL.getTexUnit(0)->bind(mModelPreview);
+
+
+	LLView* preview_panel = getChild<LLView>("preview_panel");
+
+	if (!preview_panel)
+	{
+		LL_WARNS() << "preview_panel not found in floater definition" << LL_ENDL;
+	}
+	LLRect rect = preview_panel->getRect();
+
+	if (rect != mPreviewRect)
+	{
+		mModelPreview->refresh();
+		mPreviewRect = preview_panel->getRect();
+	}
+
+	gGL.begin( LLRender::QUADS );
+	{
+		gGL.texCoord2f(0.f, 1.f);
+		gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop-1);
+		gGL.texCoord2f(0.f, 0.f);
+		gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom);
+		gGL.texCoord2f(1.f, 0.f);
+		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom);
+		gGL.texCoord2f(1.f, 1.f);
+		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mTop-1);
+	}
+	gGL.end();
+
+	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+}
 
 //-----------------------------------------------------------------------------
 // draw()
@@ -1218,6 +1266,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
 , mResetJoints( false )
 , mModelNoErrors( true )
 , mLastJointUpdate( false )
+, mHasDegenerate( false ) // <FS:Beq>
 {
 	mNeedsUpdate = TRUE;
 	mCameraDistance = 0.f;
@@ -2710,8 +2759,20 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
 
 void LLModelPreview::updateStatusMessages()
 {
+// <FS:Beq> bit mask values for physics errors. used to prevent overwrite of single line status
+// TODO: use this to provied multiline status
+	enum PhysicsError
+	{
+		NONE=0,
+		NOHAVOK=1,
+		DEGENERATE=2,
+		TOOMANYHULLS=4,
+		TOOMANYVERTSINHULL=8
+	};
+// </FS:Beq>
 	assert_main_thread();
 
+	U32 has_physics_error{ PhysicsError::NONE }; // <FS:Beq> physics error bitmap
 	//triangle/vertex/submesh count for each mesh asset for each lod
 	std::vector<S32> tris[LLModel::NUM_LODS];
 	std::vector<S32> verts[LLModel::NUM_LODS];
@@ -2800,44 +2861,72 @@ void LLModelPreview::updateStatusMessages()
 	{
 		mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH];
 	}
-
-	bool has_degenerate = false;
-
+	// <FS:Beq> make has_degenerate a member so that we can use it in the render method
+	// has_degenerate = false
+	mHasDegenerate = false;
 	{//check for degenerate triangles in physics mesh
 		U32 lod = LLModel::LOD_PHYSICS;
 		const LLVector4a scale(0.5f);
-		for (U32 i = 0; i < mModel[lod].size() && !has_degenerate; ++i)
+		for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i)// <FS:Beq> make has_degenerate a member 
 		{ //for each model in the lod
 			if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty())
 			{ //no decomp exists
 				S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces();
-				for (S32 j = 0; j < cur_submeshes && !has_degenerate; ++j)
+				for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j)// <FS:Beq> make has_degenerate a member 
 				{ //for each submesh (face), add triangles and vertices to current total
 					LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j);
-					for (S32 k = 0; (k < face.mNumIndices) && !has_degenerate; )
+					for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate; )// <FS:Beq> make has_degenerate a member 
 					{
-						U16 index_a = face.mIndices[k+0];
-						U16 index_b = face.mIndices[k+1];
-						U16 index_c = face.mIndices[k+2];
-
-						LLVector4a v1; v1.setMul(face.mPositions[index_a], scale);
-						LLVector4a v2; v2.setMul(face.mPositions[index_b], scale);
-						LLVector4a v3; v3.setMul(face.mPositions[index_c], scale);
-
-						if (ll_is_degenerate(v1,v2,v3))
+						U16 index_a = face.mIndices[k + 0];
+						U16 index_b = face.mIndices[k + 1];
+						U16 index_c = face.mIndices[k + 2];
+						// <FS:Beq> FIRE-23367/23387 - Allow forced empty triangle placeholders created by the LOD processing.
+						//	LLVector4a v1; v1.setMul(face.mPositions[index_a], scale);
+						//	LLVector4a v2; v2.setMul(face.mPositions[index_b], scale);
+						//	LLVector4a v3; v3.setMul(face.mPositions[index_c], scale);
+
+						//	if (ll_is_degenerate(v1, v2, v3))
+						//	{
+						//		mHasDegenerate = true;// <FS:Beq> make has_degenerate a member 
+						//	}
+						//	else
+						//	{
+						//		k += 3;
+						//	}
+						if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case
 						{
-							has_degenerate = true;
+							LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL;
 						}
 						else
 						{
-							k += 3;
+							LLVector4a v1; v1.setMul(face.mPositions[index_a], scale);
+							LLVector4a v2; v2.setMul(face.mPositions[index_b], scale);
+							LLVector4a v3; v3.setMul(face.mPositions[index_c], scale);
+							if (ll_is_degenerate(v1, v2, v3))
+							{
+								mHasDegenerate = true;// <FS:Beq> make has_degenerate a member 
+							}
 						}
+						k += 3;
 					}
 				}
 			}
 		}
 	}
 
+	// <FS:Beq> flag degenerates here rather than deferring to a MAV error later
+	mFMP->childSetVisible("physics_status_message_text", mHasDegenerate); //display or clear
+	auto degenerateIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
+	degenerateIcon->setVisible(mHasDegenerate);
+	if (mHasDegenerate)
+	{
+		has_physics_error |= PhysicsError::DEGENERATE;
+		mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_degenerate_triangles"));
+		LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error");
+		degenerateIcon->setImage(img);
+	}
+	// </FS:Beq>
+
 	mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH]));
 
 	std::string mesh_status_na = mFMP->getString("mesh_status_na");
@@ -2962,14 +3051,22 @@ void LLModelPreview::updateStatusMessages()
 			}
 		}
 	}
-	mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit);
-	LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
-	physStatusIcon->setVisible(physExceededVertexLimit);
+
 	if (physExceededVertexLimit)
 	{
-		mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded"));
-		LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning");
-		physStatusIcon->setImage(img);
+		has_physics_error |= PhysicsError::TOOMANYVERTSINHULL;
+	}
+
+	if (!(has_physics_error & PhysicsError::DEGENERATE)){ // only update this field (incluides clearing it) if it is not already in use.
+		mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit);
+		LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
+		physStatusIcon->setVisible(physExceededVertexLimit);
+		if (physExceededVertexLimit)
+		{
+			mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded"));
+			LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning");
+			physStatusIcon->setImage(img);
+		}
 	}
 
 	if (getLoadState() >= LLModelLoader::ERROR_PARSING)
@@ -2998,12 +3095,21 @@ void LLModelPreview::updateStatusMessages()
 			mModelNoErrors = false;
 		}
 	}
-
-	// Todo: investigate use of has_degenerate and include into mModelNoErrors upload blocking mechanics
-	// current use of has_degenerate won't block upload permanently - later checks will restore the button
-	if (!mModelNoErrors || has_degenerate)
+	// <FS:Beq> Improve the error checking the TO DO here is no longer applicable but not an FS comment so edited to stop it being picked up
+	//// To do investigate use of has_degenerate and include into mModelNoErrors upload blocking mechanics
+	//// current use of has_degenerate won't block upload permanently - later checks will restore the button
+	//if (!mModelNoErrors || mHasDegenerate)
+	//{
+	//	mFMP->childDisable("ok_btn");
+	if (!mModelNoErrors || mHasDegenerate)
 	{
 		mFMP->childDisable("ok_btn");
+		mFMP->childDisable("calculate_btn");
+	}
+	else
+	{
+		mFMP->childEnable("ok_btn");
+		mFMP->childEnable("calculate_btn");
 	}
 	
 	//add up physics triangles etc
@@ -3619,11 +3725,30 @@ BOOL LLModelPreview::render()
 	bool textures = mViewOption["show_textures"];
 	bool physics = mViewOption["show_physics"];
 
+	// <FS:Beq> Extra configurability, to be exposed later as controls?
+	static LLCachedControl<LLColor4> canvas_col(gSavedSettings, "MeshPreviewCanvasColor");
+	static LLCachedControl<LLColor4> edge_col(gSavedSettings, "MeshPreviewEdgeColor");
+	static LLCachedControl<LLColor4> base_col(gSavedSettings, "MeshPreviewBaseColor");
+	static LLCachedControl<LLColor3> brightness(gSavedSettings, "MeshPreviewBrightnessColor");
+	static LLCachedControl<F32> edge_width(gSavedSettings, "MeshPreviewEdgeWidth");
+	static LLCachedControl<LLColor4> phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor");
+	static LLCachedControl<LLColor4> phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor");
+	static LLCachedControl<F32> phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth");
+	static LLCachedControl<LLColor4> deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor");
+	static LLCachedControl<LLColor4> deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor");	
+	static LLCachedControl<F32> deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth");
+	static LLCachedControl<F32> deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize");
+	// </FS:Beq>
 	S32 width = getWidth();
 	S32 height = getHeight();
 
 	LLGLSUIDefault def;
 	LLGLDisable no_blend(GL_BLEND);
+// <FS:Beq> Clean up render of mesh preview
+//	LLGLEnable blend(GL_BLEND);
+//	gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
+// </FS:Beq> 
+
 	LLGLEnable cull(GL_CULL_FACE);
 	LLGLDepthTest depth(GL_TRUE);
 	LLGLDisable fog(GL_FOG);
@@ -3642,9 +3767,9 @@ BOOL LLModelPreview::render()
 		gGL.matrixMode(LLRender::MM_MODELVIEW);
 		gGL.pushMatrix();
 		gGL.loadIdentity();
-
-		gGL.color4f(0.169f, 0.169f, 0.169f, 1.f);
-
+		// <FS:Beq> uploader improvements
+		//gGL.color4f(0.169f, 0.169f, 0.169f, 1.f);
+		gGL.color4fv(static_cast<LLColor4>(canvas_col).mV);
 		gl_rect_2d_simple( width, height );
 
 		gGL.matrixMode(LLRender::MM_PROJECTION);
@@ -3792,8 +3917,11 @@ BOOL LLModelPreview::render()
 	stop_glerror();
 
 	gGL.pushMatrix();
-	const F32 BRIGHTNESS = 0.9f;
-	gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS);
+	// <FS:Beq> mesh uploader improvements configurable brightness
+	//const F32 BRIGHTNESS = 0.9f;
+	//gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS);
+	gGL.color4fv(edge_col().mV);
+	// </FS:Beq>
 
 	const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
 
@@ -3878,16 +4006,22 @@ BOOL LLModelPreview::render()
 						}
 						else
 						{
-							gGL.diffuseColor4f(1,1,1,1);
+						// <FS:Beq> improved mesh uploader
+						//	gGL.diffuseColor4f(1,1,1,1);
+							gGL.diffuseColor4fv(static_cast<LLColor4>(base_col).mV);
+						// </FS:Beq>
+
 						}
 
 						buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
 						gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-						gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);
-
+						// <FS:Beq> improved mesh uploader
+						//gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);
+						gGL.diffuseColor4fv(static_cast<LLColor4>(edge_col).mV);
+						// </FS:Beq> 
 						if (edges)
 						{
-							glLineWidth(3.f);
+							glLineWidth(edge_width);
 							glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 							buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
 							glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -3899,11 +4033,25 @@ BOOL LLModelPreview::render()
 
 			if (physics)
 			{
+				// <FS:Beq> model upload improvements - use the settings
+				////Vector4a physicsFillColour(0.4, 0.4, 0.4, 0.4);
+				//const LLColor4 physicsFillColour(0.0, 0.5, 1.0, 0.5);
+				////LLVector4a physicsEdgeColour(1.0, 1.0, 0.0, 1.0);
+				//const LLColor4 physicsEdgeColour=physicsFillColour*0.5;
+				//const LLColor4 degenerateFill(1.0, 0.0, 0.0, 0.5);
+				//const LLColor4 degenerateEdge(1.0,0.0,0.0,1.0);
+				// </FS:Beq> 
+
 				glClear(GL_DEPTH_BUFFER_BIT);
-				
-				for (U32 i = 0; i < 2; i++)
+				//<FS:Beq> refactor to remove silly variable names
+				//				for (U32 i = 0; i < 2; i++)
+				for (U32 pass = 0; pass < 2; pass++)
+				//</FS:Beq>
 				{
-					if (i == 0)
+					//<FS:Beq> refactor to remove silly variable names
+					//if (i == 0)
+					if (pass == 0)
+					//</FS:Beq>
 					{ //depth only pass
 						gGL.setColorMask(false, false);
 					}
@@ -3913,8 +4061,11 @@ BOOL LLModelPreview::render()
 					}
 
 					//enable alpha blending on second pass but not first pass
-					LLGLState blend(GL_BLEND, i); 
-					
+					//<FS:Beq> refactor to remove silly variable names
+					//LLGLState blend(GL_BLEND, i);
+					LLGLState blend(GL_BLEND, pass);
+					//</FS:Beq>
+
 					gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
 
 					for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
@@ -3923,175 +4074,196 @@ BOOL LLModelPreview::render()
 
 						LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
 
-							if (!model)
-							{
-								continue;
-							}
+						if (!model)
+						{
+							continue;
+						}
 
-							gGL.pushMatrix();
-							LLMatrix4 mat = instance.mTransform;
+						gGL.pushMatrix();
+						LLMatrix4 mat = instance.mTransform;
 
-						gGL.multMatrix((GLfloat*) mat.mMatrix);
+						gGL.multMatrix((GLfloat*)mat.mMatrix);
 
 
-							bool render_mesh = true;
+						bool render_mesh = true;
+						LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
+						if (decomp)
+						{
+							LLMutexLock(decomp->mMutex);
+
+							LLModel::Decomposition& physics = model->mPhysics;
 
-							LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
-							if (decomp)
+							if (!physics.mHull.empty())
 							{
-								LLMutexLock(decomp->mMutex);
-
-								LLModel::Decomposition& physics = model->mPhysics;
+								render_mesh = false;
 
-								if (!physics.mHull.empty())
-								{
-									render_mesh = false;
+								if (physics.mMesh.empty())
+								{ //build vertex buffer for physics mesh
+									gMeshRepo.buildPhysicsMesh(physics);
+								}
 
-									if (physics.mMesh.empty())
-									{ //build vertex buffer for physics mesh
-										gMeshRepo.buildPhysicsMesh(physics);
-									}
-						
-									if (!physics.mMesh.empty())
-									{ //render hull instead of mesh
-										for (U32 i = 0; i < physics.mMesh.size(); ++i)
+								if (!physics.mMesh.empty())
+								{ //render hull instead of mesh
+									for (U32 i = 0; i < physics.mMesh.size(); ++i)
+									{
+										if (explode > 0.f)
 										{
-											if (explode > 0.f)
-											{
-												gGL.pushMatrix();
+											gGL.pushMatrix();
 
-												LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters;
-												offset *= explode;
+											LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters;
+											offset *= explode;
 
-												gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
-											}
+											gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
+										}
 
-											static std::vector<LLColor4U> hull_colors;
+										static std::vector<LLColor4U> hull_colors;
 
-											if (i+1 >= hull_colors.size())
-											{
-												hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 128));
-											}
+										if (i + 1 >= hull_colors.size())
+										{
+											hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128));
+										}
 
-											gGL.diffuseColor4ubv(hull_colors[i].mV);
-											LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals);
+										gGL.diffuseColor4ubv(hull_colors[i].mV);
+										LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals);
 
-											if (explode > 0.f)
-											{
-												gGL.popMatrix();
-											}
+										if (explode > 0.f)
+										{
+											gGL.popMatrix();
 										}
 									}
 								}
 							}
-						
-							if (render_mesh)
+						}
+
+						if (render_mesh)
+						{
+							if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
 							{
-								if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
-								{
-									genBuffers(LLModel::LOD_PHYSICS, false);
-								}
+								genBuffers(LLModel::LOD_PHYSICS, false);
+							}
 
-								U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
+							U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
+							if (pass > 0){
 								for (U32 i = 0; i < num_models; ++i)
 								{
 									LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
 
 									gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-									gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f);
+									gGL.diffuseColor4fv(phys_fill_col().mV);
 
 									buffer->setBuffer(type_mask & buffer->getTypeMask());
-									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
-
-									gGL.diffuseColor3f(1.f, 1.f, 0.f);
+									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
 
-									glLineWidth(2.f);
+									gGL.diffuseColor4fv(phys_edge_col().mV);
+									glLineWidth(phys_edge_width);
 									glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
 
 									glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 									glLineWidth(1.f);
 								}
 							}
-
-							gGL.popMatrix();
 						}
 
-					glLineWidth(3.f);
-					glPointSize(8.f);
-					gPipeline.enableLightsFullbright(LLColor4::white);
-					//show degenerate triangles
-					LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS);
-					LLGLDisable cull(GL_CULL_FACE);
-					gGL.diffuseColor4f(1.f,0.f,0.f,1.f);
-					const LLVector4a scale(0.5f);
+						gGL.popMatrix();
+					}
 
-					for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+					//<FS:Beq> refactor to remove silly variable names
+					// also only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks]
+					//if (i > 0)
+					if (pass > 0 && mHasDegenerate)
+					//</FS:Beq>
 					{
-						LLModelInstance& instance = *iter;
-
-						LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
-
-						if (!model)
+						glLineWidth(deg_edge_width);
+						glPointSize(deg_point_size);
+// <FS:Beq> This single line is why the degenerate triangles display has been crap forever. 
+// 						gPipeline.enableLightsFullbright(LLColor4::white);
+						//show degenerate triangles
+						LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS);
+						LLGLDisable cull(GL_CULL_FACE);
+						const LLVector4a scale(0.5f);
+
+						for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
 						{
-							continue;
-						}
+							LLModelInstance& instance = *iter;
 
-						gGL.pushMatrix();
-						LLMatrix4 mat = instance.mTransform;
+							LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
 
-						gGL.multMatrix((GLfloat*) mat.mMatrix);
+							if (!model)
+							{
+								continue;
+							}
 
+							gGL.pushMatrix();
+							LLMatrix4 mat = instance.mTransform;
 
-						LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
-						if (decomp)
-						{
-							LLMutexLock(decomp->mMutex);
+							gGL.multMatrix((GLfloat*)mat.mMatrix);
 
-							LLModel::Decomposition& physics = model->mPhysics;
 
-							if (physics.mHull.empty())
+							LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
+							if (decomp)
 							{
-								if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
-								{
-									genBuffers(LLModel::LOD_PHYSICS, false);
-								}
-							
-								for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i)
-								{
-									LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
+								LLMutexLock(decomp->mMutex);
 
-									buffer->setBuffer(type_mask & buffer->getTypeMask());
+								LLModel::Decomposition& physics = model->mPhysics;
 
-									LLStrider<LLVector3> pos_strider; 
-									buffer->getVertexStrider(pos_strider, 0);
-									LLVector4a* pos = (LLVector4a*) pos_strider.get();
-							
-									LLStrider<U16> idx;
-									buffer->getIndexStrider(idx, 0);
+								if (physics.mHull.empty())
+								{
+									if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
+									{
+										genBuffers(LLModel::LOD_PHYSICS, false);
+									}
 
-									for (U32 i = 0; i < buffer->getNumIndices(); i += 3)
+									auto num_degenerate = 0;
+									//<FS:Beq> More nested i variable silliness
+									//									for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i)
+									auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
+									for (U32 v = 0; v < num_models; ++v)
 									{
-										LLVector4a v1; v1.setMul(pos[*idx++], scale);
-										LLVector4a v2; v2.setMul(pos[*idx++], scale);
-										LLVector4a v3; v3.setMul(pos[*idx++], scale);
+										LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v];
+									//</FS:Beq>
+										if(buffer->getNumVerts() < 3)continue;
+
+										buffer->setBuffer(type_mask & buffer->getTypeMask());
+
+										LLStrider<LLVector3> pos_strider;
+										buffer->getVertexStrider(pos_strider, 0);
+										LLVector4a* pos = (LLVector4a*)pos_strider.get();
+
+										LLStrider<U16> idx;
+										buffer->getIndexStrider(idx, 0);
 
-										if (ll_is_degenerate(v1,v2,v3))
+										LLVector4a v1, v2, v3;
+										//<FS:Beq> rename inner most i to avoid merge confusion
+										for (U32 indices_offset = 0; indices_offset < buffer->getNumIndices(); indices_offset += 3)
 										{
-											buffer->draw(LLRender::LINE_LOOP, 3, i);
-											buffer->draw(LLRender::POINTS, 3, i);
+											v1.setMul(pos[*idx++], scale);
+											v2.setMul(pos[*idx++], scale);
+											v3.setMul(pos[*idx++], scale);
+
+											if (ll_is_degenerate(v1, v2, v3))
+											{
+												num_degenerate++;
+												glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+												gGL.diffuseColor3fv(deg_edge_col().mV);
+												buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
+												buffer->drawRange(LLRender::POINTS, 0, 2, 3, indices_offset);
+												glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+												gGL.diffuseColor3fv(deg_fill_col().mV);
+												buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
+											}
 										}
 									}
 								}
 							}
-						}
 
-						gGL.popMatrix();
+							gGL.popMatrix();
+						}
+						glLineWidth(1.f);
+						glPointSize(1.f);
+						gPipeline.enableLightsPreview();
+						gGL.setSceneBlendType(LLRender::BT_ALPHA);
 					}
-					glLineWidth(1.f);
-					glPointSize(1.f);
-					gPipeline.enableLightsPreview();
-					gGL.setSceneBlendType(LLRender::BT_ALPHA);
 				}
 			}
 		}
@@ -4173,16 +4345,19 @@ BOOL LLModelPreview::render()
 							}
 						
 							buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
-							gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);
+							// <FS:Beq> configurable colour and width
+							//gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);
 
 							if (edges)
 							{
-								glLineWidth(3.f);
+								gGL.diffuseColor4fv(edge_col().mV);
+								glLineWidth(edge_width);
 								glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 								buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
 								glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 								glLineWidth(1.f);
 							}
+							// </FS:Beq>
 						}
 					}
 				}
@@ -4240,8 +4415,11 @@ void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians)
 void LLModelPreview::zoom(F32 zoom_amt)
 {
 	F32 new_zoom = mCameraZoom+zoom_amt;
-
-	mCameraZoom	= llclamp(new_zoom, 1.f, 10.f);
+	// <FS:Beq> add configurable zoom TODO: stop clamping in render
+	// mCameraZoom = llclamp(new_zoom, 1.f, 10.f);
+	static LLCachedControl<F32> zoom_limit(gSavedSettings, "MeshPreviewZoomLimit");
+	mCameraZoom	= llclamp(new_zoom, 1.f, zoom_limit());
+	// </FS:Beq>
 }
 
 void LLModelPreview::pan(F32 right, F32 up)
@@ -4447,11 +4625,22 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
 		childSetTextArg("server_weight", "[SIM]", tbd);
 		childSetTextArg("physics_weight", "[PH]", tbd);
 		childSetTextArg("upload_fee", "[FEE]", tbd);
-		childSetTextArg("price_breakdown", "[STREAMING]", tbd);
-		childSetTextArg("price_breakdown", "[PHYSICS]", tbd);
-		childSetTextArg("price_breakdown", "[INSTANCES]", tbd);
-		childSetTextArg("price_breakdown", "[TEXTURES]", tbd);
-		childSetTextArg("price_breakdown", "[MODEL]", tbd);
+		// <FS:Beq> add extended info fields
+		//childSetTextArg("price_breakdown", "[STREAMING]", dashes);
+		//childSetTextArg("price_breakdown", "[PHYSICS]", dashes);
+		//childSetTextArg("price_breakdown", "[INSTANCES]", dashes);
+		//childSetTextArg("price_breakdown", "[TEXTURES]", dashes);
+		//childSetTextArg("price_breakdown", "[MODEL]", dashes);
+		std::string dashes = hasString("--") ? getString("--") : "--";
+		childSetTextArg("price_breakdown", "[STREAMING]", dashes);
+		childSetTextArg("price_breakdown", "[PHYSICS]", dashes);
+		childSetTextArg("price_breakdown", "[INSTANCES]", dashes);
+		childSetTextArg("price_breakdown", "[TEXTURES]", dashes);
+		childSetTextArg("price_breakdown", "[MODEL]", dashes);
+		childSetTextArg("physics_breakdown", "[PCH]", dashes);
+		childSetTextArg("physics_breakdown", "[PM]", dashes);
+		childSetTextArg("physics_breakdown", "[PHU]", dashes);
+		// </FS:Beq>
 	}
 }
 
@@ -4501,6 +4690,16 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived()
 	childSetTextArg("price_breakdown", "[INSTANCES]", llformat("%d", result["upload_price_breakdown"]["mesh_instance"].asInteger()));
 	childSetTextArg("price_breakdown", "[TEXTURES]", llformat("%d", result["upload_price_breakdown"]["texture"].asInteger()));
 	childSetTextArg("price_breakdown", "[MODEL]", llformat("%d", result["upload_price_breakdown"]["model"].asInteger()));
+//<FS:Beq> Updates for enhanced Mesh feedback at upload
+	childSetTextArg("physics_breakdown", "[PCH]", llformat("%0.3f", result["model_physics_cost"]["hull"].asReal()));
+	childSetTextArg("physics_breakdown", "[PM]", llformat("%0.3f", result["model_physics_cost"]["mesh"].asReal()));
+	childSetTextArg("physics_breakdown", "[PHU]", llformat("%0.3f", result["model_physics_cost"]["decomposition"].asReal()));
+	childSetTextArg("streaming_breakdown", "[STR_TOTAL]", llformat("%d", result["streaming_cost"].asInteger()));
+	childSetTextArg("streaming_breakdown", "[STR_HIGH]", llformat("%d", result["streaming_params"]["high_lod"].asInteger()));
+	childSetTextArg("streaming_breakdown", "[STR_MED]", llformat("%d", result["streaming_params"]["medium_lod"].asInteger()));
+	childSetTextArg("streaming_breakdown", "[STR_LOW]", llformat("%d", result["streaming_params"]["low_lod"].asInteger()));
+	childSetTextArg("streaming_breakdown", "[STR_LOWEST]", llformat("%d", result["streaming_params"]["lowest_lod"].asInteger()));
+//</FS:Beq>
 	childSetVisible("upload_fee", true);
 	childSetVisible("price_breakdown", true);
 	mUploadBtn->setEnabled(isModelUploadAllowed());
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 7ec6a58ac7..564f4c39de 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -152,6 +152,7 @@ protected:
 	static void		onAutoFillCommit(LLUICtrl*,void*);
 	
 	void onLODParamCommit(S32 lod, bool enforce_tri_limit);
+	void draw3dPreview();
 
 	static void		onExplodeCommit(LLUICtrl*, void*);
 	
@@ -310,6 +311,7 @@ public:
 	static bool 		sIgnoreLoadedCallback;
     std::vector<S32> mLodsQuery;
     std::vector<S32> mLodsWithParsingError;
+	bool mHasDegenerate;
 
 protected:
 
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 40d6d325ba..c7626304ed 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -6582,7 +6582,7 @@ void LLPipeline::enableLightsPreview()
 	light->enable();
 	light->setPosition(light_pos);
 	light->setDiffuse(diffuse0);
-	light->setAmbient(LLColor4::black);
+	light->setAmbient(ambient);
 	light->setSpecular(specular0);
 	light->setSpotExponent(0.f);
 	light->setSpotCutoff(180.f);
@@ -6593,7 +6593,7 @@ void LLPipeline::enableLightsPreview()
 	light->enable();
 	light->setPosition(light_pos);
 	light->setDiffuse(diffuse1);
-	light->setAmbient(LLColor4::black);
+	light->setAmbient(ambient);
 	light->setSpecular(specular1);
 	light->setSpotExponent(0.f);
 	light->setSpotCutoff(180.f);
@@ -6603,7 +6603,7 @@ void LLPipeline::enableLightsPreview()
 	light->enable();
 	light->setPosition(light_pos);
 	light->setDiffuse(diffuse2);
-	light->setAmbient(LLColor4::black);
+	light->setAmbient(ambient);
 	light->setSpecular(specular2);
 	light->setSpotExponent(0.f);
 	light->setSpotCutoff(180.f);
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 3db431de1b..ec9104d5e7 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -2,12 +2,12 @@
 <floater
  can_close="true"
  can_drag_on_left="false"
- can_minimize="false"
- can_resize="false"
- height="480"
- min_height="480"
- width="980"
- min_width="980"
+ can_minimize="true"
+ can_resize="true"
+ height="600"
+ min_height="600"
+ width="1024"
+ min_width="1024"
  name="Model Preview"
  title="UPLOAD MODEL"
  help_topic="upload_model" >
@@ -33,19 +33,21 @@
   <string name="mesh_status_missing_lod">Missing required level of detail.</string>
   <string name="mesh_status_invalid_material_list">LOD materials are not a subset of reference model.</string>
   <string name="phys_status_vertex_limit_exceeded">Some physical hulls exceed vertex limitations.</string>
+  <string name="phys_status_degenerate_triangles">The physics mesh too dense remove the small thin triangles (see preview)</string>
+  <string name="phys_status_no_havok">The Firestorm OpenSim build is not supported for physics upload in SL.</string>
   <string name="layer_all">All</string> <!-- Text to display in physics layer combo box for "all layers" -->
   <string name="decomposing">Analyzing...</string>
   <string name="simplifying">Simplifying...</string>
   <string name="tbd">TBD</string>
 
-<panel
-  follows="top|left"
-  height="455"
-  layout="topleft"
-  left="3"
-  name="left_panel"
-  top_pad="10"
-  width="630">
+  <panel
+    follows="top|left"
+    height="580"
+    layout="topleft"
+    left="3"
+    name="left_panel"
+    top_pad="0"
+    width="630">
     <panel
       follows="all"
       height="50"
@@ -784,8 +786,9 @@
                       name="first_step_name"
                       text_color="White"
                       top_pad="0"
-                      width="210">
-                      Step 1: Level of Detail
+                      width="210"
+                      valign="center">
+                      Step 1: Pick a physics model :
                     </text>
                     <combo_box
                       follows="left|top"
@@ -827,7 +830,7 @@
               layout="topleft"
               left="18"
               name="physics_tab_border"
-              top_pad="15"
+              top_pad="10"
               width="589"/>
                 <panel
                   bg_alpha_color="0 0 0 0"
@@ -836,7 +839,7 @@
                   follows="top|left"
                   left="18"
                   name="physics analysis"
-                  top_pad="15"
+                  top_pad="10"
                   visible="true"
                   width="589">
                     <text
@@ -848,7 +851,7 @@
                       name="method_label"
                       text_color="White"
                       top_pad="0">
-                      Step 2: Analyze
+                      Step 2: Convert to hulls (optional)
                     </text>
                     <text
                       follows="top|left"
@@ -934,7 +937,7 @@
               layout="topleft"
               left="18"
               name="physics_tab_border"
-              top_pad="15"
+              top_pad="10"
               width="589"/>
                 <panel
                   bg_alpha_color="0 0 0 0"
@@ -943,7 +946,7 @@
                   height="66"
                   left="18"
                   name="physics simplification"
-                  top_pad="15"
+                  top_pad="10"
                   width="589">
                     <text
                       text_color="White"
@@ -1042,7 +1045,7 @@
               layout="topleft"
               left="18"
               name="physics_tab_border"
-              top_pad="15"
+              top_pad="10"
               width="589"/>
                 <panel
                   bg_alpha_color="0 0 0 0"
@@ -1104,10 +1107,9 @@
                  follows="left|top"
                  height="19"
                  layout="topleft"
-                 left_pad="5"
-                 top_delta="0"
+                 top_pad="5"
                  name="physics message"
-                 width="270">
+                 width="589">
                      <icon
                       follows="left|top"
                       height="16"
@@ -1122,7 +1124,7 @@
                       layout="topleft"
                       left_pad="2"
                       name="physics_status_message_text"
-                      width="252"
+                      width="573"
                       top_delta="3"/>
                 </panel>
         </panel>
@@ -1248,13 +1250,14 @@
      </panel>
     </tab_container>
     <panel
-     follows="top|left"
-     height="80"
-     layout="top|left"
-     left="0"
+     follows="top|left|bottom"
+     layout="topleft"
+     height="184"
+     left="4"
+     border="true"
      name="weights_and_warning_panel"
      top_pad="3"
-     width="625">
+     width="629">
        <button
          follows="top|left"
          label="Calculate weights &amp; fee"
@@ -1294,10 +1297,10 @@
          label_color="White"
          layout="topleft"
          name="reset_btn"
-         right="-2"
+         right="-5"
          top="3"
          height="20"
-         width="275"/>
+         width="265"/>
        <!-- ========== WEIGHTS ==========-->
        <text
          follows="top|left"
@@ -1316,7 +1319,7 @@
          left_pad="0"
          name="prim_weight"
          top_delta="0"
-         width="120"
+         width="130"
          word_wrap="true">
          Land impact: [EQ]
        </text>
@@ -1326,7 +1329,7 @@
          left_pad="0"
          name="download_weight"
          top_delta="0"
-         width="100"
+         width="130"
          word_wrap="true">
          Download: [ST]
        </text>
@@ -1336,7 +1339,7 @@
          layout="topleft"
          left_pad="0"
          name="physics_weight"
-         width="90"
+         width="130"
          word_wrap="true">
          Physics: [PH]
        </text>
@@ -1346,17 +1349,148 @@
          layout="topleft"
          left_pad="0"
          name="server_weight"
-         width="83"
+         width="130"
          word_wrap="true">
          Server: [SIM]
        </text>
-       <!-- ========== NOTE MESSAGE ========== -->
+       <!-- =========== Cost breakdown ======== -->
+      <panel
+        border="true"
+        top_pad="5"
+        layout="topleft"
+        left="6"
+        name="price_breakdown_panel"
+        width="120"
+        height="100">
+        <text
+          layout="topleft"
+          left="3">
+          Price Breakdown
+        </text>
+        <view_border
+          bevel_style="none"
+          follows="top|left"
+          height="0"
+          layout="topleft"
+          left="3"
+          name="price_breakdown_border"
+          top_pad="5"
+          width="110"/>
+        <text
+          height="80"
+          top_pad="5"
+          layout="topleft"
+          left="3"
+          name="price_breakdown_labels"
+          width="70"
+          word_wrap="false">
+Download:
+Physics:
+Instances:
+Textures:
+Model:
+        </text>
+        <text
+          height="80"
+          top_delta="0"
+          layout="topleft"
+          halign="right"
+          left_pad="0"
+          name="price_breakdown"
+          width="40"
+          word_wrap="false">
+[STREAMING]
+[PHYSICS]
+[INSTANCES]
+[TEXTURES]
+[MODEL]
+        </text>
+      </panel>
+       <!-- 
+       Streaming breakdown numbers are available but not fully understood
+       uncommenting the following sections will display the numbers for debugging purposes
+       <text
+        height="80"
+        top_delta="0"
+        layout="topleft"
+        left="130"
+        name="streaming_breakdown_labels"
+        width="65"
+        word_wrap="true">
+Streaming/Download:
+High:
+Medium:
+Low:
+Lowest:
+      </text>
        <text
+        height="80"
+        top_delta="0"
+        layout="topleft"
+        left_pad="0"
+        name="streaming_breakdown"
+        width="95"
+        word_wrap="true">
+[STR_TOTAL]
+[STR_HIGH]
+[STR_MED]
+[STR_LOW]
+[STR_LOWEST]
+      </text>-->
+      <panel
+        border="true"
+        layout="topleft"
+        left_pad="265"
+        name="price_breakdown_panel"
+        width="120"
+        height="100">
+        <text
+          layout="topleft"
+          left="3">
+          Physics Costs
+        </text>
+        <view_border
+          bevel_style="none"
+          follows="top|left"
+          height="0"
+          layout="topleft"
+          left="3"
+          name="price_breakdown_border"
+          top_pad="5"
+          width="110"/>
+        <text
+         height="80"
+         top_pad="5"
+         layout="topleft"
+         left="5"
+         name="physics_breakdown_labels"
+         width="65">
+Base Hull:
+Mesh:
+Analysed:
+        </text>
+        <text
+         height="80"
+         top_delta="0"
+         layout="topleft"
+         left_pad="0"
+         name="physics_breakdown"
+         width="40"
+         halign="right"
+         word_wrap="false"
+         visible="true">
+[PCH]
+[PM]
+[PHU]
+        </text>-->
+      </panel>
+      <!-- ========== NOTE MESSAGE ========== -->
+      <text
          font="SansSerif"
          layout="topleft"
          left="6"
          name="warning_title"
-         top_pad="10"
+         top_pad="5"
          text_color="DrYellow"
          visible="false"
          width="40">
@@ -1375,104 +1509,110 @@
          visible="false">
          You dont have rights to upload mesh models. [[VURL] Find out how] to get certified.
        </text>
-       <text text_color="Yellow" layout="topleft" top_delta="20" left="6" name="status">[STATUS]</text>
-  
+       <text text_color="Yellow" layout="topleft" top_delta="5" left="6" name="status">
+[STATUS]
+       </text>
     </panel>
-</panel>
-
-<text 
- follows="left|top"
- layout="topleft"
- left="640"
- name="lod_label"
- text_color="White"
- top="13"
- height="15"
- width="290">
- Preview:
- </text>
-<panel
- border="true"
- bevel_style="none"
- follows="top|left"
- name="preview_panel"
- top_pad="4"
- width="290"
- height="290"/>
-
-<panel
-  follows="all"
-  height="130"
-  layout="topleft"
-  name="right_panel"
-  top_pad="5"
-  width="340">
-    <combo_box
-      top_pad="3"
+  </panel>
+  <panel
+    follows="top|left|bottom|right"
+    can_resize="true"
+    name="right_panel"
+    top="0"
+    left="640"
+    background_visible="true"
+    width="375">
+    <text
       follows="left|top"
-      height="18"
       layout="topleft"
-      name="preview_lod_combo"
-      width="150"
-      tool_tip="LOD to view in preview render">
+      left="0"
+      name="lod_label"
+      text_color="White"
+      top="13"
+      height="15"
+      width="290">
+      Preview:
+    </text>
+    <panel
+      can_resize="false"
+      follows="top|left"
+      height="20"
+      name="right_upper_panel"
+      top="8"
+      left="60"
+      background_visible="true"
+      width="315">
+      <combo_box
+        top_pad="3"
+        can_resize="false"
+        follows="top|left"
+        height="18"
+        layout="topleft"
+        name="preview_lod_combo"
+        width="75"
+        tool_tip="LOD to view in preview render">
         <combo_item name="high">   High   </combo_item>
         <combo_item name="medium"> Medium </combo_item>
         <combo_item name="low">    Low    </combo_item>
         <combo_item name="lowest"> Lowest </combo_item>
-    </combo_box>
-    <text
-      follows="top|left"
-      layout="topleft"
-      text_color="White"
-      top="5"
-      left_pad="20"
-      name="label_display"
-      width="50">
-      Display...
-    </text>
-    <check_box
-      follows="top|left"
-      label="Edges"
+      </combo_box>
+    </panel>
+  </panel>
+  <panel
+     border="true"
+     bevel_style="none"
+     follows="top|left|right|bottom"
+     layout="topleft"
+     name="preview_panel"
+     top="30"
+     width="375"
+     height="525"/>
+
+   <panel
+     follows="left|right|bottom"
+     layout="topleft"
+     height="40"
+     name="lower_right_panel"
+     top_pad="5"
+     width="375">
+     <check_box
+       follows="right|bottom"
+       label="Edges"
       label_text.text_color="White"
       layout="topleft"
-      left_delta="0"
       name="show_edges"
-      top_pad="8">
-    </check_box>
+      width="70"
+      left="0"
+      top_pad="8"/>
     <check_box
-      follows="top|left"
+      follows="right|bottom"
+      left_pad="8"
       label="Physics"
       label_text.text_color="White"
-      layout="topleft"
-      name="show_physics"
-      top_pad="8">
-    </check_box>
+      name="show_physics"/>
     <check_box
-      follows="top|left"
+      follows="right|bottom"
       label="Textures"
       label_text.text_color="White"
       layout="topleft"
       name="show_textures"
-      top_pad="8">
-    </check_box>
+      left_pad="0"/>
     <check_box
-      follows="top|left"
-      label="Skin weights"
+      follows="right|bottom"
+      label="Weights"
       label_text.text_color="White"
       layout="topleft"
       name="show_skin_weight"
-      top_pad="8">
-    </check_box>
+      left_pad="0"/>
     <check_box
-      follows="top|left"
+      follows="right|bottom"
       label="Joints"
       label_text.text_color="White"
       layout="topleft"
       name="show_joint_positions"
-      top_pad="8">
-    </check_box>
+      left_pad="0"/>
     <text
-      follows="top|left"
+      follows="right|bottom"
       layout="topleft"
       left="2"
       name="physics_explode_label"
@@ -1482,12 +1622,13 @@
     </text>
     <slider
       name="physics_explode"
-      follows="top|left"
-      top="100"
-      left="0"
+      follows="right|bottom"
+      valign="center"
+      top="15"
+      left="80"
       min_val="0.0"
       max_val="3.0"
       height="20"
-      width="150"/>
-</panel>
+      width="120"/>
+  </panel>
 </floater>
-- 
cgit v1.2.3


From c9d0222526c422ee7a5bff140b034e88aa5d0b39 Mon Sep 17 00:00:00 2001
From: AndreyL ProductEngine <alihatskiy@productengine.com>
Date: Thu, 3 Jan 2019 01:19:41 +0200
Subject: SL-10288 settings.xml fix

---
 indra/newview/app_settings/settings.xml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index a58fa2d7a0..091992d1ab 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -6810,6 +6810,7 @@
     <key>Value</key>
     <real>10.0</real>
   </map>
+  <key>MigrateCacheDirectory</key>
   <map>
       <key>Comment</key>
       <string>Check for old version of disk cache to migrate to current location</string>
@@ -16469,3 +16470,4 @@
 </map>
 </llsd>
 
+
-- 
cgit v1.2.3


From dd3a3693729cc700872a6e94e0ae4898cf25c3fb Mon Sep 17 00:00:00 2001
From: AndreyL ProductEngine <alihatskiy@productengine.com>
Date: Thu, 3 Jan 2019 14:30:01 +0200
Subject: SL-10288 comments cleanup

---
 .../shaders/class1/objects/previewV.glsl           |   2 -
 indra/newview/lldynamictexture.cpp                 |  15 +--
 indra/newview/llfloatermodelpreview.cpp            | 118 ++++-----------------
 3 files changed, 21 insertions(+), 114 deletions(-)

diff --git a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
index 3424613e94..de2ea2a065 100644
--- a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
@@ -91,9 +91,7 @@ void main()
 
 	// Collect normal lights (need to be divided by two, as we later multiply by 2)
 	col.rgb += light_diffuse[1].rgb * calcDirectionalLight(norm, light_position[1].xyz);
-//	col.rgb += light_diffuse[2].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[2], light_direction[2], light_attenuation[2].x, light_attenuation[2].z);
 	col.rgb += light_diffuse[2].rgb * calcDirectionalLight(norm, light_position[2].xyz);
-//	col.rgb += light_diffuse[3].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[3], light_direction[3], light_attenuation[3].x, light_attenuation[3].z);
 	col.rgb += light_diffuse[3].rgb * calcDirectionalLight(norm, light_position[3].xyz);
 	col /= 2.0;
 	vertex_color = col*color;
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index af6977d3cd..e180d91461 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -125,17 +125,11 @@ BOOL LLViewerDynamicTexture::render()
 //-----------------------------------------------------------------------------
 void LLViewerDynamicTexture::preRender(BOOL clear_depth)
 {
-	// <FS:Beq> changes to support higher resolution rendering in the preview
-	////only images up to 512x512 are supported
-	//llassert(mFullHeight <= 512);
-	//llassert(mFullWidth <= 512);
 	gPipeline.allocatePhysicsBuffer();
 	llassert(mFullWidth <= static_cast<S32>(gPipeline.mPhysicsDisplay.getWidth()));
 	llassert(mFullHeight <= static_cast<S32>(gPipeline.mPhysicsDisplay.getHeight()));
 
-//	if (gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI)
 	if (gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsATI)
-// </FS:Beq>
 	{ //using offscreen render target, just use the bottom left corner
 		mOrigin.set(0, 0);
 	}
@@ -221,15 +215,13 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
 	{
 		return TRUE;
 	}
-	// <FS:Beq> changes to support higher resolution rendering in the preview
-	//	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI;
+
 	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsATI;
 	if (use_fbo)
 	{
-//		gPipeline.mWaterDis.bindTarget();
 		gPipeline.mPhysicsDisplay.bindTarget();
 	}
-	// </FS:Beq>
+
 	LLGLSLShader::bindNoShader();
 	LLVertexBuffer::unbind();
 	
@@ -265,10 +257,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
 
 	if (use_fbo)
 	{
-		// <FS:Beq> changes to support higher resolution rendering in the preview
-		// gPipeline.mWaterDis.flush();
 		gPipeline.mPhysicsDisplay.flush();
-		// </FS:Beq>
 	}
 
 	return ret;
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 78d97753d3..ffc993e868 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -427,11 +427,8 @@ void LLFloaterModelPreview::initModelPreview()
 	{
 		delete mModelPreview;
 	}
-	// <FS:Beq> mesh uploader changes to allow higher resolution render
-	//	mModelPreview = new LLModelPreview(512, 512, this);
 	auto size = gSavedSettings.getS32("PreviewRenderSize");
 	mModelPreview = new LLModelPreview(size, size, this );
-	// </FS:Beq>
 	mModelPreview->setPreviewTarget(16.f);
 	mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5));
 	mModelPreview->setModelUpdatedCallback(boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this, _1));
@@ -441,8 +438,6 @@ void LLFloaterModelPreview::onViewOptionChecked(LLUICtrl* ctrl)
 {
 	if (mModelPreview)
 	{
-		// <FS:Beq> only show explode when phsyics is on
-		//		mModelPreview->mViewOption[ctrl->getName()] = !mModelPreview->mViewOption[ctrl->getName()];
 		auto name = ctrl->getName();
 		mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name];
 		if (name == "show_physics")
@@ -664,7 +659,6 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
 	}
 }
 
-// <FS:Beq> extracted method to simplify changes in layout
 void LLFloaterModelPreview::draw3dPreview()
 {
 	gGL.color3f(1.f, 1.f, 1.f);
@@ -1266,7 +1260,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
 , mResetJoints( false )
 , mModelNoErrors( true )
 , mLastJointUpdate( false )
-, mHasDegenerate( false ) // <FS:Beq>
+, mHasDegenerate( false )
 {
 	mNeedsUpdate = TRUE;
 	mCameraDistance = 0.f;
@@ -2759,7 +2753,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
 
 void LLModelPreview::updateStatusMessages()
 {
-// <FS:Beq> bit mask values for physics errors. used to prevent overwrite of single line status
+// bit mask values for physics errors. used to prevent overwrite of single line status
 // TODO: use this to provied multiline status
 	enum PhysicsError
 	{
@@ -2769,10 +2763,10 @@ void LLModelPreview::updateStatusMessages()
 		TOOMANYHULLS=4,
 		TOOMANYVERTSINHULL=8
 	};
-// </FS:Beq>
+
 	assert_main_thread();
 
-	U32 has_physics_error{ PhysicsError::NONE }; // <FS:Beq> physics error bitmap
+	U32 has_physics_error{ PhysicsError::NONE }; // physics error bitmap
 	//triangle/vertex/submesh count for each mesh asset for each lod
 	std::vector<S32> tris[LLModel::NUM_LODS];
 	std::vector<S32> verts[LLModel::NUM_LODS];
@@ -2861,38 +2855,25 @@ void LLModelPreview::updateStatusMessages()
 	{
 		mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH];
 	}
-	// <FS:Beq> make has_degenerate a member so that we can use it in the render method
-	// has_degenerate = false
+
 	mHasDegenerate = false;
 	{//check for degenerate triangles in physics mesh
 		U32 lod = LLModel::LOD_PHYSICS;
 		const LLVector4a scale(0.5f);
-		for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i)// <FS:Beq> make has_degenerate a member 
+		for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i)
 		{ //for each model in the lod
 			if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty())
 			{ //no decomp exists
 				S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces();
-				for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j)// <FS:Beq> make has_degenerate a member 
+				for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j)
 				{ //for each submesh (face), add triangles and vertices to current total
 					LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j);
-					for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate; )// <FS:Beq> make has_degenerate a member 
+					for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate; )
 					{
 						U16 index_a = face.mIndices[k + 0];
 						U16 index_b = face.mIndices[k + 1];
 						U16 index_c = face.mIndices[k + 2];
-						// <FS:Beq> FIRE-23367/23387 - Allow forced empty triangle placeholders created by the LOD processing.
-						//	LLVector4a v1; v1.setMul(face.mPositions[index_a], scale);
-						//	LLVector4a v2; v2.setMul(face.mPositions[index_b], scale);
-						//	LLVector4a v3; v3.setMul(face.mPositions[index_c], scale);
-
-						//	if (ll_is_degenerate(v1, v2, v3))
-						//	{
-						//		mHasDegenerate = true;// <FS:Beq> make has_degenerate a member 
-						//	}
-						//	else
-						//	{
-						//		k += 3;
-						//	}
+
 						if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case
 						{
 							LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL;
@@ -2904,7 +2885,7 @@ void LLModelPreview::updateStatusMessages()
 							LLVector4a v3; v3.setMul(face.mPositions[index_c], scale);
 							if (ll_is_degenerate(v1, v2, v3))
 							{
-								mHasDegenerate = true;// <FS:Beq> make has_degenerate a member 
+								mHasDegenerate = true;
 							}
 						}
 						k += 3;
@@ -2914,7 +2895,7 @@ void LLModelPreview::updateStatusMessages()
 		}
 	}
 
-	// <FS:Beq> flag degenerates here rather than deferring to a MAV error later
+	// flag degenerates here rather than deferring to a MAV error later
 	mFMP->childSetVisible("physics_status_message_text", mHasDegenerate); //display or clear
 	auto degenerateIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
 	degenerateIcon->setVisible(mHasDegenerate);
@@ -2925,7 +2906,6 @@ void LLModelPreview::updateStatusMessages()
 		LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error");
 		degenerateIcon->setImage(img);
 	}
-	// </FS:Beq>
 
 	mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH]));
 
@@ -3095,12 +3075,7 @@ void LLModelPreview::updateStatusMessages()
 			mModelNoErrors = false;
 		}
 	}
-	// <FS:Beq> Improve the error checking the TO DO here is no longer applicable but not an FS comment so edited to stop it being picked up
-	//// To do investigate use of has_degenerate and include into mModelNoErrors upload blocking mechanics
-	//// current use of has_degenerate won't block upload permanently - later checks will restore the button
-	//if (!mModelNoErrors || mHasDegenerate)
-	//{
-	//	mFMP->childDisable("ok_btn");
+
 	if (!mModelNoErrors || mHasDegenerate)
 	{
 		mFMP->childDisable("ok_btn");
@@ -3725,7 +3700,7 @@ BOOL LLModelPreview::render()
 	bool textures = mViewOption["show_textures"];
 	bool physics = mViewOption["show_physics"];
 
-	// <FS:Beq> Extra configurability, to be exposed later as controls?
+	// Extra configurability, to be exposed later as controls?
 	static LLCachedControl<LLColor4> canvas_col(gSavedSettings, "MeshPreviewCanvasColor");
 	static LLCachedControl<LLColor4> edge_col(gSavedSettings, "MeshPreviewEdgeColor");
 	static LLCachedControl<LLColor4> base_col(gSavedSettings, "MeshPreviewBaseColor");
@@ -3738,16 +3713,12 @@ BOOL LLModelPreview::render()
 	static LLCachedControl<LLColor4> deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor");	
 	static LLCachedControl<F32> deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth");
 	static LLCachedControl<F32> deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize");
-	// </FS:Beq>
+
 	S32 width = getWidth();
 	S32 height = getHeight();
 
 	LLGLSUIDefault def;
 	LLGLDisable no_blend(GL_BLEND);
-// <FS:Beq> Clean up render of mesh preview
-//	LLGLEnable blend(GL_BLEND);
-//	gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
-// </FS:Beq> 
 
 	LLGLEnable cull(GL_CULL_FACE);
 	LLGLDepthTest depth(GL_TRUE);
@@ -3767,8 +3738,7 @@ BOOL LLModelPreview::render()
 		gGL.matrixMode(LLRender::MM_MODELVIEW);
 		gGL.pushMatrix();
 		gGL.loadIdentity();
-		// <FS:Beq> uploader improvements
-		//gGL.color4f(0.169f, 0.169f, 0.169f, 1.f);
+
 		gGL.color4fv(static_cast<LLColor4>(canvas_col).mV);
 		gl_rect_2d_simple( width, height );
 
@@ -3917,11 +3887,7 @@ BOOL LLModelPreview::render()
 	stop_glerror();
 
 	gGL.pushMatrix();
-	// <FS:Beq> mesh uploader improvements configurable brightness
-	//const F32 BRIGHTNESS = 0.9f;
-	//gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS);
 	gGL.color4fv(edge_col().mV);
-	// </FS:Beq>
 
 	const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
 
@@ -4006,19 +3972,12 @@ BOOL LLModelPreview::render()
 						}
 						else
 						{
-						// <FS:Beq> improved mesh uploader
-						//	gGL.diffuseColor4f(1,1,1,1);
 							gGL.diffuseColor4fv(static_cast<LLColor4>(base_col).mV);
-						// </FS:Beq>
-
 						}
 
 						buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
 						gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-						// <FS:Beq> improved mesh uploader
-						//gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);
 						gGL.diffuseColor4fv(static_cast<LLColor4>(edge_col).mV);
-						// </FS:Beq> 
 						if (edges)
 						{
 							glLineWidth(edge_width);
@@ -4033,25 +3992,10 @@ BOOL LLModelPreview::render()
 
 			if (physics)
 			{
-				// <FS:Beq> model upload improvements - use the settings
-				////Vector4a physicsFillColour(0.4, 0.4, 0.4, 0.4);
-				//const LLColor4 physicsFillColour(0.0, 0.5, 1.0, 0.5);
-				////LLVector4a physicsEdgeColour(1.0, 1.0, 0.0, 1.0);
-				//const LLColor4 physicsEdgeColour=physicsFillColour*0.5;
-				//const LLColor4 degenerateFill(1.0, 0.0, 0.0, 0.5);
-				//const LLColor4 degenerateEdge(1.0,0.0,0.0,1.0);
-				// </FS:Beq> 
-
 				glClear(GL_DEPTH_BUFFER_BIT);
-				//<FS:Beq> refactor to remove silly variable names
-				//				for (U32 i = 0; i < 2; i++)
 				for (U32 pass = 0; pass < 2; pass++)
-				//</FS:Beq>
 				{
-					//<FS:Beq> refactor to remove silly variable names
-					//if (i == 0)
 					if (pass == 0)
-					//</FS:Beq>
 					{ //depth only pass
 						gGL.setColorMask(false, false);
 					}
@@ -4061,10 +4005,7 @@ BOOL LLModelPreview::render()
 					}
 
 					//enable alpha blending on second pass but not first pass
-					//<FS:Beq> refactor to remove silly variable names
-					//LLGLState blend(GL_BLEND, i);
 					LLGLState blend(GL_BLEND, pass);
-					//</FS:Beq>
 
 					gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
 
@@ -4168,16 +4109,11 @@ BOOL LLModelPreview::render()
 						gGL.popMatrix();
 					}
 
-					//<FS:Beq> refactor to remove silly variable names
-					// also only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks]
-					//if (i > 0)
+					// only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks]
 					if (pass > 0 && mHasDegenerate)
-					//</FS:Beq>
 					{
 						glLineWidth(deg_edge_width);
 						glPointSize(deg_point_size);
-// <FS:Beq> This single line is why the degenerate triangles display has been crap forever. 
-// 						gPipeline.enableLightsFullbright(LLColor4::white);
 						//show degenerate triangles
 						LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS);
 						LLGLDisable cull(GL_CULL_FACE);
@@ -4215,13 +4151,10 @@ BOOL LLModelPreview::render()
 									}
 
 									auto num_degenerate = 0;
-									//<FS:Beq> More nested i variable silliness
-									//									for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i)
 									auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
 									for (U32 v = 0; v < num_models; ++v)
 									{
 										LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v];
-									//</FS:Beq>
 										if(buffer->getNumVerts() < 3)continue;
 
 										buffer->setBuffer(type_mask & buffer->getTypeMask());
@@ -4234,7 +4167,6 @@ BOOL LLModelPreview::render()
 										buffer->getIndexStrider(idx, 0);
 
 										LLVector4a v1, v2, v3;
-										//<FS:Beq> rename inner most i to avoid merge confusion
 										for (U32 indices_offset = 0; indices_offset < buffer->getNumIndices(); indices_offset += 3)
 										{
 											v1.setMul(pos[*idx++], scale);
@@ -4345,8 +4277,6 @@ BOOL LLModelPreview::render()
 							}
 						
 							buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
-							// <FS:Beq> configurable colour and width
-							//gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);
 
 							if (edges)
 							{
@@ -4357,7 +4287,6 @@ BOOL LLModelPreview::render()
 								glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 								glLineWidth(1.f);
 							}
-							// </FS:Beq>
 						}
 					}
 				}
@@ -4415,11 +4344,9 @@ void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians)
 void LLModelPreview::zoom(F32 zoom_amt)
 {
 	F32 new_zoom = mCameraZoom+zoom_amt;
-	// <FS:Beq> add configurable zoom TODO: stop clamping in render
-	// mCameraZoom = llclamp(new_zoom, 1.f, 10.f);
+	// TODO: stop clamping in render
 	static LLCachedControl<F32> zoom_limit(gSavedSettings, "MeshPreviewZoomLimit");
 	mCameraZoom	= llclamp(new_zoom, 1.f, zoom_limit());
-	// </FS:Beq>
 }
 
 void LLModelPreview::pan(F32 right, F32 up)
@@ -4625,12 +4552,6 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
 		childSetTextArg("server_weight", "[SIM]", tbd);
 		childSetTextArg("physics_weight", "[PH]", tbd);
 		childSetTextArg("upload_fee", "[FEE]", tbd);
-		// <FS:Beq> add extended info fields
-		//childSetTextArg("price_breakdown", "[STREAMING]", dashes);
-		//childSetTextArg("price_breakdown", "[PHYSICS]", dashes);
-		//childSetTextArg("price_breakdown", "[INSTANCES]", dashes);
-		//childSetTextArg("price_breakdown", "[TEXTURES]", dashes);
-		//childSetTextArg("price_breakdown", "[MODEL]", dashes);
 		std::string dashes = hasString("--") ? getString("--") : "--";
 		childSetTextArg("price_breakdown", "[STREAMING]", dashes);
 		childSetTextArg("price_breakdown", "[PHYSICS]", dashes);
@@ -4640,7 +4561,6 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
 		childSetTextArg("physics_breakdown", "[PCH]", dashes);
 		childSetTextArg("physics_breakdown", "[PM]", dashes);
 		childSetTextArg("physics_breakdown", "[PHU]", dashes);
-		// </FS:Beq>
 	}
 }
 
@@ -4690,7 +4610,7 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived()
 	childSetTextArg("price_breakdown", "[INSTANCES]", llformat("%d", result["upload_price_breakdown"]["mesh_instance"].asInteger()));
 	childSetTextArg("price_breakdown", "[TEXTURES]", llformat("%d", result["upload_price_breakdown"]["texture"].asInteger()));
 	childSetTextArg("price_breakdown", "[MODEL]", llformat("%d", result["upload_price_breakdown"]["model"].asInteger()));
-//<FS:Beq> Updates for enhanced Mesh feedback at upload
+
 	childSetTextArg("physics_breakdown", "[PCH]", llformat("%0.3f", result["model_physics_cost"]["hull"].asReal()));
 	childSetTextArg("physics_breakdown", "[PM]", llformat("%0.3f", result["model_physics_cost"]["mesh"].asReal()));
 	childSetTextArg("physics_breakdown", "[PHU]", llformat("%0.3f", result["model_physics_cost"]["decomposition"].asReal()));
@@ -4699,7 +4619,7 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived()
 	childSetTextArg("streaming_breakdown", "[STR_MED]", llformat("%d", result["streaming_params"]["medium_lod"].asInteger()));
 	childSetTextArg("streaming_breakdown", "[STR_LOW]", llformat("%d", result["streaming_params"]["low_lod"].asInteger()));
 	childSetTextArg("streaming_breakdown", "[STR_LOWEST]", llformat("%d", result["streaming_params"]["lowest_lod"].asInteger()));
-//</FS:Beq>
+
 	childSetVisible("upload_fee", true);
 	childSetVisible("price_breakdown", true);
 	mUploadBtn->setEnabled(isModelUploadAllowed());
-- 
cgit v1.2.3


From e906af7c3c6e4efd84b344c9572167f6ddf836fc Mon Sep 17 00:00:00 2001
From: maxim_productengine <mnikolenko@productengine.com>
Date: Fri, 11 Jan 2019 17:39:26 +0200
Subject: SL-10327 FIXED Scalable preview of mesh model is shown with black bar
 on top in the Upload Model menu

---
 indra/newview/app_settings/settings.xml               |  2 +-
 indra/newview/llfloatermodelpreview.cpp               | 19 +++++++++++++++++--
 .../skins/default/xui/en/floater_model_preview.xml    |  8 +++-----
 3 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 091992d1ab..76cfc03b53 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -8053,7 +8053,7 @@
   <key>PreviewRenderSize</key>  
   <map>
     <key>Comment</key>
-    <string>Resolution of the image rendered for the mesh upload preview</string>
+    <string>Resolution of the image rendered for the mesh upload preview (must be a power of 2)</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index ffc993e868..62157306b1 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -427,8 +427,23 @@ void LLFloaterModelPreview::initModelPreview()
 	{
 		delete mModelPreview;
 	}
-	auto size = gSavedSettings.getS32("PreviewRenderSize");
-	mModelPreview = new LLModelPreview(size, size, this );
+
+	S32 tex_width = 512;
+	S32 tex_height = 512;
+
+	S32 max_width = llmin(gSavedSettings.getS32("PreviewRenderSize"), (S32)gPipeline.mScreenWidth);
+	S32 max_height = llmin(gSavedSettings.getS32("PreviewRenderSize"), (S32)gPipeline.mScreenHeight);
+	
+	while ((tex_width << 1) <= max_width)
+	{
+		tex_width <<= 1;
+	}
+	while ((tex_height << 1) <= max_height)
+	{
+		tex_height <<= 1;
+	}
+
+	mModelPreview = new LLModelPreview(tex_width, tex_height, this);
 	mModelPreview->setPreviewTarget(16.f);
 	mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5));
 	mModelPreview->setModelUpdatedCallback(boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this, _1));
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 ec9104d5e7..1b038a0244 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1520,7 +1520,6 @@ Analysed:
     name="right_panel"
     top="0"
     left="640"
-    background_visible="true"
     width="375">
     <text
       follows="left|top"
@@ -1540,7 +1539,6 @@ Analysed:
       name="right_upper_panel"
       top="8"
       left="60"
-      background_visible="true"
       width="315">
       <combo_box
         top_pad="3"
@@ -1616,7 +1614,7 @@ Analysed:
       layout="topleft"
       left="2"
       name="physics_explode_label"
-      top="85"
+      bottom_pad="2"
       width="150">
       Preview Spread:
     </text>
@@ -1624,8 +1622,8 @@ Analysed:
       name="physics_explode"
       follows="right|bottom"
       valign="center"
-      top="15"
-      left="80"
+      left="105"
+      top_delta="-3"
       min_val="0.0"
       max_val="3.0"
       height="20"
-- 
cgit v1.2.3


From a988b230c2d321dbc7270b2da83645cffb35c4dd Mon Sep 17 00:00:00 2001
From: maxim_productengine <mnikolenko@productengine.com>
Date: Wed, 13 Feb 2019 16:55:22 +0200
Subject: SL-10328 FIXED Some parameters are not reset in the Upload Model menu
 when pressing button 'Clear settings & reset form'

---
 indra/newview/llfloatermodelpreview.cpp | 62 ++++++++++++++++++++++++++-------
 indra/newview/llfloatermodelpreview.h   |  5 ++-
 2 files changed, 53 insertions(+), 14 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 62157306b1..10a8a1af3b 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -304,10 +304,10 @@ BOOL LLFloaterModelPreview::postBuild()
 		getChild<LLSpinCtrl>("lod_triangle_limit_" + lod_name[lod])->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLODParamCommit, this, lod, true));
 	}
 
-	childSetCommitCallback("upload_skin", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL);
-	childSetCommitCallback("upload_joints", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL);
-	childSetCommitCallback("lock_scale_if_joint_position", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL);
-	childSetCommitCallback("upload_textures", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL);
+	childSetCommitCallback("upload_skin", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);
+	childSetCommitCallback("upload_joints", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);
+	childSetCommitCallback("lock_scale_if_joint_position", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);
+	childSetCommitCallback("upload_textures", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);
 
 	childSetTextArg("status", "[STATUS]", getString("status_idle"));
 
@@ -449,6 +449,16 @@ void LLFloaterModelPreview::initModelPreview()
 	mModelPreview->setModelUpdatedCallback(boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this, _1));
 }
 
+void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl)
+{
+	if (mModelPreview)
+	{
+		auto name = ctrl->getName();
+		mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name];
+	}
+	toggleCalculateButton(true);
+}
+
 void LLFloaterModelPreview::onViewOptionChecked(LLUICtrl* ctrl)
 {
 	if (mModelPreview)
@@ -631,6 +641,7 @@ void LLFloaterModelPreview::onGenerateNormalsCommit(LLUICtrl* ctrl, void* userda
 void LLFloaterModelPreview::toggleGenarateNormals()
 {
 	bool enabled = childGetValue("gen_normals").asBoolean();
+	mModelPreview->mViewOption["gen_normals"] = enabled;
 	childSetEnabled("crease_angle", enabled);
 	if(enabled) {
 		mModelPreview->generateNormals();
@@ -1156,10 +1167,8 @@ void LLFloaterModelPreview::initDecompControls()
 							std::string label = llformat("%.1f", value);
 							combo_box->add(label, value, ADD_BOTTOM, true);
 						}
-						combo_box->setValue(param[i].mDefault.mFloat);
-
 					}
-
+					combo_box->setValue(param[i].mDefault.mFloat);
 					combo_box->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
 				}
 			}
@@ -1231,7 +1240,7 @@ void LLFloaterModelPreview::initDecompControls()
 			//LL_INFOS() << "-----------------------------" << LL_ENDL;
 		}
 	}
-
+	mDefaultDecompParams = mDecompParams;
 	childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this);
 }
 
@@ -4423,6 +4432,7 @@ void LLFloaterModelPreview::onReset(void* user_data)
 	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
 
 	fmp->resetDisplayOptions();
+	fmp->resetUploadOptions();
 	//reset model preview
 	fmp->initModelPreview();
 
@@ -4536,11 +4546,6 @@ void LLFloaterModelPreview::setStatusMessage(const std::string& msg)
 	mStatusMessage = msg;
 }
 
-void LLFloaterModelPreview::toggleCalculateButton()
-{
-	toggleCalculateButton(true);
-}
-
 void LLFloaterModelPreview::toggleCalculateButton(bool visible)
 {
 	mCalculateBtn->setVisible(visible);
@@ -4602,6 +4607,37 @@ void LLFloaterModelPreview::resetDisplayOptions()
 	}
 }
 
+void LLFloaterModelPreview::resetUploadOptions()
+{
+	childSetValue("import_scale", 1);
+	childSetValue("pelvis_offset", 0);
+	childSetValue("physics_explode", 0);
+	childSetValue("physics_file", "");
+	childSetVisible("Retain%", false);
+	childSetVisible("Retain%_label", false);
+	childSetVisible("Detail Scale", true);
+	childSetVisible("Detail Scale label", true);
+
+	getChild<LLComboBox>("lod_source_" + lod_name[NUM_LOD - 1])->setCurrentByIndex(LLModelPreview::LOD_FROM_FILE);
+	for (S32 lod = 0; lod < NUM_LOD - 1; ++lod)
+	{
+		getChild<LLComboBox>("lod_source_" + lod_name[lod])->setCurrentByIndex(LLModelPreview::GENERATE);
+		childSetValue("lod_file_" + lod_name[lod], "");
+	}
+
+	getChild<LLComboBox>("physics_lod_combo")->setCurrentByIndex(0);
+
+	for(auto& p : mDefaultDecompParams)
+	{
+		std::string ctrl_name(p.first);
+		LLUICtrl* ctrl = getChild<LLUICtrl>(ctrl_name);
+		if (ctrl)
+		{
+			ctrl->setValue(p.second);
+		}
+	}
+}
+
 void LLFloaterModelPreview::onModelPhysicsFeeReceived(const LLSD& result, std::string upload_url)
 {
 	mModelPhysicsFee = result;
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 564f4c39de..41208daa64 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -109,6 +109,7 @@ public:
 	void 			loadModel(S32 lod, const std::string& file_name, bool force_disable_slm = false);
 	
 	void onViewOptionChecked(LLUICtrl* ctrl);
+	void onUploadOptionChecked(LLUICtrl* ctrl);
 	bool isViewOptionChecked(const LLSD& userdata);
 	bool isViewOptionEnabled(const LLSD& userdata);
 	void setViewOptionEnabled(const std::string& option, bool enabled);
@@ -179,6 +180,7 @@ protected:
 	LLModelPreview*	mModelPreview;
 	
 	LLPhysicsDecomp::decomp_params mDecompParams;
+	LLPhysicsDecomp::decomp_params mDefaultDecompParams;
 	
 	S32				mLastMouseX;
 	S32				mLastMouseY;
@@ -203,7 +205,6 @@ protected:
 
 private:
 	void onClickCalculateBtn();
-	void toggleCalculateButton();
 
 	void onLoDSourceCommit(S32 lod);
 
@@ -213,6 +214,8 @@ private:
 	// resets display options of model preview to their defaults.
 	void resetDisplayOptions();
 
+	void resetUploadOptions();
+
 	void createSmoothComboBox(LLComboBox* combo_box, float min, float max);
 
 	LLButton* mUploadBtn;
-- 
cgit v1.2.3


From 3cb20abfc928868fb6e95eaa78ea05b494d08af3 Mon Sep 17 00:00:00 2001
From: maxim_productengine <mnikolenko@productengine.com>
Date: Thu, 10 Jan 2019 15:41:48 +0200
Subject: SL-10329 Increase panel height to avoid message overlapping

---
 indra/newview/skins/default/xui/en/floater_model_preview.xml | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

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 1b038a0244..ceea5b7421 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -34,7 +34,6 @@
   <string name="mesh_status_invalid_material_list">LOD materials are not a subset of reference model.</string>
   <string name="phys_status_vertex_limit_exceeded">Some physical hulls exceed vertex limitations.</string>
   <string name="phys_status_degenerate_triangles">The physics mesh too dense remove the small thin triangles (see preview)</string>
-  <string name="phys_status_no_havok">The Firestorm OpenSim build is not supported for physics upload in SL.</string>
   <string name="layer_all">All</string> <!-- Text to display in physics layer combo box for "all layers" -->
   <string name="decomposing">Analyzing...</string>
   <string name="simplifying">Simplifying...</string>
@@ -1252,7 +1251,7 @@
     <panel
      follows="top|left|bottom"
      layout="topleft"
-     height="184"
+     height="197"
      left="4"
      border="true"
      name="weights_and_warning_panel"
@@ -1441,7 +1440,7 @@ Lowest:
         border="true"
         layout="topleft"
         left_pad="265"
-        name="price_breakdown_panel"
+        name="physics_costs_panel"
         width="120"
         height="100">
         <text
@@ -1509,7 +1508,7 @@ Analysed:
          visible="false">
          You dont have rights to upload mesh models. [[VURL] Find out how] to get certified.
        </text>
-       <text text_color="Yellow" layout="topleft" top_delta="5" left="6" name="status">
+       <text text_color="Yellow" layout="topleft" top_pad="-1" left="6" name="status">
 [STATUS]
        </text>
     </panel>
-- 
cgit v1.2.3


From 9bd1e26b0d7015c2da9f5e65e8ab8437c7e3bf57 Mon Sep 17 00:00:00 2001
From: maxim_productengine <mnikolenko@productengine.com>
Date: Mon, 25 Feb 2019 16:28:31 +0200
Subject: SL-10613 Set default 'Smooth' parameter to None

---
 indra/newview/llfloatermodelpreview.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 10a8a1af3b..5ce62d67d8 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -4625,8 +4625,6 @@ void LLFloaterModelPreview::resetUploadOptions()
 		childSetValue("lod_file_" + lod_name[lod], "");
 	}
 
-	getChild<LLComboBox>("physics_lod_combo")->setCurrentByIndex(0);
-
 	for(auto& p : mDefaultDecompParams)
 	{
 		std::string ctrl_name(p.first);
@@ -4636,6 +4634,8 @@ void LLFloaterModelPreview::resetUploadOptions()
 			ctrl->setValue(p.second);
 		}
 	}
+	getChild<LLComboBox>("physics_lod_combo")->setCurrentByIndex(0);
+	getChild<LLComboBox>("Cosine%")->setCurrentByIndex(0);
 }
 
 void LLFloaterModelPreview::onModelPhysicsFeeReceived(const LLSD& result, std::string upload_url)
-- 
cgit v1.2.3


From 4a0ebfad4234b59d5f9e6d9a8684b2126ad6292d Mon Sep 17 00:00:00 2001
From: maxim_productengine <mnikolenko@productengine.com>
Date: Wed, 27 Feb 2019 17:17:44 +0200
Subject: SL-10611 FIXED Unable to pan the mesh model with 'ctrl + shift +
 mouse drag' in the preview window when 'Weights' is ticked

---
 indra/newview/llfloatermodelpreview.cpp | 27 ++++++++++++++++++++-------
 indra/newview/llfloatermodelpreview.h   |  1 +
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 5ce62d67d8..19e1d77b38 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -108,6 +108,8 @@ const double RETAIN_COEFFICIENT = 100;
 // So this const is used as a size of Smooth combobox list.
 const S32 SMOOTH_VALUES_NUMBER = 10;
 
+const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f;
+
 void drawBoxOutline(const LLVector3& pos, const LLVector3& size);
 
 
@@ -328,7 +330,7 @@ BOOL LLFloaterModelPreview::postBuild()
 	getChild<LLCheckBoxCtrl>("show_edges")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
 	getChild<LLCheckBoxCtrl>("show_physics")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
 	getChild<LLCheckBoxCtrl>("show_textures")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
-	getChild<LLCheckBoxCtrl>("show_skin_weight")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
+	getChild<LLCheckBoxCtrl>("show_skin_weight")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onShowSkinWeightChecked, this, _1));
 	getChild<LLCheckBoxCtrl>("show_joint_positions")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
 
 	childDisable("upload_skin");
@@ -459,6 +461,15 @@ void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl)
 	toggleCalculateButton(true);
 }
 
+void LLFloaterModelPreview::onShowSkinWeightChecked(LLUICtrl* ctrl)
+{
+	if (mModelPreview)
+	{
+		mModelPreview->mCameraOffset.clearVec();
+		onViewOptionChecked(ctrl);
+	}
+}
+
 void LLFloaterModelPreview::onViewOptionChecked(LLUICtrl* ctrl)
 {
 	if (mModelPreview)
@@ -3877,10 +3888,9 @@ BOOL LLModelPreview::render()
 
 	if (skin_weight)
 	{
-		target_pos = getPreviewAvatar()->getPositionAgent();
+		target_pos = getPreviewAvatar()->getPositionAgent() + offset;
 		z_near = 0.01f;
 		z_far = 1024.f;
-		mCameraDistance = 16.f;
 
 		//render avatar previews every frame
 		refresh();
@@ -3898,8 +3908,9 @@ BOOL LLModelPreview::render()
 	LLQuaternion(mCameraYaw, LLVector3::z_axis);
 
 	LLQuaternion av_rot = camera_rot;
+	F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance;
 	LLViewerCamera::getInstance()->setOriginAndLookAt(
-													  target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot),		// camera
+													  target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera
 													  LLVector3::z_axis,																	// up
 													  target_pos);											// point of interest
 
@@ -4228,7 +4239,7 @@ BOOL LLModelPreview::render()
 			target_pos = getPreviewAvatar()->getPositionAgent();
 
 			LLViewerCamera::getInstance()->setOriginAndLookAt(
-															  target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot),		// camera
+															  target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera
 															  LLVector3::z_axis,																	// up
 															  target_pos);											// point of interest
 
@@ -4375,8 +4386,10 @@ void LLModelPreview::zoom(F32 zoom_amt)
 
 void LLModelPreview::pan(F32 right, F32 up)
 {
-	mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f);
-	mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f);
+	bool skin_weight = mViewOption["show_skin_weight"];
+	F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance;
+	mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f);
+	mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f);
 }
 
 void LLModelPreview::setPreviewLOD(S32 lod)
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 41208daa64..096544cdbf 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -115,6 +115,7 @@ public:
 	void setViewOptionEnabled(const std::string& option, bool enabled);
 	void enableViewOption(const std::string& option);
 	void disableViewOption(const std::string& option);
+	void onShowSkinWeightChecked(LLUICtrl* ctrl);
 
 	bool isModelLoading();
 
-- 
cgit v1.2.3


From 5614f6cbf6867b0114a45aadbe1d975a3d71000c Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Thu, 29 Nov 2018 21:04:35 +0200
Subject: SL-9747 [Mesh Uploader] Remove confusing model_metric from upload

---
 indra/llprimitive/llmodel.h                        |  3 ---
 indra/newview/llfloatermodelpreview.cpp            |  3 ---
 indra/newview/llmeshrepository.cpp                 | 14 +----------
 .../skins/default/xui/en/floater_model_preview.xml | 29 ----------------------
 4 files changed, 1 insertion(+), 48 deletions(-)

diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index cf3288489a..51fa2f8079 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -154,7 +154,6 @@ public:
 	void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); }
 
 	std::string getName() const;
-	std::string getMetric() const {return mMetric;}
 	EModelStatus getStatus() const {return mStatus;}
 	static std::string getStatusString(U32 status) ;
 
@@ -260,8 +259,6 @@ public:
 	std::string mRequestedLabel; // name requested in UI, if any.
 	std::string mLabel; // name computed from dae.
 
-	std::string mMetric; // user-supplied metric data for upload
-
 	LLVector3 mNormalizedScale;
 	LLVector3 mNormalizedTranslation;
 
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 19e1d77b38..8be405df9e 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1496,8 +1496,6 @@ void LLModelPreview::rebuildUploadData()
 
 	std::string requested_name = mFMP->getChild<LLUICtrl>("description_form")->getValue().asString();
 
-	std::string metric = mFMP->getChild<LLUICtrl>("model_category_combo")->getValue().asString();
-
 	LLSpinCtrl* scale_spinner = mFMP->getChild<LLSpinCtrl>("import_scale");
 
 	F32 scale = scale_spinner->getValue().asReal();
@@ -1538,7 +1536,6 @@ void LLModelPreview::rebuildUploadData()
 			if (base_model && !requested_name.empty())
 			{
 				base_model->mRequestedLabel = requested_name;
-				base_model->mMetric = metric;
 			}
 
 			for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--)
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index e38bd8846d..a91817ba8a 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2174,7 +2174,6 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 
 	std::map<LLModel*,S32> mesh_index;
 	std::string model_name;
-	std::string model_metric;
 
 	S32 instance_num = 0;
 	
@@ -2204,11 +2203,6 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 				model_name = data.mBaseModel->getName();
 			}
 
-			if (model_metric.empty())
-			{
-				model_metric = data.mBaseModel->getMetric();
-			}
-
 			std::stringstream ostr;
 			
 			LLModel::Decomposition& decomp =
@@ -2363,11 +2357,6 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 				model_name = data.mBaseModel->getName();
 			}
 
-			if (model_metric.empty())
-			{
-				model_metric = data.mBaseModel->getMetric();
-			}
-
 			std::stringstream ostr;
 			
 			LLModel::Decomposition& decomp =
@@ -2498,8 +2487,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 
 	if (model_name.empty()) model_name = "mesh model";
 	result["name"] = model_name;
-	if (model_metric.empty()) model_metric = "MUT_Unspecified";
-	res["metric"] = model_metric;
+	res["metric"] = "MUT_Unspecified";
 	result["asset_resources"] = res;
 	dump_llsd_to_file(result,make_dump_name("whole_model_",dump_num));
 
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 ceea5b7421..274e6e6c7a 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -74,35 +74,6 @@
           prevalidate_callback="ascii"
           top_pad="5"
           width="290" />
-        <text
-          follows="left|top"
-          height="15"
-          layout="topleft"
-          left_pad="15"
-          name="model_category_label"
-          text_color="White"
-          top="0"
-          width="200">
-          This model represents...
-        </text>
-        <combo_box
-          follows="left|top"
-          height="23"
-          left_pad="10"
-          name="model_category_combo"
-          top_pad="10"
-          width="200">
-        <combo_box.drop_down_button
-          label_color="White"/>
-          <combo_item name="Choose one" label="Choose One..." value="MUT_Unspecified"/>
-          <combo_item name="Avatar shape" label="Avatar shape" value="MUT_AvatarShape"/>
-          <combo_item name="Avatar attachment" label="Avatar attachment" value="MUT_AvatarAttachment"/>
-          <combo_item name="Moving object (vehicle, animal)" label="Moving object (vehicle, animal)" value="MUT_MovingObject"/>
-          <combo_item name="Building Component" label="Building Component" value="MUT_BuildingComponent"/>
-          <combo_item name="Large, non moving etc" label="Large, non moving etc" value="MUT_LargeStationary"/>
-          <combo_item name="Smaller, non-moving etc" label="Smaller, non-moving etc" value="MUT_SmallStationary"/>
-          <combo_item name="Not really any of these" label="Not really any of these" value="MUT_Other"/>
-         </combo_box>
     </panel>
     <tab_container
       follows="top|left"
-- 
cgit v1.2.3


From 580b35c8ea59187d5197e712022b706df3655f86 Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Mon, 1 Apr 2019 20:22:25 +0300
Subject: SL-307 Display in-viewer all warning messages logged by the mesh
 uploader

---
 indra/llprimitive/llmodelloader.cpp                |   4 +
 indra/llprimitive/llmodelloader.h                  |   5 +
 indra/llui/lltabcontainer.cpp                      |   4 +
 indra/llui/lltabcontainer.h                        |   6 +
 indra/llui/lltextbase.cpp                          |  12 ++
 indra/llui/lltextbase.h                            |   2 +
 indra/newview/llfloatermodelpreview.cpp            | 126 ++++++++++++++++++---
 indra/newview/llfloatermodelpreview.h              |   9 +-
 .../skins/default/xui/en/floater_model_preview.xml |  39 ++++++-
 9 files changed, 191 insertions(+), 16 deletions(-)

diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index 4e468ff45f..c8da68afc8 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -146,6 +146,7 @@ LLModelLoader::~LLModelLoader()
 
 void LLModelLoader::run()
 {
+	mWarningStream.clear();
 	doLoadModel();
 	doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
 }
@@ -426,6 +427,7 @@ bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAs
     {
         LL_WARNS() << "Rigged to " << jointListFromAsset.size() << " joints, max is " << mMaxJointsPerMesh << LL_ENDL;
         LL_WARNS() << "Skinning disabled due to too many joints" << LL_ENDL;
+        mWarningStream << "Skinning disabled due to too many joints, maximum amount per mesh: " << mMaxJointsPerMesh << "\n";
         return false;
     }
 
@@ -437,12 +439,14 @@ bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAs
         if (mJointMap.find(*it)==mJointMap.end())
         {
             LL_WARNS() << "Rigged to unrecognized joint name " << *it << LL_ENDL;
+            mWarningStream << "Rigged to unrecognized joint name " << *it << "\n";
             unknown_joint_count++;
         }
     }
     if (unknown_joint_count>0)
     {
         LL_WARNS() << "Skinning disabled due to unknown joints" << LL_ENDL;
+        mWarningStream << "Skinning disabled due to unknown joints\n";
         return false;
     }
 
diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h
index 643c45a6d8..8dde176b54 100644
--- a/indra/llprimitive/llmodelloader.h
+++ b/indra/llprimitive/llmodelloader.h
@@ -185,6 +185,9 @@ public:
 		return name != NULL && mJointMap.find(name) != mJointMap.end();
 	}
 
+	std::string logOut() { return mWarningStream.str(); }
+	void clearLog() { mWarningStream.clear(); }
+
 protected:
 
 	LLModelLoader::load_callback_t		mLoadCallback;
@@ -201,6 +204,8 @@ protected:
 
 	JointTransformMap	mJointTransformMap;
 
+	std::ostringstream mWarningStream; // preview floater will pull logs from here
+
 	static std::list<LLModelLoader*> sActiveLoaderList;
 	static bool isAlive(LLModelLoader* loader) ;
 };
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 1b2f09cff5..0af97bfab2 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -217,6 +217,7 @@ LLTabContainer::Params::Params()
 	last_tab("last_tab"),
 	use_custom_icon_ctrl("use_custom_icon_ctrl", false),
 	open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false),
+	enable_tabs_flashing("enable_tabs_flashing", false),
 	tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
 	use_ellipses("use_ellipses"),
 	font_halign("halign")
@@ -1090,6 +1091,9 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
 		    p.pad_left( mLabelPadLeft );
 		    p.pad_right(2);
 		}
+
+		// inits flash timer
+		p.button_flash_enable = mEnableTabsFlashing;
 		
 		// *TODO : It seems wrong not to use p in both cases considering the way p is initialized
 		if (mCustomIconCtrlUsed)
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 4a5f08f5d3..a8cf380333 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -109,6 +109,11 @@ public:
 		 * Open tabs on hover in drag and drop situations
 		 */
 		Optional<bool>						open_tabs_on_drag_and_drop;
+
+		/**
+		 * Open tabs on hover in drag and drop situations
+		 */
+		Optional<bool>						enable_tabs_flashing;
 		
 		/**
 		 *  Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true)
@@ -308,6 +313,7 @@ private:
 
 	bool							mCustomIconCtrlUsed;
 	bool							mOpenTabsOnDragAndDrop;
+	bool							mEnableTabsFlashing;
 	S32								mTabIconCtrlPad;
 	bool							mUseTabEllipses;
 };
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index c570285856..f4713e19ba 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -2215,6 +2215,18 @@ void LLTextBase::needsReflow(S32 index)
 	mReflowIndex = llmin(mReflowIndex, index);
 }
 
+S32	LLTextBase::removeFirstLine()
+{
+    if (!mLineInfoList.empty())
+    {
+        S32 length = getLineEnd(0);
+        deselect();
+        removeStringNoUndo(0, length);
+        return length;
+    }
+    return 0;
+}
+
 void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params)
 {
 	segment_vec_t segments;
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 5fdde445ef..6913374bac 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -401,6 +401,7 @@ public:
 	virtual void			setText(const LLStringExplicit &utf8str , const LLStyle::Params& input_params = LLStyle::Params()); // uses default style
 	virtual std::string		getText() const;
 	void					setMaxTextLength(S32 length) { mMaxTextByteLength = length; }
+	S32						getMaxTextLength() { return mMaxTextByteLength; }
 
 	// wide-char versions
 	void					setWText(const LLWString& text);
@@ -429,6 +430,7 @@ public:
 
 	S32						getLength() const { return getWText().length(); }
 	S32						getLineCount() const { return mLineInfoList.size(); }
+	S32						removeFirstLine(); // returns removed length
 
 	void					addDocumentChild(LLView* view);
 	void					removeDocumentChild(LLView* view);
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 8be405df9e..6387dfee55 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -76,6 +76,7 @@
 #include "llsdserialize.h"
 #include "llsliderctrl.h"
 #include "llspinctrl.h"
+#include "lltabcontainer.h"
 #include "lltoggleablemenu.h"
 #include "lltrans.h"
 #include "llvfile.h"
@@ -83,6 +84,7 @@
 #include "llcallbacklist.h"
 #include "llviewerobjectlist.h"
 #include "llanimationstates.h"
+#include "llviewertexteditor.h"
 #include "llviewernetwork.h"
 #include "llviewershadermgr.h"
 
@@ -263,7 +265,9 @@ void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LL
 LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) :
 LLFloaterModelUploadBase(key),
 mUploadBtn(NULL),
-mCalculateBtn(NULL)
+mCalculateBtn(NULL),
+mUploadLogText(NULL),
+mTabContainer(NULL)
 {
 	sInstance = this;
 	mLastMouseX = 0;
@@ -392,6 +396,13 @@ BOOL LLFloaterModelPreview::postBuild()
 
 	mUploadBtn = getChild<LLButton>("ok_btn");
 	mCalculateBtn = getChild<LLButton>("calculate_btn");
+	mUploadLogText = getChild<LLViewerTextEditor>("log_text");
+	mTabContainer = getChild<LLTabContainer>("import_tab");
+
+	// Disable Logs tab untill it has something to show
+	LLPanel* panel = mTabContainer->getPanelByName("logs_panel");
+	S32 index = mTabContainer->getIndexForPanel(panel);
+	mTabContainer->enableTabButton(index, false);
 
 	if (LLConvexDecomposition::getInstance() != NULL)
 	{
@@ -1280,6 +1291,64 @@ void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handl
 	gViewerWindow->showCursor();
 }
 
+//-----------------------------------------------------------------------------
+// addStringToLog()
+//-----------------------------------------------------------------------------
+// static
+void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash)
+{
+    if (sInstance)
+    {
+        sInstance->addStringToLogTab(str, flash);
+    }
+}
+
+// static
+void LLFloaterModelPreview::addStringToLog(const std::ostringstream& strm, bool flash)
+{
+    if (sInstance)
+    {
+        sInstance->addStringToLogTab(strm.str(), flash);
+    }
+}
+
+//-----------------------------------------------------------------------------
+// addStringToLogTab()
+//-----------------------------------------------------------------------------
+void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash)
+{
+    if (str.empty())
+    {
+        return;
+    }
+
+    LLWString text = utf8str_to_wstring(str);
+    S32 add_text_len = text.length() + 1; // newline
+    S32 editor_max_len = mUploadLogText->getMaxTextLength();
+    if (add_text_len > editor_max_len)
+    {
+        return;
+    }
+
+    LLPanel* panel = mTabContainer->getPanelByName("logs_panel");
+    S32 index = mTabContainer->getIndexForPanel(panel);
+    mTabContainer->enableTabButton(index, true);
+
+    // Make sure we have space for new string
+    S32 editor_text_len = mUploadLogText->getLength();
+    while (editor_max_len < (editor_text_len + add_text_len))
+    {
+        editor_text_len -= mUploadLogText->removeFirstLine();
+    }
+
+    mUploadLogText->appendText(str, true);
+
+    if (flash && mTabContainer->getCurrentPanel() != panel)
+    {
+        mTabContainer->setTabPanelFlashing(panel, true);
+    }
+}
+
 //-----------------------------------------------------------------------------
 // LLModelPreview
 //-----------------------------------------------------------------------------
@@ -1710,8 +1779,14 @@ void LLModelPreview::rebuildUploadData()
                     LLQuaternion identity;
                     if (!bind_rot.isEqualEps(identity,0.01))
                     {
-                        LL_WARNS() << "non-identity bind shape rot. mat is " << high_lod_model->mSkinInfo.mBindShapeMatrix 
-                                   << " bind_rot " << bind_rot << LL_ENDL;
+                        std::ostringstream out;
+                        out << "non-identity bind shape rot. mat is ";
+                        out << high_lod_model->mSkinInfo.mBindShapeMatrix;
+                        out << " bind_rot ";
+                        out << bind_rot;
+                        LL_WARNS() << out.str() << LL_ENDL;
+
+                        LLFloaterModelPreview::addStringToLog(out, false);
                         setLoadState( LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION );
                     }
                 }
@@ -1882,7 +1957,11 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
 
 	if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1)
 	{
-		LL_WARNS() << "Invalid level of detail: " << lod << LL_ENDL;
+		std::ostringstream out;
+		out << "Invalid level of detail: ";
+		out << lod;
+		LL_WARNS() << out.str() << LL_ENDL;
+		LLFloaterModelPreview::addStringToLog(out, true);
 		assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);
 		return;
 	}
@@ -2259,7 +2338,12 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 
 								if (importerDebug)
 								{
-									LL_WARNS() << "Loded model name " << mModel[loaded_lod][idx]->mLabel << " for LOD " << loaded_lod << " doesn't match the base model. Renaming to " << name << LL_ENDL;
+									std::ostringstream out;
+									out << "Loded model name " << mModel[loaded_lod][idx]->mLabel;
+									out << " for LOD " << loaded_lod;
+									out << " doesn't match the base model. Renaming to " << name;
+									LL_WARNS() << out.str() << LL_ENDL;
+									LLFloaterModelPreview::addStringToLog(out, false);
 								}
 
 								mModel[loaded_lod][idx]->mLabel = name;
@@ -2417,7 +2501,10 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
 	// Allow LoD from -1 to LLModel::LOD_PHYSICS
 	if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1)
 	{
-		LL_WARNS() << "Invalid level of detail: " << which_lod << LL_ENDL;
+		std::ostringstream out;
+		out << "Invalid level of detail: " << which_lod;
+		LL_WARNS() << out.str() << LL_ENDL;
+		LLFloaterModelPreview::addStringToLog(out, false);
 		assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS);
 		return;
 	}
@@ -3288,7 +3375,10 @@ void LLModelPreview::updateLodControls(S32 lod)
 {
 	if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH)
 	{
-		LL_WARNS() << "Invalid level of detail: " << lod << LL_ENDL;
+		std::ostringstream out;
+		out << "Invalid level of detail: " << lod;
+		LL_WARNS() << out.str() << LL_ENDL;
+		LLFloaterModelPreview::addStringToLog(out, false);
 		assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH);
 		return;
 	}
@@ -3484,9 +3574,12 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
 			if (!vb->allocateBuffer(num_vertices, num_indices, TRUE))
 			{
 				// We are likely to crash due this failure, if this happens, find a way to gracefully stop preview
-				LL_WARNS() << "Failed to allocate Vertex Buffer for model preview "
-					<< num_vertices << " vertices and "
-					<< num_indices << " indices" << LL_ENDL;
+									std::ostringstream out;
+									out << "Failed to allocate Vertex Buffer for model preview ";
+									out << num_vertices << " vertices and ";
+									out << num_indices << " indices";
+									LL_WARNS() << out.str() << LL_ENDL;
+									LLFloaterModelPreview::addStringToLog(out, true);
 			}
 
 			LLStrider<LLVector3> vertex_strider;
@@ -3634,8 +3727,11 @@ void LLModelPreview::loadedCallback(
 	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
 	if (pPreview && !LLModelPreview::sIgnoreLoadedCallback)
 	{
-		pPreview->loadModelCallback(lod);
-	}	
+		LLFloaterModelPreview::addStringToLog(pPreview->mModelLoader->logOut(), true);
+		pPreview->mModelLoader->clearLog();
+		pPreview->loadModelCallback(lod); // removes mModelLoader in some cases
+	}
+
 }
 
 void LLModelPreview::stateChangedCallback(U32 state,void* opaque)
@@ -4688,7 +4784,11 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived()
 
 void LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(S32 status, const std::string& reason)
 {
-	LL_WARNS() << "LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(" << status << " : " << reason << ")" << LL_ENDL;
+	std::ostringstream out;
+	out << "LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(" << status;
+	out << " : " << reason << ")";
+	LL_WARNS() << out.str() << LL_ENDL;
+	LLFloaterModelPreview::addStringToLog(out, false);
 	doOnIdleOneTime(boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this, true));
 }
 
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 096544cdbf..c459b9296b 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -57,7 +57,9 @@ class domController;
 class domSkin;
 class domMesh;
 class LLMenuButton;
+class LLTabContainer;
 class LLToggleableMenu;
+class LLViewerTextEditor;
 
 class LLFloaterModelPreview : public LLFloaterModelUploadBase
 {
@@ -93,6 +95,8 @@ public:
 
 	static void onMouseCaptureLostModelPreview(LLMouseHandler*);
 	static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+	static void addStringToLog(const std::string& str, bool flash);
+	static void addStringToLog(const std::ostringstream& strm, bool flash);
 
 	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
 	void setPreviewLOD(S32 lod);
@@ -176,7 +180,8 @@ protected:
     // FIXME - this function and mStatusMessage have no visible effect, and the
     // actual status messages are managed by directly manipulation of
     // the UI element.
-	void setStatusMessage(const std::string& msg);
+    void setStatusMessage(const std::string& msg);
+    void addStringToLogTab(const std::string& str, bool flash);
 
 	LLModelPreview*	mModelPreview;
 	
@@ -221,6 +226,8 @@ private:
 
 	LLButton* mUploadBtn;
 	LLButton* mCalculateBtn;
+	LLViewerTextEditor* mUploadLogText;
+	LLTabContainer* mTabContainer;
 };
 
 class LLMeshFilePicker : public LLFilePickerThread
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 274e6e6c7a..cbc5aeb37d 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -82,7 +82,8 @@
       height="300"
       width="635"
       name="import_tab"
-      tab_position="top">
+      tab_position="top"
+      enable_tabs_flashing="true">
       <!-- LOD PANEL -->
         <panel
          help_topic="upload_model_lod"
@@ -98,7 +99,7 @@
              left="3"
              name="lod_tab_border"
              top_pad="0"
-             width="629" />
+             width="619" />
           <text
            follows="left|top"
            height="18"
@@ -1218,6 +1219,40 @@
              value="0.0"
              width="80"/>
      </panel>
+      <panel
+       label="Log"
+       layout="topleft"
+       name="logs_panel"
+       title="Log">
+        <view_border
+         bevel_style="none"
+         follows="top|left"
+         height="275"
+         layout="topleft"
+         left="3"
+         name="log_tab_border"
+         top_pad="0"
+         width="619" />
+        <text_editor
+         type="string"
+         length="1"
+         embedded_items="false"
+         follows="top|left"
+         font="SansSerif"
+         ignore_tab="false"
+         layout="topleft"
+         height="275"
+         left="4"
+         top="0"
+         right="-11"
+         max_length="65536"
+         name="log_text"
+         parse_urls="true"
+         spellcheck="false"
+         read_only="true"
+         word_wrap="true">
+        </text_editor>
+      </panel>
     </tab_container>
     <panel
      follows="top|left|bottom"
-- 
cgit v1.2.3


From 6d9601a4d4153fc7e4a0c7bd934ccabafe293a44 Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Tue, 28 May 2019 20:38:37 +0300
Subject: SL-1105 Pack a name for server to use

---
 indra/newview/llmeshrepository.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 38fd2d777e..31e3d408d7 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2277,6 +2277,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 			instance_entry["material"] = LL_MCODE_WOOD;
 			instance_entry["physics_shape_type"] = data.mModel[LLModel::LOD_PHYSICS].notNull() ? (U8)(LLViewerObject::PHYSICS_SHAPE_PRIM) : (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
 			instance_entry["mesh"] = mesh_index[data.mBaseModel];
+			instance_entry["mesh_name"] = instance.mLabel;
 
 			instance_entry["face_list"] = LLSD::emptyArray();
 
-- 
cgit v1.2.3


From 1840868178cbf3577ffa9757f093207f3bb10fd7 Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Wed, 27 Nov 2019 20:34:26 +0200
Subject: SL-304 [Mesh Uploader] Localization support for logs, better warnings
 for joints

---
 indra/llprimitive/llmodelloader.cpp                | 42 +++++++-----
 indra/llprimitive/llmodelloader.h                  | 19 ++++--
 indra/newview/llfloatermodelpreview.cpp            | 76 ++++++++++++++++++----
 indra/newview/llfloatermodelpreview.h              | 10 +--
 .../skins/default/xui/en/floater_model_preview.xml | 31 ++++++++-
 5 files changed, 136 insertions(+), 42 deletions(-)

diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index c8da68afc8..5171621007 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -127,7 +127,7 @@ LLModelLoader::LLModelLoader(
 , mStateCallback(state_cb)
 , mOpaqueData(opaque_userdata)
 , mRigValidJointUpload(true)
-, mLegacyRigValid(true)
+, mLegacyRigFlags(0)
 , mNoNormalize(false)
 , mNoOptimize(false)
 , mCacheOnlyHitIfRigged(false)
@@ -136,6 +136,7 @@ LLModelLoader::LLModelLoader(
 {    
 	assert_main_thread();
 	sActiveLoaderList.push_back(this) ;
+	mWarningsArray = LLSD::emptyArray();
 }
 
 LLModelLoader::~LLModelLoader()
@@ -146,7 +147,7 @@ LLModelLoader::~LLModelLoader()
 
 void LLModelLoader::run()
 {
-	mWarningStream.clear();
+	mWarningsArray.clear();
 	doLoadModel();
 	doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
 }
@@ -388,7 +389,7 @@ void LLModelLoader::critiqueRigForUploadApplicability( const std::vector<std::st
 	//2. It is suitable for upload as standard av with just skin weights
 	
 	bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset );
-	bool isRigLegacyOK			 = isRigLegacy( jointListFromAsset );
+	U32 legacy_rig_flags		 = determineRigLegacyFlags( jointListFromAsset );
 
 	// It's OK that both could end up being true.
 
@@ -402,19 +403,16 @@ void LLModelLoader::critiqueRigForUploadApplicability( const std::vector<std::st
 		setRigValidForJointPositionUpload( false );
 	}
 
-	if ( !isRigLegacyOK) 
-	{	
-        // This starts out true, becomes false if false for any loaded
-        // mesh. 
-		setLegacyRigValid( false );
-	}
+	legacy_rig_flags |= getLegacyRigFlags();
+	// This starts as 0, changes if any loaded mesh has issues
+	setLegacyRigFlags(legacy_rig_flags);
 
 }
 
 //-----------------------------------------------------------------------------
-// isRigLegacy()
+// determineRigLegacyFlags()
 //-----------------------------------------------------------------------------
-bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAsset )
+U32 LLModelLoader::determineRigLegacyFlags( const std::vector<std::string> &jointListFromAsset )
 {
 	//No joints in asset
 	if ( jointListFromAsset.size() == 0 )
@@ -427,8 +425,12 @@ bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAs
     {
         LL_WARNS() << "Rigged to " << jointListFromAsset.size() << " joints, max is " << mMaxJointsPerMesh << LL_ENDL;
         LL_WARNS() << "Skinning disabled due to too many joints" << LL_ENDL;
-        mWarningStream << "Skinning disabled due to too many joints, maximum amount per mesh: " << mMaxJointsPerMesh << "\n";
-        return false;
+        LLSD args;
+        args["Message"] = "TooManyJoint";
+        args["[JOINTS]"] = LLSD::Integer(jointListFromAsset.size());
+        args["[MAX]"] = LLSD::Integer(mMaxJointsPerMesh);
+        mWarningsArray.append(args);
+        return LEGACY_RIG_FLAG_TOO_MANY_JOINTS;
     }
 
     // Unknown joints in asset
@@ -439,18 +441,24 @@ bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAs
         if (mJointMap.find(*it)==mJointMap.end())
         {
             LL_WARNS() << "Rigged to unrecognized joint name " << *it << LL_ENDL;
-            mWarningStream << "Rigged to unrecognized joint name " << *it << "\n";
+            LLSD args;
+            args["Message"] = "UnrecognizedJoint";
+            args["[NAME]"] = *it;
+            mWarningsArray.append(args);
             unknown_joint_count++;
         }
     }
     if (unknown_joint_count>0)
     {
         LL_WARNS() << "Skinning disabled due to unknown joints" << LL_ENDL;
-        mWarningStream << "Skinning disabled due to unknown joints\n";
-        return false;
+        LLSD args;
+        args["Message"] = "UnknownJoints";
+        args["[COUNT]"] = LLSD::Integer(unknown_joint_count);
+        mWarningsArray.append(args);
+        return LEGACY_RIG_FLAG_UNKNOWN_JOINT;
     }
 
-	return true;
+    return LEGACY_RIG_OK;
 }
 //-----------------------------------------------------------------------------
 // isRigSuitableForJointPositionUpload()
diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h
index 8dde176b54..fbc74554a0 100644
--- a/indra/llprimitive/llmodelloader.h
+++ b/indra/llprimitive/llmodelloader.h
@@ -42,6 +42,10 @@ typedef std::deque<std::string>						JointNameSet;
 const S32 SLM_SUPPORTED_VERSION	= 3;
 const S32 NUM_LOD						= 4;
 
+const U32 LEGACY_RIG_OK = 0;
+const U32 LEGACY_RIG_FLAG_TOO_MANY_JOINTS = 1;
+const U32 LEGACY_RIG_FLAG_UNKNOWN_JOINT = 2;
+
 class LLModelLoader : public LLThread
 {
 public:
@@ -166,7 +170,7 @@ public:
 	void critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset );
 
 	//Determines if a rig is a legacy from the joint list
-	bool isRigLegacy( const std::vector<std::string> &jointListFromAsset );
+	U32 determineRigLegacyFlags( const std::vector<std::string> &jointListFromAsset );
 
 	//Determines if a rig is suitable for upload
 	bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset );
@@ -174,8 +178,9 @@ public:
 	const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; }
 	void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }
 
-	const bool isLegacyRigValid( void ) const { return mLegacyRigValid; }
-	void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }		
+	const bool isLegacyRigValid(void) const { return mLegacyRigFlags == 0; }
+	U32 getLegacyRigFlags() const { return mLegacyRigFlags; }
+	void setLegacyRigFlags( U32 rigFlags ) { mLegacyRigFlags = rigFlags; }
 
 	//-----------------------------------------------------------------------------
 	// isNodeAJoint()
@@ -185,8 +190,8 @@ public:
 		return name != NULL && mJointMap.find(name) != mJointMap.end();
 	}
 
-	std::string logOut() { return mWarningStream.str(); }
-	void clearLog() { mWarningStream.clear(); }
+	const LLSD logOut() const { return mWarningsArray; }
+	void clearLog() { mWarningsArray.clear(); }
 
 protected:
 
@@ -197,14 +202,14 @@ protected:
 	void*								mOpaqueData;
 
 	bool		mRigValidJointUpload;
-	bool		mLegacyRigValid;
+	U32			mLegacyRigFlags;
 
 	bool		mNoNormalize;
 	bool		mNoOptimize;
 
 	JointTransformMap	mJointTransformMap;
 
-	std::ostringstream mWarningStream; // preview floater will pull logs from here
+	LLSD mWarningsArray; // preview floater will pull logs from here
 
 	static std::list<LLModelLoader*> sActiveLoaderList;
 	static bool isAlive(LLModelLoader* loader) ;
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index e1665dc255..b3012ed025 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -343,6 +343,9 @@ BOOL LLFloaterModelPreview::postBuild()
 	childDisable("upload_joints");
 	childDisable("lock_scale_if_joint_position");
 
+	childSetVisible("skin_too_many_joints", false);
+	childSetVisible("skin_unknown_joint", false);
+
 	initDecompControls();
 
 	LLView* preview_panel = getChild<LLView>("preview_panel");
@@ -1306,6 +1309,34 @@ void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handl
 //-----------------------------------------------------------------------------
 // addStringToLog()
 //-----------------------------------------------------------------------------
+//static
+void LLFloaterModelPreview::addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod)
+{
+    if (sInstance && sInstance->hasString(message))
+    {
+        std::string str;
+        switch (lod)
+        {
+        case LLModel::LOD_IMPOSTOR: str = "LOD0 "; break;
+        case LLModel::LOD_LOW:      str = "LOD1 "; break;
+        case LLModel::LOD_MEDIUM:   str = "LOD2 "; break;
+        case LLModel::LOD_PHYSICS:  str = "PHYS "; break;
+        case LLModel::LOD_HIGH:     str = "LOD3 ";   break;
+        default: break;
+        }
+        
+        LLStringUtil::format_map_t args_msg;
+        LLSD::map_const_iterator iter = args.beginMap();
+        LLSD::map_const_iterator end = args.endMap();
+        for (; iter != end; ++iter)
+        {
+            args_msg[iter->first] = iter->second.asString();
+        }
+        str += sInstance->getString(message, args_msg);
+        sInstance->addStringToLogTab(str, flash);
+    }
+}
+
 // static
 void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash)
 {
@@ -1370,7 +1401,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
 , mLodsQuery()
 , mLodsWithParsingError()
 , mPelvisZOffset( 0.0f )
-, mLegacyRigValid( false )
+, mLegacyRigFlags( U32_MAX )
 , mRigValidJointUpload( false )
 , mPhysicsSearchLOD( LLModel::LOD_PHYSICS )
 , mResetJoints( false )
@@ -2160,7 +2191,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 	// Copy determinations about rig so UI will reflect them
 	//
 	setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());
-	setLegacyRigValid(mModelLoader->isLegacyRigValid());
+	setLegacyRigFlags(mModelLoader->getLegacyRigFlags());
 
 	mModelLoader->loadTextures() ;
 
@@ -3739,9 +3770,18 @@ void LLModelPreview::loadedCallback(
 	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
 	if (pPreview && !LLModelPreview::sIgnoreLoadedCallback)
 	{
-		LLFloaterModelPreview::addStringToLog(pPreview->mModelLoader->logOut(), true);
-		pPreview->mModelLoader->clearLog();
-		pPreview->loadModelCallback(lod); // removes mModelLoader in some cases
+        const LLSD out = pPreview->mModelLoader->logOut();
+        LLSD::array_const_iterator iter_out = out.beginArray();
+        LLSD::array_const_iterator end_out = out.endArray();
+        for (; iter_out != end_out; ++iter_out)
+        {
+            if (iter_out->has("Message"))
+            {
+                LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod);
+            }
+        }
+        pPreview->mModelLoader->clearLog();
+        pPreview->loadModelCallback(lod); // removes mModelLoader in some cases
 	}
 
 }
@@ -3920,13 +3960,25 @@ BOOL LLModelPreview::render()
 
 	if (has_skin_weights && lodsReady())
 	{ //model has skin weights, enable view options for skin weights and joint positions
-		if (fmp && isLegacyRigValid() )
-		{
-			fmp->enableViewOption("show_skin_weight");
-			fmp->setViewOptionEnabled("show_joint_positions", skin_weight);	
-			mFMP->childEnable("upload_skin");
-			mFMP->childSetValue("show_skin_weight", skin_weight);
-		}
+        U32 flags = getLegacyRigFlags();
+        if (fmp)
+        {
+            if (flags == LEGACY_RIG_OK)
+            {
+                fmp->enableViewOption("show_skin_weight");
+                fmp->setViewOptionEnabled("show_joint_positions", skin_weight);
+                mFMP->childEnable("upload_skin");
+                mFMP->childSetValue("show_skin_weight", skin_weight);
+            }
+            else if ((flags & LEGACY_RIG_FLAG_TOO_MANY_JOINTS) > 0)
+            {
+                mFMP->childSetVisible("skin_too_many_joints", true);
+            }
+            else if ((flags & LEGACY_RIG_FLAG_UNKNOWN_JOINT) > 0)
+            {
+                mFMP->childSetVisible("skin_unknown_joint", true);
+            }
+        }
 	}
 	else
 	{
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index a080965ffc..5bb6714099 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -94,7 +94,8 @@ public:
 	/*virtual*/ void onClose(bool app_quitting);
 
 	static void onMouseCaptureLostModelPreview(LLMouseHandler*);
-	static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+    static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+    static void addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod = -1);
 	static void addStringToLog(const std::string& str, bool flash);
 	static void addStringToLog(const std::ostringstream& strm, bool flash);
 
@@ -308,8 +309,9 @@ public:
 	void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }
 
 	//Accessors for the legacy rigs
-	const bool isLegacyRigValid( void ) const { return mLegacyRigValid; }
-	void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }		
+	const bool isLegacyRigValid( void ) const { return mLegacyRigFlags == 0; }
+	U32 getLegacyRigFlags() const { return mLegacyRigFlags; }
+	void setLegacyRigFlags( U32 rigFlags ) { mLegacyRigFlags = rigFlags; }
 
 	static void	textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata );
     static bool lodQueryCallback();
@@ -423,7 +425,7 @@ private:
 	float		mPelvisZOffset;
 	
 	bool		mRigValidJointUpload;
-	bool		mLegacyRigValid;
+	U32			mLegacyRigFlags;
 
 	bool		mLastJointUpdate;
 
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 cbc5aeb37d..7e92e0360e 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -38,6 +38,12 @@
   <string name="decomposing">Analyzing...</string>
   <string name="simplifying">Simplifying...</string>
   <string name="tbd">TBD</string>
+  
+  <!-- Warnings from model loader-->
+  <string name="TooManyJoint">Skinning disabled due to too many joints: [JOINTS], maximum: [MAX]</string>
+  <string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string>
+  <string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string>
+  
 
   <panel
     follows="top|left"
@@ -1184,20 +1190,41 @@
              label_text.text_color="White"
              name="upload_skin"
              top_pad="15"/>
+           <text
+             follows="top|left"
+             height="17"
+             left="40"
+             name="skin_too_many_joints"
+             text_color="Orange"
+             top_pad="-2"
+             width="150">
+             Too many skinned joints
+           </text>
+           <text
+             follows="top|left"
+             height="17"
+             left="40"
+             name="skin_unknown_joint"
+             text_color="Orange"
+             top_pad="-17"
+             width="150">
+             Model has an unknown joint(s)
+           </text>
            <check_box
              follows="top|left"
              height="15"
              label="Include joint positions"
              label_text.text_color="White"
              name="upload_joints"
-             top_pad="15"/>
+             left_delta="-20"
+             top_pad="1"/>
            <check_box
              follows="top|left"
              height="15"
              label="Lock scale if joint position defined"
              label_text.text_color="White"
              name="lock_scale_if_joint_position"
-             top_pad="15"/>
+             top_pad="16"/>
            <text
              follows="top|left"
              height="15"
-- 
cgit v1.2.3


From 5c7b0dfe84f745bc5f6ccc0630ed1beb0ad87d00 Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Thu, 28 Nov 2019 20:23:22 +0200
Subject: SL-304 [Mesh Uploader] A bit of cleanup (parse warnings, missing
 items, cleanup log)

---
 indra/newview/llfloatermodelpreview.cpp               | 19 ++++++++++++-------
 indra/newview/llfloatermodelpreview.h                 |  1 +
 .../skins/default/xui/en/floater_model_preview.xml    |  4 ----
 3 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index b3012ed025..38b5fc271f 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -553,6 +553,8 @@ void LLFloaterModelPreview::loadModel(S32 lod, const std::string& file_name, boo
 
 void LLFloaterModelPreview::onClickCalculateBtn()
 {
+	clearLogTab();
+
 	mModelPreview->rebuildUploadData();
 
 	bool upload_skinweights = childGetValue("upload_skin").asBoolean();
@@ -2410,7 +2412,6 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 	mLoading = false;
 	if (mFMP)
 	{
-		mFMP->getChild<LLCheckBoxCtrl>("confirm_checkbox")->set(FALSE);
 		if (!mBaseModel.empty())
 		{
 			const std::string& model_name = mBaseModel[0]->getName();
@@ -4561,12 +4562,6 @@ void LLModelPreview::setPreviewLOD(S32 lod)
 		combo_box->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order
 		mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]);
 
-		LLComboBox* combo_box2 = mFMP->getChild<LLComboBox>("preview_lod_combo2");
-		combo_box2->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order
-		
-		LLComboBox* combo_box3 = mFMP->getChild<LLComboBox>("preview_lod_combo3");
-		combo_box3->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order
-
 		LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor");
 		LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor");
 
@@ -4596,8 +4591,10 @@ void LLFloaterModelPreview::onReset(void* user_data)
 {
 	assert_main_thread();
 
+
 	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
 	fmp->childDisable("reset_btn");
+	fmp->clearLogTab();
 	LLModelPreview* mp = fmp->mModelPreview;
 	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
 
@@ -4616,6 +4613,7 @@ void LLFloaterModelPreview::onUpload(void* user_data)
 	assert_main_thread();
 
 	LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+	mp->clearLogTab();
 
 	mp->mUploadBtn->setEnabled(false);
 
@@ -4808,6 +4806,13 @@ void LLFloaterModelPreview::resetUploadOptions()
 	getChild<LLComboBox>("Cosine%")->setCurrentByIndex(0);
 }
 
+void LLFloaterModelPreview::clearLogTab()
+{
+    mUploadLogText->clear();
+    LLPanel* panel = mTabContainer->getPanelByName("logs_panel");
+    mTabContainer->setTabPanelFlashing(panel, false);
+}
+
 void LLFloaterModelPreview::onModelPhysicsFeeReceived(const LLSD& result, std::string upload_url)
 {
 	mModelPhysicsFee = result;
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 5bb6714099..8f6ca49cb8 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -224,6 +224,7 @@ private:
 	void resetDisplayOptions();
 
 	void resetUploadOptions();
+	void clearLogTab();
 
 	void createSmoothComboBox(LLComboBox* combo_box, float min, float max);
 
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 7e92e0360e..f5be4bab10 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1548,7 +1548,6 @@ Analysed:
   </panel>
   <panel
     follows="top|left|bottom|right"
-    can_resize="true"
     name="right_panel"
     top="0"
     left="640"
@@ -1565,7 +1564,6 @@ Analysed:
       Preview:
     </text>
     <panel
-      can_resize="false"
       follows="top|left"
       height="20"
       name="right_upper_panel"
@@ -1574,7 +1572,6 @@ Analysed:
       width="315">
       <combo_box
         top_pad="3"
-        can_resize="false"
         follows="top|left"
         height="18"
         layout="topleft"
@@ -1646,7 +1643,6 @@ Analysed:
       layout="topleft"
       left="2"
       name="physics_explode_label"
-      bottom_pad="2"
       width="150">
       Preview Spread:
     </text>
-- 
cgit v1.2.3


From c5859778bc18ef8114b57b616675de15a8e177cb Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Fri, 17 Jan 2020 21:08:38 +0200
Subject: SL-379 WIP Joint overrides tab

---
 indra/llcharacter/lljoint.cpp                      |   4 +-
 indra/llcharacter/lljoint.h                        |   4 +-
 indra/llui/llscrolllistctrl.cpp                    |   3 +-
 indra/llui/llscrolllistctrl.h                      |   1 +
 indra/newview/CMakeLists.txt                       |   1 +
 indra/newview/llfloatermodelpreview.cpp            | 141 ++++++++++++++++++++-
 indra/newview/llfloatermodelpreview.h              |   8 +-
 indra/newview/lljointoverridedata.h                |  52 ++++++++
 indra/newview/llvoavatar.cpp                       |  50 ++++++++
 indra/newview/llvoavatar.h                         |  26 ++--
 .../skins/default/xui/en/floater_model_preview.xml | 116 +++++++++++++++++
 11 files changed, 383 insertions(+), 23 deletions(-)
 create mode 100644 indra/newview/lljointoverridedata.h

diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index e2f512f86e..441ef1a352 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -510,7 +510,7 @@ void LLJoint::clearAttachmentPosOverrides()
 // getAllAttachmentPosOverrides()
 //--------------------------------------------------------------------
 void LLJoint::getAllAttachmentPosOverrides(S32& num_pos_overrides,
-                                           std::set<LLVector3>& distinct_pos_overrides)
+                                           std::set<LLVector3>& distinct_pos_overrides) const
 {
     num_pos_overrides = m_attachmentPosOverrides.count();
     LLVector3OverrideMap::map_type::const_iterator it = m_attachmentPosOverrides.getMap().begin();
@@ -524,7 +524,7 @@ void LLJoint::getAllAttachmentPosOverrides(S32& num_pos_overrides,
 // getAllAttachmentScaleOverrides()
 //--------------------------------------------------------------------
 void LLJoint::getAllAttachmentScaleOverrides(S32& num_scale_overrides,
-                                             std::set<LLVector3>& distinct_scale_overrides)
+                                             std::set<LLVector3>& distinct_scale_overrides) const
 {
     num_scale_overrides = m_attachmentScaleOverrides.count();
     LLVector3OverrideMap::map_type::const_iterator it = m_attachmentScaleOverrides.getMap().begin();
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 8112d246f2..6f69786f53 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -287,9 +287,9 @@ public:
     void showAttachmentScaleOverrides(const std::string& av_info) const;
 
     void getAllAttachmentPosOverrides(S32& num_pos_overrides,
-                                      std::set<LLVector3>& distinct_pos_overrides);
+                                      std::set<LLVector3>& distinct_pos_overrides) const;
     void getAllAttachmentScaleOverrides(S32& num_scale_overrides,
-                                        std::set<LLVector3>& distinct_scale_overrides);
+                                        std::set<LLVector3>& distinct_scale_overrides) const;
     
     // These are used in checks of whether a pos/scale override is considered significant.
     bool aboveJointPosThreshold(const LLVector3& pos) const;
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index f4028057e8..a564b5b2ce 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -132,6 +132,7 @@ LLScrollListCtrl::Params::Params()
 	sort_ascending("sort_ascending", true),
 	mouse_wheel_opaque("mouse_wheel_opaque", false),
 	commit_on_keyboard_movement("commit_on_keyboard_movement", true),
+	commit_on_selection_change("commit_on_selection_change", false),
 	heading_height("heading_height"),
 	page_lines("page_lines", 0),
 	background_visible("background_visible"),
@@ -161,7 +162,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
 	mMaxSelectable(0),
 	mAllowKeyboardMovement(true),
 	mCommitOnKeyboardMovement(p.commit_on_keyboard_movement),
-	mCommitOnSelectionChange(false),
+	mCommitOnSelectionChange(p.commit_on_selection_change),
 	mSelectionChanged(false),
 	mNeedsScroll(false),
 	mCanSelect(true),
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index b35a8608e7..0d6ff67321 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -97,6 +97,7 @@ public:
 		// behavioral flags
 		Optional<bool>	multi_select,
 						commit_on_keyboard_movement,
+						commit_on_selection_change,
 						mouse_wheel_opaque;
 
 		// display flags
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 694e89ab99..7995e7ddf8 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -1000,6 +1000,7 @@ set(viewer_HEADER_FILES
     llinventorymodelbackgroundfetch.h
     llinventoryobserver.h
     llinventorypanel.h
+    lljointoverridedata.h
     lljoystickbutton.h
     lllandmarkactions.h
     lllandmarklist.h
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 38b5fc271f..3e4d9020b7 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -404,9 +404,15 @@ BOOL LLFloaterModelPreview::postBuild()
 	mUploadLogText = getChild<LLViewerTextEditor>("log_text");
 	mTabContainer = getChild<LLTabContainer>("import_tab");
 
+    // Disable Overrides tab untill it has something to show and set callbacks
+    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    S32 index = mTabContainer->getIndexForPanel(panel);
+    mTabContainer->enableTabButton(index, false);
+    panel->getChild<LLScrollListCtrl>("joints_list")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onJointListSelection, this));
+
 	// Disable Logs tab untill it has something to show
-	LLPanel* panel = mTabContainer->getPanelByName("logs_panel");
-	S32 index = mTabContainer->getIndexForPanel(panel);
+	panel = mTabContainer->getPanelByName("logs_panel");
+	index = mTabContainer->getIndexForPanel(panel);
 	mTabContainer->enableTabButton(index, false);
 
 	if (LLConvexDecomposition::getInstance() != NULL)
@@ -563,9 +569,12 @@ void LLFloaterModelPreview::onClickCalculateBtn()
 
     if (upload_joint_positions)
     {
-        // Diagnostic message showing list of joints for which joint offsets are defined.
-        // FIXME - given time, would be much better to put this in the UI, in updateStatusMessages().
-		mModelPreview->getPreviewAvatar()->showAttachmentOverrides();
+        // Todo: this probably should be enabled when checkbox enables, not on calculate
+        populateOverridesTab();
+    }
+    else
+    {
+        disableOverridesTab();
     }
 
 	mUploadModelUrl.clear();
@@ -580,6 +589,89 @@ void LLFloaterModelPreview::onClickCalculateBtn()
 	mUploadBtn->setEnabled(false);
 }
 
+void populate_list_with_vectors(LLScrollListCtrl *list, const std::set<LLVector3> &vector_set, const LLVector3 &active)
+{
+    if (vector_set.empty())
+    {
+        return;
+    }
+    S32 count = 0;
+    LLScrollListCell::Params cell_params;
+    cell_params.font = LLFontGL::getFontSansSerif();
+    // Start out right justifying numeric displays
+    cell_params.font_halign = LLFontGL::HCENTER;
+
+    std::set<LLVector3>::const_iterator iter = vector_set.begin();
+    std::set<LLVector3>::const_iterator end = vector_set.end();
+    while (iter != end)
+    {
+        LLScrollListItem::Params item_params;
+        item_params.value = LLSD::Integer(count);
+
+        cell_params.column = "override";
+        if (*iter != active)
+        {
+            cell_params.value = "";
+        }
+        else
+        {
+            cell_params.value = "active"; //todo: localize
+        }
+
+        item_params.columns.add(cell_params);
+
+        cell_params.column = "axis_x";
+        cell_params.value = iter->mV[VX];
+        item_params.columns.add(cell_params);
+
+        cell_params.column = "axis_y";
+        cell_params.value = iter->mV[VY];
+        item_params.columns.add(cell_params);
+
+        cell_params.column = "axis_z";
+        cell_params.value = iter->mV[VZ];
+
+        item_params.columns.add(cell_params);
+
+        list->addRow(item_params);
+        count++;
+        iter++;
+    }
+}
+
+void LLFloaterModelPreview::onJointListSelection()
+{
+    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
+    LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
+    LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list");
+    LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr");
+    LLTextBox *joint_scale_descr = panel->getChild<LLTextBox>("scale_overrides_descr");
+
+    joints_pos->deleteAllItems();
+    joints_scale->deleteAllItems();
+
+    LLScrollListItem *selected = joints_list->getFirstSelected();
+    if (selected)
+    {
+        std::string label = selected->getValue().asString();
+        LLJointOverrideData *data = &mJointOverrides[label];
+        populate_list_with_vectors(joints_pos, data->mPosOverrides, data->mActivePosOverride);
+        populate_list_with_vectors(joints_scale, data->mScaleOverrides, data->mActiveScaleOverride);
+
+        joint_pos_descr->setTextArg("[JOINT]", label);
+        joint_scale_descr->setTextArg("[JOINT]", label);
+    }
+    else
+    {
+        // temporary value (shouldn't happen)
+        std::string label = "mPelvis";
+        joint_pos_descr->setTextArg("[JOINT]", label);
+        joint_scale_descr->setTextArg("[JOINT]", label);
+    }
+
+}
+
 void LLFloaterModelPreview::onDescriptionKeystroke(LLUICtrl* ctrl)
 {
 	// Workaround for SL-4186, server doesn't allow name changes after 'calculate' stage
@@ -3861,7 +3953,7 @@ void LLModelPreview::addEmptyFace( LLModel* pTarget )
 	pTarget->setNumVolumeFaces( faceCnt+1 );	
 	pTarget->setVolumeFaceData( faceCnt+1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices() );
 	
-}	
+}
 
 //-----------------------------------------------------------------------------
 // render()
@@ -4813,6 +4905,43 @@ void LLFloaterModelPreview::clearLogTab()
     mTabContainer->setTabPanelFlashing(panel, false);
 }
 
+void LLFloaterModelPreview::populateOverridesTab()
+{
+    mJointOverrides.clear();
+    attach_override_data_map_t attach_not_in_use;
+    mModelPreview->getPreviewAvatar()->getAttachmentOverrides(mJointOverrides, attach_not_in_use);
+
+    if (mJointOverrides.empty())
+    {
+        disableOverridesTab();
+        return;
+    }
+
+    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    S32 index = mTabContainer->getIndexForPanel(panel);
+    mTabContainer->enableTabButton(index, true);
+
+    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
+    joints_list->deleteAllItems();
+    
+    joint_override_data_map_t::iterator joint_iter = mJointOverrides.begin();
+    joint_override_data_map_t::iterator joint_end = mJointOverrides.end();
+    while (joint_iter != joint_end)
+    {
+        const std::string& listName = joint_iter->first;
+        joints_list->addSimpleElement(listName);
+        joint_iter++;
+    }
+    joints_list->selectFirstItem();
+}
+
+void LLFloaterModelPreview::disableOverridesTab()
+{
+    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    S32 index = mTabContainer->getIndexForPanel(panel);
+    mTabContainer->enableTabButton(index, false);
+}
+
 void LLFloaterModelPreview::onModelPhysicsFeeReceived(const LLSD& result, std::string upload_url)
 {
 	mModelPhysicsFee = result;
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 8f6ca49cb8..8988dd2565 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -30,6 +30,7 @@
 #include "llfloaternamedesc.h"
 
 #include "lldynamictexture.h"
+#include "lljointoverridedata.h"
 #include "llquaternion.h"
 #include "llmeshrepository.h"
 #include "llmodel.h"
@@ -213,7 +214,8 @@ protected:
 	LLSD mModelPhysicsFee;
 
 private:
-	void onClickCalculateBtn();
+    void onClickCalculateBtn();
+    void onJointListSelection();
 
 	void onLoDSourceCommit(S32 lod);
 
@@ -225,6 +227,8 @@ private:
 
 	void resetUploadOptions();
 	void clearLogTab();
+	void populateOverridesTab();
+	void disableOverridesTab();
 
 	void createSmoothComboBox(LLComboBox* combo_box, float min, float max);
 
@@ -232,6 +236,8 @@ private:
 	LLButton* mCalculateBtn;
 	LLViewerTextEditor* mUploadLogText;
 	LLTabContainer* mTabContainer;
+
+	joint_override_data_map_t mJointOverrides;
 };
 
 class LLMeshFilePicker : public LLFilePickerThread
diff --git a/indra/newview/lljointoverridedata.h b/indra/newview/lljointoverridedata.h
new file mode 100644
index 0000000000..55e1878eec
--- /dev/null
+++ b/indra/newview/lljointoverridedata.h
@@ -0,0 +1,52 @@
+/**
+ * @file lljointoverridedata.h
+ * @brief Declaration of LLJointOverrideData and LLAttachmentOverrideData
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Second Life Viewer2020, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_JOINTOVERRIDEDATA_H
+#define LL_JOINTOVERRIDEDATA_H
+
+//#include <map>
+//#include <string>
+//#include <vector>
+
+
+struct LLJointOverrideData
+{
+    std::set<LLVector3> mPosOverrides;
+    LLVector3 mActivePosOverride;
+    std::set<LLVector3> mScaleOverrides;
+    LLVector3 mActiveScaleOverride;
+};
+
+struct LLAttachmentOverrideData
+{
+    std::set<LLVector3> mPosOverrides;
+    LLVector3 mActivePosOverride;
+};
+
+typedef std::map<std::string, LLJointOverrideData> joint_override_data_map_t;
+typedef std::map<std::string, LLAttachmentOverrideData> attach_override_data_map_t;
+
+#endif // LL_JOINTOVERRIDEDATA_H
+
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index ca1216b89d..7700109fa4 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -6374,6 +6374,56 @@ void LLVOAvatar::showAttachmentOverrides(bool verbose) const
     }
 }
 
+//-----------------------------------------------------------------------------
+// getAttachmentOverrides
+//-----------------------------------------------------------------------------
+void LLVOAvatar::getAttachmentOverrides(joint_override_data_map_t &joint_overrides, attach_override_data_map_t &attach_overrides) const
+{
+    LLVector3 pos, scale;
+    LLUUID mesh_id;
+    S32 count = 0;
+
+    // Bones
+    for (avatar_joint_list_t::const_iterator iter = mSkeleton.begin();
+        iter != mSkeleton.end(); ++iter)
+    {
+        const LLJoint* pJoint = (*iter);
+        LLJointOverrideData data;
+        bool joint_override = false;
+        if (pJoint && pJoint->hasAttachmentPosOverride(pos, mesh_id))
+        {
+            pJoint->getAllAttachmentPosOverrides(count, data.mPosOverrides);
+            data.mActivePosOverride = pos;
+            joint_override = true;
+        }
+        if (pJoint && pJoint->hasAttachmentScaleOverride(scale, mesh_id))
+        {
+            pJoint->getAllAttachmentScaleOverrides(count, data.mPosOverrides);
+            data.mActiveScaleOverride = scale;
+            joint_override = true;
+        }
+        if (joint_override)
+        {
+            joint_overrides[pJoint->getName()] = data;
+        }
+    }
+
+    // Attachment points
+    for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin();
+        iter != mAttachmentPoints.end();
+        ++iter)
+    {
+        const LLViewerJointAttachment *attachment_pt = (*iter).second;
+        if (attachment_pt && attachment_pt->hasAttachmentPosOverride(pos, mesh_id))
+        {
+            LLAttachmentOverrideData data;
+            attachment_pt->getAllAttachmentPosOverrides(count, data.mPosOverrides);
+            data.mActivePosOverride = pos;
+            attach_overrides[attachment_pt->getName()] = data;
+        }
+    }
+}
+
 //-----------------------------------------------------------------------------
 // removeAttachmentOverridesForObject
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 00dccc5d12..53a1d48d72 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -36,23 +36,25 @@
 #include <boost/signals2/trackable.hpp>
 
 #include "llavatarappearance.h"
+#include "llavatarappearancedefines.h"
+#include "llavatarrendernotifier.h"
+#include "llcontrol.h"
+#include "llcharacter.h"
 #include "llchat.h"
 #include "lldrawpoolalpha.h"
-#include "llviewerobject.h"
-#include "llcharacter.h"
-#include "llcontrol.h"
-#include "llviewerjointmesh.h"
-#include "llviewerjointattachment.h"
-#include "llrendertarget.h"
-#include "llavatarappearancedefines.h"
-#include "lltexglobalcolor.h"
 #include "lldriverparam.h"
-#include "llviewertexlayer.h"
-#include "material_codes.h"		// LL_MCODE_END
+#include "lljointoverridedata.h"
+#include "llrendertarget.h"
 #include "llrigginginfo.h"
+#include "lltexglobalcolor.h"
+#include "llviewerjointmesh.h"
+#include "llviewerjointattachment.h"
+#include "llviewerobject.h"
 #include "llviewerstats.h"
+#include "llviewertexlayer.h"
 #include "llvovolume.h"
-#include "llavatarrendernotifier.h"
+
+#include "material_codes.h"		// LL_MCODE_END
 
 extern const LLUUID ANIM_AGENT_BODY_NOISE;
 extern const LLUUID ANIM_AGENT_BREATHE_ROT;
@@ -215,6 +217,8 @@ public:
 	void					rebuildAttachmentOverrides();
     void					updateAttachmentOverrides();
     void                    showAttachmentOverrides(bool verbose = false) const;
+    void                    getAttachmentOverrides(joint_override_data_map_t& joint_overrides,
+                                                   attach_override_data_map_t& attach_overrides) const;
     void                    getAttachmentOverrideNames(std::set<std::string>& pos_names, 
                                                        std::set<std::string>& scale_names) const;
 
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 f5be4bab10..b675a3e3be 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1246,6 +1246,122 @@
              value="0.0"
              width="80"/>
      </panel>
+      <panel
+       label="Overrides"
+       layout="topleft"
+       name="overrides_panel"
+       title="Overrides">
+        <view_border
+         bevel_style="none"
+         follows="top|left"
+         height="275"
+         layout="topleft"
+         left="3"
+         name="log_tab_border"
+         top_pad="0"
+         width="619" />
+        <text
+          layout="topleft"
+          follows="top|left"
+          height="15"
+          left="6"
+          name="joints_descr"
+          top="4"
+          width="300">
+          Joints:
+        </text>
+        <scroll_list
+         layout="topleft"
+         follows="top|left"
+         name="joints_list"
+         column_padding="0"
+         draw_heading="false"
+         draw_stripes="false"
+         commit_on_selection_change="true"
+         heading_height="23"
+         height="253"
+         left="6"
+         top_pad="0"
+         width="150"/>
+        <text
+          layout="topleft"
+          follows="top|left"
+          height="15"
+          left_pad="5"
+          name="pos_overrides_descr"
+          top="4"
+          width="300">
+          Position overrides for joint '[JOINT]':
+        </text>
+        <scroll_list
+         layout="topleft"
+         follows="top|left"
+         name="pos_overrides_list"
+         column_padding="0"
+         draw_heading="true"
+         draw_stripes="false"
+         heading_height="23"
+         height="100"
+         left_delta="0"
+         top_pad="0"
+         width="310">
+          <scroll_list.columns
+           label="*"
+           name="override"
+           relative_width="0.22"  />
+          <scroll_list.columns
+           label="X"
+           name="axis_x"
+           relative_width="0.26"  />
+          <scroll_list.columns
+           label="Y"
+           name="axis_y"
+           relative_width="0.26"  />
+          <scroll_list.columns
+           label="Z"
+           name="axis_z"
+           relative_width="0.26"  />
+        </scroll_list>
+        <text
+          layout="topleft"
+          follows="top|left"
+          height="15"
+          left_delta="0"
+          name="scale_overrides_descr"
+          top_pad="3"
+          width="300">
+          Scale overrides for joint '[JOINT]':
+        </text>
+        <scroll_list
+         layout="topleft"
+         follows="top|left"
+         name="scale_overrides_list"
+         column_padding="0"
+         draw_heading="true"
+         draw_stripes="false"
+         heading_height="23"
+         height="100"
+         left_delta="0"
+         top_pad="0"
+         width="310">
+          <scroll_list.columns
+           label="*"
+           name="override"
+           relative_width="0.22"  />
+          <scroll_list.columns
+           label="X"
+           name="axis_x"
+           relative_width="0.26"  />
+          <scroll_list.columns
+           label="Y"
+           name="axis_y"
+           relative_width="0.26"  />
+          <scroll_list.columns
+           label="Z"
+           name="axis_z"
+           relative_width="0.26"  />
+        </scroll_list>
+      </panel>
       <panel
        label="Log"
        layout="topleft"
-- 
cgit v1.2.3


From 748d25d1269751dc505a9ad93453d40b8dcea77f Mon Sep 17 00:00:00 2001
From: AndreyL ProductEngine <alihatskiy@productengine.com>
Date: Tue, 21 Jan 2020 16:25:12 +0200
Subject: Copyright fix

---
 indra/newview/lljointoverridedata.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/indra/newview/lljointoverridedata.h b/indra/newview/lljointoverridedata.h
index 55e1878eec..99baa5f334 100644
--- a/indra/newview/lljointoverridedata.h
+++ b/indra/newview/lljointoverridedata.h
@@ -3,7 +3,8 @@
  * @brief Declaration of LLJointOverrideData and LLAttachmentOverrideData
  *
  * $LicenseInfo:firstyear=2020&license=viewerlgpl$
- * Second Life Viewer2020, Linden Research, Inc.
+ * Second Life Viewer Source Code
+ * Copyright (C) 2020, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
-- 
cgit v1.2.3


From 5fddc374566a0111375df2191b9c38cd8752881e Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 5 Feb 2020 21:06:35 +0200
Subject: SL-12673 Make mesh uploader fit into 980px

---
 .../skins/default/xui/en/floater_model_preview.xml | 148 ++++++++++-----------
 1 file changed, 74 insertions(+), 74 deletions(-)

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 b675a3e3be..bd8b764fde 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -6,8 +6,8 @@
  can_resize="true"
  height="600"
  min_height="600"
- width="1024"
- min_width="1024"
+ width="980"
+ min_width="980"
  name="Model Preview"
  title="UPLOAD MODEL"
  help_topic="upload_model" >
@@ -1656,121 +1656,121 @@ Analysed:
          width="462"
          visible="false">
          You dont have rights to upload mesh models. [[VURL] Find out how] to get certified.
-       </text>
+       </text> 
        <text text_color="Yellow" layout="topleft" top_pad="-1" left="6" name="status">
 [STATUS]
        </text>
     </panel>
   </panel>
+  
+  <text
+   follows="left|top"
+   layout="topleft"
+   left="640"
+   name="lod_label"
+   text_color="White"
+   top="4"
+   height="15"
+   width="290">
+    Preview:
+  </text>
   <panel
-    follows="top|left|bottom|right"
+   border="true"
+   bevel_style="none"
+   follows="top|left"
+   name="preview_panel"
+   top_pad="4"
+   width="325"
+   height="408"/>
+  <panel
+    follows="all"
+    can_resize="true"
+    height="140"
+    layout="topleft"
     name="right_panel"
-    top="0"
-    left="640"
-    width="375">
-    <text
+    top_pad="5"
+    width="340">
+    <combo_box
+      top_pad="3"
       follows="left|top"
+      height="18"
+      layout="topleft"
+      name="preview_lod_combo"
+      width="150"
+      tool_tip="LOD to view in preview render">
+      <combo_item name="high">   High   </combo_item>
+      <combo_item name="medium"> Medium </combo_item>
+      <combo_item name="low">    Low    </combo_item>
+      <combo_item name="lowest"> Lowest </combo_item>
+    </combo_box>
+    <text
+      follows="top|left"
       layout="topleft"
-      left="0"
-      name="lod_label"
       text_color="White"
-      top="13"
-      height="15"
-      width="290">
-      Preview:
+      top="5"
+      left_pad="20"
+      name="label_display"
+      width="50">
+      Display...
     </text>
-    <panel
+    <check_box
       follows="top|left"
-      height="20"
-      name="right_upper_panel"
-      top="8"
-      left="60"
-      width="315">
-      <combo_box
-        top_pad="3"
-        follows="top|left"
-        height="18"
-        layout="topleft"
-        name="preview_lod_combo"
-        width="75"
-        tool_tip="LOD to view in preview render">
-        <combo_item name="high">   High   </combo_item>
-        <combo_item name="medium"> Medium </combo_item>
-        <combo_item name="low">    Low    </combo_item>
-        <combo_item name="lowest"> Lowest </combo_item>
-      </combo_box>
-    </panel>
-  </panel>
-  <panel
-     border="true"
-     bevel_style="none"
-     follows="top|left|right|bottom"
-     layout="topleft"
-     name="preview_panel"
-     top="30"
-     width="375"
-     height="525"/>
-
-   <panel
-     follows="left|right|bottom"
-     layout="topleft"
-     height="40"
-     name="lower_right_panel"
-     top_pad="5"
-     width="375">
-     <check_box
-       follows="right|bottom"
-       label="Edges"
+      label="Edges"
       label_text.text_color="White"
       layout="topleft"
+      left_delta="0"
       name="show_edges"
-      width="70"
-      left="0"
-      top_pad="8"/>
+      top_pad="8">
+    </check_box>
     <check_box
-      follows="right|bottom"
-      left_pad="8"
+      follows="top|left"
       label="Physics"
       label_text.text_color="White"
-      name="show_physics"/>
+      layout="topleft"
+      name="show_physics"
+      top_pad="8">
+    </check_box>
     <check_box
-      follows="right|bottom"
+      follows="top|left"
       label="Textures"
       label_text.text_color="White"
       layout="topleft"
       name="show_textures"
-      left_pad="0"/>
+      top_pad="8">
+    </check_box>
     <check_box
-      follows="right|bottom"
-      label="Weights"
+      follows="top|left"
+      label="Skin weights"
       label_text.text_color="White"
       layout="topleft"
       name="show_skin_weight"
-      left_pad="0"/>
+      top_pad="8">
+    </check_box>
     <check_box
-      follows="right|bottom"
+      follows="top|left"
       label="Joints"
       label_text.text_color="White"
       layout="topleft"
       name="show_joint_positions"
-      left_pad="0"/>
+      top_pad="8">
+    </check_box>
     <text
-      follows="right|bottom"
+      follows="top|left"
       layout="topleft"
       left="2"
       name="physics_explode_label"
+      top="85"
       width="150">
       Preview Spread:
     </text>
     <slider
       name="physics_explode"
-      follows="right|bottom"
-      valign="center"
-      left="105"
-      top_delta="-3"
+      follows="top|left"
+      top="100"
+      left="0"
       min_val="0.0"
       max_val="3.0"
       height="20"
-      width="120"/>
+      width="150"/>
   </panel>
 </floater>
-- 
cgit v1.2.3


From f851ce08d27c983423176e3508cebd9ac9f9cdd4 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 6 Feb 2020 20:00:21 +0200
Subject: SL-548 Add control for joint positions in model upload preview

---
 indra/newview/llfloatermodelpreview.cpp            | 56 ++++++++++++++++++++--
 .../skins/default/xui/en/floater_model_preview.xml | 12 ++++-
 2 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 2a8638e340..b846853755 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -337,6 +337,7 @@ BOOL LLFloaterModelPreview::postBuild()
 	getChild<LLCheckBoxCtrl>("show_physics")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
 	getChild<LLCheckBoxCtrl>("show_textures")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
 	getChild<LLCheckBoxCtrl>("show_skin_weight")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onShowSkinWeightChecked, this, _1));
+	getChild<LLCheckBoxCtrl>("show_joint_overrides")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
 	getChild<LLCheckBoxCtrl>("show_joint_positions")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));
 
 	childDisable("upload_skin");
@@ -2295,7 +2296,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 		mBaseScene.clear();
 
 		bool skin_weights = false;
-		bool joint_positions = false;
+		bool joint_overrides = false;
 		bool lock_scale_if_joint_position = false;
 
 		for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
@@ -2342,7 +2343,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 
 							if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty())
 							{
-								joint_positions = true;
+								joint_overrides = true;
 							}
 							if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition)
 							{
@@ -2365,8 +2366,10 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 				fmp->childSetValue("upload_skin", true);
 			}
 
-			if (joint_positions)
-			{ 
+			if (joint_overrides)
+			{
+				fmp->enableViewOption("show_joint_overrides");
+				mViewOption["show_joint_overrides"] = true;
 				fmp->enableViewOption("show_joint_positions");
 				mViewOption["show_joint_positions"] = true;
 				fmp->childSetValue("upload_joints", true);
@@ -3977,6 +3980,7 @@ BOOL LLModelPreview::render()
 	bool use_shaders = LLGLSLShader::sNoFixedFunction;
 
 	bool edges = mViewOption["show_edges"];
+	bool joints = mViewOption["show_joint_overrides"];
 	bool joint_positions = mViewOption["show_joint_positions"];
 	bool skin_weight = mViewOption["show_skin_weight"];
 	bool textures = mViewOption["show_textures"];
@@ -4068,6 +4072,7 @@ BOOL LLModelPreview::render()
             if (flags == LEGACY_RIG_OK)
             {
                 fmp->enableViewOption("show_skin_weight");
+                fmp->setViewOptionEnabled("show_joint_overrides", skin_weight);
                 fmp->setViewOptionEnabled("show_joint_positions", skin_weight);
                 mFMP->childEnable("upload_skin");
                 mFMP->childSetValue("show_skin_weight", skin_weight);
@@ -4089,6 +4094,7 @@ BOOL LLModelPreview::render()
 		{
 			mViewOption["show_skin_weight"] = false;
 			fmp->disableViewOption("show_skin_weight");
+			fmp->disableViewOption("show_joint_overrides");
 			fmp->disableViewOption("show_joint_positions");
 
 			skin_weight = false;
@@ -4496,6 +4502,7 @@ BOOL LLModelPreview::render()
 		else
 		{
 			target_pos = getPreviewAvatar()->getPositionAgent();
+			bool pelvis_recalc = false;
 
 			LLViewerCamera::getInstance()->setOriginAndLookAt(
 															  target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera
@@ -4532,6 +4539,41 @@ BOOL LLModelPreview::render()
 							U32 count = LLSkinningUtil::getMeshJointCount(skin);
                             LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count,
                                                                         skin, getPreviewAvatar());
+                            getPreviewAvatar()->clearAttachmentOverrides();
+                            if (joints)
+                            {
+                                LLUUID fake_mesh_id;
+                                fake_mesh_id.generate();
+                                for (U32 j = 0; j < count; ++j)
+                                {
+                                    LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]);
+                                    if (joint && skin->mAlternateBindMatrix.size() > 0)
+                                    {
+                                        const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
+                                        if (joint->aboveJointPosThreshold(jointPos))
+                                        {
+                                            bool override_changed;
+                                            joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed);
+
+                                            if (override_changed)
+                                            {
+                                                //If joint is a pelvis then handle old/new pelvis to foot values
+                                                if (joint->getName() == "mPelvis")
+                                                {
+                                                    pelvis_recalc = true;
+                                                }
+                                            }
+                                            if (skin->mLockScaleIfJointPosition)
+                                            {
+                                                // Note that unlike positions, there's no threshold check here,
+                                                // just a lock at the default value.
+                                                joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model");
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+
                             LLMatrix4a bind_shape_matrix;
                             bind_shape_matrix.loadu(skin->mBindShapeMatrix);
                             U32 max_joints = LLSkinningUtil::getMaxJointCount();
@@ -4601,6 +4643,11 @@ BOOL LLModelPreview::render()
 				}
 			}
 
+            if (pelvis_recalc)
+            {
+                // size/scale recalculation
+                getPreviewAvatar()->postPelvisSetRecalc();
+            }
 		}
 	}
 
@@ -4918,6 +4965,7 @@ void LLFloaterModelPreview::populateOverridesTab()
 {
     mJointOverrides.clear();
     attach_override_data_map_t attach_not_in_use;
+    // Todo: use mAlternateBindMatrix
     mModelPreview->getPreviewAvatar()->getAttachmentOverrides(mJointOverrides, attach_not_in_use);
 
     if (mJointOverrides.empty())
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 bd8b764fde..699ea7b9db 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1746,13 +1746,23 @@ Analysed:
       name="show_skin_weight"
       top_pad="8">
     </check_box>
+    <check_box
+      follows="top|left"
+      label="Joint position overrides"
+      label_text.text_color="White"
+      word_wrap="down"
+      width="130"
+      layout="topleft"
+      name="show_joint_overrides"
+      top_pad="8">
+    </check_box>
     <check_box
       follows="top|left"
       label="Joints"
       label_text.text_color="White"
       layout="topleft"
       name="show_joint_positions"
-      top_pad="8">
+      top_pad="17">
     </check_box>
     <text
       follows="top|left"
-- 
cgit v1.2.3


From dd015ce7fc3f6ae3ccf0c6c7f04b2f1b94a90670 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 6 Feb 2020 20:33:09 +0200
Subject: SL-12673 Fixed resize

---
 indra/newview/skins/default/xui/en/floater_model_preview.xml | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

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 699ea7b9db..9009eeeb04 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1675,16 +1675,17 @@ Analysed:
     Preview:
   </text>
   <panel
+   follows="all"
+   layout="topleft"
    border="true"
    bevel_style="none"
-   follows="top|left"
    name="preview_panel"
    top_pad="4"
    width="325"
    height="408"/>
   <panel
-    follows="all"
-    can_resize="true"
+    follows="right|bottom"
+    can_resize="false"
     height="140"
     layout="topleft"
     name="right_panel"
-- 
cgit v1.2.3


From 1a923ff8d3f4c1bc9c44be42b75295073e80a4c8 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 7 Feb 2020 15:41:45 +0200
Subject: SL-548 Fixed pelvis position and optimized

---
 indra/newview/llfloatermodelpreview.cpp | 80 ++++++++++++++++++---------------
 1 file changed, 43 insertions(+), 37 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index b846853755..82b3fa200c 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -3875,6 +3875,7 @@ void LLModelPreview::loadedCallback(
 	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
 	if (pPreview && !LLModelPreview::sIgnoreLoadedCallback)
 	{
+        // Load loader's warnings into floater's log tab
         const LLSD out = pPreview->mModelLoader->logOut();
         LLSD::array_const_iterator iter_out = out.beginArray();
         LLSD::array_const_iterator end_out = out.endArray();
@@ -3980,7 +3981,7 @@ BOOL LLModelPreview::render()
 	bool use_shaders = LLGLSLShader::sNoFixedFunction;
 
 	bool edges = mViewOption["show_edges"];
-	bool joints = mViewOption["show_joint_overrides"];
+	bool joint_overrides = mViewOption["show_joint_overrides"];
 	bool joint_positions = mViewOption["show_joint_positions"];
 	bool skin_weight = mViewOption["show_skin_weight"];
 	bool textures = mViewOption["show_textures"];
@@ -4502,6 +4503,10 @@ BOOL LLModelPreview::render()
 		else
 		{
 			target_pos = getPreviewAvatar()->getPositionAgent();
+            getPreviewAvatar()->clearAttachmentOverrides(); // removes pelvis fixup
+            LLUUID fake_mesh_id;
+            fake_mesh_id.generate();
+            getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id);
 			bool pelvis_recalc = false;
 
 			LLViewerCamera::getInstance()->setOriginAndLookAt(
@@ -4518,6 +4523,43 @@ BOOL LLModelPreview::render()
 
 					if (!model->mSkinWeights.empty())
 					{
+                        const LLMeshSkinInfo *skin = &model->mSkinInfo;
+                        U32 count = LLSkinningUtil::getMeshJointCount(skin);
+
+                        if (joint_overrides)
+                        {
+                            LLUUID fake_mesh_id;
+                            fake_mesh_id.generate();
+                            for (U32 j = 0; j < count; ++j)
+                            {
+                                LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]);
+                                if (joint && skin->mAlternateBindMatrix.size() > 0)
+                                {
+                                    const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
+                                    if (joint->aboveJointPosThreshold(jointPos))
+                                    {
+                                        bool override_changed;
+                                        joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed);
+
+                                        if (override_changed)
+                                        {
+                                            //If joint is a pelvis then handle old/new pelvis to foot values
+                                            if (joint->getName() == "mPelvis")
+                                            {
+                                                pelvis_recalc = true;
+                                            }
+                                        }
+                                        if (skin->mLockScaleIfJointPosition)
+                                        {
+                                            // Note that unlike positions, there's no threshold check here,
+                                            // just a lock at the default value.
+                                            joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model");
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
 						for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i)
 						{
 							LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
@@ -4535,44 +4577,8 @@ BOOL LLModelPreview::render()
 							//build matrix palette
 
 							LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
-                            const LLMeshSkinInfo *skin = &model->mSkinInfo;
-							U32 count = LLSkinningUtil::getMeshJointCount(skin);
                             LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count,
                                                                         skin, getPreviewAvatar());
-                            getPreviewAvatar()->clearAttachmentOverrides();
-                            if (joints)
-                            {
-                                LLUUID fake_mesh_id;
-                                fake_mesh_id.generate();
-                                for (U32 j = 0; j < count; ++j)
-                                {
-                                    LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]);
-                                    if (joint && skin->mAlternateBindMatrix.size() > 0)
-                                    {
-                                        const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
-                                        if (joint->aboveJointPosThreshold(jointPos))
-                                        {
-                                            bool override_changed;
-                                            joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed);
-
-                                            if (override_changed)
-                                            {
-                                                //If joint is a pelvis then handle old/new pelvis to foot values
-                                                if (joint->getName() == "mPelvis")
-                                                {
-                                                    pelvis_recalc = true;
-                                                }
-                                            }
-                                            if (skin->mLockScaleIfJointPosition)
-                                            {
-                                                // Note that unlike positions, there's no threshold check here,
-                                                // just a lock at the default value.
-                                                joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model");
-                                            }
-                                        }
-                                    }
-                                }
-                            }
 
                             LLMatrix4a bind_shape_matrix;
                             bind_shape_matrix.loadu(skin->mBindShapeMatrix);
-- 
cgit v1.2.3


From f249365f23e5ed3509a7f665ead53d4e781ff6d2 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 7 Feb 2020 16:59:50 +0200
Subject: SL-548 Fixed potential case where mJointNums will not be initialized

---
 indra/newview/llfloatermodelpreview.cpp | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 82b3fa200c..4e9e4754b3 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -4524,16 +4524,24 @@ BOOL LLModelPreview::render()
 					if (!model->mSkinWeights.empty())
 					{
                         const LLMeshSkinInfo *skin = &model->mSkinInfo;
+                        LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary
                         U32 count = LLSkinningUtil::getMeshJointCount(skin);
 
-                        if (joint_overrides)
+                        if (joint_overrides && skin->mAlternateBindMatrix.size() > 0)
                         {
+                            // mesh_id is used to determine which mesh gets to
+                            // set the joint offset, in the event of a conflict. Since
+                            // we don't know the mesh id yet, we can't guarantee that
+                            // joint offsets will be applied with the same priority as
+                            // in the uploaded model. If the file contains multiple
+                            // meshes with conflicting joint offsets, preview may be
+                            // incorrect.
                             LLUUID fake_mesh_id;
                             fake_mesh_id.generate();
                             for (U32 j = 0; j < count; ++j)
                             {
                                 LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]);
-                                if (joint && skin->mAlternateBindMatrix.size() > 0)
+                                if (joint)
                                 {
                                     const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
                                     if (joint->aboveJointPosThreshold(jointPos))
@@ -4544,7 +4552,7 @@ BOOL LLModelPreview::render()
                                         if (override_changed)
                                         {
                                             //If joint is a pelvis then handle old/new pelvis to foot values
-                                            if (joint->getName() == "mPelvis")
+                                            if (joint->getName() == "mPelvis")// or skin->mJointNames[j]
                                             {
                                                 pelvis_recalc = true;
                                             }
-- 
cgit v1.2.3


From 67b908cd2020231f30386294e4d0c5f199a007a0 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 7 Feb 2020 18:43:31 +0200
Subject: SL-379 WIP Joint overrides tab

---
 indra/newview/CMakeLists.txt                       |   1 -
 indra/newview/llfloatermodelpreview.cpp            | 191 +++++++++++++--------
 indra/newview/llfloatermodelpreview.h              |  24 ++-
 indra/newview/lljointoverridedata.h                |  53 ------
 indra/newview/llvoavatar.cpp                       |  50 ------
 indra/newview/llvoavatar.h                         |  26 ++-
 .../skins/default/xui/en/floater_model_preview.xml |  53 +-----
 7 files changed, 156 insertions(+), 242 deletions(-)
 delete mode 100644 indra/newview/lljointoverridedata.h

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index caa636cc83..cf8f99ed25 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -996,7 +996,6 @@ set(viewer_HEADER_FILES
     llinventorymodelbackgroundfetch.h
     llinventoryobserver.h
     llinventorypanel.h
-    lljointoverridedata.h
     lljoystickbutton.h
     lllandmarkactions.h
     lllandmarklist.h
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 4e9e4754b3..c791ca8e77 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -568,16 +568,6 @@ void LLFloaterModelPreview::onClickCalculateBtn()
 	bool upload_joint_positions = childGetValue("upload_joints").asBoolean();
     bool lock_scale_if_joint_position = childGetValue("lock_scale_if_joint_position").asBoolean();
 
-    if (upload_joint_positions)
-    {
-        // Todo: this probably should be enabled when checkbox enables, not on calculate
-        populateOverridesTab();
-    }
-    else
-    {
-        disableOverridesTab();
-    }
-
 	mUploadModelUrl.clear();
 
 	gMeshRepo.uploadModel(mModelPreview->mUploadData, mModelPreview->mPreviewScale,
@@ -590,9 +580,9 @@ void LLFloaterModelPreview::onClickCalculateBtn()
 	mUploadBtn->setEnabled(false);
 }
 
-void populate_list_with_vectors(LLScrollListCtrl *list, const std::set<LLVector3> &vector_set, const LLVector3 &active)
+void populate_list_with_map(LLScrollListCtrl *list, const std::map<std::string, LLVector3> &vector_map)
 {
-    if (vector_set.empty())
+    if (vector_map.empty())
     {
         return;
     }
@@ -602,35 +592,28 @@ void populate_list_with_vectors(LLScrollListCtrl *list, const std::set<LLVector3
     // Start out right justifying numeric displays
     cell_params.font_halign = LLFontGL::HCENTER;
 
-    std::set<LLVector3>::const_iterator iter = vector_set.begin();
-    std::set<LLVector3>::const_iterator end = vector_set.end();
+    std::map<std::string, LLVector3>::const_iterator iter = vector_map.begin();
+    std::map<std::string, LLVector3>::const_iterator end = vector_map.end();
     while (iter != end)
     {
         LLScrollListItem::Params item_params;
         item_params.value = LLSD::Integer(count);
 
-        cell_params.column = "override";
-        if (*iter != active)
-        {
-            cell_params.value = "";
-        }
-        else
-        {
-            cell_params.value = "active"; //todo: localize
-        }
+        cell_params.column = "model_name";
+        cell_params.value = iter->first;
 
         item_params.columns.add(cell_params);
 
         cell_params.column = "axis_x";
-        cell_params.value = iter->mV[VX];
+        cell_params.value = iter->second.mV[VX];
         item_params.columns.add(cell_params);
 
         cell_params.column = "axis_y";
-        cell_params.value = iter->mV[VY];
+        cell_params.value = iter->second.mV[VY];
         item_params.columns.add(cell_params);
 
         cell_params.column = "axis_z";
-        cell_params.value = iter->mV[VZ];
+        cell_params.value = iter->second.mV[VZ];
 
         item_params.columns.add(cell_params);
 
@@ -642,12 +625,12 @@ void populate_list_with_vectors(LLScrollListCtrl *list, const std::set<LLVector3
 
 void LLFloaterModelPreview::onJointListSelection()
 {
+    S32 display_lod = mModelPreview->mPreviewLOD;
     LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
     LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
     LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list");
     LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr");
-    LLTextBox *joint_scale_descr = panel->getChild<LLTextBox>("scale_overrides_descr");
 
     joints_pos->deleteAllItems();
     joints_scale->deleteAllItems();
@@ -656,19 +639,19 @@ void LLFloaterModelPreview::onJointListSelection()
     if (selected)
     {
         std::string label = selected->getValue().asString();
-        LLJointOverrideData *data = &mJointOverrides[label];
-        populate_list_with_vectors(joints_pos, data->mPosOverrides, data->mActivePosOverride);
-        populate_list_with_vectors(joints_scale, data->mScaleOverrides, data->mActiveScaleOverride);
+        LLJointOverrideData &data = mJointOverrides[display_lod][label];
+        populate_list_with_map(joints_pos, data.mPosOverrides);
+        //populate_list_with_vectors(joints_scale, data.mScaleOverrides, data.mActiveScaleOverride);
 
         joint_pos_descr->setTextArg("[JOINT]", label);
-        joint_scale_descr->setTextArg("[JOINT]", label);
+        //joint_scale_descr->setTextArg("[JOINT]", label);
     }
     else
     {
         // temporary value (shouldn't happen)
         std::string label = "mPelvis";
         joint_pos_descr->setTextArg("[JOINT]", label);
-        joint_scale_descr->setTextArg("[JOINT]", label);
+        //joint_scale_descr->setTextArg("[JOINT]", label);
     }
 
 }
@@ -1450,6 +1433,98 @@ void LLFloaterModelPreview::addStringToLog(const std::ostringstream& strm, bool
     }
 }
 
+
+void LLFloaterModelPreview::clearOverridesTab()
+{
+    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
+    joints_list->deleteAllItems();
+
+    for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
+    {
+        mJointOverrides[i].clear();
+    }
+}
+
+void LLFloaterModelPreview::showOverridesTab()
+{
+    S32 display_lod = mModelPreview->mPreviewLOD;
+    if (mModelPreview->mModel[display_lod].empty())
+    {
+        return;
+    }
+    
+    // Todo: Are overrides identical for all lods?
+    if (mJointOverrides[display_lod].empty())
+    {
+        // populate list
+        for (LLModelLoader::scene::iterator iter = mModelPreview->mScene[display_lod].begin(); iter != mModelPreview->mScene[display_lod].end(); ++iter)
+        {
+            for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+            {
+                LLModelInstance& instance = *model_iter;
+                LLModel* model = instance.mModel;
+                const LLMeshSkinInfo *skin = &model->mSkinInfo;
+                if (skin->mAlternateBindMatrix.size() > 0)
+                {
+                    U32 count = LLSkinningUtil::getMeshJointCount(skin);
+                    for (U32 j = 0; j < count; ++j)
+                    {
+                        const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
+                        LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
+                        if (data.mPosOverrides.size() > 0
+                            && (data.mPosOverrides.begin()->second - jointPos).inRange(-F_APPROXIMATELY_ZERO, F_APPROXIMATELY_ZERO))
+                        {
+                            // File contains multiple meshes with conflicting joint offsets
+                            // preview may be incorrect, upload result might wary (depends onto mesh_id).
+                            data.mHasConflicts = true;
+                        }
+                        data.mPosOverrides[model->getName()] = jointPos;
+
+                    }
+                }
+            }
+        }
+    }
+    
+    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    S32 index = mTabContainer->getIndexForPanel(panel);
+    mTabContainer->enableTabButton(index, true);
+
+    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
+
+    joint_override_data_map_t::iterator joint_iter = mJointOverrides[display_lod].begin();
+    joint_override_data_map_t::iterator joint_end = mJointOverrides[display_lod].end();
+    while (joint_iter != joint_end)
+    {
+        const std::string& listName = joint_iter->first;
+
+        LLScrollListItem::Params item_params;
+        item_params.value(listName);
+
+        LLScrollListCell::Params cell_params;
+        cell_params.font = LLFontGL::getFontSansSerif();
+        cell_params.value = listName;
+        if (joint_iter->second.mHasConflicts)
+        {
+            cell_params.color = LLColor4::orange;
+        }
+
+        item_params.columns.add(cell_params);
+
+        joints_list->addRow(item_params, ADD_BOTTOM);
+        joint_iter++;
+    }
+    joints_list->selectFirstItem();
+}
+
+void LLFloaterModelPreview::hideOverridesTab()
+{
+    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    S32 index = mTabContainer->getIndexForPanel(panel);
+    mTabContainer->enableTabButton(index, false);
+}
+
 //-----------------------------------------------------------------------------
 // addStringToLogTab()
 //-----------------------------------------------------------------------------
@@ -2374,6 +2449,11 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 				mViewOption["show_joint_positions"] = true;
 				fmp->childSetValue("upload_joints", true);
 			}
+            else
+            {
+                fmp->clearOverridesTab();
+                fmp->hideOverridesTab();
+            }
 
 			if (lock_scale_if_joint_position)
 			{
@@ -4119,11 +4199,19 @@ BOOL LLModelPreview::render()
     if (upload_skin && upload_joints)
     {
         mFMP->childEnable("lock_scale_if_joint_position");
+        if (fmp)
+        {
+            fmp->showOverridesTab();
+        }
     }
     else
     {
         mFMP->childDisable("lock_scale_if_joint_position");
         mFMP->childSetValue("lock_scale_if_joint_position", false);
+        if (fmp)
+        {
+            fmp->hideOverridesTab();
+        }
     }
     
 	//Only enable joint offsets if it passed the earlier critiquing
@@ -4757,6 +4845,7 @@ void LLFloaterModelPreview::onReset(void* user_data)
 	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
 	fmp->childDisable("reset_btn");
 	fmp->clearLogTab();
+	fmp->clearOverridesTab();
 	LLModelPreview* mp = fmp->mModelPreview;
 	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
 
@@ -4975,44 +5064,6 @@ void LLFloaterModelPreview::clearLogTab()
     mTabContainer->setTabPanelFlashing(panel, false);
 }
 
-void LLFloaterModelPreview::populateOverridesTab()
-{
-    mJointOverrides.clear();
-    attach_override_data_map_t attach_not_in_use;
-    // Todo: use mAlternateBindMatrix
-    mModelPreview->getPreviewAvatar()->getAttachmentOverrides(mJointOverrides, attach_not_in_use);
-
-    if (mJointOverrides.empty())
-    {
-        disableOverridesTab();
-        return;
-    }
-
-    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
-    S32 index = mTabContainer->getIndexForPanel(panel);
-    mTabContainer->enableTabButton(index, true);
-
-    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
-    joints_list->deleteAllItems();
-    
-    joint_override_data_map_t::iterator joint_iter = mJointOverrides.begin();
-    joint_override_data_map_t::iterator joint_end = mJointOverrides.end();
-    while (joint_iter != joint_end)
-    {
-        const std::string& listName = joint_iter->first;
-        joints_list->addSimpleElement(listName);
-        joint_iter++;
-    }
-    joints_list->selectFirstItem();
-}
-
-void LLFloaterModelPreview::disableOverridesTab()
-{
-    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
-    S32 index = mTabContainer->getIndexForPanel(panel);
-    mTabContainer->enableTabButton(index, false);
-}
-
 void LLFloaterModelPreview::onModelPhysicsFeeReceived(const LLSD& result, std::string upload_url)
 {
 	mModelPhysicsFee = result;
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 8988dd2565..f53478f31b 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -30,7 +30,6 @@
 #include "llfloaternamedesc.h"
 
 #include "lldynamictexture.h"
-#include "lljointoverridedata.h"
 #include "llquaternion.h"
 #include "llmeshrepository.h"
 #include "llmodel.h"
@@ -62,6 +61,16 @@ class LLTabContainer;
 class LLToggleableMenu;
 class LLViewerTextEditor;
 
+
+class LLJointOverrideData
+{
+public:
+    LLJointOverrideData() : mHasConflicts(false) {};
+    std::map<std::string, LLVector3> mPosOverrides;
+    bool mHasConflicts;
+};
+typedef std::map<std::string, LLJointOverrideData> joint_override_data_map_t;
+
 class LLFloaterModelPreview : public LLFloaterModelUploadBase
 {
 public:
@@ -95,10 +104,13 @@ public:
 	/*virtual*/ void onClose(bool app_quitting);
 
 	static void onMouseCaptureLostModelPreview(LLMouseHandler*);
-    static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
-    static void addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod = -1);
+	static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+	static void addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod = -1);
 	static void addStringToLog(const std::string& str, bool flash);
-	static void addStringToLog(const std::ostringstream& strm, bool flash);
+	static void addStringToLog(const std::ostringstream& strm, bool flash);    
+	void clearOverridesTab();
+	void showOverridesTab();
+	void hideOverridesTab();
 
 	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
 	void setPreviewLOD(S32 lod);
@@ -227,8 +239,6 @@ private:
 
 	void resetUploadOptions();
 	void clearLogTab();
-	void populateOverridesTab();
-	void disableOverridesTab();
 
 	void createSmoothComboBox(LLComboBox* combo_box, float min, float max);
 
@@ -237,7 +247,7 @@ private:
 	LLViewerTextEditor* mUploadLogText;
 	LLTabContainer* mTabContainer;
 
-	joint_override_data_map_t mJointOverrides;
+	joint_override_data_map_t mJointOverrides[LLModel::NUM_LODS];
 };
 
 class LLMeshFilePicker : public LLFilePickerThread
diff --git a/indra/newview/lljointoverridedata.h b/indra/newview/lljointoverridedata.h
deleted file mode 100644
index 99baa5f334..0000000000
--- a/indra/newview/lljointoverridedata.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * @file lljointoverridedata.h
- * @brief Declaration of LLJointOverrideData and LLAttachmentOverrideData
- *
- * $LicenseInfo:firstyear=2020&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2020, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_JOINTOVERRIDEDATA_H
-#define LL_JOINTOVERRIDEDATA_H
-
-//#include <map>
-//#include <string>
-//#include <vector>
-
-
-struct LLJointOverrideData
-{
-    std::set<LLVector3> mPosOverrides;
-    LLVector3 mActivePosOverride;
-    std::set<LLVector3> mScaleOverrides;
-    LLVector3 mActiveScaleOverride;
-};
-
-struct LLAttachmentOverrideData
-{
-    std::set<LLVector3> mPosOverrides;
-    LLVector3 mActivePosOverride;
-};
-
-typedef std::map<std::string, LLJointOverrideData> joint_override_data_map_t;
-typedef std::map<std::string, LLAttachmentOverrideData> attach_override_data_map_t;
-
-#endif // LL_JOINTOVERRIDEDATA_H
-
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index dac516d15c..3b51d07f96 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -6375,56 +6375,6 @@ void LLVOAvatar::showAttachmentOverrides(bool verbose) const
     }
 }
 
-//-----------------------------------------------------------------------------
-// getAttachmentOverrides
-//-----------------------------------------------------------------------------
-void LLVOAvatar::getAttachmentOverrides(joint_override_data_map_t &joint_overrides, attach_override_data_map_t &attach_overrides) const
-{
-    LLVector3 pos, scale;
-    LLUUID mesh_id;
-    S32 count = 0;
-
-    // Bones
-    for (avatar_joint_list_t::const_iterator iter = mSkeleton.begin();
-        iter != mSkeleton.end(); ++iter)
-    {
-        const LLJoint* pJoint = (*iter);
-        LLJointOverrideData data;
-        bool joint_override = false;
-        if (pJoint && pJoint->hasAttachmentPosOverride(pos, mesh_id))
-        {
-            pJoint->getAllAttachmentPosOverrides(count, data.mPosOverrides);
-            data.mActivePosOverride = pos;
-            joint_override = true;
-        }
-        if (pJoint && pJoint->hasAttachmentScaleOverride(scale, mesh_id))
-        {
-            pJoint->getAllAttachmentScaleOverrides(count, data.mPosOverrides);
-            data.mActiveScaleOverride = scale;
-            joint_override = true;
-        }
-        if (joint_override)
-        {
-            joint_overrides[pJoint->getName()] = data;
-        }
-    }
-
-    // Attachment points
-    for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin();
-        iter != mAttachmentPoints.end();
-        ++iter)
-    {
-        const LLViewerJointAttachment *attachment_pt = (*iter).second;
-        if (attachment_pt && attachment_pt->hasAttachmentPosOverride(pos, mesh_id))
-        {
-            LLAttachmentOverrideData data;
-            attachment_pt->getAllAttachmentPosOverrides(count, data.mPosOverrides);
-            data.mActivePosOverride = pos;
-            attach_overrides[attachment_pt->getName()] = data;
-        }
-    }
-}
-
 //-----------------------------------------------------------------------------
 // removeAttachmentOverridesForObject
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 53a1d48d72..00dccc5d12 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -36,25 +36,23 @@
 #include <boost/signals2/trackable.hpp>
 
 #include "llavatarappearance.h"
-#include "llavatarappearancedefines.h"
-#include "llavatarrendernotifier.h"
-#include "llcontrol.h"
-#include "llcharacter.h"
 #include "llchat.h"
 #include "lldrawpoolalpha.h"
-#include "lldriverparam.h"
-#include "lljointoverridedata.h"
-#include "llrendertarget.h"
-#include "llrigginginfo.h"
-#include "lltexglobalcolor.h"
+#include "llviewerobject.h"
+#include "llcharacter.h"
+#include "llcontrol.h"
 #include "llviewerjointmesh.h"
 #include "llviewerjointattachment.h"
-#include "llviewerobject.h"
-#include "llviewerstats.h"
+#include "llrendertarget.h"
+#include "llavatarappearancedefines.h"
+#include "lltexglobalcolor.h"
+#include "lldriverparam.h"
 #include "llviewertexlayer.h"
-#include "llvovolume.h"
-
 #include "material_codes.h"		// LL_MCODE_END
+#include "llrigginginfo.h"
+#include "llviewerstats.h"
+#include "llvovolume.h"
+#include "llavatarrendernotifier.h"
 
 extern const LLUUID ANIM_AGENT_BODY_NOISE;
 extern const LLUUID ANIM_AGENT_BREATHE_ROT;
@@ -217,8 +215,6 @@ public:
 	void					rebuildAttachmentOverrides();
     void					updateAttachmentOverrides();
     void                    showAttachmentOverrides(bool verbose = false) const;
-    void                    getAttachmentOverrides(joint_override_data_map_t& joint_overrides,
-                                                   attach_override_data_map_t& attach_overrides) const;
     void                    getAttachmentOverrideNames(std::set<std::string>& pos_names, 
                                                        std::set<std::string>& scale_names) const;
 
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 9009eeeb04..a69fa8c57c 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1304,62 +1304,23 @@
          height="100"
          left_delta="0"
          top_pad="0"
-         width="310">
+         width="330">
           <scroll_list.columns
-           label="*"
-           name="override"
-           relative_width="0.22"  />
+           label="Model"
+           name="model_name"
+           relative_width="0.40"  />
           <scroll_list.columns
            label="X"
            name="axis_x"
-           relative_width="0.26"  />
+           relative_width="0.20"  />
           <scroll_list.columns
            label="Y"
            name="axis_y"
-           relative_width="0.26"  />
+           relative_width="0.20"  />
           <scroll_list.columns
            label="Z"
            name="axis_z"
-           relative_width="0.26"  />
-        </scroll_list>
-        <text
-          layout="topleft"
-          follows="top|left"
-          height="15"
-          left_delta="0"
-          name="scale_overrides_descr"
-          top_pad="3"
-          width="300">
-          Scale overrides for joint '[JOINT]':
-        </text>
-        <scroll_list
-         layout="topleft"
-         follows="top|left"
-         name="scale_overrides_list"
-         column_padding="0"
-         draw_heading="true"
-         draw_stripes="false"
-         heading_height="23"
-         height="100"
-         left_delta="0"
-         top_pad="0"
-         width="310">
-          <scroll_list.columns
-           label="*"
-           name="override"
-           relative_width="0.22"  />
-          <scroll_list.columns
-           label="X"
-           name="axis_x"
-           relative_width="0.26"  />
-          <scroll_list.columns
-           label="Y"
-           name="axis_y"
-           relative_width="0.26"  />
-          <scroll_list.columns
-           label="Z"
-           name="axis_z"
-           relative_width="0.26"  />
+           relative_width="0.20"  />
         </scroll_list>
       </panel>
       <panel
-- 
cgit v1.2.3


From 6a3cb73009bccdef6ae1fbb5d8b632f90343ad91 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Mon, 10 Feb 2020 18:29:52 +0200
Subject: SL-379 Joint overrides tab

---
 indra/llcharacter/lljoint.cpp                      |  2 +-
 indra/llcharacter/lljoint.h                        |  2 +
 indra/newview/llfloatermodelpreview.cpp            | 87 ++++++++++++++--------
 indra/newview/llfloatermodelpreview.h              |  7 +-
 .../skins/default/xui/en/floater_model_preview.xml | 14 +++-
 5 files changed, 76 insertions(+), 36 deletions(-)

diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index 441ef1a352..bf99db2ce4 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -407,7 +407,7 @@ void showJointScaleOverrides( const LLJoint& joint, const std::string& note, con
 bool LLJoint::aboveJointPosThreshold(const LLVector3& pos) const
 {
     LLVector3 diff = pos - getDefaultPosition();
-	const F32 max_joint_pos_offset = 0.0001f; // 0.1 mm
+    const F32 max_joint_pos_offset = LL_JOINT_TRESHOLD_POS_OFFSET; // 0.1 mm
 	return diff.lengthSquared() > max_joint_pos_offset * max_joint_pos_offset;
 }
 
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 6f69786f53..79d9637f21 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -53,6 +53,8 @@ const U32 LL_FACE_JOINT_NUM = (LL_CHARACTER_MAX_ANIMATED_JOINTS-2);
 const S32 LL_CHARACTER_MAX_PRIORITY = 7;
 const F32 LL_MAX_PELVIS_OFFSET = 5.f;
 
+const F32 LL_JOINT_TRESHOLD_POS_OFFSET = 0.0001f; //0.1 mm
+
 class LLVector3OverrideMap
 {
 public:
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index c791ca8e77..b30054c818 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -641,17 +641,14 @@ void LLFloaterModelPreview::onJointListSelection()
         std::string label = selected->getValue().asString();
         LLJointOverrideData &data = mJointOverrides[display_lod][label];
         populate_list_with_map(joints_pos, data.mPosOverrides);
-        //populate_list_with_vectors(joints_scale, data.mScaleOverrides, data.mActiveScaleOverride);
 
         joint_pos_descr->setTextArg("[JOINT]", label);
-        //joint_scale_descr->setTextArg("[JOINT]", label);
     }
     else
     {
         // temporary value (shouldn't happen)
         std::string label = "mPelvis";
         joint_pos_descr->setTextArg("[JOINT]", label);
-        //joint_scale_descr->setTextArg("[JOINT]", label);
     }
 
 }
@@ -1433,7 +1430,6 @@ void LLFloaterModelPreview::addStringToLog(const std::ostringstream& strm, bool
     }
 }
 
-
 void LLFloaterModelPreview::clearOverridesTab()
 {
     LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
@@ -1446,6 +1442,16 @@ void LLFloaterModelPreview::clearOverridesTab()
     }
 }
 
+void LLFloaterModelPreview::resetOverridesTab()
+{
+    clearOverridesTab();
+
+    for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
+    {
+        mJointOverrides[i].clear();
+    }
+}
+
 void LLFloaterModelPreview::showOverridesTab()
 {
     S32 display_lod = mModelPreview->mPreviewLOD;
@@ -1453,11 +1459,13 @@ void LLFloaterModelPreview::showOverridesTab()
     {
         return;
     }
-    
-    // Todo: Are overrides identical for all lods?
+
+    // Joints will be listed as long as they are listed in mAlternateBindMatrix
+    // even if they are for some reason identical to defaults.
+    // Todo: Are overrides always identical for all lods? They normally are, but there might be situations where they aren't.
     if (mJointOverrides[display_lod].empty())
     {
-        // populate list
+        // populate map
         for (LLModelLoader::scene::iterator iter = mModelPreview->mScene[display_lod].begin(); iter != mModelPreview->mScene[display_lod].end(); ++iter)
         {
             for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
@@ -1473,10 +1481,11 @@ void LLFloaterModelPreview::showOverridesTab()
                         const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
                         LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
                         if (data.mPosOverrides.size() > 0
-                            && (data.mPosOverrides.begin()->second - jointPos).inRange(-F_APPROXIMATELY_ZERO, F_APPROXIMATELY_ZERO))
+                            && (data.mPosOverrides.begin()->second - jointPos).lengthSquared() > (LL_JOINT_TRESHOLD_POS_OFFSET * LL_JOINT_TRESHOLD_POS_OFFSET))
                         {
                             // File contains multiple meshes with conflicting joint offsets
-                            // preview may be incorrect, upload result might wary (depends onto mesh_id).
+                            // preview may be incorrect, upload result might wary (depends onto
+                            // mesh_id that hasn't been generated yet).
                             data.mHasConflicts = true;
                         }
                         data.mPosOverrides[model->getName()] = jointPos;
@@ -1486,36 +1495,45 @@ void LLFloaterModelPreview::showOverridesTab()
             }
         }
     }
-    
+
     LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
     S32 index = mTabContainer->getIndexForPanel(panel);
     mTabContainer->enableTabButton(index, true);
-
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
 
-    joint_override_data_map_t::iterator joint_iter = mJointOverrides[display_lod].begin();
-    joint_override_data_map_t::iterator joint_end = mJointOverrides[display_lod].end();
-    while (joint_iter != joint_end)
+    if (joints_list->isEmpty())
     {
-        const std::string& listName = joint_iter->first;
+        // Populate table
+        S32 conflicts = 0;
+        joint_override_data_map_t::iterator joint_iter = mJointOverrides[display_lod].begin();
+        joint_override_data_map_t::iterator joint_end = mJointOverrides[display_lod].end();
+        while (joint_iter != joint_end)
+        {
+            const std::string& listName = joint_iter->first;
 
-        LLScrollListItem::Params item_params;
-        item_params.value(listName);
+            LLScrollListItem::Params item_params;
+            item_params.value(listName);
 
-        LLScrollListCell::Params cell_params;
-        cell_params.font = LLFontGL::getFontSansSerif();
-        cell_params.value = listName;
-        if (joint_iter->second.mHasConflicts)
-        {
-            cell_params.color = LLColor4::orange;
-        }
+            LLScrollListCell::Params cell_params;
+            cell_params.font = LLFontGL::getFontSansSerif();
+            cell_params.value = listName;
+            if (joint_iter->second.mHasConflicts)
+            {
+                cell_params.color = LLColor4::orange;
+                conflicts++;
+            }
 
-        item_params.columns.add(cell_params);
+            item_params.columns.add(cell_params);
 
-        joints_list->addRow(item_params, ADD_BOTTOM);
-        joint_iter++;
+            joints_list->addRow(item_params, ADD_BOTTOM);
+            joint_iter++;
+        }
+        joints_list->selectFirstItem();
+
+        LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("conflicts_description");
+        joint_pos_descr->setTextArg("[CONFLICTS]", llformat("%d", conflicts));
+        joint_pos_descr->setTextArg("[JOINTS_COUNT]", llformat("%d", mJointOverrides[display_lod].size()));
     }
-    joints_list->selectFirstItem();
 }
 
 void LLFloaterModelPreview::hideOverridesTab()
@@ -2451,7 +2469,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 			}
             else
             {
-                fmp->clearOverridesTab();
+                fmp->resetOverridesTab();
                 fmp->hideOverridesTab();
             }
 
@@ -4051,6 +4069,8 @@ void LLModelPreview::addEmptyFace( LLModel* pTarget )
 //-----------------------------------------------------------------------------
 // render()
 //-----------------------------------------------------------------------------
+// Todo: we shouldn't be setting all those UI elements on render.
+// Note: Render happens each frame with skinned avatars
 BOOL LLModelPreview::render()
 {
 	assert_main_thread();
@@ -4824,6 +4844,13 @@ void LLModelPreview::setPreviewLOD(S32 lod)
 			mFMP->childSetColor(lod_triangles_name[i], color);
 			mFMP->childSetColor(lod_vertices_name[i], color);
 		}
+
+        LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP;
+        if (fmp)
+        {
+            // make preview repopulate tab
+            fmp->clearOverridesTab();
+        }
 	}
 	refresh();
 	updateStatusMessages();
@@ -4845,7 +4872,7 @@ void LLFloaterModelPreview::onReset(void* user_data)
 	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
 	fmp->childDisable("reset_btn");
 	fmp->clearLogTab();
-	fmp->clearOverridesTab();
+	fmp->resetOverridesTab();
 	LLModelPreview* mp = fmp->mModelPreview;
 	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
 
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index f53478f31b..2adbfc1e79 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -107,9 +107,10 @@ public:
 	static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
 	static void addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod = -1);
 	static void addStringToLog(const std::string& str, bool flash);
-	static void addStringToLog(const std::ostringstream& strm, bool flash);    
-	void clearOverridesTab();
-	void showOverridesTab();
+	static void addStringToLog(const std::ostringstream& strm, bool flash);
+	void clearOverridesTab(); // clears table
+	void resetOverridesTab(); // clears table and cleans all data
+	void showOverridesTab(); // populates table and data as nessesary
 	void hideOverridesTab();
 
 	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
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 a69fa8c57c..9ebcb0c09d 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1279,10 +1279,20 @@
          draw_stripes="false"
          commit_on_selection_change="true"
          heading_height="23"
-         height="253"
+         height="238"
          left="6"
          top_pad="0"
-         width="150"/>
+         width="200"/>
+        <text
+          layout="topleft"
+          follows="top|left"
+          height="15"
+          left="6"
+          name="conflicts_description"
+          top_pad="2"
+          width="200">
+          [CONFLICTS] conflicts in [JOINTS_COUNT] joints
+        </text>
         <text
           layout="topleft"
           follows="top|left"
-- 
cgit v1.2.3


From e66e31f380563962429c8b285f1aa5dabf8ea5d2 Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Sat, 22 Feb 2020 19:24:13 +0200
Subject: post-merge buildfix

---
 indra/newview/llfloatermodelpreview.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index d7fea67e60..c359722431 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -235,6 +235,7 @@ private:
 	void modelUpdated(bool calculate_visible);
 
 	// Toggles between "Calculate weights & fee" and "Upload" buttons.
+    void toggleCalculateButton();
 	void toggleCalculateButton(bool visible);
 
 	// resets display options of model preview to their defaults.
-- 
cgit v1.2.3


From eaa9b152bfe9b5da44f724ddce1a2ecc86dc61bf Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 27 Mar 2020 19:05:23 +0200
Subject: SL-307 Implemented ability to specify color and changed flashing
 color

---
 indra/llui/llbutton.cpp                                     |  5 +++++
 indra/llui/llbutton.h                                       |  1 +
 indra/llui/lltabcontainer.cpp                               | 11 +++++++++++
 indra/llui/lltabcontainer.h                                 |  3 ++-
 indra/newview/llfloatermodelpreview.cpp                     |  5 ++++-
 indra/newview/skins/default/xui/en/floater_script_debug.xml |  3 ++-
 6 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 27444b7f5b..804204cce0 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -734,6 +734,11 @@ void LLButton::draw()
 			{
                 glow_color = highlighting_color;
 			}
+            else
+            {
+                // will fade from highlight color
+                glow_color = flash_color;
+            }
 		}
 	}
 
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index 7629ed1fea..572d36996c 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -205,6 +205,7 @@ public:
 	void			setFlashing( bool b, bool force_flashing = false );
 	BOOL			getFlashing() const		{ return mFlashing; }
     LLFlashTimer*   getFlashTimer() {return mFlashingTimer;}
+	void			setFlashColor(const LLUIColor &color) { mFlashBgColor = color; };
 
 	void			setHAlign( LLFontGL::HAlign align )		{ mHAlign = align; }
 	LLFontGL::HAlign getHAlign() const						{ return mHAlign; }
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 2a221fc19c..750a3aff9c 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -260,6 +260,7 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
 	mCustomIconCtrlUsed(p.use_custom_icon_ctrl),
 	mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop),
 	mTabIconCtrlPad(p.tab_icon_ctrl_pad),
+	mEnableTabsFlashing(p.enable_tabs_flashing),
 	mUseTabEllipses(p.use_ellipses)
 {
 	static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
@@ -1641,6 +1642,16 @@ void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state )
 	}
 }
 
+void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state, LLUIColor color)
+{
+    LLTabTuple* tuple = getTabByPanel(child);
+    if (tuple)
+    {
+        tuple->mButton->setFlashColor(color);
+        tuple->mButton->setFlashing(state);
+    }
+}
+
 void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
 {
 	LLTabTuple* tuple = getTabByPanel(child);
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 8f93f1c47d..5339bec3dd 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -111,7 +111,7 @@ public:
 		Optional<bool>						open_tabs_on_drag_and_drop;
 
 		/**
-		 * Open tabs on hover in drag and drop situations
+		 * Enable tab flashing
 		 */
 		Optional<bool>						enable_tabs_flashing;
 		
@@ -203,6 +203,7 @@ public:
 
 	BOOL        getTabPanelFlashing(LLPanel* child);
 	void		setTabPanelFlashing(LLPanel* child, BOOL state);
+	void		setTabPanelFlashing(LLPanel* child, BOOL state, LLUIColor color);
 	void 		setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white);
 	void 		setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white);
 	void		setTabImage(LLPanel* child, LLIconCtrl* icon);
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index e694340b16..f386e9e305 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1583,7 +1583,10 @@ void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash
 
     if (flash && mTabContainer->getCurrentPanel() != panel)
     {
-        mTabContainer->setTabPanelFlashing(panel, true);
+        // This will makes colors pale due to "glow_type = LLRender::BT_ALPHA"
+        // So instead of using "MenuItemFlashBgColor" added stronger color
+        static LLUIColor sFlashBgColor(LLColor4U(255, 99, 0));
+        mTabContainer->setTabPanelFlashing(panel, true, sFlashBgColor);
     }
 }
 
diff --git a/indra/newview/skins/default/xui/en/floater_script_debug.xml b/indra/newview/skins/default/xui/en/floater_script_debug.xml
index cd88048d6b..6c49cfa1a8 100644
--- a/indra/newview/skins/default/xui/en/floater_script_debug.xml
+++ b/indra/newview/skins/default/xui/en/floater_script_debug.xml
@@ -17,5 +17,6 @@
      name="Preview Tabs"
      tab_position="bottom"
      top="16"
-     width="448" />
+     width="448"
+     enable_tabs_flashing="true"/>
 </multi_floater>
-- 
cgit v1.2.3


From 46c76eabb86fff6ca6c8352e10988b15c20ef8a6 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Mon, 30 Mar 2020 15:14:44 +0300
Subject: SL-379 Do not disable tabs

---
 indra/newview/llfloatermodelpreview.cpp | 20 +++-----------------
 indra/newview/llfloatermodelpreview.h   |  3 +--
 2 files changed, 4 insertions(+), 19 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index f386e9e305..d53b0c1155 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -407,13 +407,11 @@ BOOL LLFloaterModelPreview::postBuild()
     // Disable Overrides tab untill it has something to show and set callbacks
     LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
     S32 index = mTabContainer->getIndexForPanel(panel);
-    mTabContainer->enableTabButton(index, false);
     panel->getChild<LLScrollListCtrl>("joints_list")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onJointListSelection, this));
 
 	// Disable Logs tab untill it has something to show
 	panel = mTabContainer->getPanelByName("logs_panel");
 	index = mTabContainer->getIndexForPanel(panel);
-	mTabContainer->enableTabButton(index, false);
 
 	if (LLConvexDecomposition::getInstance() != NULL)
 	{
@@ -1459,7 +1457,7 @@ void LLFloaterModelPreview::resetOverridesTab()
     }
 }
 
-void LLFloaterModelPreview::showOverridesTab()
+void LLFloaterModelPreview::updateOverridesTab()
 {
     S32 display_lod = mModelPreview->mPreviewLOD;
     if (mModelPreview->mModel[display_lod].empty())
@@ -1504,8 +1502,6 @@ void LLFloaterModelPreview::showOverridesTab()
     }
 
     LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
-    S32 index = mTabContainer->getIndexForPanel(panel);
-    mTabContainer->enableTabButton(index, true);
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
 
     if (joints_list->isEmpty())
@@ -1543,13 +1539,6 @@ void LLFloaterModelPreview::showOverridesTab()
     }
 }
 
-void LLFloaterModelPreview::hideOverridesTab()
-{
-    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
-    S32 index = mTabContainer->getIndexForPanel(panel);
-    mTabContainer->enableTabButton(index, false);
-}
-
 //-----------------------------------------------------------------------------
 // addStringToLogTab()
 //-----------------------------------------------------------------------------
@@ -1569,8 +1558,6 @@ void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash
     }
 
     LLPanel* panel = mTabContainer->getPanelByName("logs_panel");
-    S32 index = mTabContainer->getIndexForPanel(panel);
-    mTabContainer->enableTabButton(index, true);
 
     // Make sure we have space for new string
     S32 editor_text_len = mUploadLogText->getLength();
@@ -2480,7 +2467,6 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
             else
             {
                 fmp->resetOverridesTab();
-                fmp->hideOverridesTab();
             }
 
 			if (lock_scale_if_joint_position)
@@ -4231,7 +4217,7 @@ BOOL LLModelPreview::render()
         mFMP->childEnable("lock_scale_if_joint_position");
         if (fmp)
         {
-            fmp->showOverridesTab();
+            fmp->updateOverridesTab();
         }
     }
     else
@@ -4240,7 +4226,7 @@ BOOL LLModelPreview::render()
         mFMP->childSetValue("lock_scale_if_joint_position", false);
         if (fmp)
         {
-            fmp->hideOverridesTab();
+            fmp->resetOverridesTab();
         }
     }
     
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index c359722431..f64a7b6feb 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -110,8 +110,7 @@ public:
 	static void addStringToLog(const std::ostringstream& strm, bool flash);
 	void clearOverridesTab(); // clears table
 	void resetOverridesTab(); // clears table and cleans all data
-	void showOverridesTab(); // populates table and data as nessesary
-	void hideOverridesTab();
+	void updateOverridesTab(); // populates table and data as nessesary
 
 	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
 	void setPreviewLOD(S32 lod);
-- 
cgit v1.2.3


From acb6e12135452d8d4e53a9a61e1fb50cdeec2469 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Mon, 30 Mar 2020 16:47:54 +0300
Subject: SL-379 Remake Overrides tab into Avatar

---
 indra/newview/llfloatermodelpreview.cpp            |  44 ++--
 indra/newview/llfloatermodelpreview.h              |   1 -
 .../skins/default/xui/en/floater_model_preview.xml | 222 ++++++++++-----------
 3 files changed, 136 insertions(+), 131 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index d53b0c1155..f99b42ab2c 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -346,6 +346,10 @@ BOOL LLFloaterModelPreview::postBuild()
 	childSetVisible("skin_too_many_joints", false);
 	childSetVisible("skin_unknown_joint", false);
 
+    childSetVisible("warning_title", false);
+    childSetVisible("warning_message", false);
+    childSetVisible("status", false);
+
 	initDecompControls();
 
 	LLView* preview_panel = getChild<LLView>("preview_panel");
@@ -656,6 +660,7 @@ void LLFloaterModelPreview::onJointListSelection()
         joint_pos_descr->setTextArg("[JOINT]", label);
     }
 
+    // Note: We can make a version of renderBones() to highlight selected joint
 }
 
 void LLFloaterModelPreview::onDescriptionKeystroke(LLUICtrl* ctrl)
@@ -1440,21 +1445,22 @@ void LLFloaterModelPreview::clearOverridesTab()
     LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
     joints_list->deleteAllItems();
+    LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
+    joints_pos->deleteAllItems();
+
 
     for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
     {
         mJointOverrides[i].clear();
     }
-}
 
-void LLFloaterModelPreview::resetOverridesTab()
-{
-    clearOverridesTab();
+    LLTextBox *joint_total_descr = panel->getChild<LLTextBox>("conflicts_description");
+    joint_total_descr->setTextArg("[CONFLICTS]", llformat("%d", 0));
+    joint_total_descr->setTextArg("[JOINTS_COUNT]", llformat("%d", 0));
 
-    for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
-    {
-        mJointOverrides[i].clear();
-    }
+
+    LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr");
+    joint_pos_descr->setTextArg("[JOINT]", std::string("mPelvis")); // Might be better to hide it
 }
 
 void LLFloaterModelPreview::updateOverridesTab()
@@ -1507,6 +1513,10 @@ void LLFloaterModelPreview::updateOverridesTab()
     if (joints_list->isEmpty())
     {
         // Populate table
+
+        std::map<std::string, std::string> joint_alias_map;
+        mModelPreview->getJointAliases(joint_alias_map);
+
         S32 conflicts = 0;
         joint_override_data_map_t::iterator joint_iter = mJointOverrides[display_lod].begin();
         joint_override_data_map_t::iterator joint_end = mJointOverrides[display_lod].end();
@@ -1520,8 +1530,14 @@ void LLFloaterModelPreview::updateOverridesTab()
             LLScrollListCell::Params cell_params;
             cell_params.font = LLFontGL::getFontSansSerif();
             cell_params.value = listName;
+            if (joint_alias_map.find(listName) == joint_alias_map.end())
+            {
+                // Missing names
+                cell_params.color = LLColor4::red;
+            }
             if (joint_iter->second.mHasConflicts)
             {
+                // Conflicts
                 cell_params.color = LLColor4::orange;
                 conflicts++;
             }
@@ -1533,9 +1549,9 @@ void LLFloaterModelPreview::updateOverridesTab()
         }
         joints_list->selectFirstItem();
 
-        LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("conflicts_description");
-        joint_pos_descr->setTextArg("[CONFLICTS]", llformat("%d", conflicts));
-        joint_pos_descr->setTextArg("[JOINTS_COUNT]", llformat("%d", mJointOverrides[display_lod].size()));
+        LLTextBox *joint_conf_descr = panel->getChild<LLTextBox>("conflicts_description");
+        joint_conf_descr->setTextArg("[CONFLICTS]", llformat("%d", conflicts));
+        joint_conf_descr->setTextArg("[JOINTS_COUNT]", llformat("%d", mJointOverrides[display_lod].size()));
     }
 }
 
@@ -2466,7 +2482,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 			}
             else
             {
-                fmp->resetOverridesTab();
+                fmp->clearOverridesTab();
             }
 
 			if (lock_scale_if_joint_position)
@@ -4226,7 +4242,7 @@ BOOL LLModelPreview::render()
         mFMP->childSetValue("lock_scale_if_joint_position", false);
         if (fmp)
         {
-            fmp->resetOverridesTab();
+            fmp->clearOverridesTab();
         }
     }
     
@@ -4868,7 +4884,7 @@ void LLFloaterModelPreview::onReset(void* user_data)
 	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
 	fmp->childDisable("reset_btn");
 	fmp->clearLogTab();
-	fmp->resetOverridesTab();
+	fmp->clearOverridesTab();
 	LLModelPreview* mp = fmp->mModelPreview;
 	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
 
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index f64a7b6feb..d079a3d782 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -109,7 +109,6 @@ public:
 	static void addStringToLog(const std::string& str, bool flash);
 	static void addStringToLog(const std::ostringstream& strm, bool flash);
 	void clearOverridesTab(); // clears table
-	void resetOverridesTab(); // clears table and cleans all data
 	void updateOverridesTab(); // populates table and data as nessesary
 
 	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
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 9ebcb0c09d..053c482831 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -47,12 +47,12 @@
 
   <panel
     follows="top|left"
-    height="580"
+    height="595"
     layout="topleft"
     left="3"
     name="left_panel"
     top_pad="0"
-    width="630">
+    width="635">
     <panel
       follows="all"
       height="50"
@@ -83,9 +83,9 @@
     </panel>
     <tab_container
       follows="top|left"
-      top_pad="15"
+      top_pad="10"
       left="0"
-      height="300"
+      height="330"
       width="635"
       name="import_tab"
       tab_position="top"
@@ -100,12 +100,12 @@
             <view_border
              bevel_style="none"
              follows="top|left"
-             height="275"
+             height="306"
              layout="topleft"
              left="3"
              name="lod_tab_border"
              top_pad="0"
-             width="619" />
+             width="628" />
           <text
            follows="left|top"
            height="18"
@@ -696,7 +696,7 @@
              left="10"
              name="lod_tab_border"
              top_pad="20"
-             width="605" />
+             width="614" />
             <check_box
              follows="top|left"
              height="15"
@@ -738,12 +738,12 @@
             <view_border
               bevel_style="none"
               follows="top|left"
-              height="275"
+              height="306"
               layout="topleft"
               left="3"
               name="physics_tab_border"
               top_pad="0"
-              width="619"/>
+              width="628"/>
                 <panel
                   bg_alpha_color="0 0 0 0"
                   bg_opaque_color="0 0 0 0.3"
@@ -1113,12 +1113,12 @@
          <view_border
           bevel_style="none"  
           follows="top|left"
-          height="275"
+          height="306"
           layout="topleft"
           left="3"
           name="border"
           top_pad="0"
-          width="619"/>
+          width="628"/>
            <text
              follows="top|left"
              height="16"
@@ -1165,109 +1165,94 @@
              label_text.text_color="White"
              left="20"
              top_pad="20"/>
-           <view_border
-             bevel_style="none"
-             follows="top|left"
-             height="0"
-             layout="topleft"
-             name="border"
-             top_pad="20"
-             width="579"/>
-           <text
-             follows="top|left"
-             height="15"
-             left="20"
-             name="include_label"
-             text_color="White"
-             top_pad="20"
-             width="150">
-             For avatar models only:
-           </text>
-           <check_box
-             follows="top|left"
-             height="15"
-             label="Include skin weight"
-             label_text.text_color="White"
-             name="upload_skin"
-             top_pad="15"/>
-           <text
-             follows="top|left"
-             height="17"
-             left="40"
-             name="skin_too_many_joints"
-             text_color="Orange"
-             top_pad="-2"
-             width="150">
-             Too many skinned joints
-           </text>
-           <text
-             follows="top|left"
-             height="17"
-             left="40"
-             name="skin_unknown_joint"
-             text_color="Orange"
-             top_pad="-17"
-             width="150">
-             Model has an unknown joint(s)
-           </text>
-           <check_box
-             follows="top|left"
-             height="15"
-             label="Include joint positions"
-             label_text.text_color="White"
-             name="upload_joints"
-             left_delta="-20"
-             top_pad="1"/>
-           <check_box
-             follows="top|left"
-             height="15"
-             label="Lock scale if joint position defined"
-             label_text.text_color="White"
-             name="lock_scale_if_joint_position"
-             top_pad="16"/>
-           <text
-             follows="top|left"
-             height="15"
-             layout="topleft"
-             left="220"
-             name="pelvis_offset_label"
-             text_color="White"
-             top="134"
-             width="200">
-             Z offset (raise or lower avatar):
-           </text>
-           <spinner
-             follows="top|left"
-             height="20"
-             min_val="-3.00"
-             max_val="3.0"
-             name="pelvis_offset"
-             top_pad="10"
-             value="0.0"
-             width="80"/>
      </panel>
       <panel
        label="Overrides"
        layout="topleft"
        name="overrides_panel"
-       title="Overrides">
+       title="Avatar">
         <view_border
          bevel_style="none"
          follows="top|left"
-         height="275"
+         height="306"
          layout="topleft"
          left="3"
-         name="log_tab_border"
+         name="avatar_tab_border"
          top_pad="0"
-         width="619" />
+         width="628" />
+        <check_box
+          follows="top|left"
+          height="15"
+          label="Include skin weight"
+          label_text.text_color="White"
+          name="upload_skin"
+          top="8"
+          left="20"/>
+        <check_box
+          follows="top|left"
+          height="15"
+          label="Include joint positions"
+          label_text.text_color="White"
+          name="upload_joints"
+          left_delta="0"
+          top_pad="7"/>
+        <check_box
+          follows="top|left"
+          height="15"
+          label="Lock scale if joint position defined"
+          label_text.text_color="White"
+          name="lock_scale_if_joint_position"
+          top_pad="7"/>
+        <text
+          follows="top|left"
+          height="15"
+          layout="topleft"
+          left="220"
+          name="pelvis_offset_label"
+          text_color="White"
+          top="8"
+          width="200">
+          Z offset (raise or lower avatar):
+        </text>
+        <spinner
+          follows="top|left"
+          height="20"
+          min_val="-3.00"
+          max_val="3.0"
+          name="pelvis_offset"
+          top_pad="10"
+          value="0.0"
+          width="80"/>
+        <text
+          follows="top|left"
+          height="17"
+          left="425"
+          name="skin_too_many_joints"
+          text_color="Orange"
+          top="7"
+          width="195"
+          word_wrap="true">
+          Too many skinned joints
+        </text>
+        <text
+          follows="top|left"
+          height="32"
+          left="425"
+          name="skin_unknown_joint"
+          text_color="Orange"
+          top="8"
+          width="195"
+          word_wrap="true">
+          Model has an unknown joint(s)
+        </text>
         <text
           layout="topleft"
           follows="top|left"
           height="15"
-          left="6"
+          left="20"
           name="joints_descr"
-          top="4"
-          width="300">
+          top="73"
+          width="150">
           Joints:
         </text>
         <scroll_list
@@ -1279,15 +1264,15 @@
          draw_stripes="false"
          commit_on_selection_change="true"
          heading_height="23"
-         height="238"
-         left="6"
+         height="199"
+         left_delta="0"
          top_pad="0"
          width="200"/>
         <text
           layout="topleft"
           follows="top|left"
           height="15"
-          left="6"
+          left_delta="0"
           name="conflicts_description"
           top_pad="2"
           width="200">
@@ -1299,7 +1284,7 @@
           height="15"
           left_pad="5"
           name="pos_overrides_descr"
-          top="4"
+          top="73"
           width="300">
           Position overrides for joint '[JOINT]':
         </text>
@@ -1314,23 +1299,23 @@
          height="100"
          left_delta="0"
          top_pad="0"
-         width="330">
+         width="385">
           <scroll_list.columns
            label="Model"
            name="model_name"
-           relative_width="0.40"  />
+           relative_width="0.49" />
           <scroll_list.columns
            label="X"
            name="axis_x"
-           relative_width="0.20"  />
+           relative_width="0.17" />
           <scroll_list.columns
            label="Y"
            name="axis_y"
-           relative_width="0.20"  />
+           relative_width="0.17" />
           <scroll_list.columns
            label="Z"
            name="axis_z"
-           relative_width="0.20"  />
+           relative_width="0.17" />
         </scroll_list>
       </panel>
       <panel
@@ -1341,12 +1326,12 @@
         <view_border
          bevel_style="none"
          follows="top|left"
-         height="275"
+         height="306"
          layout="topleft"
          left="3"
          name="log_tab_border"
          top_pad="0"
-         width="619" />
+         width="628" />
         <text_editor
          type="string"
          length="1"
@@ -1355,10 +1340,10 @@
          font="SansSerif"
          ignore_tab="false"
          layout="topleft"
-         height="275"
+         height="306"
          left="4"
          top="0"
-         right="-11"
+         right="-1"
          max_length="65536"
          name="log_text"
          parse_urls="true"
@@ -1371,7 +1356,7 @@
     <panel
      follows="top|left|bottom"
      layout="topleft"
-     height="197"
+     height="195"
      left="4"
      border="true"
      name="weights_and_warning_panel"
@@ -1611,7 +1596,7 @@ Analysed:
          name="warning_title"
          top_pad="5"
          text_color="DrYellow"
-         visible="false"
+         visible="true"
          width="40">
          NOTE:
        </text>
@@ -1622,13 +1607,18 @@ Analysed:
          left_pad="1"
          name="warning_message"
          parse_urls="true"
-         top_delta="2"
+         top_delta="1"
          wrap="true"
          width="462"
-         visible="false">
+         visible="true">
          You dont have rights to upload mesh models. [[VURL] Find out how] to get certified.
        </text> 
-       <text text_color="Yellow" layout="topleft" top_pad="-1" left="6" name="status">
+       <text
+         text_color="Yellow"
+         layout="topleft"
+         top_pad="-2"
+         left="6"
+         name="status">
 [STATUS]
        </text>
     </panel>
-- 
cgit v1.2.3


From aaddcc0b704f28555fbd4a1f6068f44242966295 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Mon, 30 Mar 2020 20:04:25 +0300
Subject: SL-307 A separator to see where model loading logs ends and other
 state starts

---
 indra/newview/llfloatermodelpreview.cpp                      | 4 ++++
 indra/newview/skins/default/xui/en/floater_model_preview.xml | 4 ++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index f99b42ab2c..b0dfe19451 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -2625,6 +2625,10 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 			{
 				description_form->setText(model_name);
 			}
+            // Add info to log that loading is complete (purpose: separator between loading and other logs)
+            LLSD args;
+            args["MODEL_NAME"] = model_name; // Teoretically shouldn't be empty, but might be better idea to add filename here
+            LLFloaterModelPreview::addStringToLog("ModelLoaded", args, false, loaded_lod);
 		}
 	}
 	refresh();
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 053c482831..aeed3b4623 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -39,11 +39,11 @@
   <string name="simplifying">Simplifying...</string>
   <string name="tbd">TBD</string>
   
-  <!-- Warnings from model loader-->
+  <!-- Warnings and info from model loader-->
   <string name="TooManyJoint">Skinning disabled due to too many joints: [JOINTS], maximum: [MAX]</string>
   <string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string>
   <string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string>
-  
+  <string name="ModelLoaded">Model [MODEL_NAME] loaded</string>
 
   <panel
     follows="top|left"
-- 
cgit v1.2.3


From 3c2f3edb5fefff69e370f4e427dd23236cf8edbf Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 31 Mar 2020 15:40:48 +0300
Subject: SL-379 Render selected joint yellow when Avatar tab is open

---
 indra/newview/llfloatermodelpreview.cpp            | 52 +++++++++++++---------
 indra/newview/llfloatermodelpreview.h              |  7 ++-
 indra/newview/llvoavatar.cpp                       | 21 +++++++--
 indra/newview/llvoavatar.h                         |  2 +-
 .../skins/default/xui/en/floater_model_preview.xml |  2 +-
 5 files changed, 55 insertions(+), 29 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index b0dfe19451..2a0e51f496 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -266,7 +266,8 @@ LLFloaterModelUploadBase(key),
 mUploadBtn(NULL),
 mCalculateBtn(NULL),
 mUploadLogText(NULL),
-mTabContainer(NULL)
+mTabContainer(NULL),
+mAvatarTabIndex(0)
 {
 	sInstance = this;
 	mLastMouseX = 0;
@@ -408,15 +409,10 @@ BOOL LLFloaterModelPreview::postBuild()
 	mUploadLogText = getChild<LLViewerTextEditor>("log_text");
 	mTabContainer = getChild<LLTabContainer>("import_tab");
 
-    // Disable Overrides tab untill it has something to show and set callbacks
-    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
-    S32 index = mTabContainer->getIndexForPanel(panel);
+    LLPanel *panel = mTabContainer->getPanelByName("avatar_panel");
+    mAvatarTabIndex = mTabContainer->getIndexForPanel(panel);
     panel->getChild<LLScrollListCtrl>("joints_list")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onJointListSelection, this));
 
-	// Disable Logs tab untill it has something to show
-	panel = mTabContainer->getPanelByName("logs_panel");
-	index = mTabContainer->getIndexForPanel(panel);
-
 	if (LLConvexDecomposition::getInstance() != NULL)
 	{
 	mCalculateBtn->setClickedCallback(boost::bind(&LLFloaterModelPreview::onClickCalculateBtn, this));
@@ -635,7 +631,7 @@ void populate_list_with_map(LLScrollListCtrl *list, const std::map<std::string,
 void LLFloaterModelPreview::onJointListSelection()
 {
     S32 display_lod = mModelPreview->mPreviewLOD;
-    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    LLPanel *panel = mTabContainer->getPanelByName("avatar_panel");
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
     LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
     LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list");
@@ -652,12 +648,14 @@ void LLFloaterModelPreview::onJointListSelection()
         populate_list_with_map(joints_pos, data.mPosOverrides);
 
         joint_pos_descr->setTextArg("[JOINT]", label);
+        mSelectedJointName = label;
     }
     else
     {
         // temporary value (shouldn't happen)
         std::string label = "mPelvis";
         joint_pos_descr->setTextArg("[JOINT]", label);
+        mSelectedJointName.clear();
     }
 
     // Note: We can make a version of renderBones() to highlight selected joint
@@ -1440,14 +1438,13 @@ void LLFloaterModelPreview::addStringToLog(const std::ostringstream& strm, bool
     }
 }
 
-void LLFloaterModelPreview::clearOverridesTab()
+void LLFloaterModelPreview::clearAvatarTab()
 {
-    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    LLPanel *panel = mTabContainer->getPanelByName("avatar_panel");
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
     joints_list->deleteAllItems();
     LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
-    joints_pos->deleteAllItems();
-
+    joints_pos->deleteAllItems();    mSelectedJointName.clear();
 
     for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
     {
@@ -1463,11 +1460,12 @@ void LLFloaterModelPreview::clearOverridesTab()
     joint_pos_descr->setTextArg("[JOINT]", std::string("mPelvis")); // Might be better to hide it
 }
 
-void LLFloaterModelPreview::updateOverridesTab()
+void LLFloaterModelPreview::updateAvatarTab()
 {
     S32 display_lod = mModelPreview->mPreviewLOD;
     if (mModelPreview->mModel[display_lod].empty())
     {
+        mSelectedJointName.clear();
         return;
     }
 
@@ -1507,7 +1505,7 @@ void LLFloaterModelPreview::updateOverridesTab()
         }
     }
 
-    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel");
+    LLPanel *panel = mTabContainer->getPanelByName("avatar_panel");
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
 
     if (joints_list->isEmpty())
@@ -1548,6 +1546,11 @@ void LLFloaterModelPreview::updateOverridesTab()
             joint_iter++;
         }
         joints_list->selectFirstItem();
+        LLScrollListItem *selected = joints_list->getFirstSelected();
+        if (selected)
+        {
+            mSelectedJointName = selected->getValue().asString();
+        }
 
         LLTextBox *joint_conf_descr = panel->getChild<LLTextBox>("conflicts_description");
         joint_conf_descr->setTextArg("[CONFLICTS]", llformat("%d", conflicts));
@@ -2482,7 +2485,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 			}
             else
             {
-                fmp->clearOverridesTab();
+                fmp->clearAvatarTab();
             }
 
 			if (lock_scale_if_joint_position)
@@ -4237,7 +4240,7 @@ BOOL LLModelPreview::render()
         mFMP->childEnable("lock_scale_if_joint_position");
         if (fmp)
         {
-            fmp->updateOverridesTab();
+            fmp->updateAvatarTab();
         }
     }
     else
@@ -4246,7 +4249,7 @@ BOOL LLModelPreview::render()
         mFMP->childSetValue("lock_scale_if_joint_position", false);
         if (fmp)
         {
-            fmp->clearOverridesTab();
+            fmp->clearAvatarTab();
         }
     }
     
@@ -4774,7 +4777,14 @@ BOOL LLModelPreview::render()
 					gDebugProgram.bind();
 				}
 				getPreviewAvatar()->renderCollisionVolumes();
-				getPreviewAvatar()->renderBones();
+                if (fmp->mTabContainer->getCurrentPanelIndex() == fmp->mAvatarTabIndex)
+                {
+                    getPreviewAvatar()->renderBones(fmp->mSelectedJointName);
+                }
+                else
+                {
+                    getPreviewAvatar()->renderBones();
+                }
 				if (shader)
 				{
 					shader->bind();
@@ -4865,7 +4875,7 @@ void LLModelPreview::setPreviewLOD(S32 lod)
         if (fmp)
         {
             // make preview repopulate tab
-            fmp->clearOverridesTab();
+            fmp->clearAvatarTab();
         }
 	}
 	refresh();
@@ -4888,7 +4898,7 @@ void LLFloaterModelPreview::onReset(void* user_data)
 	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
 	fmp->childDisable("reset_btn");
 	fmp->clearLogTab();
-	fmp->clearOverridesTab();
+	fmp->clearAvatarTab();
 	LLModelPreview* mp = fmp->mModelPreview;
 	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
 
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index d079a3d782..1542e97160 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -108,8 +108,8 @@ public:
 	static void addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod = -1);
 	static void addStringToLog(const std::string& str, bool flash);
 	static void addStringToLog(const std::ostringstream& strm, bool flash);
-	void clearOverridesTab(); // clears table
-	void updateOverridesTab(); // populates table and data as nessesary
+	void clearAvatarTab(); // clears table
+	void updateAvatarTab(); // populates table and data as nessesary
 
 	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
 	void setPreviewLOD(S32 lod);
@@ -249,6 +249,9 @@ private:
 	LLViewerTextEditor* mUploadLogText;
 	LLTabContainer* mTabContainer;
 
+	S32			mAvatarTabIndex; // just to avoid any issues in case of xml changes
+	std::string	mSelectedJointName;
+
 	joint_override_data_map_t mJointOverrides[LLModel::NUM_LODS];
 };
 
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index b524db478e..32e6535465 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -1574,13 +1574,16 @@ void LLVOAvatar::renderCollisionVolumes()
 	}
 }
 
-void LLVOAvatar::renderBones()
+void LLVOAvatar::renderBones(const std::string &selected_joint)
 {
     LLGLEnable blend(GL_BLEND);
 
 	avatar_joint_list_t::iterator iter = mSkeleton.begin();
-	avatar_joint_list_t::iterator end  = mSkeleton.end();
+    avatar_joint_list_t::iterator end = mSkeleton.end();
 
+    // For selected joints
+    static LLVector3 SELECTED_COLOR_OCCLUDED(1.0f, 1.0f, 0.0f);
+    static LLVector3 SELECTED_COLOR_VISIBLE(0.5f, 0.5f, 0.5f);
     // For bones with position overrides defined
     static LLVector3 OVERRIDE_COLOR_OCCLUDED(1.0f, 0.0f, 0.0f);
     static LLVector3 OVERRIDE_COLOR_VISIBLE(0.5f, 0.5f, 0.5f);
@@ -1607,7 +1610,18 @@ void LLVOAvatar::renderBones()
 
         LLVector3 pos;
         LLUUID mesh_id;
-        if (jointp->hasAttachmentPosOverride(pos,mesh_id))
+        F32 sphere_scale = SPHERE_SCALEF;
+
+        // We are in render, so it is preferable to implement selection
+        // in a different way, but since this is for debug/preview, this
+        // is low priority
+        if (jointp->getName() == selected_joint)
+        {
+            sphere_scale *= 16;
+            occ_color = SELECTED_COLOR_OCCLUDED;
+            visible_color = SELECTED_COLOR_VISIBLE;
+        }
+        else if (jointp->hasAttachmentPosOverride(pos,mesh_id))
         {
             occ_color = OVERRIDE_COLOR_OCCLUDED;
             visible_color = OVERRIDE_COLOR_VISIBLE;
@@ -1628,7 +1642,6 @@ void LLVOAvatar::renderBones()
         LLVector3 begin_pos(0,0,0);
         LLVector3 end_pos(jointp->getEnd());
 
-        F32 sphere_scale = SPHERE_SCALEF;
         
 		gGL.pushMatrix();
 		gGL.multMatrix( &jointp->getXform()->getWorldMatrix().mMatrix[0][0] );
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 00dccc5d12..f19bdb3071 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -443,7 +443,7 @@ public:
 	F32			getLastSkinTime() { return mLastSkinTime; }
 	U32 		renderTransparent(BOOL first_pass);
 	void 		renderCollisionVolumes();
-	void		renderBones();
+	void		renderBones(const std::string &selected_joint = std::string());
 	void		renderJoints();
 	static void	deleteCachedImages(bool clearAll=true);
 	static void	destroyGL();
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 aeed3b4623..da4190c4fc 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1169,7 +1169,7 @@
       <panel
        label="Overrides"
        layout="topleft"
-       name="overrides_panel"
+       name="avatar_panel"
        title="Avatar">
         <view_border
          bevel_style="none"
-- 
cgit v1.2.3


From 8a630120540146c8724994dbc116a833a88ac2d6 Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Wed, 15 Apr 2020 08:42:05 +0300
Subject: SL-10642 LLModelPreview logging fix

---
 indra/newview/llfloatermodelpreview.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 1542e97160..5d86ebc6a6 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -268,7 +268,9 @@ private:
 
 
 class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
-{	
+{
+    LOG_CLASS(LLModelPreview);
+    
 	typedef boost::signals2::signal<void (F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)> details_signal_t;
 	typedef boost::signals2::signal<void (void)> model_loaded_signal_t;
 	typedef boost::signals2::signal<void (bool)> model_updated_signal_t;
-- 
cgit v1.2.3


From 3ab32456a0e2fde64b405ac9d53150dfe9561ffe Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 15 Apr 2020 14:30:33 +0300
Subject: SL-307 ImporterDebug is now logged into in-viewer log

---
 indra/newview/llfloatermodelpreview.cpp | 116 ++++++++++++++++++++++----------
 indra/newview/llfloatermodelpreview.h   |   1 +
 2 files changed, 81 insertions(+), 36 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 2a0e51f496..02ebac3b51 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1612,6 +1612,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
 , mModelNoErrors( true )
 , mLastJointUpdate( false )
 , mHasDegenerate( false )
+, mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false))
 {
 	mNeedsUpdate = TRUE;
 	mCameraDistance = 0.f;
@@ -1821,7 +1822,6 @@ void LLModelPreview::rebuildUploadData()
 
 	F32 max_scale = 0.f;
 
-	BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");
 	BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
 
 	for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter)
@@ -1901,9 +1901,14 @@ void LLModelPreview::rebuildUploadData()
 
 					if (!lod_model && i != LLModel::LOD_PHYSICS)
 					{
-						if (importerDebug)
-						{
-							LL_INFOS() << "Search of" << name_to_match << " in LOD" << i << " list failed. Searching for alternative among LOD lists." << LL_ENDL;
+						if (mImporterDebug)
+                        {
+                            std::ostringstream out;
+                            out << "Search of" << name_to_match;
+                            out << " in LOD" << i;
+                            out << " list failed. Searching for alternative among LOD lists.";
+                            LL_INFOS() << out.str() << LL_ENDL;
+                            LLFloaterModelPreview::addStringToLog(out, false);
 						}
 
 						int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i;
@@ -1943,9 +1948,14 @@ void LLModelPreview::rebuildUploadData()
 						// find reference instance for this model
 						if (mBaseModel[idx] == base_model)
 						{
-							if (importerDebug)
+							if (mImporterDebug)
 							{
-								LL_INFOS() << "Attempting to use model index " << idx << " for LOD " << i << " of " << instance.mLabel << LL_ENDL;
+                                std::ostringstream out;
+                                out << "Attempting to use model index " << idx;
+                                out << " for LOD" << i;
+                                out << " of " << instance.mLabel;
+                                LL_INFOS() << out.str() << LL_ENDL;
+                                LLFloaterModelPreview::addStringToLog(out, false);
 							}
 							break;
 						}
@@ -1958,29 +1968,38 @@ void LLModelPreview::rebuildUploadData()
 						// Assign that index from the model list for our LOD as the LOD model for this instance
 						//
 						lod_model = mModel[i][idx];
-						if (importerDebug)
-						{
-							LL_INFOS() << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel << LL_ENDL;
+						if (mImporterDebug)
+                        {
+                            std::ostringstream out;
+                            out << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel;
+                            LL_INFOS() << out.str() << LL_ENDL;
+                            LLFloaterModelPreview::addStringToLog(out, false);
 						}
 					}
-					else if (importerDebug)
+					else if (mImporterDebug)
 					{
-						LL_INFOS() << "List of models does not include index " << idx << LL_ENDL;
+                        std::ostringstream out;
+                        out << "List of models does not include index " << idx;
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
 					}
 				}
 
 				if (lod_model)
 				{
-					if (importerDebug)
-					{
+					if (mImporterDebug)
+                    {
+                        std::ostringstream out;
 						if (i == LLModel::LOD_PHYSICS)
-						{
-							LL_INFOS() << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel << LL_ENDL;
+                        {
+                            out << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel;
 						}
 						else
-						{
-							LL_INFOS() << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel << LL_ENDL;
-						}
+                        {
+                            out << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel;
+                        }
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
 					}
 					instance.mLOD[i] = lod_model;
 				}
@@ -1992,9 +2011,12 @@ void LLModelPreview::rebuildUploadData()
 						// Note: we might need to assign it regardless of conditions like named search does, to prevent crashes.
 						instance.mLOD[i] = instance.mLOD[i + 1];
 					}
-					if (importerDebug)
+					if (mImporterDebug)
 					{
-						LL_INFOS() << "List of models does not include " << instance.mLabel << LL_ENDL;
+                        std::ostringstream out;
+                        out << "List of models does not include " << instance.mLabel;
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
 					}
 				}
 			}
@@ -2061,9 +2083,12 @@ void LLModelPreview::rebuildUploadData()
 			}
 			if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID)
 			{
-				if (importerDebug)
-				{
-					LL_INFOS() << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models." <<  LL_ENDL;
+				if (mImporterDebug)
+                {
+                    std::ostringstream out;
+                    out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models.";
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, false);
 				}
 				setLoadState( LLModelLoader::ERROR_MATERIALS );
 				mFMP->childDisable( "calculate_btn" );
@@ -2526,7 +2551,6 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 		}
 		else
 		{
-			BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");
 			BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
 			if (!legacyMatching)
 			{
@@ -2539,9 +2563,12 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 						if (mBaseModel[idx]->mSubmodelID)
 						{ // don't do index-based renaming when the base model has submodels
 							has_submodels = TRUE;
-							if (importerDebug)
+							if (mImporterDebug)
 							{
-								LL_INFOS() << "High LOD has submodels" << LL_ENDL;
+                                std::ostringstream out;
+                                out << "High LOD has submodels";
+                                LL_INFOS() << out.str() << LL_ENDL;
+                                LLFloaterModelPreview::addStringToLog(out, false);
 							}
 							break;
 						}
@@ -2565,9 +2592,12 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 						}
 					}
 
-					if (importerDebug)
+					if (mImporterDebug)
 					{
-						LL_INFOS() << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found" << LL_ENDL;
+                        std::ostringstream out;
+                        out << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found";
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
 					}
 
 					if (!name_based && !has_submodels)
@@ -2589,7 +2619,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 								case LLModel::LOD_HIGH:                      break;
 								}
 
-								if (importerDebug)
+								if (mImporterDebug)
 								{
 									std::ostringstream out;
 									out << "Loded model name " << mModel[loaded_lod][idx]->mLabel;
@@ -3195,20 +3225,34 @@ void LLModelPreview::updateStatusMessages()
 
                 std::string instance_name = instance.mLabel;
 
-                BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");
-                if (importerDebug)
+                if (mImporterDebug)
                 {
                     // Useful for debugging generalized complaints below about total submeshes which don't have enough
                     // context to address exactly what needs to be fixed to move towards compliance with the rules.
                     //
-                    LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: "   << cur_verts     << LL_ENDL;
-                    LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Tris:  "   << cur_tris      << LL_ENDL;
-                    LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: "   << cur_submeshes << LL_ENDL;
-
+                    std::ostringstream out;
+                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts;
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, false);
+
+                    out.str("");
+                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Tris:  " << cur_tris;
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, false);
+
+                    out.str("");
+                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes;
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, false);
+
+                    out.str("");
                     LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin();
                     while (mat_iter != lod_model->mMaterialList.end())
                     {
-                        LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter) << LL_ENDL;
+                        out << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter);
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
+                        out.str("");
                         mat_iter++;
                     }
                 }
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 5d86ebc6a6..6f78d534f3 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -457,6 +457,7 @@ private:
 	JointTransformMap	mJointTransformMap;
 
 	LLPointer<LLVOAvatar>	mPreviewAvatar;
+	LLCachedControl<bool>	mImporterDebug;
 };
 
 #endif  // LL_LLFLOATERMODELPREVIEW_H
-- 
cgit v1.2.3


From 884d5024a3b2b7b65f2e05e29073ccab7fac6762 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 17 Apr 2020 17:19:55 +0300
Subject: SL-13061 Fixed missing scroll handling

---
 indra/newview/llfloatermodelpreview.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 02ebac3b51..5895ebe7d7 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1015,8 +1015,11 @@ BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks)
 		mModelPreview->zoom((F32)clicks * -0.2f);
 		mModelPreview->refresh();
 	}
-
-	return TRUE;
+    else
+    {
+        LLFloaterModelUploadBase::handleScrollWheel(x, y, clicks);
+    }
+    return TRUE;
 }
 
 /*virtual*/
-- 
cgit v1.2.3


From b0b155efefe224bd645a28efc24eb8b0b3e5580b Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 17 Apr 2020 17:58:58 +0300
Subject: SL-13062 Fixed Scaled-down 'Upload Model' floater having a frame at
 the bottom

The way legacy_header_height works is just wrong...
---
 indra/newview/skins/default/xui/en/floater_model_preview.xml | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

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 da4190c4fc..e8c64dfef7 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -4,13 +4,14 @@
  can_drag_on_left="false"
  can_minimize="true"
  can_resize="true"
- height="600"
- min_height="600"
+ height="625"
+ min_height="625"
  width="980"
  min_width="980"
  name="Model Preview"
  title="UPLOAD MODEL"
- help_topic="upload_model" >
+ help_topic="upload_model"
+ legacy_header_height="25">
 
   <string name="status_idle"></string>
   <string name="status_parse_error">Error: Dae parsing issue - see log for details.</string>
@@ -51,7 +52,7 @@
     layout="topleft"
     left="3"
     name="left_panel"
-    top_pad="0"
+    top_pad="25"
     width="635">
     <panel
       follows="all"
@@ -1630,7 +1631,7 @@ Analysed:
    left="640"
    name="lod_label"
    text_color="White"
-   top="4"
+   top="29"
    height="15"
    width="290">
     Preview:
-- 
cgit v1.2.3


From a8df6762ff88458916397b9707f6954b2714e14d Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 17 Apr 2020 19:24:55 +0300
Subject: SL-13065 Fixed missing error and infinite cycle caused by too much
 logging

---
 indra/newview/llfloatermodelpreview.cpp | 35 ++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 5895ebe7d7..d1056662ca 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -349,7 +349,6 @@ BOOL LLFloaterModelPreview::postBuild()
 
     childSetVisible("warning_title", false);
     childSetVisible("warning_message", false);
-    childSetVisible("status", false);
 
 	initDecompControls();
 
@@ -1579,23 +1578,41 @@ void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash
         return;
     }
 
-    LLPanel* panel = mTabContainer->getPanelByName("logs_panel");
-
     // Make sure we have space for new string
     S32 editor_text_len = mUploadLogText->getLength();
+    if (editor_max_len < (editor_text_len + add_text_len)
+        && mUploadLogText->getLineCount() <= 0)
+    {
+        mUploadLogText->getTextBoundingRect();// forces a reflow() to fix line count
+    }
     while (editor_max_len < (editor_text_len + add_text_len))
     {
-        editor_text_len -= mUploadLogText->removeFirstLine();
+        S32 shift = mUploadLogText->removeFirstLine();
+        if (shift > 0)
+        {
+            // removed a line
+            editor_text_len -= shift;
+        }
+        else
+        {
+            //nothing to remove?
+            LL_WARNS() << "Failed to clear log lines" << LL_ENDL;
+            break;
+        }
     }
 
     mUploadLogText->appendText(str, true);
 
-    if (flash && mTabContainer->getCurrentPanel() != panel)
+    if (flash)
     {
-        // This will makes colors pale due to "glow_type = LLRender::BT_ALPHA"
-        // So instead of using "MenuItemFlashBgColor" added stronger color
-        static LLUIColor sFlashBgColor(LLColor4U(255, 99, 0));
-        mTabContainer->setTabPanelFlashing(panel, true, sFlashBgColor);
+        LLPanel* panel = mTabContainer->getPanelByName("logs_panel");
+        if (mTabContainer->getCurrentPanel() != panel)
+        {
+            // This will makes colors pale due to "glow_type = LLRender::BT_ALPHA"
+            // So instead of using "MenuItemFlashBgColor" added stronger color
+            static LLUIColor sFlashBgColor(LLColor4U(255, 99, 0));
+            mTabContainer->setTabPanelFlashing(panel, true, sFlashBgColor);
+        }
     }
 }
 
-- 
cgit v1.2.3


From 9aa7485d61452bbaa4644cb15fa60f922d7d1e5b Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Tue, 21 Apr 2020 13:36:03 +0300
Subject: SL-13075 Fixed the uploaded model preview size

---
 indra/newview/llfloatermodelpreview.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 99f5fa35cd..a27272fbfe 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -455,11 +455,11 @@ void LLFloaterModelPreview::initModelPreview()
 	S32 max_width = llmin(gSavedSettings.getS32("PreviewRenderSize"), (S32)gPipeline.mScreenWidth);
 	S32 max_height = llmin(gSavedSettings.getS32("PreviewRenderSize"), (S32)gPipeline.mScreenHeight);
 	
-	while ((tex_width << 1) <= max_width)
+	while ((tex_width << 1) < max_width)
 	{
 		tex_width <<= 1;
 	}
-	while ((tex_height << 1) <= max_height)
+	while ((tex_height << 1) < max_height)
 	{
 		tex_height <<= 1;
 	}
-- 
cgit v1.2.3


From 1f8f3d13d0baf7668696d98064a6d65af52d036b Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Tue, 21 Apr 2020 15:41:42 +0300
Subject: SL-10613 The Smooth parameter should be 0 by default

---
 indra/newview/llfloatermodelpreview.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index a27272fbfe..54ab245089 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1278,7 +1278,8 @@ void LLFloaterModelPreview::initDecompControls()
 					float max = param[i].mDetails.mRange.mHigh.mFloat;
 					float delta = param[i].mDetails.mRange.mDelta.mFloat;
 
-					if ("Cosine%" == name)
+					bool is_smooth_cb = ("Cosine%" == name);
+					if (is_smooth_cb)
 					{
 						createSmoothComboBox(combo_box, min, max);
 					}
@@ -1290,7 +1291,7 @@ void LLFloaterModelPreview::initDecompControls()
 							combo_box->add(label, value, ADD_BOTTOM, true);
 						}
 					}
-					combo_box->setValue(param[i].mDefault.mFloat);
+					combo_box->setValue(is_smooth_cb ? 0: param[i].mDefault.mFloat);
 					combo_box->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
 				}
 			}
-- 
cgit v1.2.3


From 311921cad63175b276fefae4de3dbed8bce98802 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 21 Apr 2020 17:33:14 +0300
Subject: SL-13064 Artifacts on the preview with physics

---
 indra/newview/llfloatermodelpreview.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 54ab245089..264ba99170 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -4597,7 +4597,8 @@ BOOL LLModelPreview::render()
 								}
 							}
 						}
-					}
+                        gGL.popMatrix();
+                    }
 
 					// only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks]
 					if (pass > 0 && mHasDegenerate)
@@ -4642,8 +4643,7 @@ BOOL LLModelPreview::render()
 										genBuffers(LLModel::LOD_PHYSICS, false);
 									}
 
-									auto num_degenerate = 0;
-									auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
+									U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
 									for (U32 v = 0; v < num_models; ++v)
 									{
 										LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v];
@@ -4667,7 +4667,6 @@ BOOL LLModelPreview::render()
 
 											if (ll_is_degenerate(v1, v2, v3))
 											{
-												num_degenerate++;
 												glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 												gGL.diffuseColor3fv(deg_edge_col().mV);
 												buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
-- 
cgit v1.2.3


From d38fd1e7c3a48260f670e135501bba68d9d4dc51 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 21 Apr 2020 18:55:29 +0300
Subject: SL-13081 Model is unnaturally grey

---
 indra/newview/app_settings/shaders/class1/objects/previewV.glsl | 1 -
 1 file changed, 1 deletion(-)

diff --git a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
index 2cf17acf6b..4bb588335a 100644
--- a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl
@@ -93,6 +93,5 @@ void main()
 	col.rgb += light_diffuse[1].rgb * calcDirectionalLight(norm, light_position[1].xyz);
 	col.rgb += light_diffuse[2].rgb*calcLocalLight(pos.xyz, norm, light_position[2], light_direction[2], light_attenuation[2].x, light_attenuation[2].z);
 	col.rgb += light_diffuse[3].rgb*calcLocalLight(pos.xyz, norm, light_position[3], light_direction[3], light_attenuation[3].x, light_attenuation[3].z);
-	col /= 2.0;
 	vertex_color = col*color;
 }
-- 
cgit v1.2.3


From 462b0b1c2dbf68f7a45fef427de13f59b7e078a1 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Wed, 22 Apr 2020 15:57:34 +0300
Subject: =?UTF-8?q?SL-13066=20FIXED=20Model=20preview=20with=20is=20displa?=
 =?UTF-8?q?yed=20when=20the=20=E2=80=98Upload=20Model=E2=80=99=20floater?=
 =?UTF-8?q?=20is=20minimized?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 indra/newview/llfloatermodelpreview.cpp | 31 ++-----------------------------
 1 file changed, 2 insertions(+), 29 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 264ba99170..3564d7c241 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -886,36 +886,9 @@ void LLFloaterModelPreview::draw()
 	childSetTextArg("prim_cost", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost));
 	childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size()));
 
-    if (mModelPreview->lodsReady())
+    if (!isMinimized() && mModelPreview->lodsReady())
 	{
-		gGL.color3f(1.f, 1.f, 1.f);
-
-		gGL.getTexUnit(0)->bind(mModelPreview);
-
-
-		LLView* preview_panel = getChild<LLView>("preview_panel");
-
-		LLRect rect = preview_panel->getRect();
-		if (rect != mPreviewRect)
-		{
-			mModelPreview->refresh();
-			mPreviewRect = preview_panel->getRect();
-		}
-
-		gGL.begin( LLRender::QUADS );
-		{
-			gGL.texCoord2f(0.f, 1.f);
-			gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop-1);
-			gGL.texCoord2f(0.f, 0.f);
-			gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom);
-			gGL.texCoord2f(1.f, 0.f);
-			gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom);
-			gGL.texCoord2f(1.f, 1.f);
-			gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mTop-1);
-		}
-		gGL.end();
-
-		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+		draw3dPreview();
 	}
 }
 
-- 
cgit v1.2.3


From 2b459cc80ee5bc3bf29e54523dc2ddccb51ab798 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 22 Apr 2020 17:30:01 +0300
Subject: SL-13078 Split LLModelPreview and LLFloaterModelPreview into separate
 files

---
 indra/newview/CMakeLists.txt            |    2 +
 indra/newview/llfloatermodelpreview.cpp | 3607 +------------------------------
 indra/newview/llfloatermodelpreview.h   |  232 +-
 indra/newview/llmodelpreview.cpp        | 3507 ++++++++++++++++++++++++++++++
 indra/newview/llmodelpreview.h          |  308 +++
 5 files changed, 3867 insertions(+), 3789 deletions(-)
 create mode 100644 indra/newview/llmodelpreview.cpp
 create mode 100644 indra/newview/llmodelpreview.h

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index c9d5fb89ba..65cee71b1d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -399,6 +399,7 @@ set(viewer_SOURCE_FILES
     llmenuoptionpathfindingrebakenavmesh.cpp
     llmeshrepository.cpp
     llmimetypes.cpp
+    llmodelpreview.cpp
     llmorphview.cpp
     llmoveview.cpp
     llmutelist.cpp
@@ -1025,6 +1026,7 @@ set(viewer_HEADER_FILES
     llmenuoptionpathfindingrebakenavmesh.h
     llmeshrepository.h
     llmimetypes.h
+    llmodelpreview.h
     llmorphview.h
     llmoveview.h
     llmutelist.h
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 3564d7c241..9c62680dde 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -27,7 +27,7 @@
 #include "llviewerprecompiledheaders.h"
 
 #include "llmodelloader.h"
-#include "lldaeloader.h"
+#include "llmodelpreview.h"
 
 #include "llfloatermodelpreview.h"
 
@@ -40,13 +40,7 @@
 #include "llagent.h"
 #include "llbutton.h"
 #include "llcombobox.h"
-#include "lldatapacker.h"
-#include "lldrawable.h"
-#include "llrender.h"
-#include "llface.h"
 #include "llfocusmgr.h"
-#include "llfloaterperms.h"
-#include "lliconctrl.h"
 #include "llmatrix4a.h"
 #include "llmenubutton.h"
 #include "llmeshrepository.h"
@@ -57,17 +51,10 @@
 #include "lltoolmgr.h"
 #include "llui.h"
 #include "llvector4a.h"
-#include "llviewercamera.h"
 #include "llviewerwindow.h"
-#include "llvoavatar.h"
-#include "llvoavatarself.h"
 #include "pipeline.h"
-#include "lluictrlfactory.h"
 #include "llviewercontrol.h"
-#include "llviewermenu.h"
 #include "llviewermenufile.h"
-#include "llviewerregion.h"
-#include "llviewertexturelist.h"
 #include "llstring.h"
 #include "llbutton.h"
 #include "llcheckboxctrl.h"
@@ -81,21 +68,14 @@
 #include "llvfile.h"
 #include "llvfs.h"
 #include "llcallbacklist.h"
-#include "llviewerobjectlist.h"
-#include "llanimationstates.h"
 #include "llviewertexteditor.h"
 #include "llviewernetwork.h"
-#include "llviewershadermgr.h"
 
-#include "glod/glod.h"
-#include <boost/algorithm/string.hpp>
 
 //static
 S32 LLFloaterModelPreview::sUploadAmount = 10;
 LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL;
 
-bool LLModelPreview::sIgnoreLoadedCallback = false;
-
 // "Retain%" decomp parameter has values from 0.0 to 1.0 by 0.01
 // But according to the UI spec for upload model floater, this parameter
 // should be represented by Retain spinner with values from 1 to 100 by 1.
@@ -109,111 +89,17 @@ const double RETAIN_COEFFICIENT = 100;
 // So this const is used as a size of Smooth combobox list.
 const S32 SMOOTH_VALUES_NUMBER = 10;
 
-const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f;
-
-void drawBoxOutline(const LLVector3& pos, const LLVector3& size);
-
-
-std::string lod_name[NUM_LOD+1] =
-{
-	"lowest",
-	"low",
-	"medium",
-	"high",
-	"I went off the end of the lod_name array.  Me so smart."
-};
-
-std::string lod_triangles_name[NUM_LOD+1] =
-{
-	"lowest_triangles",
-	"low_triangles",
-	"medium_triangles",
-	"high_triangles",
-	"I went off the end of the lod_triangles_name array.  Me so smart."
-};
-
-std::string lod_vertices_name[NUM_LOD+1] =
-{
-	"lowest_vertices",
-	"low_vertices",
-	"medium_vertices",
-	"high_vertices",
-	"I went off the end of the lod_vertices_name array.  Me so smart."
-};
-
-std::string lod_status_name[NUM_LOD+1] =
-{
-	"lowest_status",
-	"low_status",
-	"medium_status",
-	"high_status",
-	"I went off the end of the lod_status_name array.  Me so smart."
-};
-
-std::string lod_icon_name[NUM_LOD+1] =
+class LLMeshFilePicker : public LLFilePickerThread
 {
-	"status_icon_lowest",
-	"status_icon_low",
-	"status_icon_medium",
-	"status_icon_high",
-	"I went off the end of the lod_status_name array.  Me so smart."
-};
-
-std::string lod_status_image[NUM_LOD+1] =
-{
-	"ModelImport_Status_Good",
-	"ModelImport_Status_Warning",
-	"ModelImport_Status_Error",
-	"I went off the end of the lod_status_image array.  Me so smart."
-};
+public:
+    LLMeshFilePicker(LLModelPreview* mp, S32 lod);
+    virtual void notify(const std::vector<std::string>& filenames);
 
-std::string lod_label_name[NUM_LOD+1] =
-{
-	"lowest_label",
-	"low_label",
-	"medium_label",
-	"high_label",
-	"I went off the end of the lod_label_name array.  Me so smart."
+private:
+    LLModelPreview* mMP;
+    S32 mLOD;
 };
 
-BOOL stop_gloderror()
-{
-	GLuint error = glodGetError();
-
-	if (error != GLOD_NO_ERROR)
-	{
-		LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL;
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material)
-{
-	LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW);
-
-	if (texture)
-	{
-		if (texture->getDiscardLevel() > -1)
-		{
-			gGL.getTexUnit(0)->bind(texture, true);
-			return texture;
-		}
-	}
-
-	return NULL;
-}
-
-std::string stripSuffix(std::string name)
-{
-	if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1))
-	{
-		return name.substr(0, name.rfind('_'));
-	}
-	return name;
-}
-
 LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod)
 : LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
 	{
@@ -234,30 +120,6 @@ void LLMeshFilePicker::notify(const std::vector<std::string>& filenames)
 	}
 }
 
-void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut)
-{
-    LLModelLoader::scene::iterator base_iter = scene.begin();
-    bool found = false;
-    while (!found && (base_iter != scene.end()))
-    {
-        matOut = base_iter->first;
-
-        LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin();
-        while (!found && (base_instance_iter != base_iter->second.end()))
-        {
-		    LLModelInstance& base_instance = *base_instance_iter++;					    		    
-            LLModel* base_model = base_instance.mModel;
-         
-            if (base_model && (base_model->mLabel == name_to_match))
-            {
-                baseModelOut = base_model;
-                return;
-            }
-        }
-        base_iter++;
-    }
-}
-
 //-----------------------------------------------------------------------------
 // LLFloaterModelPreview()
 //-----------------------------------------------------------------------------
@@ -564,13 +426,6 @@ void LLFloaterModelPreview::onClickCalculateBtn()
 	bool upload_joint_positions = childGetValue("upload_joints").asBoolean();
     bool lock_scale_if_joint_position = childGetValue("lock_scale_if_joint_position").asBoolean();
 
-    if (upload_joint_positions)
-    {
-        // Diagnostic message showing list of joints for which joint offsets are defined.
-        // FIXME - given time, would be much better to put this in the UI, in updateStatusMessages().
-		mModelPreview->getPreviewAvatar()->showAttachmentOverrides();
-    }
-
     mUploadModelUrl.clear();
     mModelPhysicsFee.clear();
 
@@ -1590,195 +1445,6 @@ void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash
     }
 }
 
-//-----------------------------------------------------------------------------
-// LLModelPreview
-//-----------------------------------------------------------------------------
-
-LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
-: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex()
-, mLodsQuery()
-, mLodsWithParsingError()
-, mPelvisZOffset( 0.0f )
-, mLegacyRigFlags( U32_MAX )
-, mRigValidJointUpload( false )
-, mPhysicsSearchLOD( LLModel::LOD_PHYSICS )
-, mResetJoints( false )
-, mModelNoErrors( true )
-, mLastJointUpdate( false )
-, mHasDegenerate( false )
-, mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false))
-{
-	mNeedsUpdate = TRUE;
-	mCameraDistance = 0.f;
-	mCameraYaw = 0.f;
-	mCameraPitch = 0.f;
-	mCameraZoom = 1.f;
-	mTextureName = 0;
-	mPreviewLOD = 0;
-	mModelLoader = NULL;
-	mMaxTriangleLimit = 0;
-	mDirty = false;
-	mGenLOD = false;
-	mLoading = false;
-	mLoadState = LLModelLoader::STARTING;
-	mGroup = 0;
-	mLODFrozen = false;
-	mBuildShareTolerance = 0.f;
-	mBuildQueueMode = GLOD_QUEUE_GREEDY;
-	mBuildBorderMode = GLOD_BORDER_UNLOCK;
-	mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE;
-
-	for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
-	{
-		mRequestedTriangleCount[i] = 0;
-		mRequestedCreaseAngle[i] = -1.f;
-		mRequestedLoDMode[i] = 0;
-		mRequestedErrorThreshold[i] = 0.f;
-		mRequestedBuildOperator[i] = 0;
-		mRequestedQueueMode[i] = 0;
-		mRequestedBorderMode[i] = 0;
-		mRequestedShareTolerance[i] = 0.f;
-	}
-
-	mViewOption["show_textures"] = false;
-
-	mFMP = fmp;
-
-	mHasPivot = false;
-	mModelPivot = LLVector3( 0.0f, 0.0f, 0.0f );
-	
-	glodInit();
-
-	createPreviewAvatar();
-}
-
-LLModelPreview::~LLModelPreview()
-{
-	// glod apparently has internal mem alignment issues that are angering
-	// the heap-check code in windows, these should be hunted down in that
-	// TP code, if possible
-	//
-	// kernel32.dll!HeapFree()  + 0x14 bytes	
-	// msvcr100.dll!free(void * pBlock)  Line 51	C
-	// glod.dll!glodGetGroupParameteriv()  + 0x119 bytes	
-	// glod.dll!glodShutdown()  + 0x77 bytes	
-	//
-	//glodShutdown();
-	if(mModelLoader)
-	{
-		mModelLoader->shutdown();
-	}
-}
-
-U32 LLModelPreview::calcResourceCost()
-{
-	assert_main_thread();
-
-	rebuildUploadData();
-
-	//Upload skin is selected BUT check to see if the joints coming in from the asset were malformed.
-	if ( mFMP && mFMP->childGetValue("upload_skin").asBoolean() )
-	{
-		bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
-		if ( uploadingJointPositions && !isRigValidForJointPositionUpload() )
-		{
-			mFMP->childDisable("ok_btn");		
-		}		
-	}
-	
-	std::set<LLModel*> accounted;
-	U32 num_points = 0;
-	U32 num_hulls = 0;
-
-	F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f;
-	mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f;
-	
-	if ( mFMP && mFMP->childGetValue("upload_joints").asBoolean() )
-	{
-		// FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail.
-		// see also call to addAttachmentPosOverride.
-		LLUUID fake_mesh_id;
-		fake_mesh_id.generate();
-		getPreviewAvatar()->addPelvisFixup( mPelvisZOffset, fake_mesh_id );
-	}
-
-	F32 streaming_cost = 0.f;
-	F32 physics_cost = 0.f;
-	for (U32 i = 0; i < mUploadData.size(); ++i)
-	{
-		LLModelInstance& instance = mUploadData[i];
-		
-		if (accounted.find(instance.mModel) == accounted.end())
-		{
-			accounted.insert(instance.mModel);
-
-			LLModel::Decomposition& decomp =
-			instance.mLOD[LLModel::LOD_PHYSICS] ?
-			instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics :
-			instance.mModel->mPhysics;
-			
-			//update instance skin info for each lods pelvisZoffset 
-			for ( int j=0; j<LLModel::NUM_LODS; ++j )
-			{	
-				if ( instance.mLOD[j] )
-				{
-					instance.mLOD[j]->mSkinInfo.mPelvisOffset = mPelvisZOffset;
-				}
-			}
-
-			std::stringstream ostr;
-			LLSD ret = LLModel::writeModel(ostr,
-					   instance.mLOD[4],
-					   instance.mLOD[3],
-					   instance.mLOD[2],
-					   instance.mLOD[1],
-					   instance.mLOD[0],
-					   decomp,
-					   mFMP->childGetValue("upload_skin").asBoolean(),
-					   mFMP->childGetValue("upload_joints").asBoolean(),
-					   mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(),
-					   TRUE,
-					   FALSE,
-					   instance.mModel->mSubmodelID);
-			
-			num_hulls += decomp.mHull.size();
-			for (U32 i = 0; i < decomp.mHull.size(); ++i)
-			{
-				num_points += decomp.mHull[i].size();
-			}
-
-			//calculate streaming cost
-			LLMatrix4 transformation = instance.mTransform;
-
-			LLVector3 position = LLVector3(0, 0, 0) * transformation;
-
-			LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
-			LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
-			LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
-			F32 x_length = x_transformed.normalize();
-			F32 y_length = y_transformed.normalize();
-			F32 z_length = z_transformed.normalize();
-			LLVector3 scale = LLVector3(x_length, y_length, z_length);
-
-			F32 radius = scale.length()*0.5f*debug_scale;
-
-            LLMeshCostData costs;
-            if (gMeshRepo.getCostData(ret, costs))
-            {
-                streaming_cost += costs.getRadiusBasedStreamingCost(radius);
-            }
-		}
-	}
-
-	F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f;
-
-	mDetailsSignal(mPreviewScale[0]*scale, mPreviewScale[1]*scale, mPreviewScale[2]*scale, streaming_cost, physics_cost);
-
-	updateStatusMessages();
-
-	return (U32) streaming_cost;
-}
-
 void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)
 {
 	assert_main_thread();
@@ -1795,3247 +1461,70 @@ void LLFloaterModelPreview::setPreviewLOD(S32 lod)
 	}
 }
 
-
-void LLModelPreview::rebuildUploadData()
+void LLFloaterModelPreview::onBrowseLOD(S32 lod)
 {
 	assert_main_thread();
 
-	mUploadData.clear();
-	mTextureSet.clear();
-
-	//fill uploaddata instance vectors from scene data
-
-	std::string requested_name = mFMP->getChild<LLUICtrl>("description_form")->getValue().asString();
-
-	LLSpinCtrl* scale_spinner = mFMP->getChild<LLSpinCtrl>("import_scale");
-
-	F32 scale = scale_spinner->getValue().asReal();
-
-	LLMatrix4 scale_mat;
-	scale_mat.initScale(LLVector3(scale, scale, scale));
-
-	F32 max_scale = 0.f;
-
-	BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
-
-	for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter)
-	{ //for each transform in scene
-		LLMatrix4 mat		= iter->first;
-
-		// compute position
-		LLVector3 position = LLVector3(0, 0, 0) * mat;
-
-		// compute scale
-		LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position;
-		LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position;
-		LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position;
-		F32 x_length = x_transformed.normalize();
-		F32 y_length = y_transformed.normalize();
-		F32 z_length = z_transformed.normalize();
-
-		max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length);
-
-		mat *= scale_mat;
-
-		for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();)
-		{ //for each instance with said transform applied 
-			LLModelInstance instance = *model_iter++;
-
-			LLModel* base_model = instance.mModel;
-			
-			if (base_model && !requested_name.empty())
-			{
-				base_model->mRequestedLabel = requested_name;
-			}
-
-			for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--)
-			{
-				LLModel* lod_model = NULL;
-				if (!legacyMatching)
-				{
-					// Fill LOD slots by finding matching meshes by label with name extensions
-					// in the appropriate scene for each LOD. This fixes all kinds of issues
-					// where the indexed method below fails in spectacular fashion.
-					// If you don't take the time to name your LOD and PHYS meshes
-					// with the name of their corresponding mesh in the HIGH LOD,
-					// then the indexed method will be attempted below.
-
-					LLMatrix4 transform;
-
-					std::string name_to_match = instance.mLabel;
-					llassert(!name_to_match.empty());
-
-					int extensionLOD;
-					if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty())
-					{
-						extensionLOD = i;
-					}
-					else
-					{
-						//Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for
-						extensionLOD = mPhysicsSearchLOD;
-					}
-
-					std::string toAdd;
-					switch (extensionLOD)
-					{
-					case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
-					case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
-					case LLModel::LOD_MEDIUM:   toAdd = "_LOD2"; break;
-					case LLModel::LOD_PHYSICS:  toAdd = "_PHYS"; break;
-					case LLModel::LOD_HIGH:                      break;
-					}
-
-					if (name_to_match.find(toAdd) == -1)
-					{
-						name_to_match += toAdd;
-					}
-
-					FindModel(mScene[i], name_to_match, lod_model, transform);
+	loadModel(lod);
+}
 
-					if (!lod_model && i != LLModel::LOD_PHYSICS)
-					{
-						if (mImporterDebug)
-                        {
-                            std::ostringstream out;
-                            out << "Search of" << name_to_match;
-                            out << " in LOD" << i;
-                            out << " list failed. Searching for alternative among LOD lists.";
-                            LL_INFOS() << out.str() << LL_ENDL;
-                            LLFloaterModelPreview::addStringToLog(out, false);
-						}
+//static
+void LLFloaterModelPreview::onReset(void* user_data)
+{
+	assert_main_thread();
 
-						int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i;
-						while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model)
-						{
-							std::string name_to_match = instance.mLabel;
-							llassert(!name_to_match.empty());
-
-							std::string toAdd;
-							switch (searchLOD)
-							{
-							case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
-							case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
-							case LLModel::LOD_MEDIUM:   toAdd = "_LOD2"; break;
-							case LLModel::LOD_PHYSICS:  toAdd = "_PHYS"; break;
-							case LLModel::LOD_HIGH:                      break;
-							}
-
-							if (name_to_match.find(toAdd) == -1)
-							{
-								name_to_match += toAdd;
-							}
-
-							// See if we can find an appropriately named model in LOD 'searchLOD'
-							//
-							FindModel(mScene[searchLOD], name_to_match, lod_model, transform);
-							searchLOD++;
-						}
-					}
-				}
-				else
-				{
-					// Use old method of index-based association
-					U32 idx = 0;
-					for (idx = 0; idx < mBaseModel.size(); ++idx)
-					{
-						// find reference instance for this model
-						if (mBaseModel[idx] == base_model)
-						{
-							if (mImporterDebug)
-							{
-                                std::ostringstream out;
-                                out << "Attempting to use model index " << idx;
-                                out << " for LOD" << i;
-                                out << " of " << instance.mLabel;
-                                LL_INFOS() << out.str() << LL_ENDL;
-                                LLFloaterModelPreview::addStringToLog(out, false);
-							}
-							break;
-						}
-					}
 
-					// If the model list for the current LOD includes that index...
-					//
-					if (mModel[i].size() > idx)
-					{
-						// Assign that index from the model list for our LOD as the LOD model for this instance
-						//
-						lod_model = mModel[i][idx];
-						if (mImporterDebug)
-                        {
-                            std::ostringstream out;
-                            out << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel;
-                            LL_INFOS() << out.str() << LL_ENDL;
-                            LLFloaterModelPreview::addStringToLog(out, false);
-						}
-					}
-					else if (mImporterDebug)
-					{
-                        std::ostringstream out;
-                        out << "List of models does not include index " << idx;
-                        LL_INFOS() << out.str() << LL_ENDL;
-                        LLFloaterModelPreview::addStringToLog(out, false);
-					}
-				}
+	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
+	fmp->childDisable("reset_btn");
+	fmp->clearLogTab();
+	fmp->clearAvatarTab();
+	LLModelPreview* mp = fmp->mModelPreview;
+	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
 
-				if (lod_model)
-				{
-					if (mImporterDebug)
-                    {
-                        std::ostringstream out;
-						if (i == LLModel::LOD_PHYSICS)
-                        {
-                            out << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel;
-						}
-						else
-                        {
-                            out << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel;
-                        }
-                        LL_INFOS() << out.str() << LL_ENDL;
-                        LLFloaterModelPreview::addStringToLog(out, false);
-					}
-					instance.mLOD[i] = lod_model;
-				}
-				else
-				{
-					if (i < LLModel::LOD_HIGH && !lodsReady())
-					{
-						// assign a placeholder from previous LOD until lod generation is complete.
-						// Note: we might need to assign it regardless of conditions like named search does, to prevent crashes.
-						instance.mLOD[i] = instance.mLOD[i + 1];
-					}
-					if (mImporterDebug)
-					{
-                        std::ostringstream out;
-                        out << "List of models does not include " << instance.mLabel;
-                        LL_INFOS() << out.str() << LL_ENDL;
-                        LLFloaterModelPreview::addStringToLog(out, false);
-					}
-				}
-			}
+	fmp->resetDisplayOptions();
+	fmp->resetUploadOptions();
+	//reset model preview
+	fmp->initModelPreview();
 
-			LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH];
-			if (!high_lod_model)
-			{
-				setLoadState( LLModelLoader::ERROR_MATERIALS );
-				mFMP->childDisable( "calculate_btn" );
-			}
-			else
-			{
-				for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
-				{				
-					int refFaceCnt = 0;
-					int modelFaceCnt = 0;
-					llassert(instance.mLOD[i]);
-					if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt ) )
-					{
-						setLoadState( LLModelLoader::ERROR_MATERIALS );
-						mFMP->childDisable( "calculate_btn" );
-					}
-				}
-                LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) mFMP;
-                bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean();
-                if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0)
-                {
-                    LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix);
-                    LLQuaternion identity;
-                    if (!bind_rot.isEqualEps(identity,0.01))
-                    {
-                        std::ostringstream out;
-                        out << "non-identity bind shape rot. mat is ";
-                        out << high_lod_model->mSkinInfo.mBindShapeMatrix;
-                        out << " bind_rot ";
-                        out << bind_rot;
-                        LL_WARNS() << out.str() << LL_ENDL;
-
-                        LLFloaterModelPreview::addStringToLog(out, false);
-                        setLoadState( LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION );
-                    }
-                }
-			}
-			instance.mTransform = mat;
-			mUploadData.push_back(instance);
-		}
-	}
+	mp = fmp->mModelPreview;
+	mp->loadModel(filename,LLModel::LOD_HIGH,true);
+}
 
-	for (U32 lod = 0; lod < LLModel::NUM_LODS-1; lod++)
-	{
-		// Search for models that are not included into upload data
-		// If we found any, that means something we loaded is not a sub-model.
-		for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind)
-		{
-			bool found_model = false;
-			for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
-			{
-				LLModelInstance& instance = *iter;
-				if (instance.mLOD[lod] == mModel[lod][model_ind])
-				{
-					found_model = true;
-					break;
-				}
-			}
-			if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID)
-			{
-				if (mImporterDebug)
-                {
-                    std::ostringstream out;
-                    out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models.";
-                    LL_INFOS() << out.str() << LL_ENDL;
-                    LLFloaterModelPreview::addStringToLog(out, false);
-				}
-				setLoadState( LLModelLoader::ERROR_MATERIALS );
-				mFMP->childDisable( "calculate_btn" );
-			}
-		}
-	}
+//static
+void LLFloaterModelPreview::onUpload(void* user_data)
+{
+	assert_main_thread();
 
-	F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE-0.1f)/max_scale;
+	LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+	mp->clearLogTab();
 
-	F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]);
-	max_axis = llmax(max_axis, mPreviewScale.mV[2]);
-	max_axis *= 2.f;
+	mp->mUploadBtn->setEnabled(false);
 
-	//clamp scale so that total imported model bounding box is smaller than 240m on a side
-	max_import_scale = llmin(max_import_scale, 240.f/max_axis);
+	mp->mModelPreview->rebuildUploadData();
 
-	scale_spinner->setMaxValue(max_import_scale);
+	bool upload_skinweights = mp->childGetValue("upload_skin").asBoolean();
+	bool upload_joint_positions = mp->childGetValue("upload_joints").asBoolean();
+    bool lock_scale_if_joint_position = mp->childGetValue("lock_scale_if_joint_position").asBoolean();
 
-	if (max_import_scale < scale)
+	if (gSavedSettings.getBOOL("MeshImportUseSLM"))
 	{
-		scale_spinner->setValue(max_import_scale);
-	}
+        mp->mModelPreview->saveUploadData(upload_skinweights, upload_joint_positions, lock_scale_if_joint_position);
+    }
 
+	gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale,
+						  mp->childGetValue("upload_textures").asBoolean(), 
+                          upload_skinweights, upload_joint_positions, lock_scale_if_joint_position, 
+                          mp->mUploadModelUrl,
+						  true, LLHandle<LLWholeModelFeeObserver>(), mp->getWholeModelUploadObserverHandle());
 }
 
-void LLModelPreview::saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position)
-{
-	if (!mLODFile[LLModel::LOD_HIGH].empty())
-	{
-		std::string filename = mLODFile[LLModel::LOD_HIGH];
-        std::string slm_filename;
-
-        if (LLModelLoader::getSLMFilename(filename, slm_filename))
-        {
-			saveUploadData(slm_filename, save_skinweights, save_joint_positions, lock_scale_if_joint_position);
-		}
-	}
-}
 
-void LLModelPreview::saveUploadData(const std::string& filename, 
-                                    bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position)
-{
-
-	std::set<LLPointer<LLModel> > meshes;
-	std::map<LLModel*, std::string> mesh_binary;
-
-	LLModel::hull empty_hull;
-
-	LLSD data;
-
-	data["version"] = SLM_SUPPORTED_VERSION;
-	if (!mBaseModel.empty())
-	{
-		data["name"] = mBaseModel[0]->getName();
-	}
-
-	S32 mesh_id = 0;
-
-	//build list of unique models and initialize local id
-	for (U32 i = 0; i < mUploadData.size(); ++i)
-	{
-		LLModelInstance& instance = mUploadData[i];
-		
-		if (meshes.find(instance.mModel) == meshes.end())
-		{
-			instance.mModel->mLocalID = mesh_id++;
-			meshes.insert(instance.mModel);
-
-			std::stringstream str;
-			LLModel::Decomposition& decomp =
-				instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? 
-				instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : 
-				instance.mModel->mPhysics;
-
-			LLModel::writeModel(str, 
-				instance.mLOD[LLModel::LOD_PHYSICS], 
-				instance.mLOD[LLModel::LOD_HIGH], 
-				instance.mLOD[LLModel::LOD_MEDIUM], 
-				instance.mLOD[LLModel::LOD_LOW], 
-				instance.mLOD[LLModel::LOD_IMPOSTOR], 
-				decomp, 
-				save_skinweights, 
-                save_joint_positions,
-                lock_scale_if_joint_position,
-                FALSE, TRUE, instance.mModel->mSubmodelID);
-			
-			data["mesh"][instance.mModel->mLocalID] = str.str();
-		}
-
-		data["instance"][i] = instance.asLLSD();
-	}
-
-	llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary);
-	LLSDSerialize::toBinary(data, out);
-	out.flush();
-	out.close();
-}
-
-void LLModelPreview::clearModel(S32 lod)
-{
-	if (lod < 0 || lod > LLModel::LOD_PHYSICS)
-	{
-		return;
-	}
-
-	mVertexBuffer[lod].clear();
-	mModel[lod].clear();
-	mScene[lod].clear();
-}
-
-void LLModelPreview::getJointAliases( JointMap& joint_map)
-{
-    // Get all standard skeleton joints from the preview avatar.
-    LLVOAvatar *av = getPreviewAvatar();
-    
-    //Joint names and aliases come from avatar_skeleton.xml
-    
-    joint_map = av->getJointAliases();
-
-    std::vector<std::string> cv_names, attach_names;
-    av->getSortedJointNames(1, cv_names);
-    av->getSortedJointNames(2, attach_names);
-    for (std::vector<std::string>::iterator it = cv_names.begin(); it != cv_names.end(); ++it)
-    {
-        joint_map[*it] = *it;
-    }
-    for (std::vector<std::string>::iterator it = attach_names.begin(); it != attach_names.end(); ++it)
-    {
-        joint_map[*it] = *it;
-    }
-}
-
-void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable_slm)
-{
-	assert_main_thread();
-
-	LLMutexLock lock(this);
-
-	if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1)
-	{
-		std::ostringstream out;
-		out << "Invalid level of detail: ";
-		out << lod;
-		LL_WARNS() << out.str() << LL_ENDL;
-		LLFloaterModelPreview::addStringToLog(out, true);
-		assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);
-		return;
-	}
-
-	// This triggers if you bring up the file picker and then hit CANCEL.
-	// Just use the previous model (if any) and ignore that you brought up
-	// the file picker.
-
-	if (filename.empty())
-	{
-		if (mBaseModel.empty())
-		{
-			// this is the initial file picking. Close the whole floater
-			// if we don't have a base model to show for high LOD.
-			mFMP->closeFloater(false);
-		}
-		mLoading = false;
-		return;
-	}
-
-	if (mModelLoader)
-	{
-		LL_WARNS() << "Incompleted model load operation pending." << LL_ENDL;
-		return;
-	}
-	
-	mLODFile[lod] = filename;
-
-	if (lod == LLModel::LOD_HIGH)
-	{
-		clearGLODGroup();
-	}
-
-    std::map<std::string, std::string> joint_alias_map;
-    getJointAliases(joint_alias_map);
-    
-	mModelLoader = new LLDAELoader(
-		filename,
-		lod, 
-		&LLModelPreview::loadedCallback,
-		&LLModelPreview::lookupJointByName,
-		&LLModelPreview::loadTextures,
-		&LLModelPreview::stateChangedCallback,
-		this,
-		mJointTransformMap,
-		mJointsFromNode,
-        joint_alias_map,
-		LLSkinningUtil::getMaxJointCount(),
-		gSavedSettings.getU32("ImporterModelLimit"),
-		gSavedSettings.getBOOL("ImporterPreprocessDAE"));
-
-	if (force_disable_slm)
-	{
-		mModelLoader->mTrySLM = false;
-	}
-	else
-	{
-        // For MAINT-6647, we have set force_disable_slm to true,
-        // which means this code path will never be taken. Trying to
-        // re-use SLM files has never worked properly; in particular,
-        // it tends to force the UI into strange checkbox options
-        // which cannot be altered.
-        
-		//only try to load from slm if viewer is configured to do so and this is the 
-		//initial model load (not an LoD or physics shape)
-		mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty();
-	}
-	mModelLoader->start();
-
-	mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file"));
-
-	setPreviewLOD(lod);
-
-	if ( getLoadState() >= LLModelLoader::ERROR_PARSING )
-	{
-		mFMP->childDisable("ok_btn");
-		mFMP->childDisable( "calculate_btn" );
-	}
-	
-	if (lod == mPreviewLOD)
-	{
-		mFMP->childSetValue("lod_file_" + lod_name[lod], mLODFile[lod]);
-	}
-	else if (lod == LLModel::LOD_PHYSICS)
-	{
-		mFMP->childSetValue("physics_file", mLODFile[lod]);
-	}
-
-	mFMP->openFloater();
-}
-
-void LLModelPreview::setPhysicsFromLOD(S32 lod)
-{
-	assert_main_thread();
-
-	if (lod >= 0 && lod <= 3)
-	{
-		mPhysicsSearchLOD = lod;
-		mModel[LLModel::LOD_PHYSICS] = mModel[lod];
-		mScene[LLModel::LOD_PHYSICS] = mScene[lod];
-		mLODFile[LLModel::LOD_PHYSICS].clear();
-		mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]);
-		mVertexBuffer[LLModel::LOD_PHYSICS].clear();
-		rebuildUploadData();
-		refresh();
-		updateStatusMessages();
-	}
-}
-
-void LLModelPreview::clearIncompatible(S32 lod)
-{
-	//Don't discard models if specified model is the physic rep
-	if ( lod == LLModel::LOD_PHYSICS )
-	{
-		return;
-	}
-
-	// at this point we don't care about sub-models,
-	// different amount of sub-models means face count mismatch, not incompatibility
-	U32 lod_size = countRootModels(mModel[lod]);
-	for (U32 i = 0; i <= LLModel::LOD_HIGH; i++)
-	{ //clear out any entries that aren't compatible with this model
-		if (i != lod)
-		{
-			if (countRootModels(mModel[i]) != lod_size)
-			{
-				mModel[i].clear();
-				mScene[i].clear();
-				mVertexBuffer[i].clear();
-
-				if (i == LLModel::LOD_HIGH)
-				{
-					mBaseModel = mModel[lod];
-					clearGLODGroup();
-					mBaseScene = mScene[lod];
-					mVertexBuffer[5].clear();
-				}
-			}
-		}
-	}
-}
-
-void LLModelPreview::clearGLODGroup()
-{
-	if (mGroup)
-	{
-		for (std::map<LLPointer<LLModel>, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter)
-		{
-			glodDeleteObject(iter->second);
-			stop_gloderror();
-		}
-		mObject.clear();
-
-		glodDeleteGroup(mGroup);
-		stop_gloderror();
-		mGroup = 0;
-	}
-}
-
-void LLModelPreview::loadModelCallback(S32 loaded_lod)
-{
-	assert_main_thread();
-
-	LLMutexLock lock(this);
-	if (!mModelLoader)
-	{
-		mLoading = false ;
-		return;
-	}
-	if(getLoadState() >= LLModelLoader::ERROR_PARSING)
-	{
-		mLoading = false ;
-		mModelLoader = NULL;
-		mLodsWithParsingError.push_back(loaded_lod);
-		return ;
-	}
-
-	mLodsWithParsingError.erase(std::remove(mLodsWithParsingError.begin(), mLodsWithParsingError.end(), loaded_lod), mLodsWithParsingError.end());
-	if(mLodsWithParsingError.empty())
-	{
-		mFMP->childEnable( "calculate_btn" );
-	}
-
-	// Copy determinations about rig so UI will reflect them
-	//
-	setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());
-	setLegacyRigFlags(mModelLoader->getLegacyRigFlags());
-
-	mModelLoader->loadTextures() ;
-
-	if (loaded_lod == -1)
-	{ //populate all LoDs from model loader scene
-		mBaseModel.clear();
-		mBaseScene.clear();
-
-		bool skin_weights = false;
-		bool joint_overrides = false;
-		bool lock_scale_if_joint_position = false;
-
-		for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
-		{ //for each LoD
-
-			//clear scene and model info
-			mScene[lod].clear();
-			mModel[lod].clear();
-			mVertexBuffer[lod].clear();
-			
-			if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull())
-			{ //if this LoD exists in the loaded scene
-
-				//copy scene to current LoD
-				mScene[lod] = mModelLoader->mScene;
-			
-				//touch up copied scene to look like current LoD
-				for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter)
-				{
-					LLModelLoader::model_instance_list& list = iter->second;
-
-					for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter)
-					{	
-						//override displayed model with current LoD
-						list_iter->mModel = list_iter->mLOD[lod];
-
-						if (!list_iter->mModel)
-						{
-							continue;
-						}
-
-						//add current model to current LoD's model list (LLModel::mLocalID makes a good vector index)
-						S32 idx = list_iter->mModel->mLocalID;
-
-						if (mModel[lod].size() <= idx)
-						{ //stretch model list to fit model at given index
-							mModel[lod].resize(idx+1);
-						}
-
-						mModel[lod][idx] = list_iter->mModel;
-						if (!list_iter->mModel->mSkinWeights.empty())
-						{
-							skin_weights = true;
-
-							if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty())
-							{
-								joint_overrides = true;
-							}
-							if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition)
-							{
-								lock_scale_if_joint_position = true;
-							}
-						}
-					}
-				}
-			}
-		}
-
-		if (mFMP)
-		{
-			LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) mFMP;
-
-			if (skin_weights)
-			{ //enable uploading/previewing of skin weights if present in .slm file
-				fmp->enableViewOption("show_skin_weight");
-				mViewOption["show_skin_weight"] = true;
-				fmp->childSetValue("upload_skin", true);
-			}
-
-			if (joint_overrides)
-			{
-				fmp->enableViewOption("show_joint_overrides");
-				mViewOption["show_joint_overrides"] = true;
-				fmp->enableViewOption("show_joint_positions");
-				mViewOption["show_joint_positions"] = true;
-				fmp->childSetValue("upload_joints", true);
-			}
-            else
-            {
-                fmp->clearAvatarTab();
-            }
-
-			if (lock_scale_if_joint_position)
-			{
-				fmp->enableViewOption("lock_scale_if_joint_position");
-				mViewOption["lock_scale_if_joint_position"] = true;
-				fmp->childSetValue("lock_scale_if_joint_position", true);
-			}
-		}
-
-		//copy high lod to base scene for LoD generation
-		mBaseScene = mScene[LLModel::LOD_HIGH];
-		mBaseModel = mModel[LLModel::LOD_HIGH];
-
-		mDirty = true;
-		resetPreviewTarget();
-	}
-	else
-	{ //only replace given LoD
-		mModel[loaded_lod] = mModelLoader->mModelList;
-		mScene[loaded_lod] = mModelLoader->mScene;
-		mVertexBuffer[loaded_lod].clear();
-
-		setPreviewLOD(loaded_lod);
-
-		if (loaded_lod == LLModel::LOD_HIGH)
-		{ //save a copy of the highest LOD for automatic LOD manipulation
-			if (mBaseModel.empty())
-			{ //first time we've loaded a model, auto-gen LoD
-				mGenLOD = true;
-			}
-
-			mBaseModel = mModel[loaded_lod];
-			clearGLODGroup();
-
-			mBaseScene = mScene[loaded_lod];
-			mVertexBuffer[5].clear();
-		}
-		else
-		{
-			BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
-			if (!legacyMatching)
-			{
-				if (!mBaseModel.empty())
-				{ 
-					BOOL name_based = FALSE;
-					BOOL has_submodels = FALSE;
-					for (U32 idx = 0; idx < mBaseModel.size(); ++idx)
-					{
-						if (mBaseModel[idx]->mSubmodelID)
-						{ // don't do index-based renaming when the base model has submodels
-							has_submodels = TRUE;
-							if (mImporterDebug)
-							{
-                                std::ostringstream out;
-                                out << "High LOD has submodels";
-                                LL_INFOS() << out.str() << LL_ENDL;
-                                LLFloaterModelPreview::addStringToLog(out, false);
-							}
-							break;
-						}
-					}
-
-					for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx)
-					{
-						std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel);
-
-						LLModel* found_model = NULL;
-						LLMatrix4 transform;
-						FindModel(mBaseScene, loaded_name, found_model, transform);
-						if (found_model)
-						{ // don't rename correctly named models (even if they are placed in a wrong order)
-							name_based = TRUE;
-						}
-
-						if (mModel[loaded_lod][idx]->mSubmodelID)
-						{ // don't rename the models when loaded LOD model has submodels
-							has_submodels = TRUE;
-						}
-					}
-
-					if (mImporterDebug)
-					{
-                        std::ostringstream out;
-                        out << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found";
-                        LL_INFOS() << out.str() << LL_ENDL;
-                        LLFloaterModelPreview::addStringToLog(out, false);
-					}
-
-					if (!name_based && !has_submodels)
-					{ // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601)
-					  // this actually works like "ImporterLegacyMatching" for this particular LOD
-						for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx)
-						{ 
-							std::string name = mBaseModel[idx]->mLabel;
-							std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel);
-
-							if (loaded_name != name)
-							{
-								switch (loaded_lod)
-								{
-								case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
-								case LLModel::LOD_LOW:      name += "_LOD1"; break;
-								case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
-								case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
-								case LLModel::LOD_HIGH:                      break;
-								}
-
-								if (mImporterDebug)
-								{
-									std::ostringstream out;
-									out << "Loded model name " << mModel[loaded_lod][idx]->mLabel;
-									out << " for LOD " << loaded_lod;
-									out << " doesn't match the base model. Renaming to " << name;
-									LL_WARNS() << out.str() << LL_ENDL;
-									LLFloaterModelPreview::addStringToLog(out, false);
-								}
-
-								mModel[loaded_lod][idx]->mLabel = name;
-							}
-						}
-					}
-				}
-			}
-		}
-
-		clearIncompatible(loaded_lod);
-
-		mDirty = true;
-
-		if (loaded_lod == LLModel::LOD_HIGH)
-		{
-			resetPreviewTarget();
-		}
-	}
-
-	mLoading = false;
-	if (mFMP)
-	{
-		if (!mBaseModel.empty())
-		{
-			const std::string& model_name = mBaseModel[0]->getName();
-			LLLineEditor* description_form = mFMP->getChild<LLLineEditor>("description_form");
-			if (description_form->getText().empty())
-			{
-				description_form->setText(model_name);
-			}
-            // Add info to log that loading is complete (purpose: separator between loading and other logs)
-            LLSD args;
-            args["MODEL_NAME"] = model_name; // Teoretically shouldn't be empty, but might be better idea to add filename here
-            LLFloaterModelPreview::addStringToLog("ModelLoaded", args, false, loaded_lod);
-		}
-	}
-	refresh();
-
-	mModelLoadedSignal();
-
-	mModelLoader = NULL;
-}
-
-void LLModelPreview::resetPreviewTarget()
-{
-	if ( mModelLoader )
-	{
-		mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f;
-		mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f;
-	}
-
-	setPreviewTarget(mPreviewScale.magVec()*10.f);
-}
-
-void LLModelPreview::generateNormals()
-{
-	assert_main_thread();
-
-	S32 which_lod = mPreviewLOD;
-
-	if (which_lod > 4 || which_lod < 0 ||
-		mModel[which_lod].empty())
-	{
-		return;
-	}
-
-	F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal();
-
-	mRequestedCreaseAngle[which_lod] = angle_cutoff;
-
-	angle_cutoff *= DEG_TO_RAD;
-
-	if (which_lod == 3 && !mBaseModel.empty())
-	{
-		if(mBaseModelFacesCopy.empty())
-		{
-			mBaseModelFacesCopy.reserve(mBaseModel.size());
-			for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it)
-			{
-				v_LLVolumeFace_t faces;
-				(*it)->copyFacesTo(faces);
-				mBaseModelFacesCopy.push_back(faces);
-			}
-		}
-
-		for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it)
-		{
-			(*it)->generateNormals(angle_cutoff);
-		}
-
-		mVertexBuffer[5].clear();
-	}
-
-	bool perform_copy = mModelFacesCopy[which_lod].empty();
-	if(perform_copy) {
-		mModelFacesCopy[which_lod].reserve(mModel[which_lod].size());
-	}
-
-	for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it)
-	{
-		if(perform_copy)
-		{
-			v_LLVolumeFace_t faces;
-			(*it)->copyFacesTo(faces);
-			mModelFacesCopy[which_lod].push_back(faces);
-		}
-
-		(*it)->generateNormals(angle_cutoff);
-	}
-
-	mVertexBuffer[which_lod].clear();
-	refresh();
-	updateStatusMessages();
-}
-
-void LLModelPreview::restoreNormals()
-{
-	S32 which_lod = mPreviewLOD;
-
-	if (which_lod > 4 || which_lod < 0 ||
-		mModel[which_lod].empty())
-	{
-		return;
-	}
-
-	if(!mBaseModelFacesCopy.empty())
-	{
-		llassert(mBaseModelFacesCopy.size() == mBaseModel.size());
-
-		vv_LLVolumeFace_t::const_iterator itF = mBaseModelFacesCopy.begin();
-		for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it, ++itF)
-		{
-			(*it)->copyFacesFrom((*itF));
-		}
-
-		mBaseModelFacesCopy.clear();
-	}
-	
-	if(!mModelFacesCopy[which_lod].empty())
-	{
-		vv_LLVolumeFace_t::const_iterator itF = mModelFacesCopy[which_lod].begin();
-		for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it, ++itF)
-		{
-			(*it)->copyFacesFrom((*itF));
-		}
-
-		mModelFacesCopy[which_lod].clear();
-	}
-	
-	mVertexBuffer[which_lod].clear();
-	refresh();
-	updateStatusMessages();
-}
-
-void LLModelPreview::genLODs(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)
-	{
-		std::ostringstream out;
-		out << "Invalid level of detail: " << which_lod;
-		LL_WARNS() << out.str() << LL_ENDL;
-		LLFloaterModelPreview::addStringToLog(out, false);
-		assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS);
-		return;
-	}
-
-	if (mBaseModel.empty())
-	{
-		return;
-	}
-
-	LLVertexBuffer::unbind();
-
-	bool no_ff = LLGLSLShader::sNoFixedFunction;
-	LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
-	LLGLSLShader::sNoFixedFunction = false;
-
-	if (shader)
-	{
-		shader->unbind();
-	}
-	
-	stop_gloderror();
-	static U32 cur_name = 1;
-
-	S32 limit = -1;
-
-	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 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
-
-	U32 lod_mode = 0;
-
-	F32 lod_error_threshold = 0;
-
-	// The LoD should be in range from Lowest to High
-	if (which_lod > -1 && which_lod < NUM_LOD)
-	{
-		LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]);
-		if (iface)
-		{
-			lod_mode = iface->getFirstSelectedIndex();
-		}
-
-		lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal();
-	}
-
-	if (which_lod != -1)
-	{
-		mRequestedLoDMode[which_lod] = lod_mode;
-	}
-
-	if (lod_mode == 0)
-	{
-		lod_mode = GLOD_TRIANGLE_BUDGET;
-
-		// The LoD should be in range from Lowest to High
-		if (which_lod > -1 && which_lod < NUM_LOD)
-		{
-			limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger();
-			//convert from "scene wide" to "non-instanced" triangle limit
-			limit = (S32) ( (F32) limit*triangle_ratio );
-		}
-	}
-	else
-	{
-		lod_mode = GLOD_ERROR_THRESHOLD;
-	}
-
-	bool object_dirty = false;
-
-	if (mGroup == 0)
-	{
-		object_dirty = true;
-		mGroup = cur_name++;
-		glodNewGroup(mGroup);
-	}
-
-	if (object_dirty)
-	{
-		for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter)
-		{ //build GLOD objects for each model in base model list
-			LLModel* mdl = *iter;
-
-			if (mObject[mdl] != 0)
-			{
-				glodDeleteObject(mObject[mdl]);
-			}
-
-			mObject[mdl] = cur_name++;
-
-			glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE);
-			stop_gloderror();
-
-			if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty())
-			{ //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation
-				mVertexBuffer[5].clear();
-			}
-
-			if (mVertexBuffer[5].empty())
-			{
-				genBuffers(5, false);
-			}
-
-			U32 tri_count = 0;
-			for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i)
-			{
-				LLVertexBuffer* buff = mVertexBuffer[5][mdl][i];
-				buff->setBuffer(type_mask & buff->getTypeMask());
-				
-				U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices();
-				if (num_indices > 2)
-				{
-					glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*) mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f);
-				}
-				tri_count += num_indices/3;
-				stop_gloderror();
-			}
-
-			glodBuildObject(mObject[mdl]);
-			stop_gloderror();
-		}
-	}
-
-
-	S32 start = LLModel::LOD_HIGH;
-	S32 end = 0;
-
-	if (which_lod != -1)
-	{
-		start = end = which_lod;
-	}
-
-	mMaxTriangleLimit = base_triangle_count;
-
-	for (S32 lod = start; lod >= end; --lod)
-	{
-		if (which_lod == -1)
-		{
-			if (lod < start)
-			{
-				triangle_count /= decimation;
-			}
-		}
-		else
-		{
-			if (enforce_tri_limit)
-			{
-				triangle_count = limit;
-			}
-			else
-			{
-				for (S32 j=LLModel::LOD_HIGH; j>which_lod; --j)
-				{
-					triangle_count /= decimation;
-				}
-			}
-		}
-
-		mModel[lod].clear();
-		mModel[lod].resize(mBaseModel.size());
-		mVertexBuffer[lod].clear();
-
-		U32 actual_tris = 0;
-		U32 actual_verts = 0;
-		U32 submeshes = 0;
-
-		mRequestedTriangleCount[lod] = (S32) ( (F32) triangle_count / triangle_ratio );
-		mRequestedErrorThreshold[lod] = lod_error_threshold;
-
-		glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode);
-		stop_gloderror();
-
-		glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR);
-		stop_gloderror();
-
-		glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold);
-		stop_gloderror();
-
-		if (lod_mode != GLOD_TRIANGLE_BUDGET)
-		{ 			
-			glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0);
-		}
-		else
-		{
-			//SH-632: always add 1 to desired amount to avoid decimating below desired amount
-			glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count+1);
-		}
-			
-		stop_gloderror();
-		glodAdaptGroup(mGroup);
-		stop_gloderror();		
-
-		for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx)
-		{
-			LLModel* base = mBaseModel[mdl_idx];
-
-			GLint patch_count = 0;
-			glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count);
-			stop_gloderror();
-
-			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;
-
-            switch (lod)
-            {
-                case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
-                case LLModel::LOD_LOW:      name += "_LOD1"; break;
-		        case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
-                case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
-                case LLModel::LOD_HIGH:                      break;
-            }
-
-            mModel[lod][mdl_idx]->mLabel = name;
-			mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID;
-            
-			GLint* sizes = new GLint[patch_count*2];
-			glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes);
-			stop_gloderror();
-
-			GLint* names = new GLint[patch_count];
-			glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names);
-			stop_gloderror();
-
-			mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count);
-
-			LLModel* target_model = mModel[lod][mdl_idx];
-
-			for (GLint i = 0; i < patch_count; ++i)
-			{
-				type_mask = mVertexBuffer[5][base][i]->getTypeMask();
-
-				LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0);
-
-				if (sizes[i*2+1] > 0 && sizes[i*2] > 0)
-				{
-					if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true))
-					{
-						// Todo: find a way to stop preview in this case instead of crashing
-						LL_ERRS() << "Failed buffer allocation during preview LOD generation."
-							<< " Vertices: " << sizes[i * 2 + 1]
-							<< " Indices: " << sizes[i * 2] << LL_ENDL;
-					}
-					buff->setBuffer(type_mask);
-					glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*) buff->getIndicesPointer());
-					stop_gloderror();
-				}
-				else
-				{
-					// This face was eliminated or we failed to allocate buffer,
-					// attempt to create a dummy triangle (one vertex, 3 indices, all 0)
-					buff->allocateBuffer(1, 3, true);
-					memset((U8*) buff->getMappedData(), 0, buff->getSize());
-					memset((U8*) buff->getIndicesPointer(), 0, buff->getIndicesSize());
-				}
-
-				buff->validateRange(0, buff->getNumVerts()-1, buff->getNumIndices(), 0);
-
-				LLStrider<LLVector3> pos;
-				LLStrider<LLVector3> norm;
-				LLStrider<LLVector2> tc;
-				LLStrider<U16> index;
-
-				buff->getVertexStrider(pos);
-				if (type_mask & LLVertexBuffer::MAP_NORMAL)
-				{
-					buff->getNormalStrider(norm);
-				}
-				if (type_mask & LLVertexBuffer::MAP_TEXCOORD0)
-				{
-					buff->getTexCoord0Strider(tc);
-				}
-
-				buff->getIndexStrider(index);
-
-				target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices());
-				actual_tris += buff->getNumIndices()/3;
-				actual_verts += buff->getNumVerts();
-				++submeshes;
-
-				if (!validate_face(target_model->getVolumeFace(names[i])))
-				{
-					LL_ERRS() << "Invalid face generated during LOD generation." << LL_ENDL;
-				}
-			}
-
-			//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;
-			}
-
-			delete [] sizes;
-			delete [] names;
-		}
-
-		//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();
-
-	LLVertexBuffer::unbind();
-	LLGLSLShader::sNoFixedFunction = no_ff;
-	if (shader)
-	{
-		shader->bind();
-	}
-}
-
-void LLModelPreview::updateStatusMessages()
-{
-// bit mask values for physics errors. used to prevent overwrite of single line status
-// TODO: use this to provied multiline status
-	enum PhysicsError
-	{
-		NONE=0,
-		NOHAVOK=1,
-		DEGENERATE=2,
-		TOOMANYHULLS=4,
-		TOOMANYVERTSINHULL=8
-	};
-
-	assert_main_thread();
-
-	U32 has_physics_error{ PhysicsError::NONE }; // physics error bitmap
-	//triangle/vertex/submesh count for each mesh asset for each lod
-	std::vector<S32> tris[LLModel::NUM_LODS];
-	std::vector<S32> verts[LLModel::NUM_LODS];
-	std::vector<S32> submeshes[LLModel::NUM_LODS];
-
-	//total triangle/vertex/submesh count for each lod
-	S32 total_tris[LLModel::NUM_LODS];
-	S32 total_verts[LLModel::NUM_LODS];
-	S32 total_submeshes[LLModel::NUM_LODS];
-
-    for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
-    {
-        total_tris[i] = 0;
-	    total_verts[i] = 0;
-	    total_submeshes[i] = 0;
-    }
-
-    for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
-	{
-		LLModelInstance& instance = *iter;
-
-        LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH];
-        if (!model_high_lod)
-		{
-			setLoadState( LLModelLoader::ERROR_MATERIALS );
-			mFMP->childDisable( "calculate_btn" );
-			continue;
-		}
-
-        for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
-		{
-            LLModel* lod_model = instance.mLOD[i];
-            if (!lod_model)
-            {
-                setLoadState( LLModelLoader::ERROR_MATERIALS );
-                mFMP->childDisable( "calculate_btn" );
-            }
-            else
-			{
-					//for each model in the lod
-				S32 cur_tris = 0;
-				S32 cur_verts = 0;
-				S32 cur_submeshes = lod_model->getNumVolumeFaces();
-
-				for (S32 j = 0; j < cur_submeshes; ++j)
-				{ //for each submesh (face), add triangles and vertices to current total
-					const LLVolumeFace& face = lod_model->getVolumeFace(j);
-					cur_tris += face.mNumIndices/3;
-					cur_verts += face.mNumVertices;
-				}
-
-                std::string instance_name = instance.mLabel;
-
-                if (mImporterDebug)
-                {
-                    // Useful for debugging generalized complaints below about total submeshes which don't have enough
-                    // context to address exactly what needs to be fixed to move towards compliance with the rules.
-                    //
-                    std::ostringstream out;
-                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts;
-                    LL_INFOS() << out.str() << LL_ENDL;
-                    LLFloaterModelPreview::addStringToLog(out, false);
-
-                    out.str("");
-                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Tris:  " << cur_tris;
-                    LL_INFOS() << out.str() << LL_ENDL;
-                    LLFloaterModelPreview::addStringToLog(out, false);
-
-                    out.str("");
-                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes;
-                    LL_INFOS() << out.str() << LL_ENDL;
-                    LLFloaterModelPreview::addStringToLog(out, false);
-
-                    out.str("");
-                    LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin();
-                    while (mat_iter != lod_model->mMaterialList.end())
-                    {
-                        out << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter);
-                        LL_INFOS() << out.str() << LL_ENDL;
-                        LLFloaterModelPreview::addStringToLog(out, false);
-                        out.str("");
-                        mat_iter++;
-                    }
-                }
-
-                //add this model to the lod total
-				total_tris[i] += cur_tris;
-				total_verts[i] += cur_verts;
-				total_submeshes[i] += cur_submeshes;
-
-				//store this model's counts to asset data
-				tris[i].push_back(cur_tris);
-				verts[i].push_back(cur_verts);
-				submeshes[i].push_back(cur_submeshes);
-			}
-		}
-    }
-
-	if (mMaxTriangleLimit == 0)
-	{
-		mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH];
-	}
-
-	mHasDegenerate = false;
-	{//check for degenerate triangles in physics mesh
-		U32 lod = LLModel::LOD_PHYSICS;
-		const LLVector4a scale(0.5f);
-		for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i)
-		{ //for each model in the lod
-			if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty())
-			{ //no decomp exists
-				S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces();
-				for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j)
-				{ //for each submesh (face), add triangles and vertices to current total
-					LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j);
-					for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate; )
-					{
-						U16 index_a = face.mIndices[k + 0];
-						U16 index_b = face.mIndices[k + 1];
-						U16 index_c = face.mIndices[k + 2];
-
-						if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case
-						{
-							LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL;
-						}
-						else
-						{
-							LLVector4a v1; v1.setMul(face.mPositions[index_a], scale);
-							LLVector4a v2; v2.setMul(face.mPositions[index_b], scale);
-							LLVector4a v3; v3.setMul(face.mPositions[index_c], scale);
-							if (ll_is_degenerate(v1, v2, v3))
-							{
-								mHasDegenerate = true;
-							}
-						}
-						k += 3;
-					}
-				}
-			}
-		}
-	}
-
-	// flag degenerates here rather than deferring to a MAV error later
-	mFMP->childSetVisible("physics_status_message_text", mHasDegenerate); //display or clear
-	auto degenerateIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
-	degenerateIcon->setVisible(mHasDegenerate);
-	if (mHasDegenerate)
-	{
-		has_physics_error |= PhysicsError::DEGENERATE;
-		mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_degenerate_triangles"));
-		LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error");
-		degenerateIcon->setImage(img);
-	}
-
-	mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH]));
-
-	std::string mesh_status_na = mFMP->getString("mesh_status_na");
-
-	S32 upload_status[LLModel::LOD_HIGH+1];
-
-	mModelNoErrors = true;
-
-	const U32 lod_high = LLModel::LOD_HIGH;
-	U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]);
-
-	for (S32 lod = 0; lod <= lod_high; ++lod)
-	{
-		upload_status[lod] = 0;
-
-		std::string message = "mesh_status_good";
-
-		if (total_tris[lod] > 0)
-		{
-			mFMP->childSetValue(lod_triangles_name[lod], llformat("%d", total_tris[lod]));
-			mFMP->childSetValue(lod_vertices_name[lod], llformat("%d", total_verts[lod]));
-		}
-		else
-		{
-			if (lod == lod_high)
-			{
-				upload_status[lod] = 2;
-				message = "mesh_status_missing_lod";
-			}
-			else
-			{
-				for (S32 i = lod-1; i >= 0; --i)
-				{
-					if (total_tris[i] > 0)
-					{
-						upload_status[lod] = 2;
-						message = "mesh_status_missing_lod";
-					}
-				}
-			}
-
-			mFMP->childSetValue(lod_triangles_name[lod], mesh_status_na);
-			mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na);
-		}
-
-		if (lod != lod_high)
-		{
-			if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high])
-			{ //number of submeshes is different
-				message = "mesh_status_submesh_mismatch";
-				upload_status[lod] = 2;
-			}
-			else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count)
-			{//number of submodels is different, not all faces are matched correctly.
-				message = "mesh_status_submesh_mismatch";
-				upload_status[lod] = 2;
-				// Note: Submodels in instance were loaded from higher LOD and as result face count
-				// returns same value and total_submeshes[lod] is identical to high_lod one.
-			}
-			else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size())
-			{ //number of meshes is different
-				message = "mesh_status_mesh_mismatch";
-				upload_status[lod] = 2;
-			}
-			else if (!verts[lod].empty())
-			{
-				S32 sum_verts_higher_lod = 0;
-				S32 sum_verts_this_lod = 0;
-				for (U32 i = 0; i < verts[lod].size(); ++i)
-				{
-					sum_verts_higher_lod += ((i < verts[lod+1].size()) ? verts[lod+1][i] : 0);
-					sum_verts_this_lod += verts[lod][i];
-				}
-
-				if ((sum_verts_higher_lod > 0) &&
-					(sum_verts_this_lod > sum_verts_higher_lod))
-				{
-					//too many vertices in this lod
-					message = "mesh_status_too_many_vertices";
-					upload_status[lod] = 1;
-				}
-			}
-		}
-
-		LLIconCtrl* icon = mFMP->getChild<LLIconCtrl>(lod_icon_name[lod]);
-		LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]);
-		icon->setVisible(true);
-		icon->setImage(img);
-
-		if (upload_status[lod] >= 2)
-		{
-			mModelNoErrors = false;
-		}
-
-		if (lod == mPreviewLOD)
-		{
-			mFMP->childSetValue("lod_status_message_text", mFMP->getString(message));
-			icon = mFMP->getChild<LLIconCtrl>("lod_status_message_icon");
-			icon->setImage(img);
-		}
-
-		updateLodControls(lod);
-	}
-
-
-	//warn if hulls have more than 256 points in them
-	BOOL physExceededVertexLimit = FALSE;
-	for (U32 i = 0; mModelNoErrors && i < mModel[LLModel::LOD_PHYSICS].size(); ++i)
-	{
-		LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i];
-
-		if (mdl)
-		{
-			for (U32 j = 0; j < mdl->mPhysics.mHull.size(); ++j)
-			{
-				if (mdl->mPhysics.mHull[j].size() > 256)
-				{
-					physExceededVertexLimit = TRUE;
-					LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL;
-					break;
-				}
-			}
-		}
-	}
-
-	if (physExceededVertexLimit)
-	{
-		has_physics_error |= PhysicsError::TOOMANYVERTSINHULL;
-	}
-
-	if (!(has_physics_error & PhysicsError::DEGENERATE)){ // only update this field (incluides clearing it) if it is not already in use.
-		mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit);
-		LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
-		physStatusIcon->setVisible(physExceededVertexLimit);
-		if (physExceededVertexLimit)
-		{
-			mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded"));
-			LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning");
-			physStatusIcon->setImage(img);
-		}
-	}
-
-	if (getLoadState() >= LLModelLoader::ERROR_PARSING)
-	{
-		mModelNoErrors = false;
-		LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL;
-	}
-
-	bool uploadingSkin		     = mFMP->childGetValue("upload_skin").asBoolean();
-	bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
-
-	if ( uploadingSkin )
-	{
-		if ( uploadingJointPositions && !isRigValidForJointPositionUpload() )
-		{
-			mModelNoErrors = false;
-			LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL;
-		}
-	}
-
-	if(mModelNoErrors && mModelLoader)
-	{
-		if(!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
-		{
-			// Some textures are still loading, prevent upload until they are done
-			mModelNoErrors = false;
-		}
-	}
-
-	if (!mModelNoErrors || mHasDegenerate)
-	{
-		mFMP->childDisable("ok_btn");
-		mFMP->childDisable("calculate_btn");
-	}
-	else
-	{
-		mFMP->childEnable("ok_btn");
-		mFMP->childEnable("calculate_btn");
-	}
-
-    if (mModelNoErrors && mLodsWithParsingError.empty())
-    {
-        mFMP->childEnable("calculate_btn");
-    }
-    else
-    {
-        mFMP->childDisable("calculate_btn");
-    }
-	
-	//add up physics triangles etc
-	S32 phys_tris = 0;
-	S32 phys_hulls = 0;
-	S32 phys_points = 0;
-
-	//get the triangle count for the whole scene
-	for (LLModelLoader::scene::iterator iter = mScene[LLModel::LOD_PHYSICS].begin(), endIter = mScene[LLModel::LOD_PHYSICS].end(); iter != endIter; ++iter)
-	{
-		for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance)
-		{
-			LLModel* model = instance->mModel;
-			if (model)
-			{
-				S32 cur_submeshes = model->getNumVolumeFaces();
-
-				LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull;
-
-				if (!decomp.empty())
-				{
-					phys_hulls += decomp.size();
-					for (U32 i = 0; i < decomp.size(); ++i)
-					{
-						phys_points += decomp[i].size();
-					}
-				}
-				else
-				{ //choose physics shape OR decomposition, can't use both
-					for (S32 j = 0; j < cur_submeshes; ++j)
-					{ //for each submesh (face), add triangles and vertices to current total
-						const LLVolumeFace& face = model->getVolumeFace(j);
-						phys_tris += face.mNumIndices/3;
-					}
-				}
-			}
-		}
-	}
-
-	if (phys_tris > 0)
-	{
-		mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", llformat("%d", phys_tris));
-	}
-	else
-	{
-		mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", mesh_status_na);
-	}
-
-	if (phys_hulls > 0)
-	{
-		mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls));
-		mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points));
-	}
-	else
-	{
-		mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na);
-		mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na);
-	}
-
-	LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
-	if (fmp)
-	{
-		if (phys_tris > 0 || phys_hulls > 0)
-		{
-			if (!fmp->isViewOptionEnabled("show_physics"))
-			{
-				fmp->enableViewOption("show_physics");
-				mViewOption["show_physics"] = true;
-				fmp->childSetValue("show_physics", true);
-			}
-		}
-		else
-		{
-			fmp->disableViewOption("show_physics");
-			mViewOption["show_physics"] = false;
-			fmp->childSetValue("show_physics", false);
-
-		}
-
-		//bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean();
-
-		//fmp->childSetEnabled("physics_optimize", !use_hull);
-
-		bool enable = (phys_tris > 0 || phys_hulls > 0) && fmp->mCurRequest.empty();
-		//enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean();
-
-		//enable/disable "analysis" UI
-		LLPanel* panel = fmp->getChild<LLPanel>("physics analysis");
-		LLView* child = panel->getFirstChild();
-		while (child)
-		{
-			child->setEnabled(enable);
-			child = panel->findNextSibling(child);
-		}
-
-		enable = phys_hulls > 0 && fmp->mCurRequest.empty();
-		//enable/disable "simplification" UI
-		panel = fmp->getChild<LLPanel>("physics simplification");
-		child = panel->getFirstChild();
-		while (child)
-		{
-			child->setEnabled(enable);
-			child = panel->findNextSibling(child);
-		}
-
-		if (fmp->mCurRequest.empty())
-		{
-			fmp->childSetVisible("Simplify", true);
-			fmp->childSetVisible("simplify_cancel", false);
-			fmp->childSetVisible("Decompose", true);
-			fmp->childSetVisible("decompose_cancel", false);
-
-			if (phys_hulls > 0)
-			{
-				fmp->childEnable("Simplify");
-			}
-		
-			if (phys_tris || phys_hulls > 0)
-			{
-				fmp->childEnable("Decompose");
-			}
-		}
-		else
-		{
-			fmp->childEnable("simplify_cancel");
-			fmp->childEnable("decompose_cancel");
-		}
-	}
-
-	
-	LLCtrlSelectionInterface* iface = fmp->childGetSelectionInterface("physics_lod_combo");
-	S32 which_mode = 0; 
-	S32 file_mode = 1;
-	if (iface)
-	{
-		which_mode = iface->getFirstSelectedIndex();
-		file_mode = iface->getItemCount() - 1;
-	}
-
-	if (which_mode == file_mode)
-	{
-		mFMP->childEnable("physics_file");
-		mFMP->childEnable("physics_browse");
-	}
-	else
-	{
-		mFMP->childDisable("physics_file");
-		mFMP->childDisable("physics_browse");
-	}
-
-	LLSpinCtrl* crease = mFMP->getChild<LLSpinCtrl>("crease_angle");
-	
-	if (mRequestedCreaseAngle[mPreviewLOD] == -1.f)
-	{
-		mFMP->childSetColor("crease_label", LLColor4::grey);
-		crease->forceSetValue(75.f);
-	}
-	else
-	{
-		mFMP->childSetColor("crease_label", LLColor4::white);
-		crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]);
-	}
-
-	mModelUpdatedSignal(true);
-
-}
-
-void LLModelPreview::updateLodControls(S32 lod)
-{
-	if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH)
-	{
-		std::ostringstream out;
-		out << "Invalid level of detail: " << lod;
-		LL_WARNS() << out.str() << LL_ENDL;
-		LLFloaterModelPreview::addStringToLog(out, false);
-		assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH);
-		return;
-	}
-
-	const char* lod_controls[] =
-	{
-		"lod_mode_",
-		"lod_triangle_limit_",
-		"lod_error_threshold_"
-	};
-	const U32 num_lod_controls = sizeof(lod_controls)/sizeof(char*);
-
-	const char* file_controls[] =
-	{
-		"lod_browse_",
-		"lod_file_",
-	};
-	const U32 num_file_controls = sizeof(file_controls)/sizeof(char*);
-
-	LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
-	if (!fmp) return;
-
-	LLComboBox* lod_combo = mFMP->findChild<LLComboBox>("lod_source_" + lod_name[lod]);
-	if (!lod_combo) return;
-
-	S32 lod_mode = lod_combo->getCurrentIndex();
-	if (lod_mode == LOD_FROM_FILE) // LoD from file
-	{
-		fmp->mLODMode[lod] = 0;
-		for (U32 i = 0; i < num_file_controls; ++i)
-		{
-			mFMP->childSetVisible(file_controls[i] + lod_name[lod], true);
-		}
-
-		for (U32 i = 0; i < num_lod_controls; ++i)
-		{
-			mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false);
-		}
-	}
-	else if (lod_mode == USE_LOD_ABOVE) // use LoD above
-	{
-		fmp->mLODMode[lod] = 2;
-		for (U32 i = 0; i < num_file_controls; ++i)
-		{
-			mFMP->childSetVisible(file_controls[i] + lod_name[lod], false);
-		}
-
-		for (U32 i = 0; i < num_lod_controls; ++i)
-		{
-			mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false);
-		}
-
-		if (lod < LLModel::LOD_HIGH)
-		{
-			mModel[lod] = mModel[lod + 1];
-			mScene[lod] = mScene[lod + 1];
-			mVertexBuffer[lod].clear();
-
-			// Also update lower LoD
-			if (lod > LLModel::LOD_IMPOSTOR)
-			{
-				updateLodControls(lod - 1);
-			}
-		}
-	}
-	else // auto generate, the default case for all LoDs except High
-	{
-		fmp->mLODMode[lod] = 1;
-
-		//don't actually regenerate lod when refreshing UI
-		mLODFrozen = true;
-
-		for (U32 i = 0; i < num_file_controls; ++i)
-		{
-			mFMP->getChildView(file_controls[i] + lod_name[lod])->setVisible(false);
-		}
-
-		for (U32 i = 0; i < num_lod_controls; ++i)
-		{
-			mFMP->getChildView(lod_controls[i] + lod_name[lod])->setVisible(true);
-		}
-
-
-		LLSpinCtrl* threshold = mFMP->getChild<LLSpinCtrl>("lod_error_threshold_" + lod_name[lod]);
-		LLSpinCtrl* limit = mFMP->getChild<LLSpinCtrl>("lod_triangle_limit_" + lod_name[lod]);
-
-		limit->setMaxValue(mMaxTriangleLimit);
-		limit->forceSetValue(mRequestedTriangleCount[lod]);
-
-		threshold->forceSetValue(mRequestedErrorThreshold[lod]);
-
-		mFMP->getChild<LLComboBox>("lod_mode_" + lod_name[lod])->selectNthItem(mRequestedLoDMode[lod]);
-
-		if (mRequestedLoDMode[lod] == 0)
-		{
-			limit->setVisible(true);
-			threshold->setVisible(false);
-
-			limit->setMaxValue(mMaxTriangleLimit);
-			limit->setIncrement(mMaxTriangleLimit/32);
-		}
-		else
-		{
-			limit->setVisible(false);
-			threshold->setVisible(true);
-		}
-
-		mLODFrozen = false;
-	}
-}
-
-void LLModelPreview::setPreviewTarget(F32 distance)
-{
-	mCameraDistance = distance;
-	mCameraZoom = 1.f;
-	mCameraPitch = 0.f;
-	mCameraYaw = 0.f;
-	mCameraOffset.clearVec();
-}
-
-void LLModelPreview::clearBuffers()
-{
-	for (U32 i = 0; i < 6; i++)
-	{
-		mVertexBuffer[i].clear();
-	}
-}
-
-void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
-{
-	U32 tri_count = 0;
-	U32 vertex_count = 0;
-	U32 mesh_count = 0;
-
-	
-	LLModelLoader::model_list* model = NULL;
-
-	if (lod < 0 || lod > 4)
-	{
-		model = &mBaseModel;
-		lod = 5;
-	}
-	else
-	{
-		model = &(mModel[lod]);
-	}
-
-	if (!mVertexBuffer[lod].empty())
-	{
-		mVertexBuffer[lod].clear();
-	}
-
-	mVertexBuffer[lod].clear();
-
-	LLModelLoader::model_list::iterator base_iter = mBaseModel.begin();
-
-	for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter)
-	{
-		LLModel* mdl = *iter;
-		if (!mdl)
-		{
-			continue;
-		}
-
-		LLModel* base_mdl = *base_iter;
-		base_iter++;
-
-		S32 num_faces = mdl->getNumVolumeFaces();
-		for (S32 i = 0; i < num_faces; ++i)
-		{
-			const LLVolumeFace &vf = mdl->getVolumeFace(i);
-			U32 num_vertices = vf.mNumVertices;
-			U32 num_indices = vf.mNumIndices;
-
-			if (!num_vertices || ! num_indices)
-			{
-				continue;
-			}
-
-			LLVertexBuffer* vb = NULL;
-
-			bool skinned = include_skin_weights && !mdl->mSkinWeights.empty();
-
-			U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0 ;
-
-			if (skinned)
-			{
-				mask |= LLVertexBuffer::MAP_WEIGHT4;
-			}
-
-			vb = new LLVertexBuffer(mask, 0);
-
-			if (!vb->allocateBuffer(num_vertices, num_indices, TRUE))
-			{
-				// We are likely to crash due this failure, if this happens, find a way to gracefully stop preview
-									std::ostringstream out;
-									out << "Failed to allocate Vertex Buffer for model preview ";
-									out << num_vertices << " vertices and ";
-									out << num_indices << " indices";
-									LL_WARNS() << out.str() << LL_ENDL;
-									LLFloaterModelPreview::addStringToLog(out, true);
-			}
-
-			LLStrider<LLVector3> vertex_strider;
-			LLStrider<LLVector3> normal_strider;
-			LLStrider<LLVector2> tc_strider;
-			LLStrider<U16> index_strider;
-			LLStrider<LLVector4> weights_strider;
-
-			vb->getVertexStrider(vertex_strider);
-			vb->getIndexStrider(index_strider);
-
-			if (skinned)
-			{
-				vb->getWeight4Strider(weights_strider);
-			}
-
-			LLVector4a::memcpyNonAliased16((F32*) vertex_strider.get(), (F32*) vf.mPositions, num_vertices*4*sizeof(F32));
-			
-			if (vf.mTexCoords)
-			{
-				vb->getTexCoord0Strider(tc_strider);
-				S32 tex_size = (num_vertices*2*sizeof(F32)+0xF) & ~0xF;
-				LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, tex_size);
-			}
-			
-			if (vf.mNormals)
-			{
-				vb->getNormalStrider(normal_strider);
-				LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32));
-			}
-
-			if (skinned)
-			{
-				for (U32 i = 0; i < num_vertices; i++)
-				{
-					//find closest weight to vf.mVertices[i].mPosition
-					LLVector3 pos(vf.mPositions[i].getF32ptr());
-
-					const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos);
-                    llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this
-
-					LLVector4 w(0,0,0,0);
-					
-					for (U32 i = 0; i < weight_list.size(); ++i)
-					{
-						F32 wght = llclamp(weight_list[i].mWeight, 0.001f, 0.999f);
-						F32 joint = (F32) weight_list[i].mJointIdx;
-						w.mV[i] = joint + wght;
-                        llassert(w.mV[i]-(S32)w.mV[i]>0.0f); // because weights are non-zero, and range of wt values
-                                                             //should not cause floating point precision issues.
-					}
-
-					*(weights_strider++) = w;
-				}
-			}
-
-			// build indices
-			for (U32 i = 0; i < num_indices; i++)
-			{
-				*(index_strider++) = vf.mIndices[i];
-			}
-
-			mVertexBuffer[lod][mdl].push_back(vb);
-
-			vertex_count += num_vertices;
-			tri_count += num_indices/3;
-			++mesh_count;
-
-		}
-	}
-}
-
-void LLModelPreview::update()
-{
-    if (mGenLOD)
-    {
-        bool subscribe_for_generation = mLodsQuery.empty();
-        mGenLOD = false;
-        mDirty = true;
-        mLodsQuery.clear();
-
-        for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod)
-        {
-            // adding all lods into query for generation
-            mLodsQuery.push_back(lod);
-        }
-
-        if (subscribe_for_generation)
-        {
-            doOnIdleRepeating(lodQueryCallback);
-        }
-    }
-
-    if (mDirty && mLodsQuery.empty())
-	{
-		mDirty = false;
-		mResourceCost = calcResourceCost();
-		refresh();
-		updateStatusMessages();
-	}
-}
-
-//-----------------------------------------------------------------------------
-// createPreviewAvatar
-//-----------------------------------------------------------------------------
-void LLModelPreview::createPreviewAvatar( void )
-{
-	mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer( LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR );
-	if ( mPreviewAvatar )
-	{
-		mPreviewAvatar->createDrawable( &gPipeline );
-		mPreviewAvatar->mSpecialRenderMode = 1;
-		mPreviewAvatar->startMotion( ANIM_AGENT_STAND );
-		mPreviewAvatar->hideSkirt();
-	}
-	else
-	{
-		LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL;
-	}
-}
-
-//static
-U32 LLModelPreview::countRootModels(LLModelLoader::model_list models)
-{
-	U32 root_models = 0;
-	model_list::iterator model_iter = models.begin();
-	while (model_iter != models.end())
-	{
-		LLModel* mdl = *model_iter;
-		if (mdl && mdl->mSubmodelID == 0)
-		{
-			root_models++;
-		}
-		model_iter++;
-	}
-	return root_models;
-}
-
-void LLModelPreview::loadedCallback(
-	LLModelLoader::scene& scene,
-	LLModelLoader::model_list& model_list,
-	S32 lod,
-	void* opaque)
-{
-	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
-	if (pPreview && !LLModelPreview::sIgnoreLoadedCallback)
-	{
-        // Load loader's warnings into floater's log tab
-        const LLSD out = pPreview->mModelLoader->logOut();
-        LLSD::array_const_iterator iter_out = out.beginArray();
-        LLSD::array_const_iterator end_out = out.endArray();
-        for (; iter_out != end_out; ++iter_out)
-        {
-            if (iter_out->has("Message"))
-            {
-                LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod);
-            }
-        }
-        pPreview->mModelLoader->clearLog();
-        pPreview->loadModelCallback(lod); // removes mModelLoader in some cases
-	}
-
-}
-
-void LLModelPreview::stateChangedCallback(U32 state,void* opaque)
-{
-	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
-	if (pPreview)
-	{
-	 pPreview->setLoadState(state);
-	}
-}
-
-LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque)
-{
-	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
-	if (pPreview)
-	{
-		return pPreview->getPreviewAvatar()->getJoint(str);
-	}
-	return NULL;
-}
-
-U32 LLModelPreview::loadTextures(LLImportMaterial& material,void* opaque)
-{
-	(void)opaque;
-
-	if (material.mDiffuseMapFilename.size())
-	{
-		material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >;
-		LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData));
-
-		tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + material.mDiffuseMapFilename, FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW);
-		tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE);
-		tex->forceToSaveRawImage(0, F32_MAX);
-		material.setDiffuseMap(tex->getID()); // record tex ID
-		return 1;
-	}
-
-	material.mOpaqueData = NULL;
-	return 0;	
-}
-
-void LLModelPreview::addEmptyFace( LLModel* pTarget )
-{
-	U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
-	
-	LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0);
-	
-	buff->allocateBuffer(1, 3, true);
-	memset( (U8*) buff->getMappedData(), 0, buff->getSize() );
-	memset( (U8*) buff->getIndicesPointer(), 0, buff->getIndicesSize() );
-		
-	buff->validateRange( 0, buff->getNumVerts()-1, buff->getNumIndices(), 0 );
-		
-	LLStrider<LLVector3> pos;
-	LLStrider<LLVector3> norm;
-	LLStrider<LLVector2> tc;
-	LLStrider<U16> index;
-		
-	buff->getVertexStrider(pos);
-		
-	if ( type_mask & LLVertexBuffer::MAP_NORMAL )
-	{
-		buff->getNormalStrider(norm);
-	}
-	if ( type_mask & LLVertexBuffer::MAP_TEXCOORD0 )
-	{
-		buff->getTexCoord0Strider(tc);
-	}
-		
-	buff->getIndexStrider(index);
-		
-	//resize face array
-	int faceCnt = pTarget->getNumVolumeFaces();
-	pTarget->setNumVolumeFaces( faceCnt+1 );	
-	pTarget->setVolumeFaceData( faceCnt+1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices() );
-	
-}
-
-//-----------------------------------------------------------------------------
-// render()
-//-----------------------------------------------------------------------------
-// Todo: we shouldn't be setting all those UI elements on render.
-// Note: Render happens each frame with skinned avatars
-BOOL LLModelPreview::render()
-{
-	assert_main_thread();
-
-	LLMutexLock lock(this);
-	mNeedsUpdate = FALSE;
-
-	bool use_shaders = LLGLSLShader::sNoFixedFunction;
-
-	bool edges = mViewOption["show_edges"];
-	bool joint_overrides = mViewOption["show_joint_overrides"];
-	bool joint_positions = mViewOption["show_joint_positions"];
-	bool skin_weight = mViewOption["show_skin_weight"];
-	bool textures = mViewOption["show_textures"];
-	bool physics = mViewOption["show_physics"];
-
-	// Extra configurability, to be exposed later as controls?
-	static LLCachedControl<LLColor4> canvas_col(gSavedSettings, "MeshPreviewCanvasColor");
-	static LLCachedControl<LLColor4> edge_col(gSavedSettings, "MeshPreviewEdgeColor");
-	static LLCachedControl<LLColor4> base_col(gSavedSettings, "MeshPreviewBaseColor");
-	static LLCachedControl<LLColor3> brightness(gSavedSettings, "MeshPreviewBrightnessColor");
-	static LLCachedControl<F32> edge_width(gSavedSettings, "MeshPreviewEdgeWidth");
-	static LLCachedControl<LLColor4> phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor");
-	static LLCachedControl<LLColor4> phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor");
-	static LLCachedControl<F32> phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth");
-	static LLCachedControl<LLColor4> deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor");
-	static LLCachedControl<LLColor4> deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor");	
-	static LLCachedControl<F32> deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth");
-	static LLCachedControl<F32> deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize");
-
-	S32 width = getWidth();
-	S32 height = getHeight();
-
-	LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test
-	LLGLDisable no_blend(GL_BLEND);
-	LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color
-	LLGLDisable fog(GL_FOG);
-
-	{
-		if (use_shaders)
-		{
-			gUIProgram.bind();
-		}
-		//clear background to grey
-		gGL.matrixMode(LLRender::MM_PROJECTION);
-		gGL.pushMatrix();
-		gGL.loadIdentity();
-		gGL.ortho(0.0f, width, 0.0f, height, -1.0f, 1.0f);
-
-		gGL.matrixMode(LLRender::MM_MODELVIEW);
-		gGL.pushMatrix();
-		gGL.loadIdentity();
-
-		gGL.color4fv(static_cast<LLColor4>(canvas_col).mV);
-		gl_rect_2d_simple( width, height );
-
-		gGL.matrixMode(LLRender::MM_PROJECTION);
-		gGL.popMatrix();
-
-		gGL.matrixMode(LLRender::MM_MODELVIEW);
-		gGL.popMatrix();
-		if (use_shaders)
-		{
-			gUIProgram.unbind();
-		}
-	}
-
-	LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
-	
-	bool has_skin_weights = false;
-	bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean();	
-	bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean();
-
-	if ( upload_joints != mLastJointUpdate )
-	{
-		mLastJointUpdate = upload_joints;
-	}
-
-	for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
-	{
-		for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
-		{
-			LLModelInstance& instance = *model_iter;
-			LLModel* model = instance.mModel;
-			model->mPelvisOffset = mPelvisZOffset;
-			if (!model->mSkinWeights.empty())
-			{
-				has_skin_weights = true;
-			}
-		}
-	}
-
-	if (has_skin_weights && lodsReady())
-	{ //model has skin weights, enable view options for skin weights and joint positions
-        U32 flags = getLegacyRigFlags();
-        if (fmp)
-        {
-            if (flags == LEGACY_RIG_OK)
-            {
-                fmp->enableViewOption("show_skin_weight");
-                fmp->setViewOptionEnabled("show_joint_overrides", skin_weight);
-                fmp->setViewOptionEnabled("show_joint_positions", skin_weight);
-                mFMP->childEnable("upload_skin");
-                mFMP->childSetValue("show_skin_weight", skin_weight);
-            }
-            else if ((flags & LEGACY_RIG_FLAG_TOO_MANY_JOINTS) > 0)
-            {
-                mFMP->childSetVisible("skin_too_many_joints", true);
-            }
-            else if ((flags & LEGACY_RIG_FLAG_UNKNOWN_JOINT) > 0)
-            {
-                mFMP->childSetVisible("skin_unknown_joint", true);
-            }
-        }
-	}
-	else
-	{
-		mFMP->childDisable("upload_skin");
-		if (fmp)
-		{
-			mViewOption["show_skin_weight"] = false;
-			fmp->disableViewOption("show_skin_weight");
-			fmp->disableViewOption("show_joint_overrides");
-			fmp->disableViewOption("show_joint_positions");
-
-			skin_weight = false;
-			mFMP->childSetValue("show_skin_weight", false);
-			fmp->setViewOptionEnabled("show_skin_weight", skin_weight);
-		}
-	}
-
-	if (upload_skin && !has_skin_weights)
-	{ //can't upload skin weights if model has no skin weights
-		mFMP->childSetValue("upload_skin", false);
-		upload_skin = false;
-	}
-
-	if (!upload_skin && upload_joints)
-	{ //can't upload joints if not uploading skin weights
-		mFMP->childSetValue("upload_joints", false);
-		upload_joints = false;		
-	}	
-
-    if (upload_skin && upload_joints)
-    {
-        mFMP->childEnable("lock_scale_if_joint_position");
-        if (fmp)
-        {
-            fmp->updateAvatarTab();
-        }
-    }
-    else
-    {
-        mFMP->childDisable("lock_scale_if_joint_position");
-        mFMP->childSetValue("lock_scale_if_joint_position", false);
-        if (fmp)
-        {
-            fmp->clearAvatarTab();
-        }
-    }
-    
-	//Only enable joint offsets if it passed the earlier critiquing
-	if ( isRigValidForJointPositionUpload() )  
-	{
-		mFMP->childSetEnabled("upload_joints", upload_skin);
-	}
-
-	F32 explode = mFMP->childGetValue("physics_explode").asReal();
-
-	LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview
-
-	LLRect preview_rect;
-
-	preview_rect = mFMP->getChildView("preview_panel")->getRect();
-
-	F32 aspect = (F32) preview_rect.getWidth()/preview_rect.getHeight();
-
-	LLViewerCamera::getInstance()->setAspect(aspect);
-
-	LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom);
-
-	LLVector3 offset = mCameraOffset;
-	LLVector3 target_pos = mPreviewTarget+offset;
-
-	F32 z_near = 0.001f;
-	F32 z_far = mCameraDistance*10.0f+mPreviewScale.magVec()+mCameraOffset.magVec();
-
-	if (skin_weight)
-	{
-		target_pos = getPreviewAvatar()->getPositionAgent() + offset;
-		z_near = 0.01f;
-		z_far = 1024.f;
-
-		//render avatar previews every frame
-		refresh();
-	}
-
-	if (use_shaders)
-	{
-		gObjectPreviewProgram.bind();
-	}
-
-	gGL.loadIdentity();
-	gPipeline.enableLightsPreview();
-
-	LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) *
-	LLQuaternion(mCameraYaw, LLVector3::z_axis);
-
-	LLQuaternion av_rot = camera_rot;
-	F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance;
-	LLViewerCamera::getInstance()->setOriginAndLookAt(
-													  target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera
-													  LLVector3::z_axis,																	// up
-													  target_pos);											// point of interest
-
-
-	z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f);
-
-	LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far);
-
-	stop_glerror();
-
-	gGL.pushMatrix();
-	gGL.color4fv(edge_col().mV);
-
-	const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
-
-	LLGLEnable normalize(GL_NORMALIZE);
-
-	if (!mBaseModel.empty() && mVertexBuffer[5].empty())
-	{
-		genBuffers(-1, skin_weight);
-		//genBuffers(3);
-		//genLODs();
-	}
-
-	if (!mModel[mPreviewLOD].empty())
-	{
-		mFMP->childEnable("reset_btn");
-
-		bool regen = mVertexBuffer[mPreviewLOD].empty();
-		if (!regen)
-		{
-			const std::vector<LLPointer<LLVertexBuffer> >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second;
-			if (!vb_vec.empty())
-			{
-				const LLVertexBuffer* buff = vb_vec[0];
-				regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight;
-			}
-			else
-			{
-				LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL;
-				regen = TRUE;
-			}
-		}
-
-		if (regen)
-		{
-			genBuffers(mPreviewLOD, skin_weight);
-		}
-
-		if (!skin_weight)
-		{
-			for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
-			{
-				LLModelInstance& instance = *iter;
-
-				LLModel* model = instance.mLOD[mPreviewLOD];
-
-					if (!model)
-					{
-						continue;
-					}
-
-					gGL.pushMatrix();
-					LLMatrix4 mat = instance.mTransform;
-
-					gGL.multMatrix((GLfloat*) mat.mMatrix);
-
-
-					U32 num_models = mVertexBuffer[mPreviewLOD][model].size();
-					for (U32 i = 0; i < num_models; ++i)
-					{
-						LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
-				
-						buffer->setBuffer(type_mask & buffer->getTypeMask());
-
-						if (textures)
-						{
-							int materialCnt = instance.mModel->mMaterialList.size();
-							if ( i < materialCnt )
-							{
-								const std::string& binding = instance.mModel->mMaterialList[i];						
-								const LLImportMaterial& material = instance.mMaterial[binding];
-
-								gGL.diffuseColor4fv(material.mDiffuseColor.mV);
-
-								// Find the tex for this material, bind it, and add it to our set
-								//
-								LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material);
-								if (tex)
-								{
-									mTextureSet.insert(tex);
-								}
-							}
-						}
-						else
-						{
-							gGL.diffuseColor4fv(static_cast<LLColor4>(base_col).mV);
-						}
-
-						buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
-						gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-						gGL.diffuseColor4fv(static_cast<LLColor4>(edge_col).mV);
-						if (edges)
-						{
-							glLineWidth(edge_width);
-							glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-							buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
-							glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-							glLineWidth(1.f);
-						}
-					}
-					gGL.popMatrix();
-				}
-
-			if (physics)
-			{
-				glClear(GL_DEPTH_BUFFER_BIT);
-				
-				for (U32 pass = 0; pass < 2; pass++)
-				{
-					if (pass == 0)
-					{ //depth only pass
-						gGL.setColorMask(false, false);
-					}
-					else
-					{
-						gGL.setColorMask(true, true);
-					}
-
-					//enable alpha blending on second pass but not first pass
-					LLGLState blend(GL_BLEND, pass);
-
-					gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
-
-					for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
-					{
-						LLModelInstance& instance = *iter;
-
-						LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
-
-						if (!model)
-						{
-							continue;
-						}
-
-						gGL.pushMatrix();
-						LLMatrix4 mat = instance.mTransform;
-
-						gGL.multMatrix((GLfloat*)mat.mMatrix);
-
-
-						bool render_mesh = true;
-						LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
-						if (decomp)
-						{
-							LLMutexLock(decomp->mMutex);
-
-							LLModel::Decomposition& physics = model->mPhysics;
-
-							if (!physics.mHull.empty())
-							{
-								render_mesh = false;
-
-								if (physics.mMesh.empty())
-								{ //build vertex buffer for physics mesh
-									gMeshRepo.buildPhysicsMesh(physics);
-								}
-
-								if (!physics.mMesh.empty())
-								{ //render hull instead of mesh
-									for (U32 i = 0; i < physics.mMesh.size(); ++i)
-									{
-										if (explode > 0.f)
-										{
-											gGL.pushMatrix();
-
-											LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters;
-											offset *= explode;
-
-											gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
-										}
-
-										static std::vector<LLColor4U> hull_colors;
-
-										if (i + 1 >= hull_colors.size())
-										{
-											hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128));
-										}
-
-										gGL.diffuseColor4ubv(hull_colors[i].mV);
-										LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals);
-
-										if (explode > 0.f)
-										{
-											gGL.popMatrix();
-										}
-									}
-								}
-							}
-						}
-
-						if (render_mesh)
-						{
-							if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
-							{
-								genBuffers(LLModel::LOD_PHYSICS, false);
-							}
-
-							U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
-							if (pass > 0){
-								for (U32 i = 0; i < num_models; ++i)
-								{
-									LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
-
-									gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-									gGL.diffuseColor4fv(phys_fill_col().mV);
-
-									buffer->setBuffer(type_mask & buffer->getTypeMask());
-									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
-
-									gGL.diffuseColor4fv(phys_edge_col().mV);
-									glLineWidth(phys_edge_width);
-									glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
-
-									glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-									glLineWidth(1.f);
-								}
-							}
-						}
-                        gGL.popMatrix();
-                    }
-
-					// only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks]
-					if (pass > 0 && mHasDegenerate)
-					{
-						glLineWidth(deg_edge_width);
-						glPointSize(deg_point_size);
-                        gPipeline.enableLightsFullbright();
-						//show degenerate triangles
-						LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS);
-						LLGLDisable cull(GL_CULL_FACE);
-                        gGL.diffuseColor4f(1.f, 0.f, 0.f, 1.f);
-						const LLVector4a scale(0.5f);
-
-						for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
-						{
-							LLModelInstance& instance = *iter;
-
-							LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
-
-							if (!model)
-							{
-								continue;
-							}
-
-							gGL.pushMatrix();
-							LLMatrix4 mat = instance.mTransform;
-
-							gGL.multMatrix((GLfloat*)mat.mMatrix);
-
-
-							LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
-							if (decomp)
-							{
-								LLMutexLock(decomp->mMutex);
-
-								LLModel::Decomposition& physics = model->mPhysics;
-
-								if (physics.mHull.empty())
-								{
-									if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
-									{
-										genBuffers(LLModel::LOD_PHYSICS, false);
-									}
-
-									U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
-									for (U32 v = 0; v < num_models; ++v)
-									{
-										LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v];
-										if(buffer->getNumVerts() < 3)continue;
-
-										buffer->setBuffer(type_mask & buffer->getTypeMask());
-
-										LLStrider<LLVector3> pos_strider;
-										buffer->getVertexStrider(pos_strider, 0);
-										LLVector4a* pos = (LLVector4a*)pos_strider.get();
-
-										LLStrider<U16> idx;
-										buffer->getIndexStrider(idx, 0);
-
-										LLVector4a v1, v2, v3;
-										for (U32 indices_offset = 0; indices_offset < buffer->getNumIndices(); indices_offset += 3)
-										{
-											v1.setMul(pos[*idx++], scale);
-											v2.setMul(pos[*idx++], scale);
-											v3.setMul(pos[*idx++], scale);
-
-											if (ll_is_degenerate(v1, v2, v3))
-											{
-												glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-												gGL.diffuseColor3fv(deg_edge_col().mV);
-												buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
-												buffer->drawRange(LLRender::POINTS, 0, 2, 3, indices_offset);
-												glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-												gGL.diffuseColor3fv(deg_fill_col().mV);
-												buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
-											}
-										}
-									}
-								}
-							}
-
-							gGL.popMatrix();
-						}
-						glLineWidth(1.f);
-						glPointSize(1.f);
-						gPipeline.enableLightsPreview();
-						gGL.setSceneBlendType(LLRender::BT_ALPHA);
-					}
-				}
-			}
-		}
-		else
-		{
-			target_pos = getPreviewAvatar()->getPositionAgent();
-            getPreviewAvatar()->clearAttachmentOverrides(); // removes pelvis fixup
-            LLUUID fake_mesh_id;
-            fake_mesh_id.generate();
-            getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id);
-			bool pelvis_recalc = false;
-
-			LLViewerCamera::getInstance()->setOriginAndLookAt(
-															  target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera
-															  LLVector3::z_axis,																	// up
-															  target_pos);											// point of interest
-
-			for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
-			{
-				for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
-				{
-					LLModelInstance& instance = *model_iter;
-					LLModel* model = instance.mModel;
-
-					if (!model->mSkinWeights.empty())
-					{
-                        const LLMeshSkinInfo *skin = &model->mSkinInfo;
-                        LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary
-                        U32 count = LLSkinningUtil::getMeshJointCount(skin);
-
-                        if (joint_overrides && skin->mAlternateBindMatrix.size() > 0)
-                        {
-                            // mesh_id is used to determine which mesh gets to
-                            // set the joint offset, in the event of a conflict. Since
-                            // we don't know the mesh id yet, we can't guarantee that
-                            // joint offsets will be applied with the same priority as
-                            // in the uploaded model. If the file contains multiple
-                            // meshes with conflicting joint offsets, preview may be
-                            // incorrect.
-                            LLUUID fake_mesh_id;
-                            fake_mesh_id.generate();
-                            for (U32 j = 0; j < count; ++j)
-                            {
-                                LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]);
-                                if (joint)
-                                {
-                                    const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
-                                    if (joint->aboveJointPosThreshold(jointPos))
-                                    {
-                                        bool override_changed;
-                                        joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed);
-
-                                        if (override_changed)
-                                        {
-                                            //If joint is a pelvis then handle old/new pelvis to foot values
-                                            if (joint->getName() == "mPelvis")// or skin->mJointNames[j]
-                                            {
-                                                pelvis_recalc = true;
-                                            }
-                                        }
-                                        if (skin->mLockScaleIfJointPosition)
-                                        {
-                                            // Note that unlike positions, there's no threshold check here,
-                                            // just a lock at the default value.
-                                            joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model");
-                                        }
-                                    }
-                                }
-                            }
-                        }
-
-						for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i)
-						{
-							LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
-
-							const LLVolumeFace& face = model->getVolumeFace(i);
-
-							LLStrider<LLVector3> position;
-							buffer->getVertexStrider(position);
-
-							LLStrider<LLVector4> weight;
-							buffer->getWeight4Strider(weight);
-
-							//quick 'n dirty software vertex skinning
-
-							//build matrix palette
-
-							LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
-                            LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count,
-                                                                        skin, getPreviewAvatar());
-
-                            LLMatrix4a bind_shape_matrix;
-                            bind_shape_matrix.loadu(skin->mBindShapeMatrix);
-                            U32 max_joints = LLSkinningUtil::getMaxJointCount();
-							for (U32 j = 0; j < buffer->getNumVerts(); ++j)
-							{
-                                LLMatrix4a final_mat;
-                                F32 *wptr = weight[j].mV;
-                                LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints);
-
-								//VECTORIZE THIS
-                                LLVector4a& v = face.mPositions[j];
-
-                                LLVector4a t;
-                                LLVector4a dst;
-                                bind_shape_matrix.affineTransform(v, t);
-                                final_mat.affineTransform(t, dst);
-
-								position[j][0] = dst[0];
-								position[j][1] = dst[1];
-								position[j][2] = dst[2];
-							}
-
-							llassert(model->mMaterialList.size() > i); 
-							const std::string& binding = instance.mModel->mMaterialList[i];
-							const LLImportMaterial& material = instance.mMaterial[binding];
-
-							buffer->setBuffer(type_mask & buffer->getTypeMask());
-							gGL.diffuseColor4fv(material.mDiffuseColor.mV);
-							gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
-							// Find the tex for this material, bind it, and add it to our set
-							//
-							LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material);
-							if (tex)
-							{
-								mTextureSet.insert(tex);
-							}
-						
-							buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
-
-							if (edges)
-							{
-								gGL.diffuseColor4fv(edge_col().mV);
-								glLineWidth(edge_width);
-								glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-								buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
-								glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-								glLineWidth(1.f);
-							}
-						}
-					}
-				}
-			}
-
-			if (joint_positions)
-			{
-				LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
-				if (shader)
-				{
-					gDebugProgram.bind();
-				}
-				getPreviewAvatar()->renderCollisionVolumes();
-                if (fmp->mTabContainer->getCurrentPanelIndex() == fmp->mAvatarTabIndex)
-                {
-                    getPreviewAvatar()->renderBones(fmp->mSelectedJointName);
-                }
-                else
-                {
-                    getPreviewAvatar()->renderBones();
-                }
-				if (shader)
-				{
-					shader->bind();
-				}
-			}
-
-            if (pelvis_recalc)
-            {
-                // size/scale recalculation
-                getPreviewAvatar()->postPelvisSetRecalc();
-            }
-		}
-	}
-
-	if (use_shaders)
-	{
-		gObjectPreviewProgram.unbind();
-	}
-
-	gGL.popMatrix();
-
-	return TRUE;
-}
-
-//-----------------------------------------------------------------------------
-// refresh()
-//-----------------------------------------------------------------------------
-void LLModelPreview::refresh()
-{
-	mNeedsUpdate = TRUE;
-}
-
-//-----------------------------------------------------------------------------
-// rotate()
-//-----------------------------------------------------------------------------
-void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians)
-{
-	mCameraYaw = mCameraYaw + yaw_radians;
-
-	mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f);
-}
-
-//-----------------------------------------------------------------------------
-// zoom()
-//-----------------------------------------------------------------------------
-void LLModelPreview::zoom(F32 zoom_amt)
-{
-	F32 new_zoom = mCameraZoom+zoom_amt;
-	// TODO: stop clamping in render
-	static LLCachedControl<F32> zoom_limit(gSavedSettings, "MeshPreviewZoomLimit");
-	mCameraZoom	= llclamp(new_zoom, 1.f, zoom_limit());
-}
-
-void LLModelPreview::pan(F32 right, F32 up)
-{
-	bool skin_weight = mViewOption["show_skin_weight"];
-	F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance;
-	mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f);
-	mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f);
-}
-
-void LLModelPreview::setPreviewLOD(S32 lod)
-{
-	lod = llclamp(lod, 0, (S32) LLModel::LOD_HIGH);
-
-	if (lod != mPreviewLOD)
-	{
-		mPreviewLOD = lod;
-
-		LLComboBox* combo_box = mFMP->getChild<LLComboBox>("preview_lod_combo");
-		combo_box->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order
-		mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]);
-
-		LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor");
-		LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor");
-
-		for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i)
-		{
-			const LLColor4& color = (i == lod) ? highlight_color : normal_color;
-
-			mFMP->childSetColor(lod_status_name[i], color);
-			mFMP->childSetColor(lod_label_name[i], color);
-			mFMP->childSetColor(lod_triangles_name[i], color);
-			mFMP->childSetColor(lod_vertices_name[i], color);
-		}
-
-        LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP;
-        if (fmp)
-        {
-            // make preview repopulate tab
-            fmp->clearAvatarTab();
-        }
-	}
-	refresh();
-	updateStatusMessages();
-}
-
-void LLFloaterModelPreview::onBrowseLOD(S32 lod)
-{
-	assert_main_thread();
-
-	loadModel(lod);
-}
-
-//static
-void LLFloaterModelPreview::onReset(void* user_data)
-{
-	assert_main_thread();
-
-
-	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
-	fmp->childDisable("reset_btn");
-	fmp->clearLogTab();
-	fmp->clearAvatarTab();
-	LLModelPreview* mp = fmp->mModelPreview;
-	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
-
-	fmp->resetDisplayOptions();
-	fmp->resetUploadOptions();
-	//reset model preview
-	fmp->initModelPreview();
-
-	mp = fmp->mModelPreview;
-	mp->loadModel(filename,LLModel::LOD_HIGH,true);
-}
-
-//static
-void LLFloaterModelPreview::onUpload(void* user_data)
-{
-	assert_main_thread();
-
-	LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
-	mp->clearLogTab();
-
-	mp->mUploadBtn->setEnabled(false);
-
-	mp->mModelPreview->rebuildUploadData();
-
-	bool upload_skinweights = mp->childGetValue("upload_skin").asBoolean();
-	bool upload_joint_positions = mp->childGetValue("upload_joints").asBoolean();
-    bool lock_scale_if_joint_position = mp->childGetValue("lock_scale_if_joint_position").asBoolean();
-
-	if (gSavedSettings.getBOOL("MeshImportUseSLM"))
-	{
-        mp->mModelPreview->saveUploadData(upload_skinweights, upload_joint_positions, lock_scale_if_joint_position);
-    }
-
-	gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale,
-						  mp->childGetValue("upload_textures").asBoolean(), 
-                          upload_skinweights, upload_joint_positions, lock_scale_if_joint_position, 
-                          mp->mUploadModelUrl,
-						  true, LLHandle<LLWholeModelFeeObserver>(), mp->getWholeModelUploadObserverHandle());
-}
-
-
-void LLFloaterModelPreview::refresh()
+void LLFloaterModelPreview::refresh()
 {
 	sInstance->toggleCalculateButton(true);
 	sInstance->mModelPreview->mDirty = true;
 }
 
-//static
-void LLModelPreview::textureLoadedCallback(
-    BOOL success,
-    LLViewerFetchedTexture *src_vi,
-    LLImageRaw* src,
-    LLImageRaw* src_aux,
-    S32 discard_level,
-    BOOL final,
-    void* userdata )
-{
-	LLModelPreview* preview = (LLModelPreview*) userdata;
-	preview->refresh();
-
-	if(final && preview->mModelLoader)
-	{
-		if(preview->mModelLoader->mNumOfFetchingTextures > 0)
-		{
-			preview->mModelLoader->mNumOfFetchingTextures-- ;
-		}
-	}
-}
-
-// static
-bool LLModelPreview::lodQueryCallback()
-{
-    // not the best solution, but model preview belongs to floater
-    // so it is an easy way to check that preview still exists.
-    LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
-    if (fmp && fmp->mModelPreview)
-    {
-        LLModelPreview* preview = fmp->mModelPreview;
-        if (preview->mLodsQuery.size() > 0)
-        {
-            S32 lod = preview->mLodsQuery.back();
-            preview->mLodsQuery.pop_back();
-            preview->genLODs(lod);
-
-            // return false to continue cycle
-            return false;
-        }
-    }
-    // nothing to process
-    return true;
-}
-
-void LLModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
-{
-	if (!mLODFrozen)
-	{
-		genLODs(lod, 3, enforce_tri_limit);
-		refresh();
-	}
-}
-
 LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LLModel* mdl)
 {
 	mStage = stage;
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 6f78d534f3..ca52312756 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -28,37 +28,14 @@
 #define LL_LLFLOATERMODELPREVIEW_H
 
 #include "llfloaternamedesc.h"
-
-#include "lldynamictexture.h"
-#include "llquaternion.h"
-#include "llmeshrepository.h"
-#include "llmodel.h"
-#include "llthread.h"
-#include "llviewermenufile.h"
 #include "llfloatermodeluploadbase.h"
-
-#include "lldaeloader.h"
+#include "llmeshrepository.h"
 
 class LLComboBox;
 class LLJoint;
-class LLViewerJointMesh;
-class LLVOAvatar;
-class LLTextBox;
-class LLVertexBuffer;
+class LLMeshFilePicker;
 class LLModelPreview;
-class LLFloaterModelPreview;
-class DAE;
-class daeElement;
-class domProfile_COMMON;
-class domInstance_geometry;
-class domNode;
-class domTranslate;
-class domController;
-class domSkin;
-class domMesh;
-class LLMenuButton;
 class LLTabContainer;
-class LLToggleableMenu;
 class LLViewerTextEditor;
 
 
@@ -255,209 +232,4 @@ private:
 	joint_override_data_map_t mJointOverrides[LLModel::NUM_LODS];
 };
 
-class LLMeshFilePicker : public LLFilePickerThread
-{
-public:
-	LLMeshFilePicker(LLModelPreview* mp, S32 lod);
-	virtual void notify(const std::vector<std::string>& filenames);
-
-private:
-	LLModelPreview* mMP;
-	S32 mLOD;
-};
-
-
-class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
-{
-    LOG_CLASS(LLModelPreview);
-    
-	typedef boost::signals2::signal<void (F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)> details_signal_t;
-	typedef boost::signals2::signal<void (void)> model_loaded_signal_t;
-	typedef boost::signals2::signal<void (bool)> model_updated_signal_t;
-
-public:
-
-	typedef enum
-	{
-		LOD_FROM_FILE = 0,
-		GENERATE,
-		USE_LOD_ABOVE,
-	} eLoDMode;
-
-public:
-	LLModelPreview(S32 width, S32 height, LLFloater* fmp);
-	virtual ~LLModelPreview();
-
-	void resetPreviewTarget();
-	void setPreviewTarget(F32 distance);
-	void setTexture(U32 name) { mTextureName = name; }
-
-	void setPhysicsFromLOD(S32 lod);
-	BOOL render();
-	void update();
-	void genBuffers(S32 lod, bool skinned);
-	void clearBuffers();
-	void refresh();
-	void rotate(F32 yaw_radians, F32 pitch_radians);
-	void zoom(F32 zoom_amt);
-	void pan(F32 right, F32 up);
-	virtual BOOL needsRender() { return mNeedsUpdate; }
-	void setPreviewLOD(S32 lod);
-	void clearModel(S32 lod);
-    void getJointAliases(JointMap& joint_map);
-	void loadModel(std::string filename, S32 lod, bool force_disable_slm = false);
-	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 generateNormals();
-	void restoreNormals();
-	U32 calcResourceCost();
-	void rebuildUploadData();
-	void saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position);
-	void saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position);
-	void clearIncompatible(S32 lod);
-	void updateStatusMessages();
-	void updateLodControls(S32 lod);
-	void clearGLODGroup();
-	void onLODParamCommit(S32 lod, bool enforce_tri_limit);
-	void addEmptyFace( LLModel* pTarget );
-	
-	const bool getModelPivot( void ) const { return mHasPivot; }
-	void setHasPivot( bool val ) { mHasPivot = val; }
-	void setModelPivot( const LLVector3& pivot ) { mModelPivot = pivot; }
-
-	//Is a rig valid so that it can be used as a criteria for allowing for uploading of joint positions
-	//Accessors for joint position upload friendly rigs
-	const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; }
-	void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }
-
-	//Accessors for the legacy rigs
-	const bool isLegacyRigValid( void ) const { return mLegacyRigFlags == 0; }
-	U32 getLegacyRigFlags() const { return mLegacyRigFlags; }
-	void setLegacyRigFlags( U32 rigFlags ) { mLegacyRigFlags = rigFlags; }
-
-	static void	textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata );
-    static bool lodQueryCallback();
-	
-	boost::signals2::connection setDetailsCallback( const details_signal_t::slot_type& cb ){  return mDetailsSignal.connect(cb);  }
-	boost::signals2::connection setModelLoadedCallback( const model_loaded_signal_t::slot_type& cb ){  return mModelLoadedSignal.connect(cb);  }
-	boost::signals2::connection setModelUpdatedCallback( const model_updated_signal_t::slot_type& cb ){  return mModelUpdatedSignal.connect(cb);  }
-	
-	void setLoadState( U32 state ) { mLoadState = state; }
-	U32 getLoadState() { return mLoadState; }
-	
-	static bool 		sIgnoreLoadedCallback;
-    std::vector<S32> mLodsQuery;
-    std::vector<S32> mLodsWithParsingError;
-	bool mHasDegenerate;
-
-protected:
-
-	static void			loadedCallback(LLModelLoader::scene& scene,LLModelLoader::model_list& model_list, S32 lod, void* opaque);
-	static void			stateChangedCallback(U32 state, void* opaque);
-
-	static LLJoint*	lookupJointByName(const std::string&, void* opaque);
-	static U32			loadTextures(LLImportMaterial& material, void* opaque);
-
-private:
-	//Utility function for controller vertex compare
-	bool verifyCount( int expected, int result );
-	//Creates the dummy avatar for the preview window
-	void		createPreviewAvatar( void );
-	//Accessor for the dummy avatar
-	LLVOAvatar* getPreviewAvatar( void ) { return mPreviewAvatar; }
-	// Count amount of original models, excluding sub-models
-	static U32 countRootModels(LLModelLoader::model_list models);
-
- protected:
-	friend class LLModelLoader;
-	friend class LLFloaterModelPreview;
-	friend class LLFloaterModelPreview::DecompRequest;
-	friend class LLPhysicsDecomp;
-
-	LLFloater*  mFMP;
-
-	BOOL        mNeedsUpdate;
-	bool		mDirty;
-	bool		mGenLOD;
-	U32         mTextureName;
-	F32			mCameraDistance;
-	F32			mCameraYaw;
-	F32			mCameraPitch;
-	F32			mCameraZoom;
-	LLVector3	mCameraOffset;
-	LLVector3	mPreviewTarget;
-	LLVector3	mPreviewScale;
-	S32			mPreviewLOD;
-	S32			mPhysicsSearchLOD;
-	U32			mResourceCost;
-	std::string mLODFile[LLModel::NUM_LODS];
-	bool		mLoading;
-	U32			mLoadState;
-	bool		mResetJoints;
-	bool		mModelNoErrors;
-
-	std::map<std::string, bool> mViewOption;
-
-	//GLOD object parameters (must rebuild object if these change)
-	bool mLODFrozen;
-	F32 mBuildShareTolerance;
-	U32 mBuildQueueMode;
-	U32 mBuildOperator;
-	U32 mBuildBorderMode;
-	U32 mRequestedLoDMode[LLModel::NUM_LODS];
-	S32 mRequestedTriangleCount[LLModel::NUM_LODS];
-	F32 mRequestedErrorThreshold[LLModel::NUM_LODS];
-	U32 mRequestedBuildOperator[LLModel::NUM_LODS];
-	U32 mRequestedQueueMode[LLModel::NUM_LODS];
-	U32 mRequestedBorderMode[LLModel::NUM_LODS];
-	F32 mRequestedShareTolerance[LLModel::NUM_LODS];
-	F32 mRequestedCreaseAngle[LLModel::NUM_LODS];
-
-	LLModelLoader* mModelLoader;
-
-	LLModelLoader::scene mScene[LLModel::NUM_LODS];
-	LLModelLoader::scene mBaseScene;
-
-	LLModelLoader::model_list mModel[LLModel::NUM_LODS];
-	LLModelLoader::model_list mBaseModel;
-
-	typedef std::vector<LLVolumeFace>		v_LLVolumeFace_t;
-	typedef std::vector<v_LLVolumeFace_t>	vv_LLVolumeFace_t;
-	
-	vv_LLVolumeFace_t mModelFacesCopy[LLModel::NUM_LODS];
-	vv_LLVolumeFace_t mBaseModelFacesCopy;
-
-	U32 mGroup;
-	std::map<LLPointer<LLModel>, U32> mObject;
-	U32 mMaxTriangleLimit;
-	
-	LLMeshUploadThread::instance_list mUploadData;
-	std::set<LLViewerFetchedTexture * > mTextureSet;
-
-	//map of vertex buffers to models (one vertex buffer in vector per face in model
-	std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mVertexBuffer[LLModel::NUM_LODS+1];
-
-	details_signal_t mDetailsSignal;
-	model_loaded_signal_t mModelLoadedSignal;
-	model_updated_signal_t mModelUpdatedSignal;
-	
-	LLVector3	mModelPivot;
-	bool		mHasPivot;
-	
-	float		mPelvisZOffset;
-	
-	bool		mRigValidJointUpload;
-	U32			mLegacyRigFlags;
-
-	bool		mLastJointUpdate;
-
-	JointNameSet		mJointsFromNode;
-	JointTransformMap	mJointTransformMap;
-
-	LLPointer<LLVOAvatar>	mPreviewAvatar;
-	LLCachedControl<bool>	mImporterDebug;
-};
-
 #endif  // LL_LLFLOATERMODELPREVIEW_H
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
new file mode 100644
index 0000000000..96af3b37d9
--- /dev/null
+++ b/indra/newview/llmodelpreview.cpp
@@ -0,0 +1,3507 @@
+/**
+ * @file llmodelpreview.cpp
+ * @brief LLModelPreview class implementation
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2020, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmodelloader.h"
+#include "lldaeloader.h"
+#include "llfloatermodelpreview.h"
+
+#include "llmodelpreview.h"
+
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagejpeg.h"
+#include "llimagepng.h"
+
+#include "llagent.h"
+#include "llanimationstates.h"
+#include "llcallbacklist.h"
+#include "lldatapacker.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "lliconctrl.h"
+#include "llmatrix4a.h"
+#include "llmeshrepository.h"
+#include "llrender.h"
+#include "llsdutil_math.h"
+#include "llskinningutil.h"
+#include "llstring.h"
+#include "llsdserialize.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llvector4a.h"
+#include "llviewercamera.h"
+#include "llviewercontrol.h"
+#include "llviewerobjectlist.h"
+#include "llviewernetwork.h"
+#include "llviewershadermgr.h"
+#include "llviewertexteditor.h"
+#include "llviewertexturelist.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+
+// ui controls (from floater)
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llspinctrl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+
+#include "glod/glod.h"
+#include <boost/algorithm/string.hpp>
+
+bool LLModelPreview::sIgnoreLoadedCallback = false;
+
+const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f;
+
+BOOL stop_gloderror()
+{
+    GLuint error = glodGetError();
+
+    if (error != GLOD_NO_ERROR)
+    {
+        LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material)
+{
+    LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW);
+
+    if (texture)
+    {
+        if (texture->getDiscardLevel() > -1)
+        {
+            gGL.getTexUnit(0)->bind(texture, true);
+            return texture;
+        }
+    }
+
+    return NULL;
+}
+
+std::string stripSuffix(std::string name)
+{
+    if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1))
+    {
+        return name.substr(0, name.rfind('_'));
+    }
+    return name;
+}
+
+void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut)
+{
+    LLModelLoader::scene::iterator base_iter = scene.begin();
+    bool found = false;
+    while (!found && (base_iter != scene.end()))
+    {
+        matOut = base_iter->first;
+
+        LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin();
+        while (!found && (base_instance_iter != base_iter->second.end()))
+        {
+            LLModelInstance& base_instance = *base_instance_iter++;
+            LLModel* base_model = base_instance.mModel;
+
+            if (base_model && (base_model->mLabel == name_to_match))
+            {
+                baseModelOut = base_model;
+                return;
+            }
+        }
+        base_iter++;
+    }
+}
+
+//-----------------------------------------------------------------------------
+// LLModelPreview
+//-----------------------------------------------------------------------------
+
+LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
+    : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex()
+    , mLodsQuery()
+    , mLodsWithParsingError()
+    , mPelvisZOffset(0.0f)
+    , mLegacyRigFlags(U32_MAX)
+    , mRigValidJointUpload(false)
+    , mPhysicsSearchLOD(LLModel::LOD_PHYSICS)
+    , mResetJoints(false)
+    , mModelNoErrors(true)
+    , mLastJointUpdate(false)
+    , mHasDegenerate(false)
+    , mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false))
+{
+    mNeedsUpdate = TRUE;
+    mCameraDistance = 0.f;
+    mCameraYaw = 0.f;
+    mCameraPitch = 0.f;
+    mCameraZoom = 1.f;
+    mTextureName = 0;
+    mPreviewLOD = 0;
+    mModelLoader = NULL;
+    mMaxTriangleLimit = 0;
+    mDirty = false;
+    mGenLOD = false;
+    mLoading = false;
+    mLoadState = LLModelLoader::STARTING;
+    mGroup = 0;
+    mLODFrozen = false;
+    mBuildShareTolerance = 0.f;
+    mBuildQueueMode = GLOD_QUEUE_GREEDY;
+    mBuildBorderMode = GLOD_BORDER_UNLOCK;
+    mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE;
+
+    for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
+    {
+        mRequestedTriangleCount[i] = 0;
+        mRequestedCreaseAngle[i] = -1.f;
+        mRequestedLoDMode[i] = 0;
+        mRequestedErrorThreshold[i] = 0.f;
+        mRequestedBuildOperator[i] = 0;
+        mRequestedQueueMode[i] = 0;
+        mRequestedBorderMode[i] = 0;
+        mRequestedShareTolerance[i] = 0.f;
+    }
+
+    mViewOption["show_textures"] = false;
+
+    mFMP = fmp;
+
+    mHasPivot = false;
+    mModelPivot = LLVector3(0.0f, 0.0f, 0.0f);
+
+    glodInit();
+
+    createPreviewAvatar();
+}
+
+LLModelPreview::~LLModelPreview()
+{
+    // glod apparently has internal mem alignment issues that are angering
+    // the heap-check code in windows, these should be hunted down in that
+    // TP code, if possible
+    //
+    // kernel32.dll!HeapFree()  + 0x14 bytes	
+    // msvcr100.dll!free(void * pBlock)  Line 51	C
+    // glod.dll!glodGetGroupParameteriv()  + 0x119 bytes	
+    // glod.dll!glodShutdown()  + 0x77 bytes	
+    //
+    //glodShutdown();
+    if (mModelLoader)
+    {
+        mModelLoader->shutdown();
+    }
+}
+
+U32 LLModelPreview::calcResourceCost()
+{
+    assert_main_thread();
+
+    rebuildUploadData();
+
+    //Upload skin is selected BUT check to see if the joints coming in from the asset were malformed.
+    if (mFMP && mFMP->childGetValue("upload_skin").asBoolean())
+    {
+        bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
+        if (uploadingJointPositions && !isRigValidForJointPositionUpload())
+        {
+            mFMP->childDisable("ok_btn");
+        }
+    }
+
+    std::set<LLModel*> accounted;
+    U32 num_points = 0;
+    U32 num_hulls = 0;
+
+    F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f;
+    mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f;
+
+    if (mFMP && mFMP->childGetValue("upload_joints").asBoolean())
+    {
+        // FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail.
+        // see also call to addAttachmentPosOverride.
+        LLUUID fake_mesh_id;
+        fake_mesh_id.generate();
+        getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id);
+    }
+
+    F32 streaming_cost = 0.f;
+    F32 physics_cost = 0.f;
+    for (U32 i = 0; i < mUploadData.size(); ++i)
+    {
+        LLModelInstance& instance = mUploadData[i];
+
+        if (accounted.find(instance.mModel) == accounted.end())
+        {
+            accounted.insert(instance.mModel);
+
+            LLModel::Decomposition& decomp =
+                instance.mLOD[LLModel::LOD_PHYSICS] ?
+                instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics :
+                instance.mModel->mPhysics;
+
+            //update instance skin info for each lods pelvisZoffset 
+            for (int j = 0; j<LLModel::NUM_LODS; ++j)
+            {
+                if (instance.mLOD[j])
+                {
+                    instance.mLOD[j]->mSkinInfo.mPelvisOffset = mPelvisZOffset;
+                }
+            }
+
+            std::stringstream ostr;
+            LLSD ret = LLModel::writeModel(ostr,
+                instance.mLOD[4],
+                instance.mLOD[3],
+                instance.mLOD[2],
+                instance.mLOD[1],
+                instance.mLOD[0],
+                decomp,
+                mFMP->childGetValue("upload_skin").asBoolean(),
+                mFMP->childGetValue("upload_joints").asBoolean(),
+                mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(),
+                TRUE,
+                FALSE,
+                instance.mModel->mSubmodelID);
+
+            num_hulls += decomp.mHull.size();
+            for (U32 i = 0; i < decomp.mHull.size(); ++i)
+            {
+                num_points += decomp.mHull[i].size();
+            }
+
+            //calculate streaming cost
+            LLMatrix4 transformation = instance.mTransform;
+
+            LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+            LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+            LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+            LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+            F32 x_length = x_transformed.normalize();
+            F32 y_length = y_transformed.normalize();
+            F32 z_length = z_transformed.normalize();
+            LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+            F32 radius = scale.length()*0.5f*debug_scale;
+
+            LLMeshCostData costs;
+            if (gMeshRepo.getCostData(ret, costs))
+            {
+                streaming_cost += costs.getRadiusBasedStreamingCost(radius);
+            }
+        }
+    }
+
+    F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f;
+
+    mDetailsSignal(mPreviewScale[0] * scale, mPreviewScale[1] * scale, mPreviewScale[2] * scale, streaming_cost, physics_cost);
+
+    updateStatusMessages();
+
+    return (U32)streaming_cost;
+}
+
+void LLModelPreview::rebuildUploadData()
+{
+    assert_main_thread();
+
+    mUploadData.clear();
+    mTextureSet.clear();
+
+    //fill uploaddata instance vectors from scene data
+
+    std::string requested_name = mFMP->getChild<LLUICtrl>("description_form")->getValue().asString();
+
+    LLSpinCtrl* scale_spinner = mFMP->getChild<LLSpinCtrl>("import_scale");
+
+    F32 scale = scale_spinner->getValue().asReal();
+
+    LLMatrix4 scale_mat;
+    scale_mat.initScale(LLVector3(scale, scale, scale));
+
+    F32 max_scale = 0.f;
+
+    BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
+
+    for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter)
+    { //for each transform in scene
+        LLMatrix4 mat = iter->first;
+
+        // compute position
+        LLVector3 position = LLVector3(0, 0, 0) * mat;
+
+        // compute scale
+        LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position;
+        LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position;
+        LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position;
+        F32 x_length = x_transformed.normalize();
+        F32 y_length = y_transformed.normalize();
+        F32 z_length = z_transformed.normalize();
+
+        max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length);
+
+        mat *= scale_mat;
+
+        for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();)
+        { //for each instance with said transform applied 
+            LLModelInstance instance = *model_iter++;
+
+            LLModel* base_model = instance.mModel;
+
+            if (base_model && !requested_name.empty())
+            {
+                base_model->mRequestedLabel = requested_name;
+            }
+
+            for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--)
+            {
+                LLModel* lod_model = NULL;
+                if (!legacyMatching)
+                {
+                    // Fill LOD slots by finding matching meshes by label with name extensions
+                    // in the appropriate scene for each LOD. This fixes all kinds of issues
+                    // where the indexed method below fails in spectacular fashion.
+                    // If you don't take the time to name your LOD and PHYS meshes
+                    // with the name of their corresponding mesh in the HIGH LOD,
+                    // then the indexed method will be attempted below.
+
+                    LLMatrix4 transform;
+
+                    std::string name_to_match = instance.mLabel;
+                    llassert(!name_to_match.empty());
+
+                    int extensionLOD;
+                    if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty())
+                    {
+                        extensionLOD = i;
+                    }
+                    else
+                    {
+                        //Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for
+                        extensionLOD = mPhysicsSearchLOD;
+                    }
+
+                    std::string toAdd;
+                    switch (extensionLOD)
+                    {
+                    case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
+                    case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
+                    case LLModel::LOD_MEDIUM:   toAdd = "_LOD2"; break;
+                    case LLModel::LOD_PHYSICS:  toAdd = "_PHYS"; break;
+                    case LLModel::LOD_HIGH:                      break;
+                    }
+
+                    if (name_to_match.find(toAdd) == -1)
+                    {
+                        name_to_match += toAdd;
+                    }
+
+                    FindModel(mScene[i], name_to_match, lod_model, transform);
+
+                    if (!lod_model && i != LLModel::LOD_PHYSICS)
+                    {
+                        if (mImporterDebug)
+                        {
+                            std::ostringstream out;
+                            out << "Search of" << name_to_match;
+                            out << " in LOD" << i;
+                            out << " list failed. Searching for alternative among LOD lists.";
+                            LL_INFOS() << out.str() << LL_ENDL;
+                            LLFloaterModelPreview::addStringToLog(out, false);
+                        }
+
+                        int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i;
+                        while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model)
+                        {
+                            std::string name_to_match = instance.mLabel;
+                            llassert(!name_to_match.empty());
+
+                            std::string toAdd;
+                            switch (searchLOD)
+                            {
+                            case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
+                            case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
+                            case LLModel::LOD_MEDIUM:   toAdd = "_LOD2"; break;
+                            case LLModel::LOD_PHYSICS:  toAdd = "_PHYS"; break;
+                            case LLModel::LOD_HIGH:                      break;
+                            }
+
+                            if (name_to_match.find(toAdd) == -1)
+                            {
+                                name_to_match += toAdd;
+                            }
+
+                            // See if we can find an appropriately named model in LOD 'searchLOD'
+                            //
+                            FindModel(mScene[searchLOD], name_to_match, lod_model, transform);
+                            searchLOD++;
+                        }
+                    }
+                }
+                else
+                {
+                    // Use old method of index-based association
+                    U32 idx = 0;
+                    for (idx = 0; idx < mBaseModel.size(); ++idx)
+                    {
+                        // find reference instance for this model
+                        if (mBaseModel[idx] == base_model)
+                        {
+                            if (mImporterDebug)
+                            {
+                                std::ostringstream out;
+                                out << "Attempting to use model index " << idx;
+                                out << " for LOD" << i;
+                                out << " of " << instance.mLabel;
+                                LL_INFOS() << out.str() << LL_ENDL;
+                                LLFloaterModelPreview::addStringToLog(out, false);
+                            }
+                            break;
+                        }
+                    }
+
+                    // If the model list for the current LOD includes that index...
+                    //
+                    if (mModel[i].size() > idx)
+                    {
+                        // Assign that index from the model list for our LOD as the LOD model for this instance
+                        //
+                        lod_model = mModel[i][idx];
+                        if (mImporterDebug)
+                        {
+                            std::ostringstream out;
+                            out << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel;
+                            LL_INFOS() << out.str() << LL_ENDL;
+                            LLFloaterModelPreview::addStringToLog(out, false);
+                        }
+                    }
+                    else if (mImporterDebug)
+                    {
+                        std::ostringstream out;
+                        out << "List of models does not include index " << idx;
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
+                    }
+                }
+
+                if (lod_model)
+                {
+                    if (mImporterDebug)
+                    {
+                        std::ostringstream out;
+                        if (i == LLModel::LOD_PHYSICS)
+                        {
+                            out << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel;
+                        }
+                        else
+                        {
+                            out << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel;
+                        }
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
+                    }
+                    instance.mLOD[i] = lod_model;
+                }
+                else
+                {
+                    if (i < LLModel::LOD_HIGH && !lodsReady())
+                    {
+                        // assign a placeholder from previous LOD until lod generation is complete.
+                        // Note: we might need to assign it regardless of conditions like named search does, to prevent crashes.
+                        instance.mLOD[i] = instance.mLOD[i + 1];
+                    }
+                    if (mImporterDebug)
+                    {
+                        std::ostringstream out;
+                        out << "List of models does not include " << instance.mLabel;
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
+                    }
+                }
+            }
+
+            LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH];
+            if (!high_lod_model)
+            {
+                setLoadState(LLModelLoader::ERROR_MATERIALS);
+                mFMP->childDisable("calculate_btn");
+            }
+            else
+            {
+                for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++)
+                {
+                    int refFaceCnt = 0;
+                    int modelFaceCnt = 0;
+                    llassert(instance.mLOD[i]);
+                    if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt))
+                    {
+                        setLoadState(LLModelLoader::ERROR_MATERIALS);
+                        mFMP->childDisable("calculate_btn");
+                    }
+                }
+                LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP;
+                bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean();
+                if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0)
+                {
+                    LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix);
+                    LLQuaternion identity;
+                    if (!bind_rot.isEqualEps(identity, 0.01))
+                    {
+                        std::ostringstream out;
+                        out << "non-identity bind shape rot. mat is ";
+                        out << high_lod_model->mSkinInfo.mBindShapeMatrix;
+                        out << " bind_rot ";
+                        out << bind_rot;
+                        LL_WARNS() << out.str() << LL_ENDL;
+
+                        LLFloaterModelPreview::addStringToLog(out, false);
+                        setLoadState(LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION);
+                    }
+                }
+            }
+            instance.mTransform = mat;
+            mUploadData.push_back(instance);
+        }
+    }
+
+    for (U32 lod = 0; lod < LLModel::NUM_LODS - 1; lod++)
+    {
+        // Search for models that are not included into upload data
+        // If we found any, that means something we loaded is not a sub-model.
+        for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind)
+        {
+            bool found_model = false;
+            for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+            {
+                LLModelInstance& instance = *iter;
+                if (instance.mLOD[lod] == mModel[lod][model_ind])
+                {
+                    found_model = true;
+                    break;
+                }
+            }
+            if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID)
+            {
+                if (mImporterDebug)
+                {
+                    std::ostringstream out;
+                    out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models.";
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, false);
+                }
+                setLoadState(LLModelLoader::ERROR_MATERIALS);
+                mFMP->childDisable("calculate_btn");
+            }
+        }
+    }
+
+    F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE - 0.1f) / max_scale;
+
+    F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]);
+    max_axis = llmax(max_axis, mPreviewScale.mV[2]);
+    max_axis *= 2.f;
+
+    //clamp scale so that total imported model bounding box is smaller than 240m on a side
+    max_import_scale = llmin(max_import_scale, 240.f / max_axis);
+
+    scale_spinner->setMaxValue(max_import_scale);
+
+    if (max_import_scale < scale)
+    {
+        scale_spinner->setValue(max_import_scale);
+    }
+
+}
+
+void LLModelPreview::saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position)
+{
+    if (!mLODFile[LLModel::LOD_HIGH].empty())
+    {
+        std::string filename = mLODFile[LLModel::LOD_HIGH];
+        std::string slm_filename;
+
+        if (LLModelLoader::getSLMFilename(filename, slm_filename))
+        {
+            saveUploadData(slm_filename, save_skinweights, save_joint_positions, lock_scale_if_joint_position);
+        }
+    }
+}
+
+void LLModelPreview::saveUploadData(const std::string& filename,
+    bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position)
+{
+
+    std::set<LLPointer<LLModel> > meshes;
+    std::map<LLModel*, std::string> mesh_binary;
+
+    LLModel::hull empty_hull;
+
+    LLSD data;
+
+    data["version"] = SLM_SUPPORTED_VERSION;
+    if (!mBaseModel.empty())
+    {
+        data["name"] = mBaseModel[0]->getName();
+    }
+
+    S32 mesh_id = 0;
+
+    //build list of unique models and initialize local id
+    for (U32 i = 0; i < mUploadData.size(); ++i)
+    {
+        LLModelInstance& instance = mUploadData[i];
+
+        if (meshes.find(instance.mModel) == meshes.end())
+        {
+            instance.mModel->mLocalID = mesh_id++;
+            meshes.insert(instance.mModel);
+
+            std::stringstream str;
+            LLModel::Decomposition& decomp =
+                instance.mLOD[LLModel::LOD_PHYSICS].notNull() ?
+                instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics :
+                instance.mModel->mPhysics;
+
+            LLModel::writeModel(str,
+                instance.mLOD[LLModel::LOD_PHYSICS],
+                instance.mLOD[LLModel::LOD_HIGH],
+                instance.mLOD[LLModel::LOD_MEDIUM],
+                instance.mLOD[LLModel::LOD_LOW],
+                instance.mLOD[LLModel::LOD_IMPOSTOR],
+                decomp,
+                save_skinweights,
+                save_joint_positions,
+                lock_scale_if_joint_position,
+                FALSE, TRUE, instance.mModel->mSubmodelID);
+
+            data["mesh"][instance.mModel->mLocalID] = str.str();
+        }
+
+        data["instance"][i] = instance.asLLSD();
+    }
+
+    llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary);
+    LLSDSerialize::toBinary(data, out);
+    out.flush();
+    out.close();
+}
+
+void LLModelPreview::clearModel(S32 lod)
+{
+    if (lod < 0 || lod > LLModel::LOD_PHYSICS)
+    {
+        return;
+    }
+
+    mVertexBuffer[lod].clear();
+    mModel[lod].clear();
+    mScene[lod].clear();
+}
+
+void LLModelPreview::getJointAliases(JointMap& joint_map)
+{
+    // Get all standard skeleton joints from the preview avatar.
+    LLVOAvatar *av = getPreviewAvatar();
+
+    //Joint names and aliases come from avatar_skeleton.xml
+
+    joint_map = av->getJointAliases();
+
+    std::vector<std::string> cv_names, attach_names;
+    av->getSortedJointNames(1, cv_names);
+    av->getSortedJointNames(2, attach_names);
+    for (std::vector<std::string>::iterator it = cv_names.begin(); it != cv_names.end(); ++it)
+    {
+        joint_map[*it] = *it;
+    }
+    for (std::vector<std::string>::iterator it = attach_names.begin(); it != attach_names.end(); ++it)
+    {
+        joint_map[*it] = *it;
+    }
+}
+
+void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable_slm)
+{
+    assert_main_thread();
+
+    LLMutexLock lock(this);
+
+    if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1)
+    {
+        std::ostringstream out;
+        out << "Invalid level of detail: ";
+        out << lod;
+        LL_WARNS() << out.str() << LL_ENDL;
+        LLFloaterModelPreview::addStringToLog(out, true);
+        assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);
+        return;
+    }
+
+    // This triggers if you bring up the file picker and then hit CANCEL.
+    // Just use the previous model (if any) and ignore that you brought up
+    // the file picker.
+
+    if (filename.empty())
+    {
+        if (mBaseModel.empty())
+        {
+            // this is the initial file picking. Close the whole floater
+            // if we don't have a base model to show for high LOD.
+            mFMP->closeFloater(false);
+        }
+        mLoading = false;
+        return;
+    }
+
+    if (mModelLoader)
+    {
+        LL_WARNS() << "Incompleted model load operation pending." << LL_ENDL;
+        return;
+    }
+
+    mLODFile[lod] = filename;
+
+    if (lod == LLModel::LOD_HIGH)
+    {
+        clearGLODGroup();
+    }
+
+    std::map<std::string, std::string> joint_alias_map;
+    getJointAliases(joint_alias_map);
+
+    mModelLoader = new LLDAELoader(
+        filename,
+        lod,
+        &LLModelPreview::loadedCallback,
+        &LLModelPreview::lookupJointByName,
+        &LLModelPreview::loadTextures,
+        &LLModelPreview::stateChangedCallback,
+        this,
+        mJointTransformMap,
+        mJointsFromNode,
+        joint_alias_map,
+        LLSkinningUtil::getMaxJointCount(),
+        gSavedSettings.getU32("ImporterModelLimit"),
+        gSavedSettings.getBOOL("ImporterPreprocessDAE"));
+
+    if (force_disable_slm)
+    {
+        mModelLoader->mTrySLM = false;
+    }
+    else
+    {
+        // For MAINT-6647, we have set force_disable_slm to true,
+        // which means this code path will never be taken. Trying to
+        // re-use SLM files has never worked properly; in particular,
+        // it tends to force the UI into strange checkbox options
+        // which cannot be altered.
+
+        //only try to load from slm if viewer is configured to do so and this is the 
+        //initial model load (not an LoD or physics shape)
+        mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty();
+    }
+    mModelLoader->start();
+
+    mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file"));
+
+    setPreviewLOD(lod);
+
+    if (getLoadState() >= LLModelLoader::ERROR_PARSING)
+    {
+        mFMP->childDisable("ok_btn");
+        mFMP->childDisable("calculate_btn");
+    }
+
+    if (lod == mPreviewLOD)
+    {
+        mFMP->childSetValue("lod_file_" + lod_name[lod], mLODFile[lod]);
+    }
+    else if (lod == LLModel::LOD_PHYSICS)
+    {
+        mFMP->childSetValue("physics_file", mLODFile[lod]);
+    }
+
+    mFMP->openFloater();
+}
+
+void LLModelPreview::setPhysicsFromLOD(S32 lod)
+{
+    assert_main_thread();
+
+    if (lod >= 0 && lod <= 3)
+    {
+        mPhysicsSearchLOD = lod;
+        mModel[LLModel::LOD_PHYSICS] = mModel[lod];
+        mScene[LLModel::LOD_PHYSICS] = mScene[lod];
+        mLODFile[LLModel::LOD_PHYSICS].clear();
+        mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]);
+        mVertexBuffer[LLModel::LOD_PHYSICS].clear();
+        rebuildUploadData();
+        refresh();
+        updateStatusMessages();
+    }
+}
+
+void LLModelPreview::clearIncompatible(S32 lod)
+{
+    //Don't discard models if specified model is the physic rep
+    if (lod == LLModel::LOD_PHYSICS)
+    {
+        return;
+    }
+
+    // at this point we don't care about sub-models,
+    // different amount of sub-models means face count mismatch, not incompatibility
+    U32 lod_size = countRootModels(mModel[lod]);
+    for (U32 i = 0; i <= LLModel::LOD_HIGH; i++)
+    { //clear out any entries that aren't compatible with this model
+        if (i != lod)
+        {
+            if (countRootModels(mModel[i]) != lod_size)
+            {
+                mModel[i].clear();
+                mScene[i].clear();
+                mVertexBuffer[i].clear();
+
+                if (i == LLModel::LOD_HIGH)
+                {
+                    mBaseModel = mModel[lod];
+                    clearGLODGroup();
+                    mBaseScene = mScene[lod];
+                    mVertexBuffer[5].clear();
+                }
+            }
+        }
+    }
+}
+
+void LLModelPreview::clearGLODGroup()
+{
+    if (mGroup)
+    {
+        for (std::map<LLPointer<LLModel>, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter)
+        {
+            glodDeleteObject(iter->second);
+            stop_gloderror();
+        }
+        mObject.clear();
+
+        glodDeleteGroup(mGroup);
+        stop_gloderror();
+        mGroup = 0;
+    }
+}
+
+void LLModelPreview::loadModelCallback(S32 loaded_lod)
+{
+    assert_main_thread();
+
+    LLMutexLock lock(this);
+    if (!mModelLoader)
+    {
+        mLoading = false;
+        return;
+    }
+    if (getLoadState() >= LLModelLoader::ERROR_PARSING)
+    {
+        mLoading = false;
+        mModelLoader = NULL;
+        mLodsWithParsingError.push_back(loaded_lod);
+        return;
+    }
+
+    mLodsWithParsingError.erase(std::remove(mLodsWithParsingError.begin(), mLodsWithParsingError.end(), loaded_lod), mLodsWithParsingError.end());
+    if (mLodsWithParsingError.empty())
+    {
+        mFMP->childEnable("calculate_btn");
+    }
+
+    // Copy determinations about rig so UI will reflect them
+    //
+    setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());
+    setLegacyRigFlags(mModelLoader->getLegacyRigFlags());
+
+    mModelLoader->loadTextures();
+
+    if (loaded_lod == -1)
+    { //populate all LoDs from model loader scene
+        mBaseModel.clear();
+        mBaseScene.clear();
+
+        bool skin_weights = false;
+        bool joint_overrides = false;
+        bool lock_scale_if_joint_position = false;
+
+        for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+        { //for each LoD
+
+            //clear scene and model info
+            mScene[lod].clear();
+            mModel[lod].clear();
+            mVertexBuffer[lod].clear();
+
+            if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull())
+            { //if this LoD exists in the loaded scene
+
+                //copy scene to current LoD
+                mScene[lod] = mModelLoader->mScene;
+
+                //touch up copied scene to look like current LoD
+                for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter)
+                {
+                    LLModelLoader::model_instance_list& list = iter->second;
+
+                    for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter)
+                    {
+                        //override displayed model with current LoD
+                        list_iter->mModel = list_iter->mLOD[lod];
+
+                        if (!list_iter->mModel)
+                        {
+                            continue;
+                        }
+
+                        //add current model to current LoD's model list (LLModel::mLocalID makes a good vector index)
+                        S32 idx = list_iter->mModel->mLocalID;
+
+                        if (mModel[lod].size() <= idx)
+                        { //stretch model list to fit model at given index
+                            mModel[lod].resize(idx + 1);
+                        }
+
+                        mModel[lod][idx] = list_iter->mModel;
+                        if (!list_iter->mModel->mSkinWeights.empty())
+                        {
+                            skin_weights = true;
+
+                            if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty())
+                            {
+                                joint_overrides = true;
+                            }
+                            if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition)
+                            {
+                                lock_scale_if_joint_position = true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (mFMP)
+        {
+            LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP;
+
+            if (skin_weights)
+            { //enable uploading/previewing of skin weights if present in .slm file
+                fmp->enableViewOption("show_skin_weight");
+                mViewOption["show_skin_weight"] = true;
+                fmp->childSetValue("upload_skin", true);
+            }
+
+            if (joint_overrides)
+            {
+                fmp->enableViewOption("show_joint_overrides");
+                mViewOption["show_joint_overrides"] = true;
+                fmp->enableViewOption("show_joint_positions");
+                mViewOption["show_joint_positions"] = true;
+                fmp->childSetValue("upload_joints", true);
+            }
+            else
+            {
+                fmp->clearAvatarTab();
+            }
+
+            if (lock_scale_if_joint_position)
+            {
+                fmp->enableViewOption("lock_scale_if_joint_position");
+                mViewOption["lock_scale_if_joint_position"] = true;
+                fmp->childSetValue("lock_scale_if_joint_position", true);
+            }
+        }
+
+        //copy high lod to base scene for LoD generation
+        mBaseScene = mScene[LLModel::LOD_HIGH];
+        mBaseModel = mModel[LLModel::LOD_HIGH];
+
+        mDirty = true;
+        resetPreviewTarget();
+    }
+    else
+    { //only replace given LoD
+        mModel[loaded_lod] = mModelLoader->mModelList;
+        mScene[loaded_lod] = mModelLoader->mScene;
+        mVertexBuffer[loaded_lod].clear();
+
+        setPreviewLOD(loaded_lod);
+
+        if (loaded_lod == LLModel::LOD_HIGH)
+        { //save a copy of the highest LOD for automatic LOD manipulation
+            if (mBaseModel.empty())
+            { //first time we've loaded a model, auto-gen LoD
+                mGenLOD = true;
+            }
+
+            mBaseModel = mModel[loaded_lod];
+            clearGLODGroup();
+
+            mBaseScene = mScene[loaded_lod];
+            mVertexBuffer[5].clear();
+        }
+        else
+        {
+            BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
+            if (!legacyMatching)
+            {
+                if (!mBaseModel.empty())
+                {
+                    BOOL name_based = FALSE;
+                    BOOL has_submodels = FALSE;
+                    for (U32 idx = 0; idx < mBaseModel.size(); ++idx)
+                    {
+                        if (mBaseModel[idx]->mSubmodelID)
+                        { // don't do index-based renaming when the base model has submodels
+                            has_submodels = TRUE;
+                            if (mImporterDebug)
+                            {
+                                std::ostringstream out;
+                                out << "High LOD has submodels";
+                                LL_INFOS() << out.str() << LL_ENDL;
+                                LLFloaterModelPreview::addStringToLog(out, false);
+                            }
+                            break;
+                        }
+                    }
+
+                    for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx)
+                    {
+                        std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel);
+
+                        LLModel* found_model = NULL;
+                        LLMatrix4 transform;
+                        FindModel(mBaseScene, loaded_name, found_model, transform);
+                        if (found_model)
+                        { // don't rename correctly named models (even if they are placed in a wrong order)
+                            name_based = TRUE;
+                        }
+
+                        if (mModel[loaded_lod][idx]->mSubmodelID)
+                        { // don't rename the models when loaded LOD model has submodels
+                            has_submodels = TRUE;
+                        }
+                    }
+
+                    if (mImporterDebug)
+                    {
+                        std::ostringstream out;
+                        out << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found";
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
+                    }
+
+                    if (!name_based && !has_submodels)
+                    { // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601)
+                        // this actually works like "ImporterLegacyMatching" for this particular LOD
+                        for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx)
+                        {
+                            std::string name = mBaseModel[idx]->mLabel;
+                            std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel);
+
+                            if (loaded_name != name)
+                            {
+                                switch (loaded_lod)
+                                {
+                                case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
+                                case LLModel::LOD_LOW:      name += "_LOD1"; break;
+                                case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
+                                case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
+                                case LLModel::LOD_HIGH:                      break;
+                                }
+
+                                if (mImporterDebug)
+                                {
+                                    std::ostringstream out;
+                                    out << "Loded model name " << mModel[loaded_lod][idx]->mLabel;
+                                    out << " for LOD " << loaded_lod;
+                                    out << " doesn't match the base model. Renaming to " << name;
+                                    LL_WARNS() << out.str() << LL_ENDL;
+                                    LLFloaterModelPreview::addStringToLog(out, false);
+                                }
+
+                                mModel[loaded_lod][idx]->mLabel = name;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        clearIncompatible(loaded_lod);
+
+        mDirty = true;
+
+        if (loaded_lod == LLModel::LOD_HIGH)
+        {
+            resetPreviewTarget();
+        }
+    }
+
+    mLoading = false;
+    if (mFMP)
+    {
+        if (!mBaseModel.empty())
+        {
+            const std::string& model_name = mBaseModel[0]->getName();
+            LLLineEditor* description_form = mFMP->getChild<LLLineEditor>("description_form");
+            if (description_form->getText().empty())
+            {
+                description_form->setText(model_name);
+            }
+            // Add info to log that loading is complete (purpose: separator between loading and other logs)
+            LLSD args;
+            args["MODEL_NAME"] = model_name; // Teoretically shouldn't be empty, but might be better idea to add filename here
+            LLFloaterModelPreview::addStringToLog("ModelLoaded", args, false, loaded_lod);
+        }
+    }
+    refresh();
+
+    mModelLoadedSignal();
+
+    mModelLoader = NULL;
+}
+
+void LLModelPreview::resetPreviewTarget()
+{
+    if (mModelLoader)
+    {
+        mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f;
+        mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f;
+    }
+
+    setPreviewTarget(mPreviewScale.magVec()*10.f);
+}
+
+void LLModelPreview::generateNormals()
+{
+    assert_main_thread();
+
+    S32 which_lod = mPreviewLOD;
+
+    if (which_lod > 4 || which_lod < 0 ||
+        mModel[which_lod].empty())
+    {
+        return;
+    }
+
+    F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal();
+
+    mRequestedCreaseAngle[which_lod] = angle_cutoff;
+
+    angle_cutoff *= DEG_TO_RAD;
+
+    if (which_lod == 3 && !mBaseModel.empty())
+    {
+        if (mBaseModelFacesCopy.empty())
+        {
+            mBaseModelFacesCopy.reserve(mBaseModel.size());
+            for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it)
+            {
+                v_LLVolumeFace_t faces;
+                (*it)->copyFacesTo(faces);
+                mBaseModelFacesCopy.push_back(faces);
+            }
+        }
+
+        for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it)
+        {
+            (*it)->generateNormals(angle_cutoff);
+        }
+
+        mVertexBuffer[5].clear();
+    }
+
+    bool perform_copy = mModelFacesCopy[which_lod].empty();
+    if (perform_copy) {
+        mModelFacesCopy[which_lod].reserve(mModel[which_lod].size());
+    }
+
+    for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it)
+    {
+        if (perform_copy)
+        {
+            v_LLVolumeFace_t faces;
+            (*it)->copyFacesTo(faces);
+            mModelFacesCopy[which_lod].push_back(faces);
+        }
+
+        (*it)->generateNormals(angle_cutoff);
+    }
+
+    mVertexBuffer[which_lod].clear();
+    refresh();
+    updateStatusMessages();
+}
+
+void LLModelPreview::restoreNormals()
+{
+    S32 which_lod = mPreviewLOD;
+
+    if (which_lod > 4 || which_lod < 0 ||
+        mModel[which_lod].empty())
+    {
+        return;
+    }
+
+    if (!mBaseModelFacesCopy.empty())
+    {
+        llassert(mBaseModelFacesCopy.size() == mBaseModel.size());
+
+        vv_LLVolumeFace_t::const_iterator itF = mBaseModelFacesCopy.begin();
+        for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it, ++itF)
+        {
+            (*it)->copyFacesFrom((*itF));
+        }
+
+        mBaseModelFacesCopy.clear();
+    }
+
+    if (!mModelFacesCopy[which_lod].empty())
+    {
+        vv_LLVolumeFace_t::const_iterator itF = mModelFacesCopy[which_lod].begin();
+        for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it, ++itF)
+        {
+            (*it)->copyFacesFrom((*itF));
+        }
+
+        mModelFacesCopy[which_lod].clear();
+    }
+
+    mVertexBuffer[which_lod].clear();
+    refresh();
+    updateStatusMessages();
+}
+
+void LLModelPreview::genLODs(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)
+    {
+        std::ostringstream out;
+        out << "Invalid level of detail: " << which_lod;
+        LL_WARNS() << out.str() << LL_ENDL;
+        LLFloaterModelPreview::addStringToLog(out, false);
+        assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS);
+        return;
+    }
+
+    if (mBaseModel.empty())
+    {
+        return;
+    }
+
+    LLVertexBuffer::unbind();
+
+    bool no_ff = LLGLSLShader::sNoFixedFunction;
+    LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+    LLGLSLShader::sNoFixedFunction = false;
+
+    if (shader)
+    {
+        shader->unbind();
+    }
+
+    stop_gloderror();
+    static U32 cur_name = 1;
+
+    S32 limit = -1;
+
+    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 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
+    U32 lod_mode = 0;
+
+    F32 lod_error_threshold = 0;
+
+    // The LoD should be in range from Lowest to High
+    if (which_lod > -1 && which_lod < NUM_LOD)
+    {
+        LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]);
+        if (iface)
+        {
+            lod_mode = iface->getFirstSelectedIndex();
+        }
+
+        lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal();
+    }
+
+    if (which_lod != -1)
+    {
+        mRequestedLoDMode[which_lod] = lod_mode;
+    }
+
+    if (lod_mode == 0)
+    {
+        lod_mode = GLOD_TRIANGLE_BUDGET;
+
+        // The LoD should be in range from Lowest to High
+        if (which_lod > -1 && which_lod < NUM_LOD)
+        {
+            limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger();
+            //convert from "scene wide" to "non-instanced" triangle limit
+            limit = (S32)((F32)limit*triangle_ratio);
+        }
+    }
+    else
+    {
+        lod_mode = GLOD_ERROR_THRESHOLD;
+    }
+
+    bool object_dirty = false;
+
+    if (mGroup == 0)
+    {
+        object_dirty = true;
+        mGroup = cur_name++;
+        glodNewGroup(mGroup);
+    }
+
+    if (object_dirty)
+    {
+        for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter)
+        { //build GLOD objects for each model in base model list
+            LLModel* mdl = *iter;
+
+            if (mObject[mdl] != 0)
+            {
+                glodDeleteObject(mObject[mdl]);
+            }
+
+            mObject[mdl] = cur_name++;
+
+            glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE);
+            stop_gloderror();
+
+            if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty())
+            { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation
+                mVertexBuffer[5].clear();
+            }
+
+            if (mVertexBuffer[5].empty())
+            {
+                genBuffers(5, false);
+            }
+
+            U32 tri_count = 0;
+            for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i)
+            {
+                LLVertexBuffer* buff = mVertexBuffer[5][mdl][i];
+                buff->setBuffer(type_mask & buff->getTypeMask());
+
+                U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices();
+                if (num_indices > 2)
+                {
+                    glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f);
+                }
+                tri_count += num_indices / 3;
+                stop_gloderror();
+            }
+
+            glodBuildObject(mObject[mdl]);
+            stop_gloderror();
+        }
+    }
+
+
+    S32 start = LLModel::LOD_HIGH;
+    S32 end = 0;
+
+    if (which_lod != -1)
+    {
+        start = end = which_lod;
+    }
+
+    mMaxTriangleLimit = base_triangle_count;
+
+    for (S32 lod = start; lod >= end; --lod)
+    {
+        if (which_lod == -1)
+        {
+            if (lod < start)
+            {
+                triangle_count /= decimation;
+            }
+        }
+        else
+        {
+            if (enforce_tri_limit)
+            {
+                triangle_count = limit;
+            }
+            else
+            {
+                for (S32 j = LLModel::LOD_HIGH; j>which_lod; --j)
+                {
+                    triangle_count /= decimation;
+                }
+            }
+        }
+
+        mModel[lod].clear();
+        mModel[lod].resize(mBaseModel.size());
+        mVertexBuffer[lod].clear();
+
+        U32 actual_tris = 0;
+        U32 actual_verts = 0;
+        U32 submeshes = 0;
+
+        mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio);
+        mRequestedErrorThreshold[lod] = lod_error_threshold;
+
+        glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode);
+        stop_gloderror();
+
+        glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR);
+        stop_gloderror();
+
+        glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold);
+        stop_gloderror();
+
+        if (lod_mode != GLOD_TRIANGLE_BUDGET)
+        {
+            glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0);
+        }
+        else
+        {
+            //SH-632: always add 1 to desired amount to avoid decimating below desired amount
+            glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count + 1);
+        }
+
+        stop_gloderror();
+        glodAdaptGroup(mGroup);
+        stop_gloderror();
+
+        for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx)
+        {
+            LLModel* base = mBaseModel[mdl_idx];
+
+            GLint patch_count = 0;
+            glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count);
+            stop_gloderror();
+
+            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;
+
+            switch (lod)
+            {
+            case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
+            case LLModel::LOD_LOW:      name += "_LOD1"; break;
+            case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
+            case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
+            case LLModel::LOD_HIGH:                      break;
+            }
+
+            mModel[lod][mdl_idx]->mLabel = name;
+            mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID;
+
+            GLint* sizes = new GLint[patch_count * 2];
+            glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes);
+            stop_gloderror();
+
+            GLint* names = new GLint[patch_count];
+            glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names);
+            stop_gloderror();
+
+            mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count);
+
+            LLModel* target_model = mModel[lod][mdl_idx];
+
+            for (GLint i = 0; i < patch_count; ++i)
+            {
+                type_mask = mVertexBuffer[5][base][i]->getTypeMask();
+
+                LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0);
+
+                if (sizes[i * 2 + 1] > 0 && sizes[i * 2] > 0)
+                {
+                    if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true))
+                    {
+                        // Todo: find a way to stop preview in this case instead of crashing
+                        LL_ERRS() << "Failed buffer allocation during preview LOD generation."
+                            << " Vertices: " << sizes[i * 2 + 1]
+                            << " Indices: " << sizes[i * 2] << LL_ENDL;
+                    }
+                    buff->setBuffer(type_mask);
+                    glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*)buff->getIndicesPointer());
+                    stop_gloderror();
+                }
+                else
+                {
+                    // This face was eliminated or we failed to allocate buffer,
+                    // attempt to create a dummy triangle (one vertex, 3 indices, all 0)
+                    buff->allocateBuffer(1, 3, true);
+                    memset((U8*)buff->getMappedData(), 0, buff->getSize());
+                    memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize());
+                }
+
+                buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0);
+
+                LLStrider<LLVector3> pos;
+                LLStrider<LLVector3> norm;
+                LLStrider<LLVector2> tc;
+                LLStrider<U16> index;
+
+                buff->getVertexStrider(pos);
+                if (type_mask & LLVertexBuffer::MAP_NORMAL)
+                {
+                    buff->getNormalStrider(norm);
+                }
+                if (type_mask & LLVertexBuffer::MAP_TEXCOORD0)
+                {
+                    buff->getTexCoord0Strider(tc);
+                }
+
+                buff->getIndexStrider(index);
+
+                target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices());
+                actual_tris += buff->getNumIndices() / 3;
+                actual_verts += buff->getNumVerts();
+                ++submeshes;
+
+                if (!validate_face(target_model->getVolumeFace(names[i])))
+                {
+                    LL_ERRS() << "Invalid face generated during LOD generation." << LL_ENDL;
+                }
+            }
+
+            //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;
+            }
+
+            delete[] sizes;
+            delete[] names;
+        }
+
+        //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();
+
+    LLVertexBuffer::unbind();
+    LLGLSLShader::sNoFixedFunction = no_ff;
+    if (shader)
+    {
+        shader->bind();
+    }
+}
+
+void LLModelPreview::updateStatusMessages()
+{
+    // bit mask values for physics errors. used to prevent overwrite of single line status
+    // TODO: use this to provied multiline status
+    enum PhysicsError
+    {
+        NONE = 0,
+        NOHAVOK = 1,
+        DEGENERATE = 2,
+        TOOMANYHULLS = 4,
+        TOOMANYVERTSINHULL = 8
+    };
+
+    assert_main_thread();
+
+    U32 has_physics_error{ PhysicsError::NONE }; // physics error bitmap
+    //triangle/vertex/submesh count for each mesh asset for each lod
+    std::vector<S32> tris[LLModel::NUM_LODS];
+    std::vector<S32> verts[LLModel::NUM_LODS];
+    std::vector<S32> submeshes[LLModel::NUM_LODS];
+
+    //total triangle/vertex/submesh count for each lod
+    S32 total_tris[LLModel::NUM_LODS];
+    S32 total_verts[LLModel::NUM_LODS];
+    S32 total_submeshes[LLModel::NUM_LODS];
+
+    for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++)
+    {
+        total_tris[i] = 0;
+        total_verts[i] = 0;
+        total_submeshes[i] = 0;
+    }
+
+    for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+    {
+        LLModelInstance& instance = *iter;
+
+        LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH];
+        if (!model_high_lod)
+        {
+            setLoadState(LLModelLoader::ERROR_MATERIALS);
+            mFMP->childDisable("calculate_btn");
+            continue;
+        }
+
+        for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++)
+        {
+            LLModel* lod_model = instance.mLOD[i];
+            if (!lod_model)
+            {
+                setLoadState(LLModelLoader::ERROR_MATERIALS);
+                mFMP->childDisable("calculate_btn");
+            }
+            else
+            {
+                //for each model in the lod
+                S32 cur_tris = 0;
+                S32 cur_verts = 0;
+                S32 cur_submeshes = lod_model->getNumVolumeFaces();
+
+                for (S32 j = 0; j < cur_submeshes; ++j)
+                { //for each submesh (face), add triangles and vertices to current total
+                    const LLVolumeFace& face = lod_model->getVolumeFace(j);
+                    cur_tris += face.mNumIndices / 3;
+                    cur_verts += face.mNumVertices;
+                }
+
+                std::string instance_name = instance.mLabel;
+
+                if (mImporterDebug)
+                {
+                    // Useful for debugging generalized complaints below about total submeshes which don't have enough
+                    // context to address exactly what needs to be fixed to move towards compliance with the rules.
+                    //
+                    std::ostringstream out;
+                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts;
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, false);
+
+                    out.str("");
+                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Tris:  " << cur_tris;
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, false);
+
+                    out.str("");
+                    out << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes;
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, false);
+
+                    out.str("");
+                    LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin();
+                    while (mat_iter != lod_model->mMaterialList.end())
+                    {
+                        out << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter);
+                        LL_INFOS() << out.str() << LL_ENDL;
+                        LLFloaterModelPreview::addStringToLog(out, false);
+                        out.str("");
+                        mat_iter++;
+                    }
+                }
+
+                //add this model to the lod total
+                total_tris[i] += cur_tris;
+                total_verts[i] += cur_verts;
+                total_submeshes[i] += cur_submeshes;
+
+                //store this model's counts to asset data
+                tris[i].push_back(cur_tris);
+                verts[i].push_back(cur_verts);
+                submeshes[i].push_back(cur_submeshes);
+            }
+        }
+    }
+
+    if (mMaxTriangleLimit == 0)
+    {
+        mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH];
+    }
+
+    mHasDegenerate = false;
+    {//check for degenerate triangles in physics mesh
+        U32 lod = LLModel::LOD_PHYSICS;
+        const LLVector4a scale(0.5f);
+        for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i)
+        { //for each model in the lod
+            if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty())
+            { //no decomp exists
+                S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces();
+                for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j)
+                { //for each submesh (face), add triangles and vertices to current total
+                    LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j);
+                    for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate;)
+                    {
+                        U16 index_a = face.mIndices[k + 0];
+                        U16 index_b = face.mIndices[k + 1];
+                        U16 index_c = face.mIndices[k + 2];
+
+                        if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case
+                        {
+                            LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL;
+                        }
+                        else
+                        {
+                            LLVector4a v1; v1.setMul(face.mPositions[index_a], scale);
+                            LLVector4a v2; v2.setMul(face.mPositions[index_b], scale);
+                            LLVector4a v3; v3.setMul(face.mPositions[index_c], scale);
+                            if (ll_is_degenerate(v1, v2, v3))
+                            {
+                                mHasDegenerate = true;
+                            }
+                        }
+                        k += 3;
+                    }
+                }
+            }
+        }
+    }
+
+    // flag degenerates here rather than deferring to a MAV error later
+    mFMP->childSetVisible("physics_status_message_text", mHasDegenerate); //display or clear
+    auto degenerateIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
+    degenerateIcon->setVisible(mHasDegenerate);
+    if (mHasDegenerate)
+    {
+        has_physics_error |= PhysicsError::DEGENERATE;
+        mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_degenerate_triangles"));
+        LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error");
+        degenerateIcon->setImage(img);
+    }
+
+    mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH]));
+
+    std::string mesh_status_na = mFMP->getString("mesh_status_na");
+
+    S32 upload_status[LLModel::LOD_HIGH + 1];
+
+    mModelNoErrors = true;
+
+    const U32 lod_high = LLModel::LOD_HIGH;
+    U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]);
+
+    for (S32 lod = 0; lod <= lod_high; ++lod)
+    {
+        upload_status[lod] = 0;
+
+        std::string message = "mesh_status_good";
+
+        if (total_tris[lod] > 0)
+        {
+            mFMP->childSetValue(lod_triangles_name[lod], llformat("%d", total_tris[lod]));
+            mFMP->childSetValue(lod_vertices_name[lod], llformat("%d", total_verts[lod]));
+        }
+        else
+        {
+            if (lod == lod_high)
+            {
+                upload_status[lod] = 2;
+                message = "mesh_status_missing_lod";
+            }
+            else
+            {
+                for (S32 i = lod - 1; i >= 0; --i)
+                {
+                    if (total_tris[i] > 0)
+                    {
+                        upload_status[lod] = 2;
+                        message = "mesh_status_missing_lod";
+                    }
+                }
+            }
+
+            mFMP->childSetValue(lod_triangles_name[lod], mesh_status_na);
+            mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na);
+        }
+
+        if (lod != lod_high)
+        {
+            if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high])
+            { //number of submeshes is different
+                message = "mesh_status_submesh_mismatch";
+                upload_status[lod] = 2;
+            }
+            else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count)
+            {//number of submodels is different, not all faces are matched correctly.
+                message = "mesh_status_submesh_mismatch";
+                upload_status[lod] = 2;
+                // Note: Submodels in instance were loaded from higher LOD and as result face count
+                // returns same value and total_submeshes[lod] is identical to high_lod one.
+            }
+            else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size())
+            { //number of meshes is different
+                message = "mesh_status_mesh_mismatch";
+                upload_status[lod] = 2;
+            }
+            else if (!verts[lod].empty())
+            {
+                S32 sum_verts_higher_lod = 0;
+                S32 sum_verts_this_lod = 0;
+                for (U32 i = 0; i < verts[lod].size(); ++i)
+                {
+                    sum_verts_higher_lod += ((i < verts[lod + 1].size()) ? verts[lod + 1][i] : 0);
+                    sum_verts_this_lod += verts[lod][i];
+                }
+
+                if ((sum_verts_higher_lod > 0) &&
+                    (sum_verts_this_lod > sum_verts_higher_lod))
+                {
+                    //too many vertices in this lod
+                    message = "mesh_status_too_many_vertices";
+                    upload_status[lod] = 1;
+                }
+            }
+        }
+
+        LLIconCtrl* icon = mFMP->getChild<LLIconCtrl>(lod_icon_name[lod]);
+        LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]);
+        icon->setVisible(true);
+        icon->setImage(img);
+
+        if (upload_status[lod] >= 2)
+        {
+            mModelNoErrors = false;
+        }
+
+        if (lod == mPreviewLOD)
+        {
+            mFMP->childSetValue("lod_status_message_text", mFMP->getString(message));
+            icon = mFMP->getChild<LLIconCtrl>("lod_status_message_icon");
+            icon->setImage(img);
+        }
+
+        updateLodControls(lod);
+    }
+
+
+    //warn if hulls have more than 256 points in them
+    BOOL physExceededVertexLimit = FALSE;
+    for (U32 i = 0; mModelNoErrors && i < mModel[LLModel::LOD_PHYSICS].size(); ++i)
+    {
+        LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i];
+
+        if (mdl)
+        {
+            for (U32 j = 0; j < mdl->mPhysics.mHull.size(); ++j)
+            {
+                if (mdl->mPhysics.mHull[j].size() > 256)
+                {
+                    physExceededVertexLimit = TRUE;
+                    LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL;
+                    break;
+                }
+            }
+        }
+    }
+
+    if (physExceededVertexLimit)
+    {
+        has_physics_error |= PhysicsError::TOOMANYVERTSINHULL;
+    }
+
+    if (!(has_physics_error & PhysicsError::DEGENERATE)){ // only update this field (incluides clearing it) if it is not already in use.
+        mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit);
+        LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
+        physStatusIcon->setVisible(physExceededVertexLimit);
+        if (physExceededVertexLimit)
+        {
+            mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded"));
+            LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning");
+            physStatusIcon->setImage(img);
+        }
+    }
+
+    if (getLoadState() >= LLModelLoader::ERROR_PARSING)
+    {
+        mModelNoErrors = false;
+        LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL;
+    }
+
+    bool uploadingSkin = mFMP->childGetValue("upload_skin").asBoolean();
+    bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
+
+    if (uploadingSkin)
+    {
+        if (uploadingJointPositions && !isRigValidForJointPositionUpload())
+        {
+            mModelNoErrors = false;
+            LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL;
+        }
+    }
+
+    if (mModelNoErrors && mModelLoader)
+    {
+        if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
+        {
+            // Some textures are still loading, prevent upload until they are done
+            mModelNoErrors = false;
+        }
+    }
+
+    if (!mModelNoErrors || mHasDegenerate)
+    {
+        mFMP->childDisable("ok_btn");
+        mFMP->childDisable("calculate_btn");
+    }
+    else
+    {
+        mFMP->childEnable("ok_btn");
+        mFMP->childEnable("calculate_btn");
+    }
+
+    if (mModelNoErrors && mLodsWithParsingError.empty())
+    {
+        mFMP->childEnable("calculate_btn");
+    }
+    else
+    {
+        mFMP->childDisable("calculate_btn");
+    }
+
+    //add up physics triangles etc
+    S32 phys_tris = 0;
+    S32 phys_hulls = 0;
+    S32 phys_points = 0;
+
+    //get the triangle count for the whole scene
+    for (LLModelLoader::scene::iterator iter = mScene[LLModel::LOD_PHYSICS].begin(), endIter = mScene[LLModel::LOD_PHYSICS].end(); iter != endIter; ++iter)
+    {
+        for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance)
+        {
+            LLModel* model = instance->mModel;
+            if (model)
+            {
+                S32 cur_submeshes = model->getNumVolumeFaces();
+
+                LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull;
+
+                if (!decomp.empty())
+                {
+                    phys_hulls += decomp.size();
+                    for (U32 i = 0; i < decomp.size(); ++i)
+                    {
+                        phys_points += decomp[i].size();
+                    }
+                }
+                else
+                { //choose physics shape OR decomposition, can't use both
+                    for (S32 j = 0; j < cur_submeshes; ++j)
+                    { //for each submesh (face), add triangles and vertices to current total
+                        const LLVolumeFace& face = model->getVolumeFace(j);
+                        phys_tris += face.mNumIndices / 3;
+                    }
+                }
+            }
+        }
+    }
+
+    if (phys_tris > 0)
+    {
+        mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", llformat("%d", phys_tris));
+    }
+    else
+    {
+        mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", mesh_status_na);
+    }
+
+    if (phys_hulls > 0)
+    {
+        mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls));
+        mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points));
+    }
+    else
+    {
+        mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na);
+        mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na);
+    }
+
+    LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+    if (fmp)
+    {
+        if (phys_tris > 0 || phys_hulls > 0)
+        {
+            if (!fmp->isViewOptionEnabled("show_physics"))
+            {
+                fmp->enableViewOption("show_physics");
+                mViewOption["show_physics"] = true;
+                fmp->childSetValue("show_physics", true);
+            }
+        }
+        else
+        {
+            fmp->disableViewOption("show_physics");
+            mViewOption["show_physics"] = false;
+            fmp->childSetValue("show_physics", false);
+
+        }
+
+        //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean();
+
+        //fmp->childSetEnabled("physics_optimize", !use_hull);
+
+        bool enable = (phys_tris > 0 || phys_hulls > 0) && fmp->mCurRequest.empty();
+        //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean();
+
+        //enable/disable "analysis" UI
+        LLPanel* panel = fmp->getChild<LLPanel>("physics analysis");
+        LLView* child = panel->getFirstChild();
+        while (child)
+        {
+            child->setEnabled(enable);
+            child = panel->findNextSibling(child);
+        }
+
+        enable = phys_hulls > 0 && fmp->mCurRequest.empty();
+        //enable/disable "simplification" UI
+        panel = fmp->getChild<LLPanel>("physics simplification");
+        child = panel->getFirstChild();
+        while (child)
+        {
+            child->setEnabled(enable);
+            child = panel->findNextSibling(child);
+        }
+
+        if (fmp->mCurRequest.empty())
+        {
+            fmp->childSetVisible("Simplify", true);
+            fmp->childSetVisible("simplify_cancel", false);
+            fmp->childSetVisible("Decompose", true);
+            fmp->childSetVisible("decompose_cancel", false);
+
+            if (phys_hulls > 0)
+            {
+                fmp->childEnable("Simplify");
+            }
+
+            if (phys_tris || phys_hulls > 0)
+            {
+                fmp->childEnable("Decompose");
+            }
+        }
+        else
+        {
+            fmp->childEnable("simplify_cancel");
+            fmp->childEnable("decompose_cancel");
+        }
+    }
+
+
+    LLCtrlSelectionInterface* iface = fmp->childGetSelectionInterface("physics_lod_combo");
+    S32 which_mode = 0;
+    S32 file_mode = 1;
+    if (iface)
+    {
+        which_mode = iface->getFirstSelectedIndex();
+        file_mode = iface->getItemCount() - 1;
+    }
+
+    if (which_mode == file_mode)
+    {
+        mFMP->childEnable("physics_file");
+        mFMP->childEnable("physics_browse");
+    }
+    else
+    {
+        mFMP->childDisable("physics_file");
+        mFMP->childDisable("physics_browse");
+    }
+
+    LLSpinCtrl* crease = mFMP->getChild<LLSpinCtrl>("crease_angle");
+
+    if (mRequestedCreaseAngle[mPreviewLOD] == -1.f)
+    {
+        mFMP->childSetColor("crease_label", LLColor4::grey);
+        crease->forceSetValue(75.f);
+    }
+    else
+    {
+        mFMP->childSetColor("crease_label", LLColor4::white);
+        crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]);
+    }
+
+    mModelUpdatedSignal(true);
+
+}
+
+void LLModelPreview::updateLodControls(S32 lod)
+{
+    if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH)
+    {
+        std::ostringstream out;
+        out << "Invalid level of detail: " << lod;
+        LL_WARNS() << out.str() << LL_ENDL;
+        LLFloaterModelPreview::addStringToLog(out, false);
+        assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH);
+        return;
+    }
+
+    const char* lod_controls[] =
+    {
+        "lod_mode_",
+        "lod_triangle_limit_",
+        "lod_error_threshold_"
+    };
+    const U32 num_lod_controls = sizeof(lod_controls) / sizeof(char*);
+
+    const char* file_controls[] =
+    {
+        "lod_browse_",
+        "lod_file_",
+    };
+    const U32 num_file_controls = sizeof(file_controls) / sizeof(char*);
+
+    LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+    if (!fmp) return;
+
+    LLComboBox* lod_combo = mFMP->findChild<LLComboBox>("lod_source_" + lod_name[lod]);
+    if (!lod_combo) return;
+
+    S32 lod_mode = lod_combo->getCurrentIndex();
+    if (lod_mode == LOD_FROM_FILE) // LoD from file
+    {
+        fmp->mLODMode[lod] = 0;
+        for (U32 i = 0; i < num_file_controls; ++i)
+        {
+            mFMP->childSetVisible(file_controls[i] + lod_name[lod], true);
+        }
+
+        for (U32 i = 0; i < num_lod_controls; ++i)
+        {
+            mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false);
+        }
+    }
+    else if (lod_mode == USE_LOD_ABOVE) // use LoD above
+    {
+        fmp->mLODMode[lod] = 2;
+        for (U32 i = 0; i < num_file_controls; ++i)
+        {
+            mFMP->childSetVisible(file_controls[i] + lod_name[lod], false);
+        }
+
+        for (U32 i = 0; i < num_lod_controls; ++i)
+        {
+            mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false);
+        }
+
+        if (lod < LLModel::LOD_HIGH)
+        {
+            mModel[lod] = mModel[lod + 1];
+            mScene[lod] = mScene[lod + 1];
+            mVertexBuffer[lod].clear();
+
+            // Also update lower LoD
+            if (lod > LLModel::LOD_IMPOSTOR)
+            {
+                updateLodControls(lod - 1);
+            }
+        }
+    }
+    else // auto generate, the default case for all LoDs except High
+    {
+        fmp->mLODMode[lod] = 1;
+
+        //don't actually regenerate lod when refreshing UI
+        mLODFrozen = true;
+
+        for (U32 i = 0; i < num_file_controls; ++i)
+        {
+            mFMP->getChildView(file_controls[i] + lod_name[lod])->setVisible(false);
+        }
+
+        for (U32 i = 0; i < num_lod_controls; ++i)
+        {
+            mFMP->getChildView(lod_controls[i] + lod_name[lod])->setVisible(true);
+        }
+
+
+        LLSpinCtrl* threshold = mFMP->getChild<LLSpinCtrl>("lod_error_threshold_" + lod_name[lod]);
+        LLSpinCtrl* limit = mFMP->getChild<LLSpinCtrl>("lod_triangle_limit_" + lod_name[lod]);
+
+        limit->setMaxValue(mMaxTriangleLimit);
+        limit->forceSetValue(mRequestedTriangleCount[lod]);
+
+        threshold->forceSetValue(mRequestedErrorThreshold[lod]);
+
+        mFMP->getChild<LLComboBox>("lod_mode_" + lod_name[lod])->selectNthItem(mRequestedLoDMode[lod]);
+
+        if (mRequestedLoDMode[lod] == 0)
+        {
+            limit->setVisible(true);
+            threshold->setVisible(false);
+
+            limit->setMaxValue(mMaxTriangleLimit);
+            limit->setIncrement(mMaxTriangleLimit / 32);
+        }
+        else
+        {
+            limit->setVisible(false);
+            threshold->setVisible(true);
+        }
+
+        mLODFrozen = false;
+    }
+}
+
+void LLModelPreview::setPreviewTarget(F32 distance)
+{
+    mCameraDistance = distance;
+    mCameraZoom = 1.f;
+    mCameraPitch = 0.f;
+    mCameraYaw = 0.f;
+    mCameraOffset.clearVec();
+}
+
+void LLModelPreview::clearBuffers()
+{
+    for (U32 i = 0; i < 6; i++)
+    {
+        mVertexBuffer[i].clear();
+    }
+}
+
+void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
+{
+    U32 tri_count = 0;
+    U32 vertex_count = 0;
+    U32 mesh_count = 0;
+
+
+    LLModelLoader::model_list* model = NULL;
+
+    if (lod < 0 || lod > 4)
+    {
+        model = &mBaseModel;
+        lod = 5;
+    }
+    else
+    {
+        model = &(mModel[lod]);
+    }
+
+    if (!mVertexBuffer[lod].empty())
+    {
+        mVertexBuffer[lod].clear();
+    }
+
+    mVertexBuffer[lod].clear();
+
+    LLModelLoader::model_list::iterator base_iter = mBaseModel.begin();
+
+    for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter)
+    {
+        LLModel* mdl = *iter;
+        if (!mdl)
+        {
+            continue;
+        }
+
+        LLModel* base_mdl = *base_iter;
+        base_iter++;
+
+        S32 num_faces = mdl->getNumVolumeFaces();
+        for (S32 i = 0; i < num_faces; ++i)
+        {
+            const LLVolumeFace &vf = mdl->getVolumeFace(i);
+            U32 num_vertices = vf.mNumVertices;
+            U32 num_indices = vf.mNumIndices;
+
+            if (!num_vertices || !num_indices)
+            {
+                continue;
+            }
+
+            LLVertexBuffer* vb = NULL;
+
+            bool skinned = include_skin_weights && !mdl->mSkinWeights.empty();
+
+            U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
+            if (skinned)
+            {
+                mask |= LLVertexBuffer::MAP_WEIGHT4;
+            }
+
+            vb = new LLVertexBuffer(mask, 0);
+
+            if (!vb->allocateBuffer(num_vertices, num_indices, TRUE))
+            {
+                // We are likely to crash due this failure, if this happens, find a way to gracefully stop preview
+                std::ostringstream out;
+                out << "Failed to allocate Vertex Buffer for model preview ";
+                out << num_vertices << " vertices and ";
+                out << num_indices << " indices";
+                LL_WARNS() << out.str() << LL_ENDL;
+                LLFloaterModelPreview::addStringToLog(out, true);
+            }
+
+            LLStrider<LLVector3> vertex_strider;
+            LLStrider<LLVector3> normal_strider;
+            LLStrider<LLVector2> tc_strider;
+            LLStrider<U16> index_strider;
+            LLStrider<LLVector4> weights_strider;
+
+            vb->getVertexStrider(vertex_strider);
+            vb->getIndexStrider(index_strider);
+
+            if (skinned)
+            {
+                vb->getWeight4Strider(weights_strider);
+            }
+
+            LLVector4a::memcpyNonAliased16((F32*)vertex_strider.get(), (F32*)vf.mPositions, num_vertices * 4 * sizeof(F32));
+
+            if (vf.mTexCoords)
+            {
+                vb->getTexCoord0Strider(tc_strider);
+                S32 tex_size = (num_vertices * 2 * sizeof(F32) + 0xF) & ~0xF;
+                LLVector4a::memcpyNonAliased16((F32*)tc_strider.get(), (F32*)vf.mTexCoords, tex_size);
+            }
+
+            if (vf.mNormals)
+            {
+                vb->getNormalStrider(normal_strider);
+                LLVector4a::memcpyNonAliased16((F32*)normal_strider.get(), (F32*)vf.mNormals, num_vertices * 4 * sizeof(F32));
+            }
+
+            if (skinned)
+            {
+                for (U32 i = 0; i < num_vertices; i++)
+                {
+                    //find closest weight to vf.mVertices[i].mPosition
+                    LLVector3 pos(vf.mPositions[i].getF32ptr());
+
+                    const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos);
+                    llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this
+
+                    LLVector4 w(0, 0, 0, 0);
+
+                    for (U32 i = 0; i < weight_list.size(); ++i)
+                    {
+                        F32 wght = llclamp(weight_list[i].mWeight, 0.001f, 0.999f);
+                        F32 joint = (F32)weight_list[i].mJointIdx;
+                        w.mV[i] = joint + wght;
+                        llassert(w.mV[i] - (S32)w.mV[i]>0.0f); // because weights are non-zero, and range of wt values
+                        //should not cause floating point precision issues.
+                    }
+
+                    *(weights_strider++) = w;
+                }
+            }
+
+            // build indices
+            for (U32 i = 0; i < num_indices; i++)
+            {
+                *(index_strider++) = vf.mIndices[i];
+            }
+
+            mVertexBuffer[lod][mdl].push_back(vb);
+
+            vertex_count += num_vertices;
+            tri_count += num_indices / 3;
+            ++mesh_count;
+
+        }
+    }
+}
+
+void LLModelPreview::update()
+{
+    if (mGenLOD)
+    {
+        bool subscribe_for_generation = mLodsQuery.empty();
+        mGenLOD = false;
+        mDirty = true;
+        mLodsQuery.clear();
+
+        for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod)
+        {
+            // adding all lods into query for generation
+            mLodsQuery.push_back(lod);
+        }
+
+        if (subscribe_for_generation)
+        {
+            doOnIdleRepeating(lodQueryCallback);
+        }
+    }
+
+    if (mDirty && mLodsQuery.empty())
+    {
+        mDirty = false;
+        mResourceCost = calcResourceCost();
+        refresh();
+        updateStatusMessages();
+    }
+}
+
+//-----------------------------------------------------------------------------
+// createPreviewAvatar
+//-----------------------------------------------------------------------------
+void LLModelPreview::createPreviewAvatar(void)
+{
+    mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR);
+    if (mPreviewAvatar)
+    {
+        mPreviewAvatar->createDrawable(&gPipeline);
+        mPreviewAvatar->mSpecialRenderMode = 1;
+        mPreviewAvatar->startMotion(ANIM_AGENT_STAND);
+        mPreviewAvatar->hideSkirt();
+    }
+    else
+    {
+        LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL;
+    }
+}
+
+//static
+U32 LLModelPreview::countRootModels(LLModelLoader::model_list models)
+{
+    U32 root_models = 0;
+    model_list::iterator model_iter = models.begin();
+    while (model_iter != models.end())
+    {
+        LLModel* mdl = *model_iter;
+        if (mdl && mdl->mSubmodelID == 0)
+        {
+            root_models++;
+        }
+        model_iter++;
+    }
+    return root_models;
+}
+
+void LLModelPreview::loadedCallback(
+    LLModelLoader::scene& scene,
+    LLModelLoader::model_list& model_list,
+    S32 lod,
+    void* opaque)
+{
+    LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
+    if (pPreview && !LLModelPreview::sIgnoreLoadedCallback)
+    {
+        // Load loader's warnings into floater's log tab
+        const LLSD out = pPreview->mModelLoader->logOut();
+        LLSD::array_const_iterator iter_out = out.beginArray();
+        LLSD::array_const_iterator end_out = out.endArray();
+        for (; iter_out != end_out; ++iter_out)
+        {
+            if (iter_out->has("Message"))
+            {
+                LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod);
+            }
+        }
+        pPreview->mModelLoader->clearLog();
+        pPreview->loadModelCallback(lod); // removes mModelLoader in some cases
+    }
+
+}
+
+void LLModelPreview::stateChangedCallback(U32 state, void* opaque)
+{
+    LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
+    if (pPreview)
+    {
+        pPreview->setLoadState(state);
+    }
+}
+
+LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque)
+{
+    LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
+    if (pPreview)
+    {
+        return pPreview->getPreviewAvatar()->getJoint(str);
+    }
+    return NULL;
+}
+
+U32 LLModelPreview::loadTextures(LLImportMaterial& material, void* opaque)
+{
+    (void)opaque;
+
+    if (material.mDiffuseMapFilename.size())
+    {
+        material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >;
+        LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData));
+
+        tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + material.mDiffuseMapFilename, FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW);
+        tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE);
+        tex->forceToSaveRawImage(0, F32_MAX);
+        material.setDiffuseMap(tex->getID()); // record tex ID
+        return 1;
+    }
+
+    material.mOpaqueData = NULL;
+    return 0;
+}
+
+void LLModelPreview::addEmptyFace(LLModel* pTarget)
+{
+    U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
+    LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0);
+
+    buff->allocateBuffer(1, 3, true);
+    memset((U8*)buff->getMappedData(), 0, buff->getSize());
+    memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize());
+
+    buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0);
+
+    LLStrider<LLVector3> pos;
+    LLStrider<LLVector3> norm;
+    LLStrider<LLVector2> tc;
+    LLStrider<U16> index;
+
+    buff->getVertexStrider(pos);
+
+    if (type_mask & LLVertexBuffer::MAP_NORMAL)
+    {
+        buff->getNormalStrider(norm);
+    }
+    if (type_mask & LLVertexBuffer::MAP_TEXCOORD0)
+    {
+        buff->getTexCoord0Strider(tc);
+    }
+
+    buff->getIndexStrider(index);
+
+    //resize face array
+    int faceCnt = pTarget->getNumVolumeFaces();
+    pTarget->setNumVolumeFaces(faceCnt + 1);
+    pTarget->setVolumeFaceData(faceCnt + 1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices());
+
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+// Todo: we shouldn't be setting all those UI elements on render.
+// Note: Render happens each frame with skinned avatars
+BOOL LLModelPreview::render()
+{
+    assert_main_thread();
+
+    LLMutexLock lock(this);
+    mNeedsUpdate = FALSE;
+
+    bool use_shaders = LLGLSLShader::sNoFixedFunction;
+
+    bool edges = mViewOption["show_edges"];
+    bool joint_overrides = mViewOption["show_joint_overrides"];
+    bool joint_positions = mViewOption["show_joint_positions"];
+    bool skin_weight = mViewOption["show_skin_weight"];
+    bool textures = mViewOption["show_textures"];
+    bool physics = mViewOption["show_physics"];
+
+    // Extra configurability, to be exposed later as controls?
+    static LLCachedControl<LLColor4> canvas_col(gSavedSettings, "MeshPreviewCanvasColor");
+    static LLCachedControl<LLColor4> edge_col(gSavedSettings, "MeshPreviewEdgeColor");
+    static LLCachedControl<LLColor4> base_col(gSavedSettings, "MeshPreviewBaseColor");
+    static LLCachedControl<LLColor3> brightness(gSavedSettings, "MeshPreviewBrightnessColor");
+    static LLCachedControl<F32> edge_width(gSavedSettings, "MeshPreviewEdgeWidth");
+    static LLCachedControl<LLColor4> phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor");
+    static LLCachedControl<LLColor4> phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor");
+    static LLCachedControl<F32> phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth");
+    static LLCachedControl<LLColor4> deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor");
+    static LLCachedControl<LLColor4> deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor");
+    static LLCachedControl<F32> deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth");
+    static LLCachedControl<F32> deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize");
+
+    S32 width = getWidth();
+    S32 height = getHeight();
+
+    LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test
+    LLGLDisable no_blend(GL_BLEND);
+    LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color
+    LLGLDisable fog(GL_FOG);
+
+    {
+        if (use_shaders)
+        {
+            gUIProgram.bind();
+        }
+        //clear background to grey
+        gGL.matrixMode(LLRender::MM_PROJECTION);
+        gGL.pushMatrix();
+        gGL.loadIdentity();
+        gGL.ortho(0.0f, width, 0.0f, height, -1.0f, 1.0f);
+
+        gGL.matrixMode(LLRender::MM_MODELVIEW);
+        gGL.pushMatrix();
+        gGL.loadIdentity();
+
+        gGL.color4fv(static_cast<LLColor4>(canvas_col).mV);
+        gl_rect_2d_simple(width, height);
+
+        gGL.matrixMode(LLRender::MM_PROJECTION);
+        gGL.popMatrix();
+
+        gGL.matrixMode(LLRender::MM_MODELVIEW);
+        gGL.popMatrix();
+        if (use_shaders)
+        {
+            gUIProgram.unbind();
+        }
+    }
+
+    LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+
+    bool has_skin_weights = false;
+    bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean();
+    bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean();
+
+    if (upload_joints != mLastJointUpdate)
+    {
+        mLastJointUpdate = upload_joints;
+    }
+
+    for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+    {
+        for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+        {
+            LLModelInstance& instance = *model_iter;
+            LLModel* model = instance.mModel;
+            model->mPelvisOffset = mPelvisZOffset;
+            if (!model->mSkinWeights.empty())
+            {
+                has_skin_weights = true;
+            }
+        }
+    }
+
+    if (has_skin_weights && lodsReady())
+    { //model has skin weights, enable view options for skin weights and joint positions
+        U32 flags = getLegacyRigFlags();
+        if (fmp)
+        {
+            if (flags == LEGACY_RIG_OK)
+            {
+                fmp->enableViewOption("show_skin_weight");
+                fmp->setViewOptionEnabled("show_joint_overrides", skin_weight);
+                fmp->setViewOptionEnabled("show_joint_positions", skin_weight);
+                mFMP->childEnable("upload_skin");
+                mFMP->childSetValue("show_skin_weight", skin_weight);
+            }
+            else if ((flags & LEGACY_RIG_FLAG_TOO_MANY_JOINTS) > 0)
+            {
+                mFMP->childSetVisible("skin_too_many_joints", true);
+            }
+            else if ((flags & LEGACY_RIG_FLAG_UNKNOWN_JOINT) > 0)
+            {
+                mFMP->childSetVisible("skin_unknown_joint", true);
+            }
+        }
+    }
+    else
+    {
+        mFMP->childDisable("upload_skin");
+        if (fmp)
+        {
+            mViewOption["show_skin_weight"] = false;
+            fmp->disableViewOption("show_skin_weight");
+            fmp->disableViewOption("show_joint_overrides");
+            fmp->disableViewOption("show_joint_positions");
+
+            skin_weight = false;
+            mFMP->childSetValue("show_skin_weight", false);
+            fmp->setViewOptionEnabled("show_skin_weight", skin_weight);
+        }
+    }
+
+    if (upload_skin && !has_skin_weights)
+    { //can't upload skin weights if model has no skin weights
+        mFMP->childSetValue("upload_skin", false);
+        upload_skin = false;
+    }
+
+    if (!upload_skin && upload_joints)
+    { //can't upload joints if not uploading skin weights
+        mFMP->childSetValue("upload_joints", false);
+        upload_joints = false;
+    }
+
+    if (upload_skin && upload_joints)
+    {
+        mFMP->childEnable("lock_scale_if_joint_position");
+        if (fmp)
+        {
+            fmp->updateAvatarTab();
+        }
+    }
+    else
+    {
+        mFMP->childDisable("lock_scale_if_joint_position");
+        mFMP->childSetValue("lock_scale_if_joint_position", false);
+        if (fmp)
+        {
+            fmp->clearAvatarTab();
+        }
+    }
+
+    //Only enable joint offsets if it passed the earlier critiquing
+    if (isRigValidForJointPositionUpload())
+    {
+        mFMP->childSetEnabled("upload_joints", upload_skin);
+    }
+
+    F32 explode = mFMP->childGetValue("physics_explode").asReal();
+
+    LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview
+
+    LLRect preview_rect;
+
+    preview_rect = mFMP->getChildView("preview_panel")->getRect();
+
+    F32 aspect = (F32)preview_rect.getWidth() / preview_rect.getHeight();
+
+    LLViewerCamera::getInstance()->setAspect(aspect);
+
+    LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom);
+
+    LLVector3 offset = mCameraOffset;
+    LLVector3 target_pos = mPreviewTarget + offset;
+
+    F32 z_near = 0.001f;
+    F32 z_far = mCameraDistance*10.0f + mPreviewScale.magVec() + mCameraOffset.magVec();
+
+    if (skin_weight)
+    {
+        target_pos = getPreviewAvatar()->getPositionAgent() + offset;
+        z_near = 0.01f;
+        z_far = 1024.f;
+
+        //render avatar previews every frame
+        refresh();
+    }
+
+    if (use_shaders)
+    {
+        gObjectPreviewProgram.bind();
+    }
+
+    gGL.loadIdentity();
+    gPipeline.enableLightsPreview();
+
+    LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) *
+        LLQuaternion(mCameraYaw, LLVector3::z_axis);
+
+    LLQuaternion av_rot = camera_rot;
+    F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance;
+    LLViewerCamera::getInstance()->setOriginAndLookAt(
+        target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera
+        LLVector3::z_axis,																	// up
+        target_pos);											// point of interest
+
+
+    z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f);
+
+    LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far);
+
+    stop_glerror();
+
+    gGL.pushMatrix();
+    gGL.color4fv(edge_col().mV);
+
+    const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
+    LLGLEnable normalize(GL_NORMALIZE);
+
+    if (!mBaseModel.empty() && mVertexBuffer[5].empty())
+    {
+        genBuffers(-1, skin_weight);
+        //genBuffers(3);
+        //genLODs();
+    }
+
+    if (!mModel[mPreviewLOD].empty())
+    {
+        mFMP->childEnable("reset_btn");
+
+        bool regen = mVertexBuffer[mPreviewLOD].empty();
+        if (!regen)
+        {
+            const std::vector<LLPointer<LLVertexBuffer> >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second;
+            if (!vb_vec.empty())
+            {
+                const LLVertexBuffer* buff = vb_vec[0];
+                regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight;
+            }
+            else
+            {
+                LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL;
+                regen = TRUE;
+            }
+        }
+
+        if (regen)
+        {
+            genBuffers(mPreviewLOD, skin_weight);
+        }
+
+        if (!skin_weight)
+        {
+            for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+            {
+                LLModelInstance& instance = *iter;
+
+                LLModel* model = instance.mLOD[mPreviewLOD];
+
+                if (!model)
+                {
+                    continue;
+                }
+
+                gGL.pushMatrix();
+                LLMatrix4 mat = instance.mTransform;
+
+                gGL.multMatrix((GLfloat*)mat.mMatrix);
+
+
+                U32 num_models = mVertexBuffer[mPreviewLOD][model].size();
+                for (U32 i = 0; i < num_models; ++i)
+                {
+                    LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
+
+                    buffer->setBuffer(type_mask & buffer->getTypeMask());
+
+                    if (textures)
+                    {
+                        int materialCnt = instance.mModel->mMaterialList.size();
+                        if (i < materialCnt)
+                        {
+                            const std::string& binding = instance.mModel->mMaterialList[i];
+                            const LLImportMaterial& material = instance.mMaterial[binding];
+
+                            gGL.diffuseColor4fv(material.mDiffuseColor.mV);
+
+                            // Find the tex for this material, bind it, and add it to our set
+                            //
+                            LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material);
+                            if (tex)
+                            {
+                                mTextureSet.insert(tex);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        gGL.diffuseColor4fv(static_cast<LLColor4>(base_col).mV);
+                    }
+
+                    buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
+                    gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+                    gGL.diffuseColor4fv(static_cast<LLColor4>(edge_col).mV);
+                    if (edges)
+                    {
+                        glLineWidth(edge_width);
+                        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+                        buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
+                        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                        glLineWidth(1.f);
+                    }
+                }
+                gGL.popMatrix();
+            }
+
+            if (physics)
+            {
+                glClear(GL_DEPTH_BUFFER_BIT);
+
+                for (U32 pass = 0; pass < 2; pass++)
+                {
+                    if (pass == 0)
+                    { //depth only pass
+                        gGL.setColorMask(false, false);
+                    }
+                    else
+                    {
+                        gGL.setColorMask(true, true);
+                    }
+
+                    //enable alpha blending on second pass but not first pass
+                    LLGLState blend(GL_BLEND, pass);
+
+                    gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
+
+                    for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+                    {
+                        LLModelInstance& instance = *iter;
+
+                        LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
+
+                        if (!model)
+                        {
+                            continue;
+                        }
+
+                        gGL.pushMatrix();
+                        LLMatrix4 mat = instance.mTransform;
+
+                        gGL.multMatrix((GLfloat*)mat.mMatrix);
+
+
+                        bool render_mesh = true;
+                        LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
+                        if (decomp)
+                        {
+                            LLMutexLock(decomp->mMutex);
+
+                            LLModel::Decomposition& physics = model->mPhysics;
+
+                            if (!physics.mHull.empty())
+                            {
+                                render_mesh = false;
+
+                                if (physics.mMesh.empty())
+                                { //build vertex buffer for physics mesh
+                                    gMeshRepo.buildPhysicsMesh(physics);
+                                }
+
+                                if (!physics.mMesh.empty())
+                                { //render hull instead of mesh
+                                    for (U32 i = 0; i < physics.mMesh.size(); ++i)
+                                    {
+                                        if (explode > 0.f)
+                                        {
+                                            gGL.pushMatrix();
+
+                                            LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters;
+                                            offset *= explode;
+
+                                            gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
+                                        }
+
+                                        static std::vector<LLColor4U> hull_colors;
+
+                                        if (i + 1 >= hull_colors.size())
+                                        {
+                                            hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128));
+                                        }
+
+                                        gGL.diffuseColor4ubv(hull_colors[i].mV);
+                                        LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals);
+
+                                        if (explode > 0.f)
+                                        {
+                                            gGL.popMatrix();
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        if (render_mesh)
+                        {
+                            if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
+                            {
+                                genBuffers(LLModel::LOD_PHYSICS, false);
+                            }
+
+                            U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
+                            if (pass > 0){
+                                for (U32 i = 0; i < num_models; ++i)
+                                {
+                                    LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
+
+                                    gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+                                    gGL.diffuseColor4fv(phys_fill_col().mV);
+
+                                    buffer->setBuffer(type_mask & buffer->getTypeMask());
+                                    buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
+
+                                    gGL.diffuseColor4fv(phys_edge_col().mV);
+                                    glLineWidth(phys_edge_width);
+                                    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+                                    buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
+
+                                    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                                    glLineWidth(1.f);
+                                }
+                            }
+                        }
+                        gGL.popMatrix();
+                    }
+
+                    // only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks]
+                    if (pass > 0 && mHasDegenerate)
+                    {
+                        glLineWidth(deg_edge_width);
+                        glPointSize(deg_point_size);
+                        gPipeline.enableLightsFullbright();
+                        //show degenerate triangles
+                        LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS);
+                        LLGLDisable cull(GL_CULL_FACE);
+                        gGL.diffuseColor4f(1.f, 0.f, 0.f, 1.f);
+                        const LLVector4a scale(0.5f);
+
+                        for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+                        {
+                            LLModelInstance& instance = *iter;
+
+                            LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
+
+                            if (!model)
+                            {
+                                continue;
+                            }
+
+                            gGL.pushMatrix();
+                            LLMatrix4 mat = instance.mTransform;
+
+                            gGL.multMatrix((GLfloat*)mat.mMatrix);
+
+
+                            LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
+                            if (decomp)
+                            {
+                                LLMutexLock(decomp->mMutex);
+
+                                LLModel::Decomposition& physics = model->mPhysics;
+
+                                if (physics.mHull.empty())
+                                {
+                                    if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
+                                    {
+                                        genBuffers(LLModel::LOD_PHYSICS, false);
+                                    }
+
+                                    U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
+                                    for (U32 v = 0; v < num_models; ++v)
+                                    {
+                                        LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v];
+                                        if (buffer->getNumVerts() < 3)continue;
+
+                                        buffer->setBuffer(type_mask & buffer->getTypeMask());
+
+                                        LLStrider<LLVector3> pos_strider;
+                                        buffer->getVertexStrider(pos_strider, 0);
+                                        LLVector4a* pos = (LLVector4a*)pos_strider.get();
+
+                                        LLStrider<U16> idx;
+                                        buffer->getIndexStrider(idx, 0);
+
+                                        LLVector4a v1, v2, v3;
+                                        for (U32 indices_offset = 0; indices_offset < buffer->getNumIndices(); indices_offset += 3)
+                                        {
+                                            v1.setMul(pos[*idx++], scale);
+                                            v2.setMul(pos[*idx++], scale);
+                                            v3.setMul(pos[*idx++], scale);
+
+                                            if (ll_is_degenerate(v1, v2, v3))
+                                            {
+                                                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+                                                gGL.diffuseColor3fv(deg_edge_col().mV);
+                                                buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
+                                                buffer->drawRange(LLRender::POINTS, 0, 2, 3, indices_offset);
+                                                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                                                gGL.diffuseColor3fv(deg_fill_col().mV);
+                                                buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+
+                            gGL.popMatrix();
+                        }
+                        glLineWidth(1.f);
+                        glPointSize(1.f);
+                        gPipeline.enableLightsPreview();
+                        gGL.setSceneBlendType(LLRender::BT_ALPHA);
+                    }
+                }
+            }
+        }
+        else
+        {
+            target_pos = getPreviewAvatar()->getPositionAgent();
+            getPreviewAvatar()->clearAttachmentOverrides(); // removes pelvis fixup
+            LLUUID fake_mesh_id;
+            fake_mesh_id.generate();
+            getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id);
+            bool pelvis_recalc = false;
+
+            LLViewerCamera::getInstance()->setOriginAndLookAt(
+                target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera
+                LLVector3::z_axis,																	// up
+                target_pos);											// point of interest
+
+            for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+            {
+                for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+                {
+                    LLModelInstance& instance = *model_iter;
+                    LLModel* model = instance.mModel;
+
+                    if (!model->mSkinWeights.empty())
+                    {
+                        const LLMeshSkinInfo *skin = &model->mSkinInfo;
+                        LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary
+                        U32 count = LLSkinningUtil::getMeshJointCount(skin);
+
+                        if (joint_overrides && skin->mAlternateBindMatrix.size() > 0)
+                        {
+                            // mesh_id is used to determine which mesh gets to
+                            // set the joint offset, in the event of a conflict. Since
+                            // we don't know the mesh id yet, we can't guarantee that
+                            // joint offsets will be applied with the same priority as
+                            // in the uploaded model. If the file contains multiple
+                            // meshes with conflicting joint offsets, preview may be
+                            // incorrect.
+                            LLUUID fake_mesh_id;
+                            fake_mesh_id.generate();
+                            for (U32 j = 0; j < count; ++j)
+                            {
+                                LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]);
+                                if (joint)
+                                {
+                                    const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
+                                    if (joint->aboveJointPosThreshold(jointPos))
+                                    {
+                                        bool override_changed;
+                                        joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed);
+
+                                        if (override_changed)
+                                        {
+                                            //If joint is a pelvis then handle old/new pelvis to foot values
+                                            if (joint->getName() == "mPelvis")// or skin->mJointNames[j]
+                                            {
+                                                pelvis_recalc = true;
+                                            }
+                                        }
+                                        if (skin->mLockScaleIfJointPosition)
+                                        {
+                                            // Note that unlike positions, there's no threshold check here,
+                                            // just a lock at the default value.
+                                            joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model");
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i)
+                        {
+                            LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
+
+                            const LLVolumeFace& face = model->getVolumeFace(i);
+
+                            LLStrider<LLVector3> position;
+                            buffer->getVertexStrider(position);
+
+                            LLStrider<LLVector4> weight;
+                            buffer->getWeight4Strider(weight);
+
+                            //quick 'n dirty software vertex skinning
+
+                            //build matrix palette
+
+                            LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
+                            LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count,
+                                skin, getPreviewAvatar());
+
+                            LLMatrix4a bind_shape_matrix;
+                            bind_shape_matrix.loadu(skin->mBindShapeMatrix);
+                            U32 max_joints = LLSkinningUtil::getMaxJointCount();
+                            for (U32 j = 0; j < buffer->getNumVerts(); ++j)
+                            {
+                                LLMatrix4a final_mat;
+                                F32 *wptr = weight[j].mV;
+                                LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints);
+
+                                //VECTORIZE THIS
+                                LLVector4a& v = face.mPositions[j];
+
+                                LLVector4a t;
+                                LLVector4a dst;
+                                bind_shape_matrix.affineTransform(v, t);
+                                final_mat.affineTransform(t, dst);
+
+                                position[j][0] = dst[0];
+                                position[j][1] = dst[1];
+                                position[j][2] = dst[2];
+                            }
+
+                            llassert(model->mMaterialList.size() > i);
+                            const std::string& binding = instance.mModel->mMaterialList[i];
+                            const LLImportMaterial& material = instance.mMaterial[binding];
+
+                            buffer->setBuffer(type_mask & buffer->getTypeMask());
+                            gGL.diffuseColor4fv(material.mDiffuseColor.mV);
+                            gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+                            // Find the tex for this material, bind it, and add it to our set
+                            //
+                            LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material);
+                            if (tex)
+                            {
+                                mTextureSet.insert(tex);
+                            }
+
+                            buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
+
+                            if (edges)
+                            {
+                                gGL.diffuseColor4fv(edge_col().mV);
+                                glLineWidth(edge_width);
+                                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+                                buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
+                                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                                glLineWidth(1.f);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (joint_positions)
+            {
+                LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+                if (shader)
+                {
+                    gDebugProgram.bind();
+                }
+                getPreviewAvatar()->renderCollisionVolumes();
+                if (fmp->mTabContainer->getCurrentPanelIndex() == fmp->mAvatarTabIndex)
+                {
+                    getPreviewAvatar()->renderBones(fmp->mSelectedJointName);
+                }
+                else
+                {
+                    getPreviewAvatar()->renderBones();
+                }
+                if (shader)
+                {
+                    shader->bind();
+                }
+            }
+
+            if (pelvis_recalc)
+            {
+                // size/scale recalculation
+                getPreviewAvatar()->postPelvisSetRecalc();
+            }
+        }
+    }
+
+    if (use_shaders)
+    {
+        gObjectPreviewProgram.unbind();
+    }
+
+    gGL.popMatrix();
+
+    return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// refresh()
+//-----------------------------------------------------------------------------
+void LLModelPreview::refresh()
+{
+    mNeedsUpdate = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians)
+{
+    mCameraYaw = mCameraYaw + yaw_radians;
+
+    mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f);
+}
+
+//-----------------------------------------------------------------------------
+// zoom()
+//-----------------------------------------------------------------------------
+void LLModelPreview::zoom(F32 zoom_amt)
+{
+    F32 new_zoom = mCameraZoom + zoom_amt;
+    // TODO: stop clamping in render
+    static LLCachedControl<F32> zoom_limit(gSavedSettings, "MeshPreviewZoomLimit");
+    mCameraZoom = llclamp(new_zoom, 1.f, zoom_limit());
+}
+
+void LLModelPreview::pan(F32 right, F32 up)
+{
+    bool skin_weight = mViewOption["show_skin_weight"];
+    F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance;
+    mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f);
+    mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f);
+}
+
+void LLModelPreview::setPreviewLOD(S32 lod)
+{
+    lod = llclamp(lod, 0, (S32)LLModel::LOD_HIGH);
+
+    if (lod != mPreviewLOD)
+    {
+        mPreviewLOD = lod;
+
+        LLComboBox* combo_box = mFMP->getChild<LLComboBox>("preview_lod_combo");
+        combo_box->setCurrentByIndex((NUM_LOD - 1) - mPreviewLOD); // combo box list of lods is in reverse order
+        mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]);
+
+        LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor");
+        LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor");
+
+        for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i)
+        {
+            const LLColor4& color = (i == lod) ? highlight_color : normal_color;
+
+            mFMP->childSetColor(lod_status_name[i], color);
+            mFMP->childSetColor(lod_label_name[i], color);
+            mFMP->childSetColor(lod_triangles_name[i], color);
+            mFMP->childSetColor(lod_vertices_name[i], color);
+        }
+
+        LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP;
+        if (fmp)
+        {
+            // make preview repopulate tab
+            fmp->clearAvatarTab();
+        }
+    }
+    refresh();
+    updateStatusMessages();
+}
+
+//static
+void LLModelPreview::textureLoadedCallback(
+    BOOL success,
+    LLViewerFetchedTexture *src_vi,
+    LLImageRaw* src,
+    LLImageRaw* src_aux,
+    S32 discard_level,
+    BOOL final,
+    void* userdata)
+{
+    LLModelPreview* preview = (LLModelPreview*)userdata;
+    preview->refresh();
+
+    if (final && preview->mModelLoader)
+    {
+        if (preview->mModelLoader->mNumOfFetchingTextures > 0)
+        {
+            preview->mModelLoader->mNumOfFetchingTextures--;
+        }
+    }
+}
+
+// static
+bool LLModelPreview::lodQueryCallback()
+{
+    // not the best solution, but model preview belongs to floater
+    // so it is an easy way to check that preview still exists.
+    LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+    if (fmp && fmp->mModelPreview)
+    {
+        LLModelPreview* preview = fmp->mModelPreview;
+        if (preview->mLodsQuery.size() > 0)
+        {
+            S32 lod = preview->mLodsQuery.back();
+            preview->mLodsQuery.pop_back();
+            preview->genLODs(lod);
+
+            // return false to continue cycle
+            return false;
+        }
+    }
+    // nothing to process
+    return true;
+}
+
+void LLModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
+{
+    if (!mLODFrozen)
+    {
+        genLODs(lod, 3, enforce_tri_limit);
+        refresh();
+    }
+}
+
diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h
new file mode 100644
index 0000000000..7a7b8615f9
--- /dev/null
+++ b/indra/newview/llmodelpreview.h
@@ -0,0 +1,308 @@
+/**
+ * @file llmodelpreview.h
+ * @brief LLModelPreview class definition, class
+ * responsible for model preview inside LLFloaterModelPreview
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2020, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMODELPREVIEW_H
+#define LL_LLMODELPREVIEW_H
+
+#include "lldynamictexture.h"
+#include "llfloatermodelpreview.h"
+#include "llmeshrepository.h"
+#include "llmodel.h"
+
+class LLJoint;
+class LLVOAvatar;
+class LLTextBox;
+class LLVertexBuffer;
+class DAE;
+class daeElement;
+class domProfile_COMMON;
+class domInstance_geometry;
+class domNode;
+class domTranslate;
+class domController;
+class domSkin;
+class domMesh;
+
+// const strings needed by classes that use model preivew
+static const std::string lod_name[NUM_LOD + 1] =
+{
+    "lowest",
+    "low",
+    "medium",
+    "high",
+    "I went off the end of the lod_name array.  Me so smart."
+};
+
+static const std::string lod_triangles_name[NUM_LOD + 1] =
+{
+    "lowest_triangles",
+    "low_triangles",
+    "medium_triangles",
+    "high_triangles",
+    "I went off the end of the lod_triangles_name array.  Me so smart."
+};
+
+static const std::string lod_vertices_name[NUM_LOD + 1] =
+{
+    "lowest_vertices",
+    "low_vertices",
+    "medium_vertices",
+    "high_vertices",
+    "I went off the end of the lod_vertices_name array.  Me so smart."
+};
+
+static const std::string lod_status_name[NUM_LOD + 1] =
+{
+    "lowest_status",
+    "low_status",
+    "medium_status",
+    "high_status",
+    "I went off the end of the lod_status_name array.  Me so smart."
+};
+
+static const std::string lod_icon_name[NUM_LOD + 1] =
+{
+    "status_icon_lowest",
+    "status_icon_low",
+    "status_icon_medium",
+    "status_icon_high",
+    "I went off the end of the lod_status_name array.  Me so smart."
+};
+
+static const std::string lod_status_image[NUM_LOD + 1] =
+{
+    "ModelImport_Status_Good",
+    "ModelImport_Status_Warning",
+    "ModelImport_Status_Error",
+    "I went off the end of the lod_status_image array.  Me so smart."
+};
+
+static const std::string lod_label_name[NUM_LOD + 1] =
+{
+    "lowest_label",
+    "low_label",
+    "medium_label",
+    "high_label",
+    "I went off the end of the lod_label_name array.  Me so smart."
+};
+
+class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
+{
+    LOG_CLASS(LLModelPreview);
+
+    typedef boost::signals2::signal<void(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)> details_signal_t;
+    typedef boost::signals2::signal<void(void)> model_loaded_signal_t;
+    typedef boost::signals2::signal<void(bool)> model_updated_signal_t;
+
+public:
+
+    typedef enum
+    {
+        LOD_FROM_FILE = 0,
+        GENERATE,
+        USE_LOD_ABOVE,
+    } eLoDMode;
+
+public:
+    // Todo: model preview shouldn't need floater dependency, it
+    // should just expose data to floater, not control flaoter like it does
+    LLModelPreview(S32 width, S32 height, LLFloater* fmp);
+    virtual ~LLModelPreview();
+
+    void resetPreviewTarget();
+    void setPreviewTarget(F32 distance);
+    void setTexture(U32 name) { mTextureName = name; }
+
+    void setPhysicsFromLOD(S32 lod);
+    BOOL render();
+    void update();
+    void genBuffers(S32 lod, bool skinned);
+    void clearBuffers();
+    void refresh();
+    void rotate(F32 yaw_radians, F32 pitch_radians);
+    void zoom(F32 zoom_amt);
+    void pan(F32 right, F32 up);
+    virtual BOOL needsRender() { return mNeedsUpdate; }
+    void setPreviewLOD(S32 lod);
+    void clearModel(S32 lod);
+    void getJointAliases(JointMap& joint_map);
+    void loadModel(std::string filename, S32 lod, bool force_disable_slm = false);
+    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 generateNormals();
+    void restoreNormals();
+    U32 calcResourceCost();
+    void rebuildUploadData();
+    void saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position);
+    void saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position);
+    void clearIncompatible(S32 lod);
+    void updateStatusMessages();
+    void updateLodControls(S32 lod);
+    void clearGLODGroup();
+    void onLODParamCommit(S32 lod, bool enforce_tri_limit);
+    void addEmptyFace(LLModel* pTarget);
+
+    const bool getModelPivot(void) const { return mHasPivot; }
+    void setHasPivot(bool val) { mHasPivot = val; }
+    void setModelPivot(const LLVector3& pivot) { mModelPivot = pivot; }
+
+    //Is a rig valid so that it can be used as a criteria for allowing for uploading of joint positions
+    //Accessors for joint position upload friendly rigs
+    const bool isRigValidForJointPositionUpload(void) const { return mRigValidJointUpload; }
+    void setRigValidForJointPositionUpload(bool rigValid) { mRigValidJointUpload = rigValid; }
+
+    //Accessors for the legacy rigs
+    const bool isLegacyRigValid(void) const { return mLegacyRigFlags == 0; }
+    U32 getLegacyRigFlags() const { return mLegacyRigFlags; }
+    void setLegacyRigFlags(U32 rigFlags) { mLegacyRigFlags = rigFlags; }
+
+    static void	textureLoadedCallback(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata);
+    static bool lodQueryCallback();
+
+    boost::signals2::connection setDetailsCallback(const details_signal_t::slot_type& cb){ return mDetailsSignal.connect(cb); }
+    boost::signals2::connection setModelLoadedCallback(const model_loaded_signal_t::slot_type& cb){ return mModelLoadedSignal.connect(cb); }
+    boost::signals2::connection setModelUpdatedCallback(const model_updated_signal_t::slot_type& cb){ return mModelUpdatedSignal.connect(cb); }
+
+    void setLoadState(U32 state) { mLoadState = state; }
+    U32 getLoadState() { return mLoadState; }
+
+    static bool 		sIgnoreLoadedCallback;
+    std::vector<S32> mLodsQuery;
+    std::vector<S32> mLodsWithParsingError;
+    bool mHasDegenerate;
+
+protected:
+
+    static void			loadedCallback(LLModelLoader::scene& scene, LLModelLoader::model_list& model_list, S32 lod, void* opaque);
+    static void			stateChangedCallback(U32 state, void* opaque);
+
+    static LLJoint*	lookupJointByName(const std::string&, void* opaque);
+    static U32			loadTextures(LLImportMaterial& material, void* opaque);
+
+private:
+    //Utility function for controller vertex compare
+    bool verifyCount(int expected, int result);
+    //Creates the dummy avatar for the preview window
+    void		createPreviewAvatar(void);
+    //Accessor for the dummy avatar
+    LLVOAvatar* getPreviewAvatar(void) { return mPreviewAvatar; }
+    // Count amount of original models, excluding sub-models
+    static U32 countRootModels(LLModelLoader::model_list models);
+
+protected:
+    friend class LLModelLoader;
+    friend class LLFloaterModelPreview;
+    friend class LLFloaterModelPreview::DecompRequest;
+    friend class LLPhysicsDecomp;
+
+    LLFloater*  mFMP;
+
+    BOOL        mNeedsUpdate;
+    bool		mDirty;
+    bool		mGenLOD;
+    U32         mTextureName;
+    F32			mCameraDistance;
+    F32			mCameraYaw;
+    F32			mCameraPitch;
+    F32			mCameraZoom;
+    LLVector3	mCameraOffset;
+    LLVector3	mPreviewTarget;
+    LLVector3	mPreviewScale;
+    S32			mPreviewLOD;
+    S32			mPhysicsSearchLOD;
+    U32			mResourceCost;
+    std::string mLODFile[LLModel::NUM_LODS];
+    bool		mLoading;
+    U32			mLoadState;
+    bool		mResetJoints;
+    bool		mModelNoErrors;
+
+    std::map<std::string, bool> mViewOption;
+
+    //GLOD object parameters (must rebuild object if these change)
+    bool mLODFrozen;
+    F32 mBuildShareTolerance;
+    U32 mBuildQueueMode;
+    U32 mBuildOperator;
+    U32 mBuildBorderMode;
+    U32 mRequestedLoDMode[LLModel::NUM_LODS];
+    S32 mRequestedTriangleCount[LLModel::NUM_LODS];
+    F32 mRequestedErrorThreshold[LLModel::NUM_LODS];
+    U32 mRequestedBuildOperator[LLModel::NUM_LODS];
+    U32 mRequestedQueueMode[LLModel::NUM_LODS];
+    U32 mRequestedBorderMode[LLModel::NUM_LODS];
+    F32 mRequestedShareTolerance[LLModel::NUM_LODS];
+    F32 mRequestedCreaseAngle[LLModel::NUM_LODS];
+
+    LLModelLoader* mModelLoader;
+
+    LLModelLoader::scene mScene[LLModel::NUM_LODS];
+    LLModelLoader::scene mBaseScene;
+
+    LLModelLoader::model_list mModel[LLModel::NUM_LODS];
+    LLModelLoader::model_list mBaseModel;
+
+    typedef std::vector<LLVolumeFace>		v_LLVolumeFace_t;
+    typedef std::vector<v_LLVolumeFace_t>	vv_LLVolumeFace_t;
+
+    vv_LLVolumeFace_t mModelFacesCopy[LLModel::NUM_LODS];
+    vv_LLVolumeFace_t mBaseModelFacesCopy;
+
+    U32 mGroup;
+    std::map<LLPointer<LLModel>, U32> mObject;
+    U32 mMaxTriangleLimit;
+
+    LLMeshUploadThread::instance_list mUploadData;
+    std::set<LLViewerFetchedTexture * > mTextureSet;
+
+    //map of vertex buffers to models (one vertex buffer in vector per face in model
+    std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mVertexBuffer[LLModel::NUM_LODS + 1];
+
+    details_signal_t mDetailsSignal;
+    model_loaded_signal_t mModelLoadedSignal;
+    model_updated_signal_t mModelUpdatedSignal;
+
+    LLVector3	mModelPivot;
+    bool		mHasPivot;
+
+    float		mPelvisZOffset;
+
+    bool		mRigValidJointUpload;
+    U32			mLegacyRigFlags;
+
+    bool		mLastJointUpdate;
+
+    JointNameSet		mJointsFromNode;
+    JointTransformMap	mJointTransformMap;
+
+    LLPointer<LLVOAvatar>	mPreviewAvatar;
+    LLCachedControl<bool>	mImporterDebug;
+};
+
+#endif  // LL_LLMODELPREVIEW_H
-- 
cgit v1.2.3


From 72ba7f1dad43306a761b35dc984734dae04c1c6d Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 22 Apr 2020 20:55:57 +0300
Subject: SL-13077 remove floater specific variables from settings.xml

---
 indra/newview/app_settings/settings.xml | 193 --------------------------------
 indra/newview/llfloatermodelpreview.cpp |  20 ++--
 indra/newview/llmodelpreview.cpp        |  70 ++++++------
 indra/newview/llmodelpreview.h          |   1 +
 4 files changed, 41 insertions(+), 243 deletions(-)

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index c1a2c0ff7a..b2d1d2e589 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -6661,188 +6661,6 @@
     <key>Value</key>
     <integer>600</integer>
   </map>
-  <key>MeshPreviewCanvasColor</key>
-  <map>
-    <key>Comment</key>
-    <string>Canvas colour for the Mesh uploader</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Color4</string>
-    <key>Value</key>
-    <array>
-      <real>0.169</real>
-      <real>0.169</real>
-      <real>0.169</real>
-      <real>1.0</real>
-    </array>
-  </map>
-  <key>MeshPreviewEdgeColor</key>
-  <map>
-    <key>Comment</key>
-    <string>Edge colour for the Mesh uploader preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Color4</string>
-    <key>Value</key>
-    <array>
-      <real>0.4</real>
-      <real>0.4</real>
-      <real>0.4</real>
-      <real>1.0</real>
-    </array>
-  </map>
-  <key>MeshPreviewBaseColor</key>
-  <map>
-    <key>Comment</key>
-    <string>base diffuse colour for the Mesh uploader</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Color4</string>
-    <key>Value</key>
-    <array>
-      <real>1.0</real>
-      <real>1.0</real>
-      <real>1.0</real>
-      <real>1.0</real>
-    </array>
-  </map>
-  <key>MeshPreviewBrightnessColor</key>
-  <map>
-    <key>Comment</key>
-    <string>Brightness modifier</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Color3</string>
-    <key>Value</key>
-    <array>
-      <real>0.9</real>
-      <real>0.9</real>
-      <real>0.9</real>
-    </array>
-  </map>
-  <key>MeshPreviewEdgeWidth</key>
-  <map>
-    <key>Comment</key>
-    <string>line thickness used when display edges is selected in mesh preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>F32</string>
-    <key>Value</key>
-      <real>1.0</real>
-  </map>
-  <key>MeshPreviewPhysicsEdgeColor</key>
-  <map>
-    <key>Comment</key>
-    <string>Edge colour for the Mesh uploader physics preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Color4</string>
-    <key>Value</key>
-    <array>
-      <real>0.0</real>
-      <real>0.25</real>
-      <real>0.5</real>
-      <real>0.25</real>
-    </array>
-  </map>
-  <key>MeshPreviewPhysicsFillColor</key>
-  <map>
-    <key>Comment</key>
-    <string>Fill colour for the Mesh uploader physics preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Color4</string>
-    <key>Value</key>
-    <array>
-      <real>0.0</real>
-      <real>0.5</real>
-      <real>1.0</real>
-      <real>0.5</real>
-    </array>
-  </map>
-  <key>MeshPreviewPhysicsEdgeWidth</key>
-  <map>
-    <key>Comment</key>
-    <string>line thickness used when display physics is selected in mesh preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>F32</string>
-    <key>Value</key>
-    <real>1.0</real>
-  </map>
-  <key>MeshPreviewDegenerateEdgeColor</key>
-  <map>
-    <key>Comment</key>
-    <string>Edge colour for the Mesh uploader Degenerate preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Color4</string>
-    <key>Value</key>
-    <array>
-      <real>1.0</real>
-      <real>0.0</real>
-      <real>0.0</real>
-      <real>1.0</real>
-    </array>
-  </map>
-  <key>MeshPreviewDegenerateFillColor</key>
-  <map>
-    <key>Comment</key>
-    <string>Fill colour for the Mesh uploader Degenerate preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Color4</string>
-    <key>Value</key>
-    <array>
-      <real>1.0</real>
-      <real>0.0</real>
-      <real>0.0</real>
-      <real>0.5</real>
-    </array>
-  </map>
-  <key>MeshPreviewDegenerateEdgeWidth</key>
-  <map>
-    <key>Comment</key>
-    <string>line thickness used when display Degenerate is selected in mesh preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>F32</string>
-    <key>Value</key>
-    <real>3.0</real>
-  </map>
-  <key>MeshPreviewDegeneratePointSize</key>
-  <map>
-    <key>Comment</key>
-    <string>Large point size used to highlight degenerate triangle vertices in Mesh preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>F32</string>
-    <key>Value</key>
-    <real>8.0</real>
-  </map>
-  <key>MeshPreviewZoomLimit</key>
-  <map>
-    <key>Comment</key>
-    <string>Maximum Zoom level in preview</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>F32</string>
-    <key>Value</key>
-    <real>10.0</real>
-  </map>
   <key>MigrateCacheDirectory</key>
   <map>
       <key>Comment</key>
@@ -8094,17 +7912,6 @@
       <key>Value</key>
 	  <integer>13</integer>
     </map>
-  <key>PreviewRenderSize</key>  
-  <map>
-    <key>Comment</key>
-    <string>Resolution of the image rendered for the mesh upload preview (must be a power of 2)</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>S32</string>
-    <key>Value</key>
-    <integer>1024</integer>
-  </map>
   <key>PreviewAmbientColor</key>
   <map>
     <key>Comment</key>
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 9c62680dde..5df3139d0d 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -41,8 +41,6 @@
 #include "llbutton.h"
 #include "llcombobox.h"
 #include "llfocusmgr.h"
-#include "llmatrix4a.h"
-#include "llmenubutton.h"
 #include "llmeshrepository.h"
 #include "llnotificationsutil.h"
 #include "llsdutil_math.h"
@@ -50,23 +48,17 @@
 #include "lltextbox.h"
 #include "lltoolmgr.h"
 #include "llui.h"
-#include "llvector4a.h"
 #include "llviewerwindow.h"
 #include "pipeline.h"
 #include "llviewercontrol.h"
-#include "llviewermenufile.h"
+#include "llviewermenufile.h" //LLFilePickerThread
 #include "llstring.h"
 #include "llbutton.h"
 #include "llcheckboxctrl.h"
-#include "llradiogroup.h"
-#include "llsdserialize.h"
 #include "llsliderctrl.h"
 #include "llspinctrl.h"
 #include "lltabcontainer.h"
-#include "lltoggleablemenu.h"
 #include "lltrans.h"
-#include "llvfile.h"
-#include "llvfs.h"
 #include "llcallbacklist.h"
 #include "llviewertexteditor.h"
 #include "llviewernetwork.h"
@@ -88,6 +80,8 @@ const double RETAIN_COEFFICIENT = 100;
 // should be represented by Smooth combobox with only 10 values.
 // So this const is used as a size of Smooth combobox list.
 const S32 SMOOTH_VALUES_NUMBER = 10;
+const S32 PREVIEW_RENDER_SIZE = 1024;
+const F32 PREVIEW_CAMERA_DISTANCE = 16.f;
 
 class LLMeshFilePicker : public LLFilePickerThread
 {
@@ -314,9 +308,9 @@ void LLFloaterModelPreview::initModelPreview()
 	S32 tex_width = 512;
 	S32 tex_height = 512;
 
-	S32 max_width = llmin(gSavedSettings.getS32("PreviewRenderSize"), (S32)gPipeline.mScreenWidth);
-	S32 max_height = llmin(gSavedSettings.getS32("PreviewRenderSize"), (S32)gPipeline.mScreenHeight);
-	
+	S32 max_width = llmin(PREVIEW_RENDER_SIZE, (S32)gPipeline.mScreenWidth);
+	S32 max_height = llmin(PREVIEW_RENDER_SIZE, (S32)gPipeline.mScreenHeight);
+
 	while ((tex_width << 1) < max_width)
 	{
 		tex_width <<= 1;
@@ -327,7 +321,7 @@ void LLFloaterModelPreview::initModelPreview()
 	}
 
 	mModelPreview = new LLModelPreview(tex_width, tex_height, this);
-	mModelPreview->setPreviewTarget(16.f);
+    mModelPreview->setPreviewTarget(PREVIEW_CAMERA_DISTANCE);
 	mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5));
 	mModelPreview->setModelUpdatedCallback(boost::bind(&LLFloaterModelPreview::modelUpdated, this, _1));
 }
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 96af3b37d9..9260699985 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -26,17 +26,12 @@
 
 #include "llviewerprecompiledheaders.h"
 
+#include "llmodelpreview.h"
+
 #include "llmodelloader.h"
 #include "lldaeloader.h"
 #include "llfloatermodelpreview.h"
 
-#include "llmodelpreview.h"
-
-#include "llimagebmp.h"
-#include "llimagetga.h"
-#include "llimagejpeg.h"
-#include "llimagepng.h"
-
 #include "llagent.h"
 #include "llanimationstates.h"
 #include "llcallbacklist.h"
@@ -76,6 +71,22 @@
 
 bool LLModelPreview::sIgnoreLoadedCallback = false;
 
+// Extra configurability, to be exposed later in xml (LLModelPreview probably
+// should become UI control at some point or get split into preview control)
+static const LLColor4 PREVIEW_CANVAS_COL(0.169f, 0.169f, 0.169f, 1.f);
+static const LLColor4 PREVIEW_EDGE_COL(0.4f, 0.4f, 0.4f, 1.0);
+static const LLColor4 PREVIEW_BASE_COL(1.f, 1.f, 1.f, 1.f);
+static const LLColor3 PREVIEW_BRIGHTNESS(0.9f, 0.9f, 0.9f);
+static const F32 PREVIEW_EDGE_WIDTH(1.f);
+static const LLColor4 PREVIEW_PSYH_EDGE_COL(0.f, 0.25f, 0.5f, 0.25f);
+static const LLColor4 PREVIEW_PSYH_FILL_COL(0.f, 0.5f, 1.0f, 0.5f);
+static const F32 PREVIEW_PSYH_EDGE_WIDTH(1.f);
+static const LLColor4 PREVIEW_DEG_EDGE_COL(1.f, 0.f, 0.f, 1.f);
+static const LLColor4 PREVIEW_DEG_FILL_COL(1.f, 0.f, 0.f, 0.5f);
+static const F32 PREVIEW_DEG_EDGE_WIDTH(3.f);
+static const F32 PREVIEW_DEG_POINT_SIZE(8.f);
+static const F32 PREVIEW_ZOOM_LIMIT(10.f);
+
 const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f;
 
 BOOL stop_gloderror()
@@ -2677,20 +2688,6 @@ BOOL LLModelPreview::render()
     bool textures = mViewOption["show_textures"];
     bool physics = mViewOption["show_physics"];
 
-    // Extra configurability, to be exposed later as controls?
-    static LLCachedControl<LLColor4> canvas_col(gSavedSettings, "MeshPreviewCanvasColor");
-    static LLCachedControl<LLColor4> edge_col(gSavedSettings, "MeshPreviewEdgeColor");
-    static LLCachedControl<LLColor4> base_col(gSavedSettings, "MeshPreviewBaseColor");
-    static LLCachedControl<LLColor3> brightness(gSavedSettings, "MeshPreviewBrightnessColor");
-    static LLCachedControl<F32> edge_width(gSavedSettings, "MeshPreviewEdgeWidth");
-    static LLCachedControl<LLColor4> phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor");
-    static LLCachedControl<LLColor4> phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor");
-    static LLCachedControl<F32> phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth");
-    static LLCachedControl<LLColor4> deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor");
-    static LLCachedControl<LLColor4> deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor");
-    static LLCachedControl<F32> deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth");
-    static LLCachedControl<F32> deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize");
-
     S32 width = getWidth();
     S32 height = getHeight();
 
@@ -2714,7 +2711,7 @@ BOOL LLModelPreview::render()
         gGL.pushMatrix();
         gGL.loadIdentity();
 
-        gGL.color4fv(static_cast<LLColor4>(canvas_col).mV);
+        gGL.color4fv(PREVIEW_CANVAS_COL.mV);
         gl_rect_2d_simple(width, height);
 
         gGL.matrixMode(LLRender::MM_PROJECTION);
@@ -2884,7 +2881,7 @@ BOOL LLModelPreview::render()
     stop_glerror();
 
     gGL.pushMatrix();
-    gGL.color4fv(edge_col().mV);
+    gGL.color4fv(PREVIEW_EDGE_COL.mV);
 
     const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
 
@@ -2969,15 +2966,15 @@ BOOL LLModelPreview::render()
                     }
                     else
                     {
-                        gGL.diffuseColor4fv(static_cast<LLColor4>(base_col).mV);
+                        gGL.diffuseColor4fv(PREVIEW_BASE_COL.mV);
                     }
 
                     buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
                     gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-                    gGL.diffuseColor4fv(static_cast<LLColor4>(edge_col).mV);
+                    gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV);
                     if (edges)
                     {
-                        glLineWidth(edge_width);
+                        glLineWidth(PREVIEW_EDGE_WIDTH);
                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                         buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
                         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -3088,13 +3085,13 @@ BOOL LLModelPreview::render()
                                     LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
 
                                     gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-                                    gGL.diffuseColor4fv(phys_fill_col().mV);
+                                    gGL.diffuseColor4fv(PREVIEW_PSYH_FILL_COL.mV);
 
                                     buffer->setBuffer(type_mask & buffer->getTypeMask());
                                     buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
 
-                                    gGL.diffuseColor4fv(phys_edge_col().mV);
-                                    glLineWidth(phys_edge_width);
+                                    gGL.diffuseColor4fv(PREVIEW_PSYH_EDGE_COL.mV);
+                                    glLineWidth(PREVIEW_PSYH_EDGE_WIDTH);
                                     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                                     buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);
 
@@ -3109,8 +3106,8 @@ BOOL LLModelPreview::render()
                     // only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks]
                     if (pass > 0 && mHasDegenerate)
                     {
-                        glLineWidth(deg_edge_width);
-                        glPointSize(deg_point_size);
+                        glLineWidth(PREVIEW_DEG_EDGE_WIDTH);
+                        glPointSize(PREVIEW_DEG_POINT_SIZE);
                         gPipeline.enableLightsFullbright();
                         //show degenerate triangles
                         LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS);
@@ -3174,11 +3171,11 @@ BOOL LLModelPreview::render()
                                             if (ll_is_degenerate(v1, v2, v3))
                                             {
                                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-                                                gGL.diffuseColor3fv(deg_edge_col().mV);
+                                                gGL.diffuseColor3fv(PREVIEW_DEG_EDGE_COL.mV);
                                                 buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
                                                 buffer->drawRange(LLRender::POINTS, 0, 2, 3, indices_offset);
                                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-                                                gGL.diffuseColor3fv(deg_fill_col().mV);
+                                                gGL.diffuseColor3fv(PREVIEW_DEG_FILL_COL.mV);
                                                 buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
                                             }
                                         }
@@ -3326,8 +3323,8 @@ BOOL LLModelPreview::render()
 
                             if (edges)
                             {
-                                gGL.diffuseColor4fv(edge_col().mV);
-                                glLineWidth(edge_width);
+                                gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV);
+                                glLineWidth(PREVIEW_EDGE_WIDTH);
                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                                 buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -3403,8 +3400,7 @@ void LLModelPreview::zoom(F32 zoom_amt)
 {
     F32 new_zoom = mCameraZoom + zoom_amt;
     // TODO: stop clamping in render
-    static LLCachedControl<F32> zoom_limit(gSavedSettings, "MeshPreviewZoomLimit");
-    mCameraZoom = llclamp(new_zoom, 1.f, zoom_limit());
+    mCameraZoom = llclamp(new_zoom, 1.f, PREVIEW_ZOOM_LIMIT);
 }
 
 void LLModelPreview::pan(F32 right, F32 up)
diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h
index 7a7b8615f9..4c03849d6b 100644
--- a/indra/newview/llmodelpreview.h
+++ b/indra/newview/llmodelpreview.h
@@ -31,6 +31,7 @@
 #include "lldynamictexture.h"
 #include "llfloatermodelpreview.h"
 #include "llmeshrepository.h"
+#include "llmodelloader.h" //NUM_LOD
 #include "llmodel.h"
 
 class LLJoint;
-- 
cgit v1.2.3


From cac08f80a44139f9752523dca278eefde96a6a8c Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Thu, 23 Apr 2020 20:04:01 +0300
Subject: SL-13074 FIXED [Mesh Uploader] "The texture is empty" error is
 displayed after trying to upload the model with the texture from the
 non-English directory

---
 indra/newview/llmodelpreview.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 9260699985..9e39d0cf51 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -2619,7 +2619,7 @@ U32 LLModelPreview::loadTextures(LLImportMaterial& material, void* opaque)
         material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >;
         LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData));
 
-        tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + material.mDiffuseMapFilename, FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW);
+        tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + LLURI::unescape(material.mDiffuseMapFilename), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW);
         tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE);
         tex->forceToSaveRawImage(0, F32_MAX);
         material.setDiffuseMap(tex->getID()); // record tex ID
-- 
cgit v1.2.3


From c54e0e4f12dedff2d64354215fd7bcb68b09c7fd Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 23 Apr 2020 19:33:47 +0300
Subject: SL-12678 Remove automatic retry of login

---
 indra/newview/lllogininstance.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 8a69acb8dc..873531ef22 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -62,7 +62,7 @@
 #include <boost/scoped_ptr.hpp>
 #include <sstream>
 
-const S32 LOGIN_MAX_RETRIES = 3;
+const S32 LOGIN_MAX_RETRIES = 0; // Viewer should not autmatically retry login
 const F32 LOGIN_SRV_TIMEOUT_MIN = 10;
 const F32 LOGIN_SRV_TIMEOUT_MAX = 120;
 const F32 LOGIN_DNS_TIMEOUT_FACTOR = 0.9; // make DNS wait shorter then retry time
-- 
cgit v1.2.3


From 9221c8a3f65aa14cad4fec03b7033ad800f6ba35 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 23 Apr 2020 19:34:14 +0300
Subject: SL-13080 Changes for joint listings in mesh uploader

---
 indra/llprimitive/lldaeloader.cpp       |   6 ++
 indra/newview/llfloatermodelpreview.cpp | 126 +++++++++++++++++++++++---------
 indra/newview/llfloatermodelpreview.h   |   5 +-
 indra/newview/llmodelpreview.cpp        |  36 ++++++---
 4 files changed, 126 insertions(+), 47 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 139f48fef8..431443788c 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -1470,6 +1470,12 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
 			}
 		}
 
+        U32 bind_count = model->mSkinInfo.mAlternateBindMatrix.size();
+        if (bind_count > 0 && bind_count != jointCnt)
+        {
+            LL_WARNS("Mesh") << "Model " << model->mLabel << " has invalid joint bind matrix list." << LL_ENDL;
+        }
+
 		//grab raw position array
 
 		domVertices* verts = mesh->getVertices();
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 5df3139d0d..666406d039 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -433,46 +433,82 @@ void LLFloaterModelPreview::onClickCalculateBtn()
 	mUploadBtn->setEnabled(false);
 }
 
-void populate_list_with_map(LLScrollListCtrl *list, const std::map<std::string, LLVector3> &vector_map)
+// Modified cell_params, make sure to clear values if you have to reuse cell_params outside of this function
+void add_row_to_list(LLScrollListCtrl *listp,
+                     LLScrollListCell::Params &cell_params,
+                     const LLSD &item_value,
+                     const std::string &name,
+                     const LLSD &vx,
+                     const LLSD &vy,
+                     const LLSD &vz)
 {
-    if (vector_map.empty())
+    LLScrollListItem::Params item_params;
+    item_params.value = item_value;
+
+    cell_params.column = "model_name";
+    cell_params.value = name;
+
+    item_params.columns.add(cell_params);
+
+    cell_params.column = "axis_x";
+    cell_params.value = vx;
+    item_params.columns.add(cell_params);
+
+    cell_params.column = "axis_y";
+    cell_params.value = vy;
+    item_params.columns.add(cell_params);
+
+    cell_params.column = "axis_z";
+    cell_params.value = vz;
+
+    item_params.columns.add(cell_params);
+
+    listp->addRow(item_params);
+}
+
+void populate_list_with_overrides(LLScrollListCtrl *listp, const LLJointOverrideData &data, bool include_overrides)
+{
+    if (data.mModelsNoOverrides.empty() && data.mPosOverrides.empty())
     {
         return;
     }
+
+    static const LLSD no_override_placeholder("-"); // LLSD to not conflict in '?'
+
     S32 count = 0;
     LLScrollListCell::Params cell_params;
     cell_params.font = LLFontGL::getFontSansSerif();
     // Start out right justifying numeric displays
     cell_params.font_halign = LLFontGL::HCENTER;
 
-    std::map<std::string, LLVector3>::const_iterator iter = vector_map.begin();
-    std::map<std::string, LLVector3>::const_iterator end = vector_map.end();
-    while (iter != end)
+    std::map<std::string, LLVector3>::const_iterator map_iter = data.mPosOverrides.begin();
+    std::map<std::string, LLVector3>::const_iterator map_end = data.mPosOverrides.end();
+    while (map_iter != map_end)
     {
-        LLScrollListItem::Params item_params;
-        item_params.value = LLSD::Integer(count);
-
-        cell_params.column = "model_name";
-        cell_params.value = iter->first;
-
-        item_params.columns.add(cell_params);
-
-        cell_params.column = "axis_x";
-        cell_params.value = iter->second.mV[VX];
-        item_params.columns.add(cell_params);
-
-        cell_params.column = "axis_y";
-        cell_params.value = iter->second.mV[VY];
-        item_params.columns.add(cell_params);
-
-        cell_params.column = "axis_z";
-        cell_params.value = iter->second.mV[VZ];
-
-        item_params.columns.add(cell_params);
+        add_row_to_list(listp,
+                        cell_params,
+                        LLSD::Integer(count),
+                        map_iter->first,
+                        include_overrides ? map_iter->second.mV[VX] : no_override_placeholder,
+                        include_overrides ? map_iter->second.mV[VY] : no_override_placeholder,
+                        include_overrides ? map_iter->second.mV[VZ] : no_override_placeholder);
+        count++;
+        map_iter++;
+    }
 
-        list->addRow(item_params);
+    std::set<std::string>::const_iterator set_iter = data.mModelsNoOverrides.begin();
+    std::set<std::string>::const_iterator set_end = data.mModelsNoOverrides.end();
+    while (set_iter != set_end)
+    {
+        add_row_to_list(listp,
+                        cell_params,
+                        LLSD::Integer(count),
+                        *set_iter,
+                        no_override_placeholder,
+                        no_override_placeholder,
+                        no_override_placeholder);
         count++;
-        iter++;
+        set_iter++;
     }
 }
 
@@ -493,7 +529,8 @@ void LLFloaterModelPreview::onJointListSelection()
     {
         std::string label = selected->getValue().asString();
         LLJointOverrideData &data = mJointOverrides[display_lod][label];
-        populate_list_with_map(joints_pos, data.mPosOverrides);
+        bool upload_joint_positions = childGetValue("upload_joints").asBoolean();
+        populate_list_with_overrides(joints_pos, data, upload_joint_positions);
 
         joint_pos_descr->setTextArg("[JOINT]", label);
         mSelectedJointName = label;
@@ -1285,7 +1322,7 @@ void LLFloaterModelPreview::clearAvatarTab()
     joint_pos_descr->setTextArg("[JOINT]", std::string("mPelvis")); // Might be better to hide it
 }
 
-void LLFloaterModelPreview::updateAvatarTab()
+void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)
 {
     S32 display_lod = mModelPreview->mPreviewLOD;
     if (mModelPreview->mModel[display_lod].empty())
@@ -1307,10 +1344,22 @@ void LLFloaterModelPreview::updateAvatarTab()
                 LLModelInstance& instance = *model_iter;
                 LLModel* model = instance.mModel;
                 const LLMeshSkinInfo *skin = &model->mSkinInfo;
-                if (skin->mAlternateBindMatrix.size() > 0)
+                U32 joint_count = LLSkinningUtil::getMeshJointCount(skin);
+                U32 bind_count = highlight_overrides ? skin->mAlternateBindMatrix.size() : 0; // simply do not include overrides if data is not needed
+                if (bind_count > 0 && bind_count != joint_count)
+                {
+                    std::ostringstream out;
+                    out << "Invalid joint overrides for model " << model->getName();
+                    out << ". Amount of joints " << joint_count;
+                    out << ", is different from amount of overrides " << bind_count;
+                    LL_INFOS() << out.str() << LL_ENDL;
+                    addStringToLog(out.str(), true);
+                    // Disable overrides for this model
+                    bind_count = 0;
+                }
+                if (bind_count > 0)
                 {
-                    U32 count = LLSkinningUtil::getMeshJointCount(skin);
-                    for (U32 j = 0; j < count; ++j)
+                    for (U32 j = 0; j < joint_count; ++j)
                     {
                         const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
                         LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
@@ -1323,7 +1372,14 @@ void LLFloaterModelPreview::updateAvatarTab()
                             data.mHasConflicts = true;
                         }
                         data.mPosOverrides[model->getName()] = jointPos;
-
+                    }
+                }
+                else
+                {
+                    for (U32 j = 0; j < joint_count; ++j)
+                    {
+                        LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
+                        data.mModelsNoOverrides.insert(model->getName());
                     }
                 }
             }
@@ -1364,6 +1420,10 @@ void LLFloaterModelPreview::updateAvatarTab()
                 cell_params.color = LLColor4::orange;
                 conflicts++;
             }
+            if (highlight_overrides && joint_iter->second.mPosOverrides.size() > 0)
+            {
+                cell_params.font.style = "BOLD";
+            }
 
             item_params.columns.add(cell_params);
 
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index ca52312756..48b90e9d44 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -43,7 +43,8 @@ class LLJointOverrideData
 {
 public:
     LLJointOverrideData() : mHasConflicts(false) {};
-    std::map<std::string, LLVector3> mPosOverrides;
+    std::map<std::string, LLVector3> mPosOverrides; // models with overrides
+    std::set<std::string> mModelsNoOverrides; // models without defined overrides
     bool mHasConflicts;
 };
 typedef std::map<std::string, LLJointOverrideData> joint_override_data_map_t;
@@ -86,7 +87,7 @@ public:
 	static void addStringToLog(const std::string& str, bool flash);
 	static void addStringToLog(const std::ostringstream& strm, bool flash);
 	void clearAvatarTab(); // clears table
-	void updateAvatarTab(); // populates table and data as nessesary
+	void updateAvatarTab(bool highlight_overrides); // populates table and data as nessesary
 
 	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
 	void setPreviewLOD(S32 lod);
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 9e39d0cf51..21c6895a6c 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -2734,6 +2734,10 @@ BOOL LLModelPreview::render()
     if (upload_joints != mLastJointUpdate)
     {
         mLastJointUpdate = upload_joints;
+        if (fmp)
+        {
+            fmp->clearAvatarTab();
+        }
     }
 
     for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
@@ -2801,22 +2805,27 @@ BOOL LLModelPreview::render()
         upload_joints = false;
     }
 
-    if (upload_skin && upload_joints)
+    if (fmp)
     {
-        mFMP->childEnable("lock_scale_if_joint_position");
-        if (fmp)
+        if (upload_skin)
         {
-            fmp->updateAvatarTab();
+            // will populate list of joints
+            fmp->updateAvatarTab(upload_joints);
         }
+        else
+        {
+            fmp->clearAvatarTab();
+        }
+    }
+
+    if (upload_skin && upload_joints)
+    {
+        mFMP->childEnable("lock_scale_if_joint_position");
     }
     else
     {
         mFMP->childDisable("lock_scale_if_joint_position");
         mFMP->childSetValue("lock_scale_if_joint_position", false);
-        if (fmp)
-        {
-            fmp->clearAvatarTab();
-        }
     }
 
     //Only enable joint offsets if it passed the earlier critiquing
@@ -3218,9 +3227,12 @@ BOOL LLModelPreview::render()
                     {
                         const LLMeshSkinInfo *skin = &model->mSkinInfo;
                         LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary
-                        U32 count = LLSkinningUtil::getMeshJointCount(skin);
+                        U32 joint_count = LLSkinningUtil::getMeshJointCount(skin);
+                        U32 bind_count = skin->mAlternateBindMatrix.size();
 
-                        if (joint_overrides && skin->mAlternateBindMatrix.size() > 0)
+                        if (joint_overrides
+                            && bind_count > 0
+                            && joint_count == bind_count)
                         {
                             // mesh_id is used to determine which mesh gets to
                             // set the joint offset, in the event of a conflict. Since
@@ -3231,7 +3243,7 @@ BOOL LLModelPreview::render()
                             // incorrect.
                             LLUUID fake_mesh_id;
                             fake_mesh_id.generate();
-                            for (U32 j = 0; j < count; ++j)
+                            for (U32 j = 0; j < joint_count; ++j)
                             {
                                 LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]);
                                 if (joint)
@@ -3278,7 +3290,7 @@ BOOL LLModelPreview::render()
                             //build matrix palette
 
                             LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
-                            LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count,
+                            LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, joint_count,
                                 skin, getPreviewAvatar());
 
                             LLMatrix4a bind_shape_matrix;
-- 
cgit v1.2.3


From c0ed9d28f19bcdde171842370e87eb82cf43b896 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 24 Apr 2020 02:37:35 +0300
Subject: SL-13080 Filter out default overrides

---
 indra/newview/llfloatermodelpreview.cpp | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 666406d039..083c2dab37 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1361,17 +1361,32 @@ void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)
                 {
                     for (U32 j = 0; j < joint_count; ++j)
                     {
-                        const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation();
+                        const LLVector3& joint_pos = skin->mAlternateBindMatrix[j].getTranslation();
                         LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
-                        if (data.mPosOverrides.size() > 0
-                            && (data.mPosOverrides.begin()->second - jointPos).lengthSquared() > (LL_JOINT_TRESHOLD_POS_OFFSET * LL_JOINT_TRESHOLD_POS_OFFSET))
+
+                        LLJoint* pJoint = LLModelPreview::lookupJointByName(skin->mJointNames[j], mModelPreview);
+                        if (pJoint)
                         {
-                            // File contains multiple meshes with conflicting joint offsets
-                            // preview may be incorrect, upload result might wary (depends onto
-                            // mesh_id that hasn't been generated yet).
-                            data.mHasConflicts = true;
+                            // see how voavatar uses aboveJointPosThreshold
+                            if (pJoint->aboveJointPosThreshold(joint_pos))
+                            {
+                                // valid override
+                                if (data.mPosOverrides.size() > 0
+                                    && (data.mPosOverrides.begin()->second - joint_pos).lengthSquared() > (LL_JOINT_TRESHOLD_POS_OFFSET * LL_JOINT_TRESHOLD_POS_OFFSET))
+                                {
+                                    // File contains multiple meshes with conflicting joint offsets
+                                    // preview may be incorrect, upload result might wary (depends onto
+                                    // mesh_id that hasn't been generated yet).
+                                    data.mHasConflicts = true;
+                                }
+                                data.mPosOverrides[model->getName()] = joint_pos;
+                            }
+                            else
+                            {
+                                // default value, it won't be accounted for by avatar
+                                data.mModelsNoOverrides.insert(model->getName());
+                            }
                         }
-                        data.mPosOverrides[model->getName()] = jointPos;
                     }
                 }
                 else
-- 
cgit v1.2.3


From 0bc76461fa4445491cf119e80cb854f1d6dd6896 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 24 Apr 2020 02:47:18 +0300
Subject: SL-13080 Mac build fix

---
 indra/newview/llfloatermodelpreview.cpp | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 083c2dab37..ba67f297a0 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -473,7 +473,7 @@ void populate_list_with_overrides(LLScrollListCtrl *listp, const LLJointOverride
         return;
     }
 
-    static const LLSD no_override_placeholder("-"); // LLSD to not conflict in '?'
+    static const std::string no_override_placeholder = "-";
 
     S32 count = 0;
     LLScrollListCell::Params cell_params;
@@ -485,13 +485,26 @@ void populate_list_with_overrides(LLScrollListCtrl *listp, const LLJointOverride
     std::map<std::string, LLVector3>::const_iterator map_end = data.mPosOverrides.end();
     while (map_iter != map_end)
     {
-        add_row_to_list(listp,
-                        cell_params,
-                        LLSD::Integer(count),
-                        map_iter->first,
-                        include_overrides ? map_iter->second.mV[VX] : no_override_placeholder,
-                        include_overrides ? map_iter->second.mV[VY] : no_override_placeholder,
-                        include_overrides ? map_iter->second.mV[VZ] : no_override_placeholder);
+        if (include_overrides)
+        {
+            add_row_to_list(listp,
+                cell_params,
+                LLSD::Integer(count),
+                map_iter->first,
+                LLSD::Real(map_iter->second.mV[VX]),
+                LLSD::Real(map_iter->second.mV[VY]),
+                LLSD::Real(map_iter->second.mV[VZ]));
+        }
+        else
+        {
+            add_row_to_list(listp,
+                cell_params,
+                LLSD::Integer(count),
+                map_iter->first,
+                no_override_placeholder,
+                no_override_placeholder,
+                no_override_placeholder);
+        }
         count++;
         map_iter++;
     }
-- 
cgit v1.2.3


From a0e91e9e505e747f876712fe8ec5c250b56e1dcf Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 24 Apr 2020 18:05:14 +0300
Subject: SL-307 Improvements to flashing color

---
 indra/llui/llbutton.cpp                            |  22 +++++++++++----------
 indra/llui/lltabcontainer.cpp                      |  18 ++++++++---------
 indra/llui/lltabcontainer.h                        |   3 ++-
 indra/newview/llfloatermodelpreview.cpp            |   5 +----
 .../textures/containers/TabTop_Right_Flashing.png  | Bin 0 -> 252 bytes
 indra/newview/skins/default/textures/textures.xml  |   1 +
 .../skins/default/xui/en/floater_model_preview.xml |   5 ++++-
 7 files changed, 28 insertions(+), 26 deletions(-)
 create mode 100644 indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.png

diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 804204cce0..9682c3bc10 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -643,7 +643,8 @@ void LLButton::draw()
 	LLColor4 highlighting_color = LLColor4::white;
 	LLColor4 glow_color = LLColor4::white;
 	LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
-	LLUIImage* imagep = NULL;
+    LLUIImage* imagep = NULL;
+    LLUIImage* image_glow = NULL;
 
     //  Cancel sticking of color, if the button is pressed,
 	//  or when a flashing of the previously selected button is ended
@@ -710,17 +711,18 @@ void LLButton::draw()
 		imagep = mImageDisabled;
 	}
 
+	image_glow = imagep;
+
 	if (mFlashing)
 	{
-		// if button should flash and we have icon for flashing, use it as image for button
-		if(flash && mImageFlash)
+		if (flash && mImageFlash)
 		{
-			// setting flash to false to avoid its further influence on glow
-			flash = false;
-			imagep = mImageFlash;
+			// if button should flash and we have icon for flashing, use it as image for button
+			image_glow = mImageFlash;
 		}
-		// else use usual flashing via flash_color
-		else if (mFlashingTimer)
+
+		// provide fade-in and fade-out via flash_color
+		if (mFlashingTimer)
 		{
 			LLColor4 flash_color = mFlashBgColor.get();
 			use_glow_effect = TRUE;
@@ -811,7 +813,7 @@ void LLButton::draw()
 			if (mCurGlowStrength > 0.01f)
 			{
 				gGL.setSceneBlendType(glow_type);
-				imagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha));
+				image_glow->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha));
 				gGL.setSceneBlendType(LLRender::BT_ALPHA);
 			}
 		}
@@ -822,7 +824,7 @@ void LLButton::draw()
 			if (mCurGlowStrength > 0.01f)
 			{
 				gGL.setSceneBlendType(glow_type);
-				imagep->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha));
+				image_glow->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha));
 				gGL.setSceneBlendType(LLRender::BT_ALPHA);
 			}
 		}
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 750a3aff9c..e6b43da8e5 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -221,6 +221,7 @@ LLTabContainer::Params::Params()
 	use_custom_icon_ctrl("use_custom_icon_ctrl", false),
 	open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false),
 	enable_tabs_flashing("enable_tabs_flashing", false),
+	tabs_flashing_color("tabs_flashing_color"),
 	tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
 	use_ellipses("use_ellipses"),
 	font_halign("halign")
@@ -261,6 +262,7 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
 	mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop),
 	mTabIconCtrlPad(p.tab_icon_ctrl_pad),
 	mEnableTabsFlashing(p.enable_tabs_flashing),
+	mTabsFlashingColor(p.tabs_flashing_color),
 	mUseTabEllipses(p.use_ellipses)
 {
 	static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
@@ -282,6 +284,11 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
 		mMinTabWidth = tabcntr_vert_tab_min_width;
 	}
 
+    if (p.tabs_flashing_color.isProvided())
+    {
+        mEnableTabsFlashing = true;
+    }
+
 	initButtons( );
 }
 
@@ -1107,6 +1114,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
 
 		// inits flash timer
 		p.button_flash_enable = mEnableTabsFlashing;
+		p.flash_color = mTabsFlashingColor;
 		
 		// *TODO : It seems wrong not to use p in both cases considering the way p is initialized
 		if (mCustomIconCtrlUsed)
@@ -1642,16 +1650,6 @@ void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state )
 	}
 }
 
-void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state, LLUIColor color)
-{
-    LLTabTuple* tuple = getTabByPanel(child);
-    if (tuple)
-    {
-        tuple->mButton->setFlashColor(color);
-        tuple->mButton->setFlashing(state);
-    }
-}
-
 void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
 {
 	LLTabTuple* tuple = getTabByPanel(child);
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 5339bec3dd..8f8cedb1b9 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -114,6 +114,7 @@ public:
 		 * Enable tab flashing
 		 */
 		Optional<bool>						enable_tabs_flashing;
+		Optional<LLUIColor>					tabs_flashing_color;
 		
 		/**
 		 *  Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true)
@@ -203,7 +204,6 @@ public:
 
 	BOOL        getTabPanelFlashing(LLPanel* child);
 	void		setTabPanelFlashing(LLPanel* child, BOOL state);
-	void		setTabPanelFlashing(LLPanel* child, BOOL state, LLUIColor color);
 	void 		setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white);
 	void 		setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white);
 	void		setTabImage(LLPanel* child, LLIconCtrl* icon);
@@ -317,6 +317,7 @@ private:
 	bool							mCustomIconCtrlUsed;
 	bool							mOpenTabsOnDragAndDrop;
 	bool							mEnableTabsFlashing;
+	LLUIColor						mTabsFlashingColor;
 	S32								mTabIconCtrlPad;
 	bool							mUseTabEllipses;
 };
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index ba67f297a0..1d3fb5bcfc 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1519,10 +1519,7 @@ void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash
         LLPanel* panel = mTabContainer->getPanelByName("logs_panel");
         if (mTabContainer->getCurrentPanel() != panel)
         {
-            // This will makes colors pale due to "glow_type = LLRender::BT_ALPHA"
-            // So instead of using "MenuItemFlashBgColor" added stronger color
-            static LLUIColor sFlashBgColor(LLColor4U(255, 99, 0));
-            mTabContainer->setTabPanelFlashing(panel, true, sFlashBgColor);
+            mTabContainer->setTabPanelFlashing(panel, true);
         }
     }
 }
diff --git a/indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.png b/indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.png
new file mode 100644
index 0000000000..fd13bb699d
Binary files /dev/null and b/indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 624dca48d2..980dcf76bf 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -628,6 +628,7 @@ with the same filename but different name
 
   <texture name="TabTop_Right_Off" file_name="containers/TabTop_Right_Off.png" preload="false"  scale.left="8" scale.top="8" scale.right="62" scale.bottom="9" />
   <texture name="TabTop_Right_Selected" file_name="containers/TabTop_Right_Selected.png" preload="false"  scale.left="8" scale.top="8" scale.right="62" scale.bottom="9" />
+  <texture name="TabTop_Right_Flashing" file_name="containers/TabTop_Right_Flashing.png" preload="false"  scale.left="8" scale.top="8" scale.right="62" scale.bottom="9" />
   <texture name="TabTop_Middle_Off" file_name="containers/TabTop_Middle_Off.png" preload="false" scale.left="8" scale.top="8" scale.right="120" scale.bottom="9" />
   <texture name="TabTop_Middle_Selected" file_name="containers/TabTop_Middle_Selected.png" preload="false" scale.left="8" scale.top="8" scale.right="96" scale.bottom="9" />
   <texture name="TabTop_Left_Off" file_name="containers/TabTop_Left_Off.png" preload="false" scale.left="8" scale.top="8" scale.right="120" scale.bottom="9" />
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 e8c64dfef7..66878f227b 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -90,7 +90,10 @@
       width="635"
       name="import_tab"
       tab_position="top"
-      enable_tabs_flashing="true">
+      enable_tabs_flashing="true"
+      tabs_flashing_color="MenuItemFlashBgColor">
+      <last_tab
+        tab_top_image_flash="TabTop_Right_Flashing"/> <!-- for log tab -->
       <!-- LOD PANEL -->
         <panel
          help_topic="upload_model_lod"
-- 
cgit v1.2.3


From 3cc83b2e37e0e4695410ee10d622e69ce642b7cb Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 24 Apr 2020 18:29:18 +0300
Subject: SL-307 Improvements to logging

---
 indra/newview/llfloatermodelpreview.cpp | 2 +-
 indra/newview/llmodelpreview.cpp        | 6 ++++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 1d3fb5bcfc..98ffd30bc7 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -413,7 +413,7 @@ void LLFloaterModelPreview::loadModel(S32 lod, const std::string& file_name, boo
 void LLFloaterModelPreview::onClickCalculateBtn()
 {
 	clearLogTab();
-
+	addStringToLog("Calculating model data.", false);
 	mModelPreview->rebuildUploadData();
 
 	bool upload_skinweights = childGetValue("upload_skin").asBoolean();
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 21c6895a6c..6de88b318a 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -562,6 +562,7 @@ void LLModelPreview::rebuildUploadData()
             LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH];
             if (!high_lod_model)
             {
+                LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has no High Lod (LOD3).", true);
                 setLoadState(LLModelLoader::ERROR_MATERIALS);
                 mFMP->childDisable("calculate_btn");
             }
@@ -574,6 +575,7 @@ void LLModelPreview::rebuildUploadData()
                     llassert(instance.mLOD[i]);
                     if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt))
                     {
+                        LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has mismatching materials between lods." , true);
                         setLoadState(LLModelLoader::ERROR_MATERIALS);
                         mFMP->childDisable("calculate_btn");
                     }
@@ -593,7 +595,7 @@ void LLModelPreview::rebuildUploadData()
                         out << bind_rot;
                         LL_WARNS() << out.str() << LL_ENDL;
 
-                        LLFloaterModelPreview::addStringToLog(out, false);
+                        LLFloaterModelPreview::addStringToLog(out, getLoadState() != LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION);
                         setLoadState(LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION);
                     }
                 }
@@ -626,7 +628,7 @@ void LLModelPreview::rebuildUploadData()
                     std::ostringstream out;
                     out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models.";
                     LL_INFOS() << out.str() << LL_ENDL;
-                    LLFloaterModelPreview::addStringToLog(out, false);
+                    LLFloaterModelPreview::addStringToLog(out, true);
                 }
                 setLoadState(LLModelLoader::ERROR_MATERIALS);
                 mFMP->childDisable("calculate_btn");
-- 
cgit v1.2.3


From aa014a65e76a1312dee82de38dc8d73b64e7a8b5 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 24 Apr 2020 20:50:57 +0300
Subject: SL-13123 Bind shape matrix orientation warning is misbehaving

---
 indra/newview/llfloatermodelpreview.cpp | 21 +++++----------------
 indra/newview/llfloatermodelpreview.h   |  1 -
 indra/newview/llmodelpreview.cpp        |  4 ++++
 3 files changed, 9 insertions(+), 17 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 98ffd30bc7..f609e8a91a 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -180,7 +180,6 @@ BOOL LLFloaterModelPreview::postBuild()
 
 	childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this);
 
-	childSetCommitCallback("upload_skin", onUploadSkinCommit, this);
 	childSetCommitCallback("upload_joints", onUploadJointsCommit, this);
 	childSetCommitCallback("lock_scale_if_joint_position", onUploadJointsCommit, this);
 
@@ -331,7 +330,11 @@ void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl)
 	if (mModelPreview)
 	{
 		auto name = ctrl->getName();
-		mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name];
+        mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name];
+        mModelPreview->refresh();
+        mModelPreview->resetPreviewTarget();
+        mModelPreview->clearBuffers();
+        mModelPreview->mDirty = true;
 	}
 	toggleCalculateButton(true);
 }
@@ -615,20 +618,6 @@ void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata)
 	fp->mModelPreview->refresh();
 }
 
-//static
-void LLFloaterModelPreview::onUploadSkinCommit(LLUICtrl*,void* userdata)
-{
-	LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
-
-	if (!fp->mModelPreview)
-	{
-		return;
-	}
-	fp->mModelPreview->refresh();
-	fp->mModelPreview->resetPreviewTarget();
-	fp->mModelPreview->clearBuffers();
-}
-
 //static
 void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata)
 {
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 48b90e9d44..6ecc279365 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -140,7 +140,6 @@ protected:
 	static void		onImportScaleCommit(LLUICtrl*, void*);
 	static void		onPelvisOffsetCommit(LLUICtrl*, void*);
 	static void		onUploadJointsCommit(LLUICtrl*,void*);
-	static void		onUploadSkinCommit(LLUICtrl*,void*);
 
 	static void		onPreviewLODCommit(LLUICtrl*,void*);
 	
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 6de88b318a..dbb2c162a3 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -599,6 +599,10 @@ void LLModelPreview::rebuildUploadData()
                         setLoadState(LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION);
                     }
                 }
+                else if (getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION)
+                {
+                    setLoadState(LLModelLoader::DONE);
+                }
             }
             instance.mTransform = mat;
             mUploadData.push_back(instance);
-- 
cgit v1.2.3


From 4ac08f1ac5b003b95fc9087183d03c0291f6554b Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Sat, 25 Apr 2020 14:35:44 +0300
Subject: SL-13123 Cleanup

---
 indra/newview/llfloatermodelpreview.cpp | 26 +++++++-------------------
 indra/newview/llfloatermodelpreview.h   |  1 -
 indra/newview/llmodelpreview.cpp        | 30 ++++++++++++++++++++++--------
 3 files changed, 29 insertions(+), 28 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index f609e8a91a..a52cf981c7 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -166,6 +166,7 @@ BOOL LLFloaterModelPreview::postBuild()
 		getChild<LLSpinCtrl>("lod_triangle_limit_" + lod_name[lod])->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLODParamCommit, this, lod, true));
 	}
 
+	// Upload/avatar options, they need to refresh errors/notifications
 	childSetCommitCallback("upload_skin", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);
 	childSetCommitCallback("upload_joints", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);
 	childSetCommitCallback("lock_scale_if_joint_position", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);
@@ -180,9 +181,6 @@ BOOL LLFloaterModelPreview::postBuild()
 
 	childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this);
 
-	childSetCommitCallback("upload_joints", onUploadJointsCommit, this);
-	childSetCommitCallback("lock_scale_if_joint_position", onUploadJointsCommit, this);
-
 	childSetCommitCallback("import_scale", onImportScaleCommit, this);
 	childSetCommitCallback("pelvis_offset", onPelvisOffsetCommit, this);
 
@@ -330,12 +328,15 @@ void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl)
 	if (mModelPreview)
 	{
 		auto name = ctrl->getName();
+        // update the option and notifications
+        // (this is a bit convoluted, because of the current structure of mModelPreview)
         mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name];
-        mModelPreview->refresh();
-        mModelPreview->resetPreviewTarget();
+        mModelPreview->refresh(); // a 'dirty' flag for render
+        mModelPreview->resetPreviewTarget(); 
         mModelPreview->clearBuffers();
         mModelPreview->mDirty = true;
-	}
+    }
+    // set the button visible, it will be refreshed later
 	toggleCalculateButton(true);
 }
 
@@ -605,19 +606,6 @@ void LLFloaterModelPreview::onPelvisOffsetCommit( LLUICtrl*, void* userdata )
 	fp->mModelPreview->refresh();
 }
 
-//static
-void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata)
-{
-	LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
-
-	if (!fp->mModelPreview)
-	{
-		return;
-	}
-
-	fp->mModelPreview->refresh();
-}
-
 //static
 void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata)
 {
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 6ecc279365..7b344ef2b2 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -139,7 +139,6 @@ protected:
 
 	static void		onImportScaleCommit(LLUICtrl*, void*);
 	static void		onPelvisOffsetCommit(LLUICtrl*, void*);
-	static void		onUploadJointsCommit(LLUICtrl*,void*);
 
 	static void		onPreviewLODCommit(LLUICtrl*,void*);
 	
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index dbb2c162a3..9530e02c06 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -361,6 +361,7 @@ void LLModelPreview::rebuildUploadData()
     F32 max_scale = 0.f;
 
     BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
+    U32 load_state = 0;
 
     for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter)
     { //for each transform in scene
@@ -563,7 +564,7 @@ void LLModelPreview::rebuildUploadData()
             if (!high_lod_model)
             {
                 LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has no High Lod (LOD3).", true);
-                setLoadState(LLModelLoader::ERROR_MATERIALS);
+                load_state = LLModelLoader::ERROR_MATERIALS;
                 mFMP->childDisable("calculate_btn");
             }
             else
@@ -576,7 +577,7 @@ void LLModelPreview::rebuildUploadData()
                     if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt))
                     {
                         LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has mismatching materials between lods." , true);
-                        setLoadState(LLModelLoader::ERROR_MATERIALS);
+                        load_state = LLModelLoader::ERROR_MATERIALS;
                         mFMP->childDisable("calculate_btn");
                     }
                 }
@@ -588,6 +589,8 @@ void LLModelPreview::rebuildUploadData()
                     LLQuaternion identity;
                     if (!bind_rot.isEqualEps(identity, 0.01))
                     {
+                        // Bind shape matrix is not in standard X-forward orientation.
+                        // Might be good idea to only show this once. It can be spammy.
                         std::ostringstream out;
                         out << "non-identity bind shape rot. mat is ";
                         out << high_lod_model->mSkinInfo.mBindShapeMatrix;
@@ -596,13 +599,9 @@ void LLModelPreview::rebuildUploadData()
                         LL_WARNS() << out.str() << LL_ENDL;
 
                         LLFloaterModelPreview::addStringToLog(out, getLoadState() != LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION);
-                        setLoadState(LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION);
+                        load_state = LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION;
                     }
                 }
-                else if (getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION)
-                {
-                    setLoadState(LLModelLoader::DONE);
-                }
             }
             instance.mTransform = mat;
             mUploadData.push_back(instance);
@@ -634,12 +633,27 @@ void LLModelPreview::rebuildUploadData()
                     LL_INFOS() << out.str() << LL_ENDL;
                     LLFloaterModelPreview::addStringToLog(out, true);
                 }
-                setLoadState(LLModelLoader::ERROR_MATERIALS);
+                load_state = LLModelLoader::ERROR_MATERIALS;
                 mFMP->childDisable("calculate_btn");
             }
         }
     }
 
+    // Update state for notifications
+    if (load_state > 0)
+    {
+        // encountered issues
+        setLoadState(load_state);
+    }
+    else if (getLoadState() == LLModelLoader::ERROR_MATERIALS
+             || getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION)
+    {
+        // This is only valid for these two error types because they are 
+        // only used inside rebuildUploadData() and updateStatusMessages()
+        // updateStatusMessages() is called after rebuildUploadData()
+        setLoadState(LLModelLoader::DONE);
+    }
+
     F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE - 0.1f) / max_scale;
 
     F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]);
-- 
cgit v1.2.3


From 9fad0d600803bbe438bdfa5a0b3448d193f361c2 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 12 May 2020 16:45:59 +0300
Subject: SL-13064 Reverted changes that were causing Artifacts at HD620

---
 indra/newview/llmodelpreview.cpp | 21 +++++++--------------
 1 file changed, 7 insertions(+), 14 deletions(-)

diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 9530e02c06..c0de12f58e 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -3133,7 +3133,7 @@ BOOL LLModelPreview::render()
                     }
 
                     // only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks]
-                    if (pass > 0 && mHasDegenerate)
+                    if (mHasDegenerate)
                     {
                         glLineWidth(PREVIEW_DEG_EDGE_WIDTH);
                         glPointSize(PREVIEW_DEG_POINT_SIZE);
@@ -3179,7 +3179,6 @@ BOOL LLModelPreview::render()
                                     for (U32 v = 0; v < num_models; ++v)
                                     {
                                         LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v];
-                                        if (buffer->getNumVerts() < 3)continue;
 
                                         buffer->setBuffer(type_mask & buffer->getTypeMask());
 
@@ -3190,22 +3189,16 @@ BOOL LLModelPreview::render()
                                         LLStrider<U16> idx;
                                         buffer->getIndexStrider(idx, 0);
 
-                                        LLVector4a v1, v2, v3;
-                                        for (U32 indices_offset = 0; indices_offset < buffer->getNumIndices(); indices_offset += 3)
+                                        for (U32 i = 0; i < buffer->getNumIndices(); i += 3)
                                         {
-                                            v1.setMul(pos[*idx++], scale);
-                                            v2.setMul(pos[*idx++], scale);
-                                            v3.setMul(pos[*idx++], scale);
+                                            LLVector4a v1; v1.setMul(pos[*idx++], scale);
+                                            LLVector4a v2; v2.setMul(pos[*idx++], scale);
+                                            LLVector4a v3; v3.setMul(pos[*idx++], scale);
 
                                             if (ll_is_degenerate(v1, v2, v3))
                                             {
-                                                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-                                                gGL.diffuseColor3fv(PREVIEW_DEG_EDGE_COL.mV);
-                                                buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
-                                                buffer->drawRange(LLRender::POINTS, 0, 2, 3, indices_offset);
-                                                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-                                                gGL.diffuseColor3fv(PREVIEW_DEG_FILL_COL.mV);
-                                                buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset);
+                                                buffer->draw(LLRender::LINE_LOOP, 3, i);
+                                                buffer->draw(LLRender::POINTS, 3, i);
                                             }
                                         }
                                     }
-- 
cgit v1.2.3


From 70fe69ea43d896da096f767d530f18a702c5d7a6 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Tue, 12 May 2020 19:02:48 +0300
Subject: SL-13189 Mesh uploader - Autofill from _postfixes

---
 indra/newview/llfloatermodelpreview.cpp | 26 +++++++++
 indra/newview/llfloatermodelpreview.h   |  4 ++
 indra/newview/llmodelpreview.cpp        | 95 ++++++++++++++++++++-------------
 indra/newview/llmodelpreview.h          |  3 ++
 indra/newview/llviewermenufile.cpp      |  2 +-
 5 files changed, 93 insertions(+), 37 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index a52cf981c7..f667116ca4 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -395,6 +395,12 @@ void LLFloaterModelPreview::disableViewOption(const std::string& option)
 	setViewOptionEnabled(option, false);
 }
 
+void LLFloaterModelPreview::loadHighLodModel()
+{
+	mModelPreview->mLookUpLodFiles = true;
+	loadModel(3);
+}
+
 void LLFloaterModelPreview::loadModel(S32 lod)
 {
 	mModelPreview->mLoading = true;
@@ -1593,6 +1599,26 @@ LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LL
 	assignData(mdl) ;	
 }
 
+void LLFloaterModelPreview::setCtrlLoadFromFile(S32 lod)
+{
+    if (lod == LLModel::LOD_PHYSICS)
+    {
+        LLComboBox* lod_combo = findChild<LLComboBox>("physics_lod_combo");
+        if (lod_combo)
+        {
+            lod_combo->setCurrentByIndex(5);
+        }
+    }
+    else
+    {
+        LLComboBox* lod_combo = findChild<LLComboBox>("lod_source_" + lod_name[lod]);
+        if (lod_combo)
+        {
+            lod_combo->setCurrentByIndex(0);
+        }
+    }
+}
+
 void LLFloaterModelPreview::setStatusMessage(const std::string& msg)
 {
 	LLMutexLock lock(mStatusLock);
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 7b344ef2b2..bee64fb501 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -102,6 +102,8 @@ public:
 	
 	void			loadModel(S32 lod);
 	void 			loadModel(S32 lod, const std::string& file_name, bool force_disable_slm = false);
+
+	void			loadHighLodModel();
 	
 	void onViewOptionChecked(LLUICtrl* ctrl);
 	void onUploadOptionChecked(LLUICtrl* ctrl);
@@ -174,6 +176,8 @@ protected:
     void setStatusMessage(const std::string& msg);
     void addStringToLogTab(const std::string& str, bool flash);
 
+    void setCtrlLoadFromFile(S32 lod);
+
 	LLModelPreview*	mModelPreview;
 	
 	LLPhysicsDecomp::decomp_params mDecompParams;
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index c0de12f58e..04a818b2a4 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -127,6 +127,20 @@ std::string stripSuffix(std::string name)
     return name;
 }
 
+std::string getLodSuffix(S32 lod)
+{
+    std::string suffix;
+    switch (lod)
+    {
+    case LLModel::LOD_IMPOSTOR: suffix = "_LOD0"; break;
+    case LLModel::LOD_LOW:      suffix = "_LOD1"; break;
+    case LLModel::LOD_MEDIUM:   suffix = "_LOD2"; break;
+    case LLModel::LOD_PHYSICS:  suffix = "_PHYS"; break;
+    case LLModel::LOD_HIGH:                       break;
+    }
+    return suffix;
+}
+
 void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut)
 {
     LLModelLoader::scene::iterator base_iter = scene.begin();
@@ -181,6 +195,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
     mDirty = false;
     mGenLOD = false;
     mLoading = false;
+    mLookUpLodFiles = false;
     mLoadState = LLModelLoader::STARTING;
     mGroup = 0;
     mLODFrozen = false;
@@ -421,15 +436,7 @@ void LLModelPreview::rebuildUploadData()
                         extensionLOD = mPhysicsSearchLOD;
                     }
 
-                    std::string toAdd;
-                    switch (extensionLOD)
-                    {
-                    case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
-                    case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
-                    case LLModel::LOD_MEDIUM:   toAdd = "_LOD2"; break;
-                    case LLModel::LOD_PHYSICS:  toAdd = "_PHYS"; break;
-                    case LLModel::LOD_HIGH:                      break;
-                    }
+                    std::string toAdd = getLodSuffix(extensionLOD);
 
                     if (name_to_match.find(toAdd) == -1)
                     {
@@ -456,15 +463,7 @@ void LLModelPreview::rebuildUploadData()
                             std::string name_to_match = instance.mLabel;
                             llassert(!name_to_match.empty());
 
-                            std::string toAdd;
-                            switch (searchLOD)
-                            {
-                            case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
-                            case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
-                            case LLModel::LOD_MEDIUM:   toAdd = "_LOD2"; break;
-                            case LLModel::LOD_PHYSICS:  toAdd = "_PHYS"; break;
-                            case LLModel::LOD_HIGH:                      break;
-                            }
+                            std::string toAdd = getLodSuffix(searchLOD);
 
                             if (name_to_match.find(toAdd) == -1)
                             {
@@ -1168,14 +1167,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 
                             if (loaded_name != name)
                             {
-                                switch (loaded_lod)
-                                {
-                                case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
-                                case LLModel::LOD_LOW:      name += "_LOD1"; break;
-                                case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
-                                case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
-                                case LLModel::LOD_HIGH:                      break;
-                                }
+                                name += getLodSuffix(loaded_lod);
 
                                 if (mImporterDebug)
                                 {
@@ -1578,16 +1570,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
             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;
-
-            switch (lod)
-            {
-            case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
-            case LLModel::LOD_LOW:      name += "_LOD1"; break;
-            case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
-            case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
-            case LLModel::LOD_HIGH:                      break;
-            }
+            std::string name = base->mLabel + getLodSuffix(lod);
 
             mModel[lod][mdl_idx]->mLabel = name;
             mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID;
@@ -2607,10 +2590,45 @@ void LLModelPreview::loadedCallback(
         }
         pPreview->mModelLoader->clearLog();
         pPreview->loadModelCallback(lod); // removes mModelLoader in some cases
+        if (pPreview->mLookUpLodFiles && (lod != LLModel::LOD_HIGH))
+        {
+            pPreview->lookupLODModelFiles(lod);
+        }
     }
 
 }
 
+void LLModelPreview::lookupLODModelFiles(S32 lod)
+{
+    if (lod == LLModel::LOD_PHYSICS)
+    {
+        mLookUpLodFiles = false;
+        return;
+    }
+    S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS;
+
+    std::string lod_filename = mLODFile[LLModel::LOD_HIGH];
+    std::string ext = ".dae";
+    std::string::size_type i = lod_filename.rfind(ext);
+    if (i != std::string::npos)
+    {
+        lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext);
+    }
+    if (gDirUtilp->fileExists(lod_filename))
+    {
+        LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+        if (fmp)
+        {
+            fmp->setCtrlLoadFromFile(next_lod);
+        }
+        loadModel(lod_filename, next_lod);
+    }
+    else
+    {
+        lookupLODModelFiles(next_lod);
+    }
+}
+
 void LLModelPreview::stateChangedCallback(U32 state, void* opaque)
 {
     LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
@@ -3509,6 +3527,11 @@ bool LLModelPreview::lodQueryCallback()
             preview->mLodsQuery.pop_back();
             preview->genLODs(lod);
 
+            if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH))
+            {
+                preview->lookupLODModelFiles(LLModel::LOD_HIGH);
+            }
+
             // return false to continue cycle
             return false;
         }
diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h
index 4c03849d6b..9b8200ab8a 100644
--- a/indra/newview/llmodelpreview.h
+++ b/indra/newview/llmodelpreview.h
@@ -206,6 +206,8 @@ protected:
     static LLJoint*	lookupJointByName(const std::string&, void* opaque);
     static U32			loadTextures(LLImportMaterial& material, void* opaque);
 
+    void lookupLODModelFiles(S32 lod);
+
 private:
     //Utility function for controller vertex compare
     bool verifyCount(int expected, int result);
@@ -243,6 +245,7 @@ protected:
     U32			mLoadState;
     bool		mResetJoints;
     bool		mModelNoErrors;
+    bool		mLookUpLodFiles;
 
     std::map<std::string, bool> mViewOption;
 
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index d1d3a7fc12..7706828cec 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -552,7 +552,7 @@ class LLFileUploadModel : public view_listener_t
 		LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::getInstance("upload_model");
 		if (fmp && !fmp->isModelLoading())
 		{
-			fmp->loadModel(3);
+			fmp->loadHighLodModel();
 		}
 		
 		return TRUE;
-- 
cgit v1.2.3


From 0570d6d3c1b56ebcd0309153ec0cebc7586a6286 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Mon, 27 Jul 2020 22:32:49 +0300
Subject: SL-13483 Rename Avatar tab to "Rigging"

---
 indra/newview/llfloatermodelpreview.cpp                      | 8 ++++----
 indra/newview/skins/default/xui/en/floater_model_preview.xml | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index f667116ca4..ff024e1d44 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -261,7 +261,7 @@ BOOL LLFloaterModelPreview::postBuild()
 	mUploadLogText = getChild<LLViewerTextEditor>("log_text");
 	mTabContainer = getChild<LLTabContainer>("import_tab");
 
-    LLPanel *panel = mTabContainer->getPanelByName("avatar_panel");
+    LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
     mAvatarTabIndex = mTabContainer->getIndexForPanel(panel);
     panel->getChild<LLScrollListCtrl>("joints_list")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onJointListSelection, this));
 
@@ -538,7 +538,7 @@ void populate_list_with_overrides(LLScrollListCtrl *listp, const LLJointOverride
 void LLFloaterModelPreview::onJointListSelection()
 {
     S32 display_lod = mModelPreview->mPreviewLOD;
-    LLPanel *panel = mTabContainer->getPanelByName("avatar_panel");
+    LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
     LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
     LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list");
@@ -1298,7 +1298,7 @@ void LLFloaterModelPreview::addStringToLog(const std::ostringstream& strm, bool
 
 void LLFloaterModelPreview::clearAvatarTab()
 {
-    LLPanel *panel = mTabContainer->getPanelByName("avatar_panel");
+    LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
     joints_list->deleteAllItems();
     LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
@@ -1397,7 +1397,7 @@ void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)
         }
     }
 
-    LLPanel *panel = mTabContainer->getPanelByName("avatar_panel");
+    LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
     LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
 
     if (joints_list->isEmpty())
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 66878f227b..4fa0b9af7d 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1173,8 +1173,8 @@
       <panel
        label="Overrides"
        layout="topleft"
-       name="avatar_panel"
-       title="Avatar">
+       name="rigging_panel"
+       title="Rigging">
         <view_border
          bevel_style="none"
          follows="top|left"
-- 
cgit v1.2.3


From 7a9a114e168ea68345b54e80b3cfbac7deb4764a Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Mon, 3 Aug 2020 19:14:09 +0300
Subject: SL-13698 FIXED crash on model upload

---
 indra/llprimitive/lldaeloader.cpp                  | 26 +++++++++++++++++-----
 indra/llprimitive/lldaeloader.h                    |  2 +-
 .../skins/default/xui/en/floater_model_preview.xml |  1 +
 3 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 431443788c..b9c74b7039 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -343,7 +343,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 	return LLModel::NO_ERRORS ;
 }
 
-LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly)
+LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly, LLSD& log_msg)
 {
 	domPRef p = poly->getP();
 	domListOfUInts& idx = p->getValue();
@@ -403,6 +403,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 	LLVolumeFace::VertexMapData::PointMap point_map;
 
 	U32 cur_idx = 0;
+	bool log_tc_msg = true;
 	for (U32 i = 0; i < vcount.getCount(); ++i)
 	{ //for each polygon
 		U32 first_index = 0;
@@ -426,8 +427,21 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 
 			if (tc_source)
 			{
-				cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0],
-									tc[idx[cur_idx+tc_offset]*2+1]);
+				U64 idx_x = idx[cur_idx + tc_offset] * 2 + 0;
+				U64 idx_y = idx[cur_idx + tc_offset] * 2 + 1;
+
+				if (idx_y < tc.getCount())
+				{
+					cv.mTexCoord.setVec(tc[idx_x], tc[idx_y]);
+				}			
+				else if (log_tc_msg)
+				{
+					log_tc_msg = false;
+					LL_WARNS() << "Texture coordinates data is not complete." << LL_ENDL;
+					LLSD args;
+					args["Message"] = "IncompleteTC";
+					log_msg.append(args);
+				}
 			}
 			
 			if (norm_source)
@@ -2362,7 +2376,7 @@ LLColor4 LLDAELoader::getDaeColor(daeElement* element)
 	return value;
 }
 
-bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh)
+bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& log_msg)
 {
 	LLModel::EModelStatus status = LLModel::NO_ERRORS;
 	domTriangles_Array& tris = mesh->getTriangles_array();
@@ -2384,7 +2398,7 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh)
 	for (U32 i = 0; i < polys.getCount(); ++i)
 	{
 		domPolylistRef& poly = polys.get(i);
-		status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly);
+		status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly, log_msg);
 
 		if(status != LLModel::NO_ERRORS)
 		{
@@ -2448,7 +2462,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
 
 	// Get the whole set of volume faces
 	//
-	addVolumeFacesFromDomMesh(ret, mesh);
+	addVolumeFacesFromDomMesh(ret, mesh, mWarningsArray);
 
 	U32 volume_faces = ret->getNumVolumeFaces();
 
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index 4e990dbe5e..b2db538d32 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -89,7 +89,7 @@ protected:
 	//Verify that a controller matches vertex counts
 	bool verifyController( domController* pController );
 
-	static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh);
+	static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh, LLSD& log_msg = LLSD());
 	static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh);
 
 	static LLModel* loadModelFromDomMesh(domMesh* mesh);
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 4fa0b9af7d..9c1a9d2880 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -45,6 +45,7 @@
   <string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string>
   <string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string>
   <string name="ModelLoaded">Model [MODEL_NAME] loaded</string>
+  <string name="IncompleteTC">Texture coordinates data is not complete.</string>
 
   <panel
     follows="top|left"
-- 
cgit v1.2.3


From 3239f3518b835fcc9d709e0ced312e4e82a05118 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 11 Aug 2020 20:43:31 +0300
Subject: SL-13698 Mac build fix

---
 indra/llprimitive/lldaeloader.cpp | 3 ++-
 indra/llprimitive/lldaeloader.h   | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index b9c74b7039..3cb58d894e 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -2535,7 +2535,8 @@ bool LLDAELoader::createVolumeFacesFromDomMesh(LLModel* pModel, domMesh* mesh)
 	{
 		pModel->ClearFacesAndMaterials();
 
-		addVolumeFacesFromDomMesh(pModel, mesh);
+		LLSD placeholder;
+		addVolumeFacesFromDomMesh(pModel, mesh, placeholder);
 
 		if (pModel->getNumVolumeFaces() > 0)
 		{
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index b2db538d32..2b211343e1 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -89,7 +89,7 @@ protected:
 	//Verify that a controller matches vertex counts
 	bool verifyController( domController* pController );
 
-	static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh, LLSD& log_msg = LLSD());
+	static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh, LLSD& log_msg);
 	static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh);
 
 	static LLModel* loadModelFromDomMesh(domMesh* mesh);
-- 
cgit v1.2.3


From 777e896ee4035c61d312563810c507b2bb5382be Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 11 Aug 2020 21:27:52 +0300
Subject: DRTVWR-482 Use proper reshape procedure and fix hidden borders

---
 indra/newview/llfloatermodelpreview.cpp | 39 ++++++++++++++++++---------------
 indra/newview/llfloatermodelpreview.h   |  1 +
 2 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index ff024e1d44..ae9350867f 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -279,6 +279,24 @@ BOOL LLFloaterModelPreview::postBuild()
 	return TRUE;
 }
 
+//-----------------------------------------------------------------------------
+// reshape()
+//-----------------------------------------------------------------------------
+
+void LLFloaterModelPreview::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+    LLFloaterModelUploadBase::reshape(width, height, called_from_parent);
+
+    LLView* preview_panel = getChild<LLView>("preview_panel");
+    LLRect rect = preview_panel->getRect();
+
+    if (rect != mPreviewRect)
+    {
+        mModelPreview->refresh();
+        mPreviewRect = preview_panel->getRect();
+    }
+}
+
 //-----------------------------------------------------------------------------
 // LLFloaterModelPreview()
 //-----------------------------------------------------------------------------
@@ -692,29 +710,14 @@ void LLFloaterModelPreview::draw3dPreview()
 
 	gGL.getTexUnit(0)->bind(mModelPreview);
 
-
-	LLView* preview_panel = getChild<LLView>("preview_panel");
-
-	if (!preview_panel)
-	{
-		LL_WARNS() << "preview_panel not found in floater definition" << LL_ENDL;
-	}
-	LLRect rect = preview_panel->getRect();
-
-	if (rect != mPreviewRect)
-	{
-		mModelPreview->refresh();
-		mPreviewRect = preview_panel->getRect();
-	}
-
 	gGL.begin( LLRender::QUADS );
 	{
 		gGL.texCoord2f(0.f, 1.f);
-		gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop-1);
+		gGL.vertex2i(mPreviewRect.mLeft+1, mPreviewRect.mTop-1);
 		gGL.texCoord2f(0.f, 0.f);
-		gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom);
+		gGL.vertex2i(mPreviewRect.mLeft+1, mPreviewRect.mBottom+1);
 		gGL.texCoord2f(1.f, 0.f);
-		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom);
+		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom+1);
 		gGL.texCoord2f(1.f, 1.f);
 		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mTop-1);
 	}
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index bee64fb501..8a01b0c307 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -70,6 +70,7 @@ public:
 	virtual ~LLFloaterModelPreview();
 	
 	virtual BOOL postBuild();
+    /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
 	
 	void initModelPreview();
 
-- 
cgit v1.2.3


From d240d381a26605614d1b2ec37861ef22e92e08c2 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 12 Aug 2020 00:59:45 +0300
Subject: SL-13583 Some behavior tweaks #1

---
 doc/contributions.txt                                        |  1 +
 indra/newview/skins/default/xui/en/floater_model_preview.xml | 12 ++++++++++--
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/doc/contributions.txt b/doc/contributions.txt
index bf90770807..850b7469fb 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -261,6 +261,7 @@ Benjamin Bigdipper
 Beth Walcher
 Beq Janus
 	SL-10288
+	SL-13583
 Bezilon Kasei
 Biancaluce Robbiani
 	CT-225
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 9c1a9d2880..02a21764ce 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -1331,7 +1331,7 @@
         <view_border
          bevel_style="none"
          follows="top|left"
-         height="306"
+         height="289"
          layout="topleft"
          left="3"
          name="log_tab_border"
@@ -1345,7 +1345,7 @@
          font="SansSerif"
          ignore_tab="false"
          layout="topleft"
-         height="306"
+         height="289"
          left="4"
          top="0"
          right="-1"
@@ -1356,6 +1356,14 @@
          read_only="true"
          word_wrap="true">
         </text_editor>
+        <check_box
+         control_name="ImporterDebug"
+         follows="top|left"
+         top_pad="9"
+         left="6"
+         width="70"
+         label="Enable detailed logging"
+         name="verbose_logging"/>
       </panel>
     </tab_container>
     <panel
-- 
cgit v1.2.3


From 1f2ceb97ec4941a43ef3551a9fb41108cb65e1b2 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 12 Aug 2020 22:53:23 +0300
Subject: SL-13583 Some behavior tweaks #2

---
 indra/newview/llfloatermodelpreview.cpp |  2 ++
 indra/newview/llmodelpreview.cpp        | 12 ++++++++++++
 indra/newview/llmodelpreview.h          |  1 +
 3 files changed, 15 insertions(+)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index ae9350867f..1aafd52ee7 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -348,6 +348,8 @@ void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl)
 		auto name = ctrl->getName();
         // update the option and notifications
         // (this is a bit convoluted, because of the current structure of mModelPreview)
+        // FIX ME! mViewOption is malfunctioning here! mViewOption doesn't have values like "upload_skin"!
+        // This needs to translate values like "upload_skin" into "show_skin_weights"
         mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name];
         mModelPreview->refresh(); // a 'dirty' flag for render
         mModelPreview->resetPreviewTarget(); 
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 04a818b2a4..54350fc63d 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -180,6 +180,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
     , mResetJoints(false)
     , mModelNoErrors(true)
     , mLastJointUpdate(false)
+    , mFirstSkinUpdate(true)
     , mHasDegenerate(false)
     , mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false))
 {
@@ -2799,11 +2800,22 @@ BOOL LLModelPreview::render()
         {
             if (flags == LEGACY_RIG_OK)
             {
+                if (mFirstSkinUpdate)
+                {
+                    // auto enable weight upload if weights are present
+                    // (note: all these UI updates need to be somewhere that is not render)
+                    mViewOption["show_skin_weight"] = true;
+                    skin_weight = true;
+                    fmp->childSetValue("upload_skin", true);
+                    mFirstSkinUpdate = false;
+                }
+
                 fmp->enableViewOption("show_skin_weight");
                 fmp->setViewOptionEnabled("show_joint_overrides", skin_weight);
                 fmp->setViewOptionEnabled("show_joint_positions", skin_weight);
                 mFMP->childEnable("upload_skin");
                 mFMP->childSetValue("show_skin_weight", skin_weight);
+
             }
             else if ((flags & LEGACY_RIG_FLAG_TOO_MANY_JOINTS) > 0)
             {
diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h
index 9b8200ab8a..3664a27a72 100644
--- a/indra/newview/llmodelpreview.h
+++ b/indra/newview/llmodelpreview.h
@@ -301,6 +301,7 @@ protected:
     U32			mLegacyRigFlags;
 
     bool		mLastJointUpdate;
+    bool		mFirstSkinUpdate;
 
     JointNameSet		mJointsFromNode;
     JointTransformMap	mJointTransformMap;
-- 
cgit v1.2.3


From 0ee7e9dadb0b9d6b6c4007faafe1277cee64392d Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 19 Aug 2020 23:26:44 +0300
Subject: SL-13821 Fix upload options affecting preview options incorrectly

---
 indra/newview/llfloatermodelpreview.cpp | 32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 1aafd52ee7..d03b526dce 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -346,11 +346,37 @@ void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl)
 	if (mModelPreview)
 	{
 		auto name = ctrl->getName();
+        bool value = ctrl->getValue().asBoolean();
         // update the option and notifications
         // (this is a bit convoluted, because of the current structure of mModelPreview)
-        // FIX ME! mViewOption is malfunctioning here! mViewOption doesn't have values like "upload_skin"!
-        // This needs to translate values like "upload_skin" into "show_skin_weights"
-        mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name];
+        if (name == "upload_skin")
+        {
+            childSetValue("show_skin_weight", value);
+            mModelPreview->mViewOption["show_skin_weight"] = value;
+            if (!value)
+            {
+                mModelPreview->mViewOption["show_joint_overrides"] = false;
+                mModelPreview->mViewOption["show_joint_positions"] = false;
+            }
+        }
+        else if (name == "upload_joints")
+        {
+            if (mModelPreview->mViewOption["show_skin_weight"])
+            {
+                childSetValue("show_joint_overrides", value);
+                mModelPreview->mViewOption["show_joint_overrides"] = value;
+            }
+        }
+        else if (name == "upload_textures")
+        {
+            childSetValue("show_textures", value);
+            mModelPreview->mViewOption["show_textures"] = value;
+        }
+        else if (name == "lock_scale_if_joint_position")
+        {
+            mModelPreview->mViewOption["lock_scale_if_joint_position"] = value;
+        }
+
         mModelPreview->refresh(); // a 'dirty' flag for render
         mModelPreview->resetPreviewTarget(); 
         mModelPreview->clearBuffers();
-- 
cgit v1.2.3


From bdedfb3755d54a6b4468a5251c9ddeae2b092d37 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 28 Aug 2020 21:00:04 +0300
Subject: SL-13566 'Use Joint Positions' Option causes collapsed Joints for
 some files

---
 indra/llprimitive/lldaeloader.cpp | 104 +++++++++++++++++++-------------------
 1 file changed, 53 insertions(+), 51 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 3cb58d894e..d2aa3d8dc6 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -1229,7 +1229,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
 				for (S32 i = 0; i < childCount; ++i)
 				{
 					domNode* pNode = daeSafeCast<domNode>(children[i]);
-					if ( isNodeAJoint( pNode ) )
+					if (pNode)
 					{
 						processJointNode( pNode, mJointList );
 					}
@@ -1854,59 +1854,61 @@ void LLDAELoader::processJointNode( domNode* pNode, JointTransformMap& jointTran
 	//LL_WARNS()<<"ProcessJointNode# Node:" <<pNode->getName()<<LL_ENDL;
 
 	//1. handle the incoming node - extract out translation via SID or element
+    if (isNodeAJoint(pNode))
+    {
+        LLMatrix4 workingTransform;
 
-	LLMatrix4 workingTransform;
-
-	//Pull out the translate id and store it in the jointTranslations map
-	daeSIDResolver jointResolverA( pNode, "./translate" );
-	domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() );
-	daeSIDResolver jointResolverB( pNode, "./location" );
-	domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() );
+        //Pull out the translate id and store it in the jointTranslations map
+        daeSIDResolver jointResolverA(pNode, "./translate");
+        domTranslate* pTranslateA = daeSafeCast<domTranslate>(jointResolverA.getElement());
+        daeSIDResolver jointResolverB(pNode, "./location");
+        domTranslate* pTranslateB = daeSafeCast<domTranslate>(jointResolverB.getElement());
 
-	//Translation via SID was successful
-	if ( pTranslateA )
-	{
-		extractTranslation( pTranslateA, workingTransform );
-	}
-	else
-	if ( pTranslateB )
-	{
-		extractTranslation( pTranslateB, workingTransform );
-	}
-	else
-	{
-		//Translation via child from element
-		daeElement* pTranslateElement = getChildFromElement( pNode, "translate" );
-		if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() )
-		{
-			//LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL;
-			daeSIDResolver jointResolver( pNode, "./matrix" );
-			domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() );
-			if ( pMatrix )
-			{
-				//LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL;
-				domFloat4x4 domArray = pMatrix->getValue();									
-				for ( int i = 0; i < 4; i++ )
-				{
-					for( int j = 0; j < 4; j++ )
-					{
-						workingTransform.mMatrix[i][j] = domArray[i + j*4];
-					}
-				}
-			}
-			else
-			{
-				LL_WARNS()<< "The found element is not translate or matrix node - most likely a corrupt export!" <<LL_ENDL;
-			}
-		}
-		else
-		{
-			extractTranslationViaElement( pTranslateElement, workingTransform );
-		}
-	}
+        //Translation via SID was successful
+        if (pTranslateA)
+        {
+            extractTranslation(pTranslateA, workingTransform);
+        }
+        else
+            if (pTranslateB)
+            {
+                extractTranslation(pTranslateB, workingTransform);
+            }
+            else
+            {
+                //Translation via child from element
+                daeElement* pTranslateElement = getChildFromElement(pNode, "translate");
+                if (!pTranslateElement || pTranslateElement->typeID() != domTranslate::ID())
+                {
+                    //LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL;
+                    daeSIDResolver jointResolver(pNode, "./matrix");
+                    domMatrix* pMatrix = daeSafeCast<domMatrix>(jointResolver.getElement());
+                    if (pMatrix)
+                    {
+                        //LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL;
+                        domFloat4x4 domArray = pMatrix->getValue();
+                        for (int i = 0; i < 4; i++)
+                        {
+                            for (int j = 0; j < 4; j++)
+                            {
+                                workingTransform.mMatrix[i][j] = domArray[i + j * 4];
+                            }
+                        }
+                    }
+                    else
+                    {
+                        LL_WARNS() << "The found element is not translate or matrix node - most likely a corrupt export!" << LL_ENDL;
+                    }
+                }
+                else
+                {
+                    extractTranslationViaElement(pTranslateElement, workingTransform);
+                }
+            }
 
-	//Store the working transform relative to the nodes name.
-	jointTransforms[ pNode->getName() ] = workingTransform;
+        //Store the working transform relative to the nodes name.
+        jointTransforms[pNode->getName()] = workingTransform;
+    }
 
 	//2. handle the nodes children
 
-- 
cgit v1.2.3


From 1ef45913812cc65a5b18a1c0c9248fecd48f06c1 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 23 Sep 2020 12:10:06 +0300
Subject: SL-13821 Fixed checkbox not disabling

---
 indra/newview/llfloatermodelpreview.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index d03b526dce..b9c03f66a3 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -357,6 +357,8 @@ void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl)
             {
                 mModelPreview->mViewOption["show_joint_overrides"] = false;
                 mModelPreview->mViewOption["show_joint_positions"] = false;
+                childSetValue("show_joint_overrides", false);
+                childSetValue("show_joint_positions", false);
             }
         }
         else if (name == "upload_joints")
-- 
cgit v1.2.3


From 89839721eadf0b5379042530e01c9e5061ba6c35 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 24 Sep 2020 23:52:53 +0300
Subject: SL-13888 Restore back face culling being accidently removed in
 SL-12781

Due to conflicts picked up ptolemy's fix manually
---
 indra/newview/llmodelpreview.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 54350fc63d..d60d1d748e 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -2732,6 +2732,7 @@ BOOL LLModelPreview::render()
 
     LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test
     LLGLDisable no_blend(GL_BLEND);
+    LLGLEnable cull(GL_CULL_FACE);
     LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color
     LLGLDisable fog(GL_FOG);
 
-- 
cgit v1.2.3


From a2c8c8238cfb109e0da81363995e08e99173426f Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Wed, 14 Oct 2020 14:55:31 -0400
Subject: Increment viewer version to 6.4.11 following promotion of DRTVWR-482

---
 indra/newview/VIEWER_VERSION.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index b03e20c456..e5a66bad38 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-6.4.10
+6.4.11
-- 
cgit v1.2.3