From 580b35c8ea59187d5197e712022b706df3655f86 Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Mon, 1 Apr 2019 20:22:25 +0300
Subject: [PATCH] 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 +-
 .../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 4e468ff45f5..c8da68afc8f 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 643c45a6d8e..8dde176b542 100644
--- a/indra/llprimitive/llmodelloader.h
+++ b/indra/llprimitive/llmodelloader.h
@@ -185,6 +185,9 @@ class LLModelLoader : public LLThread
 		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 @@ class LLModelLoader : public LLThread
 
 	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 1b2f09cff59..0af97bfab26 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 4a5f08f5d3c..a8cf380333f 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -109,6 +109,11 @@ class LLTabContainer : public LLPanel
 		 * 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 @@ class LLTabContainer : public LLPanel
 
 	bool							mCustomIconCtrlUsed;
 	bool							mOpenTabsOnDragAndDrop;
+	bool							mEnableTabsFlashing;
 	S32								mTabIconCtrlPad;
 	bool							mUseTabEllipses;
 };
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index c570285856d..f4713e19ba8 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 5fdde445ef5..6913374bac9 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -401,6 +401,7 @@ class LLTextBase
 	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 @@ class LLTextBase
 
 	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 8be405df9e2..6387dfee55e 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 096544cdbf4..c459b9296b1 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 @@ class LLFloaterModelPreview : public LLFloaterModelUploadBase
 
 	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 @@ class LLFloaterModelPreview : public LLFloaterModelUploadBase
     // 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 @@ class LLFloaterModelPreview : public LLFloaterModelUploadBase
 
 	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 274e6e6c7ac..cbc5aeb37d6 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"
-- 
GitLab