diff options
32 files changed, 5183 insertions, 3808 deletions
| diff --git a/doc/contributions.txt b/doc/contributions.txt index e2b69a9fe4..07a96d8766 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -261,6 +261,9 @@ Benjamin Bigdipper  Beq Janus  	BUG-227094  Beth Walcher +Beq Janus +	SL-10288 +	SL-13583  Bezilon Kasei  Biancaluce Robbiani  	CT-225 diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp index a685df5925..dee642310e 100644 --- a/indra/llcharacter/lljoint.cpp +++ b/indra/llcharacter/lljoint.cpp @@ -408,7 +408,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;  } @@ -511,7 +511,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(); @@ -525,7 +525,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 aa997a4cf7..1b646b641f 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: @@ -287,9 +289,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/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index f18f112153..dfa29fb539 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) @@ -1215,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 );  					} @@ -1471,6 +1485,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(); @@ -1835,59 +1855,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 @@ -2357,7 +2379,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(); @@ -2379,7 +2401,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)  		{ @@ -2443,7 +2465,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(); @@ -2516,7 +2538,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 4e990dbe5e..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); +	static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh, LLSD& log_msg);  	static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh);  	static LLModel* loadModelFromDomMesh(domMesh* mesh); diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp index 4e468ff45f..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,6 +147,7 @@ LLModelLoader::~LLModelLoader()  void LLModelLoader::run()  { +	mWarningsArray.clear();  	doLoadModel();  	doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));  } @@ -387,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. @@ -401,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 ) @@ -426,7 +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; -        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 @@ -437,16 +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; +            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; -        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 643c45a6d8..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,6 +190,9 @@ public:  		return name != NULL && mJointMap.find(name) != mJointMap.end();  	} +	const LLSD logOut() const { return mWarningsArray; } +	void clearLog() { mWarningsArray.clear(); } +  protected:  	LLModelLoader::load_callback_t		mLoadCallback; @@ -194,13 +202,15 @@ protected:  	void*								mOpaqueData;  	bool		mRigValidJointUpload; -	bool		mLegacyRigValid; +	U32			mLegacyRigFlags;  	bool		mNoNormalize;  	bool		mNoOptimize;  	JointTransformMap	mJointTransformMap; +	LLSD mWarningsArray; // preview floater will pull logs from here +  	static std::list<LLModelLoader*> sActiveLoaderList;  	static bool isAlive(LLModelLoader* loader) ;  }; diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 27444b7f5b..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; @@ -734,6 +736,11 @@ void LLButton::draw()  			{                  glow_color = highlighting_color;  			} +            else +            { +                // will fade from highlight color +                glow_color = flash_color; +            }  		}  	} @@ -806,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);  			}  		} @@ -817,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/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/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 8570dcf318..dc525517bf 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"), @@ -162,7 +163,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 43e1c0d707..8d00296183 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/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 6521b883f8..e6b43da8e5 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -220,6 +220,8 @@ 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), +	tabs_flashing_color("tabs_flashing_color"),  	tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),  	use_ellipses("use_ellipses"),  	font_halign("halign") @@ -259,6 +261,8 @@ 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), +	mTabsFlashingColor(p.tabs_flashing_color),  	mUseTabEllipses(p.use_ellipses)  {  	static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0); @@ -280,6 +284,11 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)  		mMinTabWidth = tabcntr_vert_tab_min_width;  	} +    if (p.tabs_flashing_color.isProvided()) +    { +        mEnableTabsFlashing = true; +    } +  	initButtons( );  } @@ -1102,6 +1111,10 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)  		    p.pad_left( mLabelPadLeft );  		    p.pad_right(2);  		} + +		// 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) diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 6bf963313c..8f8cedb1b9 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -109,6 +109,12 @@ public:  		 * Open tabs on hover in drag and drop situations  		 */  		Optional<bool>						open_tabs_on_drag_and_drop; + +		/** +		 * 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) @@ -310,6 +316,8 @@ private:  	bool							mCustomIconCtrlUsed;  	bool							mOpenTabsOnDragAndDrop; +	bool							mEnableTabsFlashing; +	LLUIColor						mTabsFlashingColor;  	S32								mTabIconCtrlPad;  	bool							mUseTabEllipses;  }; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 30bf938591..ff72417867 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -2259,6 +2259,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 8687e7aa2a..4e966b7cef 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -404,6 +404,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); @@ -432,6 +433,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/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7d4ec7ac38..fa148f1719 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -401,6 +401,7 @@ set(viewer_SOURCE_FILES      llmenuoptionpathfindingrebakenavmesh.cpp      llmeshrepository.cpp      llmimetypes.cpp +    llmodelpreview.cpp      llmorphview.cpp      llmoveview.cpp      llmutelist.cpp @@ -1032,6 +1033,7 @@ set(viewer_HEADER_FILES      llmenuoptionpathfindingrebakenavmesh.h      llmeshrepository.h      llmimetypes.h +    llmodelpreview.h      llmorphview.h      llmoveview.h      llmutelist.h 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 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 62c73c0e45..c6cc93a8c3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -6719,7 +6719,7 @@      <integer>600</integer>    </map>    <key>MigrateCacheDirectory</key> -    <map> +  <map>        <key>Comment</key>        <string>Check for old version of disk cache to migrate to current location</string>        <key>Persist</key> @@ -7969,7 +7969,6 @@        <key>Value</key>  	  <integer>13</integer>      </map> -    <key>PreviewAmbientColor</key>    <map>      <key>Comment</key> @@ -7986,8 +7985,6 @@        <real>1.0</real>      </array>    </map> - -    <key>PreviewDiffuse0</key>    <map>      <key>Comment</key> @@ -16618,3 +16615,4 @@  </map>  </llsd> + diff --git a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl index 88959266c8..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); -		  	vertex_color = col*color;  } diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index 1e8c57ac6a..89c20904c1 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -125,11 +125,11 @@ BOOL LLViewerDynamicTexture::render()  //-----------------------------------------------------------------------------  void LLViewerDynamicTexture::preRender(BOOL clear_depth)  { -	//only images up to 1024*1024 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.mBake.isComplete()) +	if (gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsATI)  	{ //using offscreen render target, just use the bottom left corner  		mOrigin.set(0, 0);  	} @@ -216,7 +216,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances()  		return TRUE;  	} -	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mBake.isComplete(); +	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mBake.isComplete() && !gGLManager.mIsATI;  	if (use_fbo)  	{ diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index bc44e37c5a..b9c03f66a3 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,15 +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"  #include "llnotificationsutil.h"  #include "llsdutil_math.h" @@ -56,44 +48,26 @@  #include "lltextbox.h"  #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 "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 "lltoggleablemenu.h" +#include "lltabcontainer.h"  #include "lltrans.h" -#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. @@ -106,114 +80,20 @@ 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; -// mCameraDistance -// Also see: mCameraZoom -const F32 MODEL_PREVIEW_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] = +class LLMeshFilePicker : public LLFilePickerThread  { -	"lowest_vertices", -	"low_vertices", -	"medium_vertices", -	"high_vertices", -	"I went off the end of the lod_vertices_name array.  Me so smart." -}; +public: +    LLMeshFilePicker(LLModelPreview* mp, S32 lod); +    virtual void notify(const std::vector<std::string>& filenames); -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." +private: +    LLModelPreview* mMP; +    S32 mLOD;  }; -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." -}; - -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." -}; - -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." -}; - -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,37 +114,16 @@ 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()  //-----------------------------------------------------------------------------  LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) :  LLFloaterModelUploadBase(key),  mUploadBtn(NULL), -mCalculateBtn(NULL) +mCalculateBtn(NULL), +mUploadLogText(NULL), +mTabContainer(NULL), +mAvatarTabIndex(0)  {  	sInstance = this;  	mLastMouseX = 0; @@ -307,10 +166,11 @@ 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); +	// 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); +	childSetCommitCallback("upload_textures", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);  	childSetTextArg("status", "[STATUS]", getString("status_idle")); @@ -321,10 +181,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); -  	childSetCommitCallback("import_scale", onImportScaleCommit, this);  	childSetCommitCallback("pelvis_offset", onPelvisOffsetCommit, this); @@ -333,13 +189,20 @@ 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_overrides")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));  	getChild<LLCheckBoxCtrl>("show_joint_positions")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1));  	childDisable("upload_skin");  	childDisable("upload_joints");  	childDisable("lock_scale_if_joint_position"); +	childSetVisible("skin_too_many_joints", false); +	childSetVisible("skin_unknown_joint", false); + +    childSetVisible("warning_title", false); +    childSetVisible("warning_message", false); +  	initDecompControls();  	LLView* preview_panel = getChild<LLView>("preview_panel"); @@ -395,6 +258,12 @@ BOOL LLFloaterModelPreview::postBuild()  	mUploadBtn = getChild<LLButton>("ok_btn");  	mCalculateBtn = getChild<LLButton>("calculate_btn"); +	mUploadLogText = getChild<LLViewerTextEditor>("log_text"); +	mTabContainer = getChild<LLTabContainer>("import_tab"); + +    LLPanel *panel = mTabContainer->getPanelByName("rigging_panel"); +    mAvatarTabIndex = mTabContainer->getIndexForPanel(panel); +    panel->getChild<LLScrollListCtrl>("joints_list")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onJointListSelection, this));  	if (LLConvexDecomposition::getInstance() != NULL)  	{ @@ -411,6 +280,24 @@ BOOL LLFloaterModelPreview::postBuild()  }  //----------------------------------------------------------------------------- +// 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()  //-----------------------------------------------------------------------------  LLFloaterModelPreview::~LLFloaterModelPreview() @@ -433,18 +320,95 @@ void LLFloaterModelPreview::initModelPreview()  		delete mModelPreview;  	} -	mModelPreview = new LLModelPreview(512, 512, this ); -	mModelPreview->setPreviewTarget(MODEL_PREVIEW_CAMERA_DISTANCE); +	S32 tex_width = 512; +	S32 tex_height = 512; + +	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; +	} +	while ((tex_height << 1) < max_height) +	{ +		tex_height <<= 1; +	} + +	mModelPreview = new LLModelPreview(tex_width, tex_height, this); +    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));  } +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) +        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; +                childSetValue("show_joint_overrides", false); +                childSetValue("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(); +        mModelPreview->mDirty = true; +    } +    // set the button visible, it will be refreshed later +	toggleCalculateButton(true); +} + +void LLFloaterModelPreview::onShowSkinWeightChecked(LLUICtrl* ctrl) +{ +	if (mModelPreview) +	{ +		mModelPreview->mCameraOffset.clearVec(); +		onViewOptionChecked(ctrl); +	} +} +  void LLFloaterModelPreview::onViewOptionChecked(LLUICtrl* ctrl)  {  	if (mModelPreview)  	{ -		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();  	}  } @@ -479,6 +443,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; @@ -500,19 +470,14 @@ 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();  	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(); @@ -526,6 +491,132 @@ void LLFloaterModelPreview::onClickCalculateBtn()  	mUploadBtn->setEnabled(false);  } +// 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) +{ +    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 std::string no_override_placeholder = "-"; + +    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 map_iter = data.mPosOverrides.begin(); +    std::map<std::string, LLVector3>::const_iterator map_end = data.mPosOverrides.end(); +    while (map_iter != map_end) +    { +        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++; +    } + +    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++; +        set_iter++; +    } +} + +void LLFloaterModelPreview::onJointListSelection() +{ +    S32 display_lod = mModelPreview->mPreviewLOD; +    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"); +    LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr"); + +    joints_pos->deleteAllItems(); +    joints_scale->deleteAllItems(); + +    LLScrollListItem *selected = joints_list->getFirstSelected(); +    if (selected) +    { +        std::string label = selected->getValue().asString(); +        LLJointOverrideData &data = mJointOverrides[display_lod][label]; +        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; +    } +    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 +} +  void LLFloaterModelPreview::onDescriptionKeystroke(LLUICtrl* ctrl)  {  	// Workaround for SL-4186, server doesn't allow name changes after 'calculate' stage @@ -570,33 +661,6 @@ void LLFloaterModelPreview::onPelvisOffsetCommit( LLUICtrl*, void* userdata )  }  //static -void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata) -{ -	LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; - -	if (!fp->mModelPreview) -	{ -		return; -	} - -	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)  {  	LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; @@ -626,6 +690,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(); @@ -669,6 +734,27 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)  	}  } +void LLFloaterModelPreview::draw3dPreview() +{ +	gGL.color3f(1.f, 1.f, 1.f); + +	gGL.getTexUnit(0)->bind(mModelPreview); + +	gGL.begin( LLRender::QUADS ); +	{ +		gGL.texCoord2f(0.f, 1.f); +		gGL.vertex2i(mPreviewRect.mLeft+1, mPreviewRect.mTop-1); +		gGL.texCoord2f(0.f, 0.f); +		gGL.vertex2i(mPreviewRect.mLeft+1, mPreviewRect.mBottom+1); +		gGL.texCoord2f(1.f, 0.f); +		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom+1); +		gGL.texCoord2f(1.f, 1.f); +		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mTop-1); +	} +	gGL.end(); + +	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); +}  //-----------------------------------------------------------------------------  // draw() @@ -715,36 +801,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();  	}  } @@ -843,8 +902,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*/ @@ -1104,7 +1166,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);  					} @@ -1115,10 +1178,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(is_smooth_cb ? 0: param[i].mDefault.mFloat);  					combo_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]);  				}  			} @@ -1190,7 +1251,7 @@ void LLFloaterModelPreview::initDecompControls()  			//LL_INFOS() << "-----------------------------" << LL_ENDL;  		}  	} - +	mDefaultDecompParams = mDecompParams;  	childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this);  } @@ -1220,3089 +1281,279 @@ void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handl  }  //----------------------------------------------------------------------------- -// LLModelPreview +// addStringToLog()  //----------------------------------------------------------------------------- - -LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) -: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex() -, mLodsQuery() -, mLodsWithParsingError() -, mPelvisZOffset( 0.0f ) -, mLegacyRigValid( false ) -, mRigValidJointUpload( false ) -, mPhysicsSearchLOD( LLModel::LOD_PHYSICS ) -, mResetJoints( false ) -, mModelNoErrors( true ) -, mLastJointUpdate( 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(); -	childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x)); -	childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", y)); -	childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", z)); -} - -void LLFloaterModelPreview::setPreviewLOD(S32 lod) -{ -	if (mModelPreview) -	{ -		mModelPreview->setPreviewLOD(lod); -	} -} - - -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 importerDebug = gSavedSettings.getBOOL("ImporterDebug"); -	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 (importerDebug) -						{ -							LL_INFOS() << "Search of" << name_to_match << " in LOD" << i << " list failed. Searching for alternative among LOD lists." << LL_ENDL; -						} - -						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 (importerDebug) -							{ -								LL_INFOS() << "Attempting to use model index " << idx << " for LOD " << i << " of " << instance.mLabel << LL_ENDL; -							} -							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 (importerDebug) -						{ -							LL_INFOS() << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel << LL_ENDL; -						} -					} -					else if (importerDebug) -					{ -						LL_INFOS() << "List of models does not include index " << idx << LL_ENDL; -					} -				} - -				if (lod_model) -				{ -					if (importerDebug) -					{ -						if (i == LLModel::LOD_PHYSICS) -						{ -							LL_INFOS() << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel << LL_ENDL; -						} -						else -						{ -							LL_INFOS() << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel << LL_ENDL; -						} -					} -					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 (importerDebug) -					{ -						LL_INFOS() << "List of models does not include " << instance.mLabel << LL_ENDL; -					} -				} -			} - -			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)) -                    { -                        LL_WARNS() << "non-identity bind shape rot. mat is " << high_lod_model->mSkinInfo.mBindShapeMatrix  -                                   << " bind_rot " << bind_rot << LL_ENDL; -                        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 (importerDebug) -				{ -					LL_INFOS() << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models." <<  LL_ENDL; -				} -				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) +//static +void LLFloaterModelPreview::addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod)  { -	if (!mLODFile[LLModel::LOD_HIGH].empty()) -	{ -		std::string filename = mLODFile[LLModel::LOD_HIGH]; -        std::string slm_filename; - -        if (LLModelLoader::getSLMFilename(filename, slm_filename)) +    if (sInstance && sInstance->hasString(message)) +    { +        std::string str; +        switch (lod)          { -			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(); +        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); +    }  } -void LLModelPreview::getJointAliases( JointMap& joint_map) +// static +void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash)  { -    // 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) +    if (sInstance)      { -        joint_map[*it] = *it; +        sInstance->addStringToLogTab(str, flash);      }  } -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) -	{ -		LL_WARNS() << "Invalid level of detail: " << lod << LL_ENDL; -		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()); -	setLegacyRigValid(mModelLoader->isLegacyRigValid()); - -	mModelLoader->loadTextures() ; - -	if (loaded_lod == -1) -	{ //populate all LoDs from model loader scene -		mBaseModel.clear(); -		mBaseScene.clear(); - -		bool skin_weights = false; -		bool joint_positions = 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_positions = 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_positions) -			{  -				fmp->enableViewOption("show_joint_positions"); -				mViewOption["show_joint_positions"] = true; -				fmp->childSetValue("upload_joints", true); -			} - -			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 importerDebug = gSavedSettings.getBOOL("ImporterDebug"); -			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 (importerDebug) -							{ -								LL_INFOS() << "High LOD has submodels" << LL_ENDL; -							} -							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 (importerDebug) -					{ -						LL_INFOS() << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found" << LL_ENDL; -					} - -					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 (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; -								} - -								mModel[loaded_lod][idx]->mLabel = name; -							} -						} -					} -				} -			} -		} - -		clearIncompatible(loaded_lod); - -		mDirty = true; - -		if (loaded_lod == LLModel::LOD_HIGH) -		{ -			resetPreviewTarget(); -		} -	} - -	mLoading = false; -	if (mFMP) -	{ -		mFMP->getChild<LLCheckBoxCtrl>("confirm_checkbox")->set(FALSE); -		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); -			} -		} -	} -	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() +// static +void LLFloaterModelPreview::addStringToLog(const std::ostringstream& strm, bool flash)  { -	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(); +    if (sInstance) +    { +        sInstance->addStringToLogTab(strm.str(), flash); +    }  } -void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) +void LLFloaterModelPreview::clearAvatarTab()  { -	// 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; -		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); +    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"); +    joints_pos->deleteAllItems();    mSelectedJointName.clear(); -			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; -			} +    for (U32 i = 0; i < LLModel::NUM_LODS; ++i) +    { +        mJointOverrides[i].clear(); +    } -			delete [] sizes; -			delete [] names; -		} +    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)); -		//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(); -	} +    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 LLModelPreview::updateStatusMessages() +void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)  { -	assert_main_thread(); - -	//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++) +    S32 display_lod = mModelPreview->mPreviewLOD; +    if (mModelPreview->mModel[display_lod].empty())      { -        total_tris[i] = 0; -	    total_verts[i] = 0; -	    total_submeshes[i] = 0; +        mSelectedJointName.clear(); +        return;      } -    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) +    // 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 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)              { -                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; - -                BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug"); -                if (importerDebug) +                LLModelInstance& instance = *model_iter; +                LLModel* model = instance.mModel; +                const LLMeshSkinInfo *skin = &model->mSkinInfo; +                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)                  { -                    // 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; - -                    LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin(); -                    while (mat_iter != lod_model->mMaterialList.end()) +                    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) +                { +                    for (U32 j = 0; j < joint_count; ++j)                      { -                        LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter) << LL_ENDL; -                        mat_iter++; +                        const LLVector3& joint_pos = skin->mAlternateBindMatrix[j].getTranslation(); +                        LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]]; + +                        LLJoint* pJoint = LLModelPreview::lookupJointByName(skin->mJointNames[j], mModelPreview); +                        if (pJoint) +                        { +                            // 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()); +                            } +                        }                      }                  } - -                //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); -			} -		} +                else +                { +                    for (U32 j = 0; j < joint_count; ++j) +                    { +                        LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]]; +                        data.mModelsNoOverrides.insert(model->getName()); +                    } +                } +            } +        }      } -	if (mMaxTriangleLimit == 0) -	{ -		mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; -	} - -	bool has_degenerate = 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 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 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; ) -					{ -						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)) -						{ -							has_degenerate = true; -						} -						else -						{ -							k += 3; -						} -					} -				} -			} -		} -	} - -	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; -				} -			} -		} -	} -	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; -		} -	} - -	// 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) -	{ -		mFMP->childDisable("ok_btn"); -	} +    LLPanel *panel = mTabContainer->getPanelByName("rigging_panel"); +    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list"); -    if (mModelNoErrors && mLodsWithParsingError.empty()) -    { -        mFMP->childEnable("calculate_btn"); -    } -    else +    if (joints_list->isEmpty())      { -        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) -	{ -		LL_WARNS() << "Invalid level of detail: " << lod << LL_ENDL; -		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; +        // Populate table -			bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); +        std::map<std::string, std::string> joint_alias_map; +        mModelPreview->getJointAliases(joint_alias_map); -			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 -				LL_WARNS() << "Failed to allocate Vertex Buffer for model preview " -					<< num_vertices << " vertices and " -					<< num_indices << " indices" << LL_ENDL; -			} - -			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); +        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; -			vertex_count += num_vertices; -			tri_count += num_indices/3; -			++mesh_count; +            LLScrollListItem::Params item_params; +            item_params.value(listName); -		} -	} -} +            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++; +            } +            if (highlight_overrides && joint_iter->second.mPosOverrides.size() > 0) +            { +                cell_params.font.style = "BOLD"; +            } -void LLModelPreview::update() -{ -    if (mGenLOD) -    { -        bool subscribe_for_generation = mLodsQuery.empty(); -        mGenLOD = false; -        mDirty = true; -        mLodsQuery.clear(); +            item_params.columns.add(cell_params); -        for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod) -        { -            // adding all lods into query for generation -            mLodsQuery.push_back(lod); +            joints_list->addRow(item_params, ADD_BOTTOM); +            joint_iter++;          } - -        if (subscribe_for_generation) +        joints_list->selectFirstItem(); +        LLScrollListItem *selected = joints_list->getFirstSelected(); +        if (selected)          { -            doOnIdleRepeating(lodQueryCallback); +            mSelectedJointName = selected->getValue().asString();          } -    } -    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) -	{ -		pPreview->loadModelCallback(lod); -	}	 -} - -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;	 +        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())); +    }  } -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() +// addStringToLogTab()  //----------------------------------------------------------------------------- -BOOL LLModelPreview::render() +void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash)  { -	assert_main_thread(); - -	LLMutexLock lock(this); -	mNeedsUpdate = FALSE; - -	bool use_shaders = LLGLSLShader::sNoFixedFunction; - -	bool edges = mViewOption["show_edges"]; -	bool joint_positions = mViewOption["show_joint_positions"]; -	bool skin_weight = mViewOption["show_skin_weight"]; -	bool textures = mViewOption["show_textures"]; -	bool physics = mViewOption["show_physics"]; - -	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.color4f(0.169f, 0.169f, 0.169f, 1.f); - -		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 -		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); -		} -	} -	else -	{ -		mFMP->childDisable("upload_skin"); -		if (fmp) -		{ -			mViewOption["show_skin_weight"] = false; -			fmp->disableViewOption("show_skin_weight"); -			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 (str.empty()) +    { +        return; +    } -	if (!upload_skin && upload_joints) -	{ //can't upload joints if not uploading skin weights -		mFMP->childSetValue("upload_joints", false); -		upload_joints = false;		 -	}	 +    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; +    } -    if (upload_skin && upload_joints) +    // 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)      { -        mFMP->childEnable("lock_scale_if_joint_position"); +        mUploadLogText->getTextBoundingRect();// forces a reflow() to fix line count      } -    else +    while (editor_max_len < (editor_text_len + add_text_len))      { -        mFMP->childDisable("lock_scale_if_joint_position"); -        mFMP->childSetValue("lock_scale_if_joint_position", false); +        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; +        }      } -     -	//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; +    mUploadLogText->appendText(str, true); -	F32 z_near = 0.001f; -	F32 z_far = mCameraDistance*10.0f+mPreviewScale.magVec()+mCameraOffset.magVec(); - -	if (skin_weight) -	{ -		target_pos = getPreviewAvatar()->getPositionAgent(); -		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; -	LLViewerCamera::getInstance()->setOriginAndLookAt( -													  target_pos + ((LLVector3(mCameraDistance, 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(); -	const F32 BRIGHTNESS = 0.9f; -	gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); - -	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.diffuseColor4f(1,1,1,1); -						} - -						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); - -						if (edges) -						{ -							glLineWidth(3.f); -							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(); -								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); - -									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); - -									glLineWidth(2.f); -									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(); -						} - -					glLineWidth(3.f); -					glPointSize(8.f); -					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); -								} -							 -								for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i) -								{ -									LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - -									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); - -									for (U32 i = 0; i < buffer->getNumIndices(); i += 3) -									{ -										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)) -										{ -											buffer->draw(LLRender::LINE_LOOP, 3, i); -											buffer->draw(LLRender::POINTS, 3, i); -										} -									} -								} -							} -						} - -						gGL.popMatrix(); -					} -					glLineWidth(1.f); -					glPointSize(1.f); -					gPipeline.enableLightsPreview(); -					gGL.setSceneBlendType(LLRender::BT_ALPHA); -				} -			} -		} -		else -		{ -			target_pos = getPreviewAvatar()->getPositionAgent(); - -			LLViewerCamera::getInstance()->setOriginAndLookAt( -															  target_pos + ((LLVector3(mCameraDistance, 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()) -					{ -						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]; -                            const LLMeshSkinInfo *skin = &model->mSkinInfo; -							U32 count = LLSkinningUtil::getMeshJointCount(skin); -                            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); -							gGL.diffuseColor3f(0.4f, 0.4f, 0.4f); - -							if (edges) -							{ -								glLineWidth(3.f); -								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(); -				getPreviewAvatar()->renderBones(); -				if (shader) -				{ -					shader->bind(); -				} -			} - -		} -	} - -	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; - -	mCameraZoom	= llclamp(new_zoom, 1.f, 10.f); +    if (flash) +    { +        LLPanel* panel = mTabContainer->getPanelByName("logs_panel"); +        if (mTabContainer->getCurrentPanel() != panel) +        { +            mTabContainer->setTabPanelFlashing(panel, true); +        } +    }  } -void LLModelPreview::pan(F32 right, F32 up) +void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)  { -	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); +	assert_main_thread(); +	childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x)); +	childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", y)); +	childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", z));  } -void LLModelPreview::setPreviewLOD(S32 lod) +void LLFloaterModelPreview::setPreviewLOD(S32 lod)  { -	lod = llclamp(lod, 0, (S32) LLModel::LOD_HIGH); - -	if (lod != mPreviewLOD) +	if (mModelPreview)  	{ -		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]); - -		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"); - -		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); -		} +		mModelPreview->setPreviewLOD(lod);  	} -	refresh(); -	updateStatusMessages();  }  void LLFloaterModelPreview::onBrowseLOD(S32 lod) @@ -4317,12 +1568,16 @@ 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(); @@ -4336,6 +1591,7 @@ void LLFloaterModelPreview::onUpload(void* user_data)  	assert_main_thread();  	LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; +	mp->clearLogTab();  	mp->mUploadBtn->setEnabled(false); @@ -4364,60 +1620,6 @@ void LLFloaterModelPreview::refresh()  	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; @@ -4430,6 +1632,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); @@ -4476,11 +1698,15 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)  		{  			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); +		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);  	}  } @@ -4507,6 +1733,44 @@ 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], ""); +	} + +	for(auto& p : mDefaultDecompParams) +	{ +		std::string ctrl_name(p.first); +		LLUICtrl* ctrl = getChild<LLUICtrl>(ctrl_name); +		if (ctrl) +		{ +			ctrl->setValue(p.second); +		} +	} +	getChild<LLComboBox>("physics_lod_combo")->setCurrentByIndex(0); +	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; @@ -4530,6 +1794,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())); + +	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())); +  	childSetVisible("upload_fee", true);  	childSetVisible("price_breakdown", true);  	mUploadBtn->setEnabled(isModelUploadAllowed()); @@ -4537,7 +1811,11 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived()  void LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(S32 status, const std::string& reason, const LLSD& result)  { -	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));      if (result.has("upload_price")) diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 1c66570650..8a01b0c307 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -28,36 +28,26 @@  #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 LLToggleableMenu; +class LLTabContainer; +class LLViewerTextEditor; + + +class LLJointOverrideData +{ +public: +    LLJointOverrideData() : mHasConflicts(false) {}; +    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;  class LLFloaterModelPreview : public LLFloaterModelUploadBase  { @@ -80,6 +70,7 @@ public:  	virtual ~LLFloaterModelPreview();  	virtual BOOL postBuild(); +    /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);  	void initModelPreview(); @@ -93,6 +84,11 @@ public:  	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 addStringToLog(const std::string& str, bool flash); +	static void addStringToLog(const std::ostringstream& strm, bool flash); +	void clearAvatarTab(); // clears table +	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); @@ -107,13 +103,17 @@ 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);  	bool isViewOptionChecked(const LLSD& userdata);  	bool isViewOptionEnabled(const LLSD& userdata);  	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(); @@ -142,8 +142,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*); @@ -154,6 +152,7 @@ protected:  	static void		onAutoFillCommit(LLUICtrl*,void*);  	void onLODParamCommit(S32 lod, bool enforce_tri_limit); +	void draw3dPreview();  	static void		onExplodeCommit(LLUICtrl*, void*); @@ -175,11 +174,15 @@ 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); + +    void setCtrlLoadFromFile(S32 lod);  	LLModelPreview*	mModelPreview;  	LLPhysicsDecomp::decomp_params mDecompParams; +	LLPhysicsDecomp::decomp_params mDefaultDecompParams;  	S32				mLastMouseX;  	S32				mLastMouseY; @@ -203,223 +206,34 @@ protected:  	LLSD mModelPhysicsFee;  private: -	void onClickCalculateBtn(); -	void toggleCalculateButton(); +    void onClickCalculateBtn(); +    void onJointListSelection();  	void onLoDSourceCommit(S32 lod);  	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.  	void resetDisplayOptions(); +	void resetUploadOptions(); +	void clearLogTab(); +  	void createSmoothComboBox(LLComboBox* combo_box, float min, float max);  	LLButton* mUploadBtn;  	LLButton* mCalculateBtn; -}; - -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 -{	 -	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 mLegacyRigValid; } -	void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }		 - -	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; - -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; -	bool		mLegacyRigValid; - -	bool		mLastJointUpdate; +	LLViewerTextEditor* mUploadLogText; +	LLTabContainer* mTabContainer; -	JointNameSet		mJointsFromNode; -	JointTransformMap	mJointTransformMap; +	S32			mAvatarTabIndex; // just to avoid any issues in case of xml changes +	std::string	mSelectedJointName; -	LLPointer<LLVOAvatar>	mPreviewAvatar; +	joint_override_data_map_t mJointOverrides[LLModel::NUM_LODS];  };  #endif  // LL_LLFLOATERMODELPREVIEW_H diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp new file mode 100644 index 0000000000..d60d1d748e --- /dev/null +++ b/indra/newview/llmodelpreview.cpp @@ -0,0 +1,3564 @@ +/** + * @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 "llmodelpreview.h" + +#include "llmodelloader.h" +#include "lldaeloader.h" +#include "llfloatermodelpreview.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; + +// 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() +{ +    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; +} + +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(); +    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) +    , mFirstSkinUpdate(true) +    , 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; +    mLookUpLodFiles = 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"); +    U32 load_state = 0; + +    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 = getLodSuffix(extensionLOD); + +                    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 = getLodSuffix(searchLOD); + +                            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) +            { +                LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has no High Lod (LOD3).", true); +                load_state = 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)) +                    { +                        LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has mismatching materials between lods." , true); +                        load_state = 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)) +                    { +                        // 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; +                        out << " bind_rot "; +                        out << bind_rot; +                        LL_WARNS() << out.str() << LL_ENDL; + +                        LLFloaterModelPreview::addStringToLog(out, getLoadState() != LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION); +                        load_state = 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, true); +                } +                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]); +    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) +                            { +                                name += getLodSuffix(loaded_lod); + +                                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 + getLodSuffix(lod); + +            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 +        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); +    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://" + 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 +        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"]; + +    S32 width = getWidth(); +    S32 height = getHeight(); + +    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); + +    { +        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(PREVIEW_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; +        if (fmp) +        { +            fmp->clearAvatarTab(); +        } +    } + +    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) +            { +                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) +            { +                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 (fmp) +    { +        if (upload_skin) +        { +            // 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); +    } + +    //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(PREVIEW_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(PREVIEW_BASE_COL.mV); +                    } + +                    buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); +                    gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); +                    gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); +                    if (edges) +                    { +                        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); +                        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(PREVIEW_PSYH_FILL_COL.mV); + +                                    buffer->setBuffer(type_mask & buffer->getTypeMask()); +                                    buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + +                                    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); + +                                    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 (mHasDegenerate) +                    { +                        glLineWidth(PREVIEW_DEG_EDGE_WIDTH); +                        glPointSize(PREVIEW_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]; + +                                        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); + +                                        for (U32 i = 0; i < buffer->getNumIndices(); i += 3) +                                        { +                                            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)) +                                            { +                                                buffer->draw(LLRender::LINE_LOOP, 3, i); +                                                buffer->draw(LLRender::POINTS, 3, i); +                                            } +                                        } +                                    } +                                } +                            } + +                            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 joint_count = LLSkinningUtil::getMeshJointCount(skin); +                        U32 bind_count = skin->mAlternateBindMatrix.size(); + +                        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 +                            // 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 < joint_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, joint_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(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); +                                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 +    mCameraZoom = llclamp(new_zoom, 1.f, PREVIEW_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); + +            if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) +            { +                preview->lookupLODModelFiles(LLModel::LOD_HIGH); +            } + +            // 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..3664a27a72 --- /dev/null +++ b/indra/newview/llmodelpreview.h @@ -0,0 +1,313 @@ +/** + * @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 "llmodelloader.h" //NUM_LOD +#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); + +    void lookupLODModelFiles(S32 lod); + +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; +    bool		mLookUpLodFiles; + +    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; +    bool		mFirstSkinUpdate; + +    JointNameSet		mJointsFromNode; +    JointTransformMap	mJointTransformMap; + +    LLPointer<LLVOAvatar>	mPreviewAvatar; +    LLCachedControl<bool>	mImporterDebug; +}; + +#endif  // LL_LLMODELPREVIEW_H diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 142108f069..865ac2bb7b 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; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 10f51f7896..0aee4a3398 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1578,13 +1578,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); @@ -1611,7 +1614,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; @@ -1632,7 +1646,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 71a81c2e3d..cfb007cbc9 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -444,7 +444,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/pipeline.cpp b/indra/newview/pipeline.cpp index b49747837d..90b45ceeb3 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -6562,7 +6562,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); @@ -6573,7 +6573,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); @@ -6583,7 +6583,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/textures/containers/TabTop_Right_Flashing.png b/indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.pngBinary files differ new file mode 100644 index 0000000000..fd13bb699d --- /dev/null +++ b/indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 7325d836d2..a875c4e848 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -634,6 +634,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 5a86eb06fb..02a21764ce 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -2,15 +2,16 @@  <floater   can_close="true"   can_drag_on_left="false" - can_minimize="false" - can_resize="false" - height="480" - min_height="480" + can_minimize="true" + can_resize="true" + 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> @@ -33,19 +34,27 @@    <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="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> +   +  <!-- 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> +  <string name="IncompleteTC">Texture coordinates data is not complete.</string> -<panel -  follows="top|left" -  height="455" -  layout="topleft" -  left="3" -  name="left_panel" -  top_pad="10" -  width="630"> +  <panel +    follows="top|left" +    height="595" +    layout="topleft" +    left="3" +    name="left_panel" +    top_pad="25" +    width="635">      <panel        follows="all"        height="50" @@ -76,12 +85,16 @@      </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"> +      tab_position="top" +      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" @@ -92,12 +105,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="629" /> +             width="628" />            <text             follows="left|top"             height="18" @@ -688,7 +701,7 @@               left="10"               name="lod_tab_border"               top_pad="20" -             width="605" /> +             width="614" />              <check_box               follows="top|left"               height="15" @@ -730,12 +743,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" @@ -755,8 +768,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" @@ -798,7 +812,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" @@ -807,7 +821,7 @@                    follows="top|left"                    left="18"                    name="physics analysis" -                  top_pad="15" +                  top_pad="10"                    visible="true"                    width="589">                      <text @@ -819,7 +833,7 @@                        name="method_label"                        text_color="White"                        top_pad="0"> -                      Step 2: Analyze +                      Step 2: Convert to hulls (optional)                      </text>                      <text                        follows="top|left" @@ -905,7 +919,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" @@ -914,7 +928,7 @@                    height="66"                    left="18"                    name="physics simplification" -                  top_pad="15" +                  top_pad="10"                    width="589">                      <text                        text_color="White" @@ -1013,7 +1027,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" @@ -1075,10 +1089,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" @@ -1093,7 +1106,7 @@                        layout="topleft"                        left_pad="2"                        name="physics_status_message_text" -                      width="252" +                      width="573"                        top_delta="3"/>                  </panel>          </panel> @@ -1105,12 +1118,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" @@ -1157,75 +1170,211 @@               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"/> -           <check_box -             follows="top|left" -             height="15" -             label="Include joint positions" -             label_text.text_color="White" -             name="upload_joints" -             top_pad="15"/> -           <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"/> -           <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="rigging_panel" +       title="Rigging"> +        <view_border +         bevel_style="none" +         follows="top|left" +         height="306" +         layout="topleft" +         left="3" +         name="avatar_tab_border" +         top_pad="0" +         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="20" +          name="joints_descr" +          top="73" +          width="150"> +          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="199" +         left_delta="0" +         top_pad="0" +         width="200"/> +        <text +          layout="topleft" +          follows="top|left" +          height="15" +          left_delta="0" +          name="conflicts_description" +          top_pad="2" +          width="200"> +          [CONFLICTS] conflicts in [JOINTS_COUNT] joints +        </text> +        <text +          layout="topleft" +          follows="top|left" +          height="15" +          left_pad="5" +          name="pos_overrides_descr" +          top="73" +          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="385"> +          <scroll_list.columns +           label="Model" +           name="model_name" +           relative_width="0.49" /> +          <scroll_list.columns +           label="X" +           name="axis_x" +           relative_width="0.17" /> +          <scroll_list.columns +           label="Y" +           name="axis_y" +           relative_width="0.17" /> +          <scroll_list.columns +           label="Z" +           name="axis_z" +           relative_width="0.17" /> +        </scroll_list> +      </panel> +      <panel +       label="Log" +       layout="topleft" +       name="logs_panel" +       title="Log"> +        <view_border +         bevel_style="none" +         follows="top|left" +         height="289" +         layout="topleft" +         left="3" +         name="log_tab_border" +         top_pad="0" +         width="628" /> +        <text_editor +         type="string" +         length="1" +         embedded_items="false" +         follows="top|left" +         font="SansSerif" +         ignore_tab="false" +         layout="topleft" +         height="289" +         left="4" +         top="0" +         right="-1" +         max_length="65536" +         name="log_text" +         parse_urls="true" +         spellcheck="false" +         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 -     follows="top|left" -     height="80" -     layout="top|left" -     left="0" +     follows="top|left|bottom" +     layout="topleft" +     height="195" +     left="4" +     border="true"       name="weights_and_warning_panel"       top_pad="3" -     width="625"> +     width="629">         <button           follows="top|left"           label="Calculate weights & fee" @@ -1265,10 +1414,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" @@ -1287,7 +1436,7 @@           left_pad="0"           name="prim_weight"           top_delta="0" -         width="120" +         width="130"           word_wrap="true">           Land impact: [EQ]         </text> @@ -1297,7 +1446,7 @@           left_pad="0"           name="download_weight"           top_delta="0" -         width="100" +         width="130"           word_wrap="true">           Download: [ST]         </text> @@ -1307,7 +1456,7 @@           layout="topleft"           left_pad="0"           name="physics_weight" -         width="90" +         width="130"           word_wrap="true">           Physics: [PH]         </text> @@ -1317,19 +1466,150 @@           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="physics_costs_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" +         visible="true"           width="40">           NOTE:         </text> @@ -1340,44 +1620,51 @@           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="-2" +         left="6" +         name="status"> +[STATUS]         </text> -       <text text_color="Yellow" layout="topleft" top_delta="20" 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"> +  </panel> +   +  <text +   follows="left|top" +   layout="topleft" +   left="640" +   name="lod_label" +   text_color="White" +   top="29" +   height="15" +   width="290"> +    Preview: +  </text> +  <panel +   follows="all" +   layout="topleft" +   border="true" +   bevel_style="none" +   name="preview_panel" +   top_pad="4" +   width="325" +   height="408"/> +  <panel +    follows="right|bottom" +    can_resize="false" +    height="140" +    layout="topleft" +    name="right_panel" +    top_pad="5" +    width="340">      <combo_box        top_pad="3"        follows="left|top" @@ -1386,10 +1673,10 @@        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_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" @@ -1436,11 +1723,21 @@      </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" @@ -1460,5 +1757,5 @@        max_val="3.0"        height="20"        width="150"/> -</panel> +  </panel>  </floater> 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> | 
