diff options
Diffstat (limited to 'indra/newview')
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 199 | ||||
| -rw-r--r-- | indra/newview/app_settings/shaders/class1/objects/previewV.glsl | 6 | ||||
| -rw-r--r-- | indra/newview/lldynamictexture.cpp | 15 | ||||
| -rw-r--r-- | indra/newview/llfloatermodelpreview.cpp | 1041 | ||||
| -rw-r--r-- | indra/newview/llfloatermodelpreview.h | 46 | ||||
| -rw-r--r-- | indra/newview/pipeline.cpp | 6 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/floater_model_preview.xml | 450 | 
7 files changed, 1451 insertions, 312 deletions
| diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index da1e87fda4..ebbe506b02 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -6639,8 +6639,190 @@      <key>Value</key>      <integer>600</integer>    </map> +  <key>MeshPreviewCanvasColor</key> +  <map> +    <key>Comment</key> +    <string>Canvas colour for the Mesh uploader</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Color4</string> +    <key>Value</key> +    <array> +      <real>0.169</real> +      <real>0.169</real> +      <real>0.169</real> +      <real>1.0</real> +    </array> +  </map> +  <key>MeshPreviewEdgeColor</key> +  <map> +    <key>Comment</key> +    <string>Edge colour for the Mesh uploader preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Color4</string> +    <key>Value</key> +    <array> +      <real>0.4</real> +      <real>0.4</real> +      <real>0.4</real> +      <real>1.0</real> +    </array> +  </map> +  <key>MeshPreviewBaseColor</key> +  <map> +    <key>Comment</key> +    <string>base diffuse colour for the Mesh uploader</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Color4</string> +    <key>Value</key> +    <array> +      <real>1.0</real> +      <real>1.0</real> +      <real>1.0</real> +      <real>1.0</real> +    </array> +  </map> +  <key>MeshPreviewBrightnessColor</key> +  <map> +    <key>Comment</key> +    <string>Brightness modifier</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Color3</string> +    <key>Value</key> +    <array> +      <real>0.9</real> +      <real>0.9</real> +      <real>0.9</real> +    </array> +  </map> +  <key>MeshPreviewEdgeWidth</key> +  <map> +    <key>Comment</key> +    <string>line thickness used when display edges is selected in mesh preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +      <real>1.0</real> +  </map> +  <key>MeshPreviewPhysicsEdgeColor</key> +  <map> +    <key>Comment</key> +    <string>Edge colour for the Mesh uploader physics preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Color4</string> +    <key>Value</key> +    <array> +      <real>0.0</real> +      <real>0.25</real> +      <real>0.5</real> +      <real>0.25</real> +    </array> +  </map> +  <key>MeshPreviewPhysicsFillColor</key> +  <map> +    <key>Comment</key> +    <string>Fill colour for the Mesh uploader physics preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Color4</string> +    <key>Value</key> +    <array> +      <real>0.0</real> +      <real>0.5</real> +      <real>1.0</real> +      <real>0.5</real> +    </array> +  </map> +  <key>MeshPreviewPhysicsEdgeWidth</key> +  <map> +    <key>Comment</key> +    <string>line thickness used when display physics is selected in mesh preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>1.0</real> +  </map> +  <key>MeshPreviewDegenerateEdgeColor</key> +  <map> +    <key>Comment</key> +    <string>Edge colour for the Mesh uploader Degenerate preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Color4</string> +    <key>Value</key> +    <array> +      <real>1.0</real> +      <real>0.0</real> +      <real>0.0</real> +      <real>1.0</real> +    </array> +  </map> +  <key>MeshPreviewDegenerateFillColor</key> +  <map> +    <key>Comment</key> +    <string>Fill colour for the Mesh uploader Degenerate preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Color4</string> +    <key>Value</key> +    <array> +      <real>1.0</real> +      <real>0.0</real> +      <real>0.0</real> +      <real>0.5</real> +    </array> +  </map> +  <key>MeshPreviewDegenerateEdgeWidth</key> +  <map> +    <key>Comment</key> +    <string>line thickness used when display Degenerate is selected in mesh preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>3.0</real> +  </map> +  <key>MeshPreviewDegeneratePointSize</key> +  <map> +    <key>Comment</key> +    <string>Large point size used to highlight degenerate triangle vertices in Mesh preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>8.0</real> +  </map> +  <key>MeshPreviewZoomLimit</key> +  <map> +    <key>Comment</key> +    <string>Maximum Zoom level in preview</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>10.0</real> +  </map>    <key>MigrateCacheDirectory</key> -    <map> +  <map>        <key>Comment</key>        <string>Check for old version of disk cache to migrate to current location</string>        <key>Persist</key> @@ -7879,7 +8061,17 @@        <key>Value</key>  	  <integer>13</integer>      </map> - +  <key>PreviewRenderSize</key>   +  <map> +    <key>Comment</key> +    <string>Resolution of the image rendered for the mesh upload preview (must be a power of 2)</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>S32</string> +    <key>Value</key> +    <integer>1024</integer> +  </map>    <key>PreviewAmbientColor</key>    <map>      <key>Comment</key> @@ -7896,8 +8088,6 @@        <real>1.0</real>      </array>    </map> - -    <key>PreviewDiffuse0</key>    <map>      <key>Comment</key> @@ -16357,3 +16547,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 7f3f84398b..de2ea2a065 100644 --- a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl @@ -91,8 +91,8 @@ void main()  	// Collect normal lights (need to be divided by two, as we later multiply by 2)  	col.rgb += light_diffuse[1].rgb * calcDirectionalLight(norm, light_position[1].xyz); -	col.rgb += light_diffuse[2].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[2], light_direction[2], light_attenuation[2].x, light_attenuation[2].z); -	col.rgb += light_diffuse[3].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[3], light_direction[3], light_attenuation[3].x, light_attenuation[3].z); -		 +	col.rgb += light_diffuse[2].rgb * calcDirectionalLight(norm, light_position[2].xyz); +	col.rgb += light_diffuse[3].rgb * calcDirectionalLight(norm, light_position[3].xyz); +	col /= 2.0;  	vertex_color = col*color;  } diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index 8ef0dd2865..e180d91461 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.mWaterDis.isComplete() && !gGLManager.mIsATI) +	if (gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsATI)  	{ //using offscreen render target, just use the bottom left corner  		mOrigin.set(0, 0);  	} @@ -216,11 +216,10 @@ BOOL LLViewerDynamicTexture::updateAllInstances()  		return TRUE;  	} -	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI; - +	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsATI;  	if (use_fbo)  	{ -		gPipeline.mWaterDis.bindTarget(); +		gPipeline.mPhysicsDisplay.bindTarget();  	}  	LLGLSLShader::bindNoShader(); @@ -258,7 +257,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances()  	if (use_fbo)  	{ -		gPipeline.mWaterDis.flush(); +		gPipeline.mPhysicsDisplay.flush();  	}  	return ret; diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 35362c0c7c..e694340b16 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -75,6 +75,7 @@  #include "llsdserialize.h"  #include "llsliderctrl.h"  #include "llspinctrl.h" +#include "lltabcontainer.h"  #include "lltoggleablemenu.h"  #include "lltrans.h"  #include "llvfile.h" @@ -82,6 +83,7 @@  #include "llcallbacklist.h"  #include "llviewerobjectlist.h"  #include "llanimationstates.h" +#include "llviewertexteditor.h"  #include "llviewernetwork.h"  #include "llviewershadermgr.h" @@ -107,6 +109,8 @@ const double RETAIN_COEFFICIENT = 100;  // So this const is used as a size of Smooth combobox list.  const S32 SMOOTH_VALUES_NUMBER = 10; +const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f; +  void drawBoxOutline(const LLVector3& pos, const LLVector3& size); @@ -260,7 +264,9 @@ void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LL  LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) :  LLFloaterModelUploadBase(key),  mUploadBtn(NULL), -mCalculateBtn(NULL) +mCalculateBtn(NULL), +mUploadLogText(NULL), +mTabContainer(NULL)  {  	sInstance = this;  	mLastMouseX = 0; @@ -303,10 +309,10 @@ BOOL LLFloaterModelPreview::postBuild()  		getChild<LLSpinCtrl>("lod_triangle_limit_" + lod_name[lod])->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLODParamCommit, this, lod, true));  	} -	childSetCommitCallback("upload_skin", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL); -	childSetCommitCallback("upload_joints", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL); -	childSetCommitCallback("lock_scale_if_joint_position", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL); -	childSetCommitCallback("upload_textures", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL); +	childSetCommitCallback("upload_skin", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL); +	childSetCommitCallback("upload_joints", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL); +	childSetCommitCallback("lock_scale_if_joint_position", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL); +	childSetCommitCallback("upload_textures", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL);  	childSetTextArg("status", "[STATUS]", getString("status_idle")); @@ -329,13 +335,17 @@ 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); +  	initDecompControls();  	LLView* preview_panel = getChild<LLView>("preview_panel"); @@ -391,6 +401,19 @@ BOOL LLFloaterModelPreview::postBuild()  	mUploadBtn = getChild<LLButton>("ok_btn");  	mCalculateBtn = getChild<LLButton>("calculate_btn"); +	mUploadLogText = getChild<LLViewerTextEditor>("log_text"); +	mTabContainer = getChild<LLTabContainer>("import_tab"); + +    // Disable Overrides tab untill it has something to show and set callbacks +    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel"); +    S32 index = mTabContainer->getIndexForPanel(panel); +    mTabContainer->enableTabButton(index, false); +    panel->getChild<LLScrollListCtrl>("joints_list")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onJointListSelection, this)); + +	// Disable Logs tab untill it has something to show +	panel = mTabContainer->getPanelByName("logs_panel"); +	index = mTabContainer->getIndexForPanel(panel); +	mTabContainer->enableTabButton(index, false);  	if (LLConvexDecomposition::getInstance() != NULL)  	{ @@ -429,18 +452,58 @@ void LLFloaterModelPreview::initModelPreview()  		delete mModelPreview;  	} -	mModelPreview = new LLModelPreview(512, 512, this ); +	S32 tex_width = 512; +	S32 tex_height = 512; + +	S32 max_width = llmin(gSavedSettings.getS32("PreviewRenderSize"), (S32)gPipeline.mScreenWidth); +	S32 max_height = llmin(gSavedSettings.getS32("PreviewRenderSize"), (S32)gPipeline.mScreenHeight); +	 +	while ((tex_width << 1) <= max_width) +	{ +		tex_width <<= 1; +	} +	while ((tex_height << 1) <= max_height) +	{ +		tex_height <<= 1; +	} + +	mModelPreview = new LLModelPreview(tex_width, tex_height, this);  	mModelPreview->setPreviewTarget(16.f);  	mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5));  	mModelPreview->setModelUpdatedCallback(boost::bind(&LLFloaterModelPreview::modelUpdated, this, _1));  } +void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl) +{ +	if (mModelPreview) +	{ +		auto name = ctrl->getName(); +		mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name]; +	} +	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();  	}  } @@ -496,6 +559,8 @@ void LLFloaterModelPreview::loadModel(S32 lod, const std::string& file_name, boo  void LLFloaterModelPreview::onClickCalculateBtn()  { +	clearLogTab(); +  	mModelPreview->rebuildUploadData();  	bool upload_skinweights = childGetValue("upload_skin").asBoolean(); @@ -522,6 +587,79 @@ void LLFloaterModelPreview::onClickCalculateBtn()  	mUploadBtn->setEnabled(false);  } +void populate_list_with_map(LLScrollListCtrl *list, const std::map<std::string, LLVector3> &vector_map) +{ +    if (vector_map.empty()) +    { +        return; +    } +    S32 count = 0; +    LLScrollListCell::Params cell_params; +    cell_params.font = LLFontGL::getFontSansSerif(); +    // Start out right justifying numeric displays +    cell_params.font_halign = LLFontGL::HCENTER; + +    std::map<std::string, LLVector3>::const_iterator iter = vector_map.begin(); +    std::map<std::string, LLVector3>::const_iterator end = vector_map.end(); +    while (iter != end) +    { +        LLScrollListItem::Params item_params; +        item_params.value = LLSD::Integer(count); + +        cell_params.column = "model_name"; +        cell_params.value = iter->first; + +        item_params.columns.add(cell_params); + +        cell_params.column = "axis_x"; +        cell_params.value = iter->second.mV[VX]; +        item_params.columns.add(cell_params); + +        cell_params.column = "axis_y"; +        cell_params.value = iter->second.mV[VY]; +        item_params.columns.add(cell_params); + +        cell_params.column = "axis_z"; +        cell_params.value = iter->second.mV[VZ]; + +        item_params.columns.add(cell_params); + +        list->addRow(item_params); +        count++; +        iter++; +    } +} + +void LLFloaterModelPreview::onJointListSelection() +{ +    S32 display_lod = mModelPreview->mPreviewLOD; +    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel"); +    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list"); +    LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list"); +    LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list"); +    LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr"); + +    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]; +        populate_list_with_map(joints_pos, data.mPosOverrides); + +        joint_pos_descr->setTextArg("[JOINT]", label); +    } +    else +    { +        // temporary value (shouldn't happen) +        std::string label = "mPelvis"; +        joint_pos_descr->setTextArg("[JOINT]", label); +    } + +} +  void LLFloaterModelPreview::onDescriptionKeystroke(LLUICtrl* ctrl)  {  	// Workaround for SL-4186, server doesn't allow name changes after 'calculate' stage @@ -622,6 +760,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(); @@ -665,6 +804,42 @@ 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); + + +	LLView* preview_panel = getChild<LLView>("preview_panel"); + +	if (!preview_panel) +	{ +		LL_WARNS() << "preview_panel not found in floater definition" << LL_ENDL; +	} +	LLRect rect = preview_panel->getRect(); + +	if (rect != mPreviewRect) +	{ +		mModelPreview->refresh(); +		mPreviewRect = preview_panel->getRect(); +	} + +	gGL.begin( LLRender::QUADS ); +	{ +		gGL.texCoord2f(0.f, 1.f); +		gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop-1); +		gGL.texCoord2f(0.f, 0.f); +		gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom); +		gGL.texCoord2f(1.f, 0.f); +		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom); +		gGL.texCoord2f(1.f, 1.f); +		gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mTop-1); +	} +	gGL.end(); + +	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); +}  //-----------------------------------------------------------------------------  // draw() @@ -1111,10 +1286,8 @@ void LLFloaterModelPreview::initDecompControls()  							std::string label = llformat("%.1f", value);  							combo_box->add(label, value, ADD_BOTTOM, true);  						} -						combo_box->setValue(param[i].mDefault.mFloat); -  					} - +					combo_box->setValue(param[i].mDefault.mFloat);  					combo_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]);  				}  			} @@ -1186,7 +1359,7 @@ void LLFloaterModelPreview::initDecompControls()  			//LL_INFOS() << "-----------------------------" << LL_ENDL;  		}  	} - +	mDefaultDecompParams = mDecompParams;  	childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this);  } @@ -1216,6 +1389,205 @@ void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handl  }  //----------------------------------------------------------------------------- +// addStringToLog() +//----------------------------------------------------------------------------- +//static +void LLFloaterModelPreview::addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod) +{ +    if (sInstance && sInstance->hasString(message)) +    { +        std::string str; +        switch (lod) +        { +        case LLModel::LOD_IMPOSTOR: str = "LOD0 "; break; +        case LLModel::LOD_LOW:      str = "LOD1 "; break; +        case LLModel::LOD_MEDIUM:   str = "LOD2 "; break; +        case LLModel::LOD_PHYSICS:  str = "PHYS "; break; +        case LLModel::LOD_HIGH:     str = "LOD3 ";   break; +        default: break; +        } +         +        LLStringUtil::format_map_t args_msg; +        LLSD::map_const_iterator iter = args.beginMap(); +        LLSD::map_const_iterator end = args.endMap(); +        for (; iter != end; ++iter) +        { +            args_msg[iter->first] = iter->second.asString(); +        } +        str += sInstance->getString(message, args_msg); +        sInstance->addStringToLogTab(str, flash); +    } +} + +// static +void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash) +{ +    if (sInstance) +    { +        sInstance->addStringToLogTab(str, flash); +    } +} + +// static +void LLFloaterModelPreview::addStringToLog(const std::ostringstream& strm, bool flash) +{ +    if (sInstance) +    { +        sInstance->addStringToLogTab(strm.str(), flash); +    } +} + +void LLFloaterModelPreview::clearOverridesTab() +{ +    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel"); +    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list"); +    joints_list->deleteAllItems(); + +    for (U32 i = 0; i < LLModel::NUM_LODS; ++i) +    { +        mJointOverrides[i].clear(); +    } +} + +void LLFloaterModelPreview::resetOverridesTab() +{ +    clearOverridesTab(); + +    for (U32 i = 0; i < LLModel::NUM_LODS; ++i) +    { +        mJointOverrides[i].clear(); +    } +} + +void LLFloaterModelPreview::showOverridesTab() +{ +    S32 display_lod = mModelPreview->mPreviewLOD; +    if (mModelPreview->mModel[display_lod].empty()) +    { +        return; +    } + +    // 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) +            { +                LLModelInstance& instance = *model_iter; +                LLModel* model = instance.mModel; +                const LLMeshSkinInfo *skin = &model->mSkinInfo; +                if (skin->mAlternateBindMatrix.size() > 0) +                { +                    U32 count = LLSkinningUtil::getMeshJointCount(skin); +                    for (U32 j = 0; j < count; ++j) +                    { +                        const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation(); +                        LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]]; +                        if (data.mPosOverrides.size() > 0 +                            && (data.mPosOverrides.begin()->second - jointPos).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()] = jointPos; + +                    } +                } +            } +        } +    } + +    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel"); +    S32 index = mTabContainer->getIndexForPanel(panel); +    mTabContainer->enableTabButton(index, true); +    LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list"); + +    if (joints_list->isEmpty()) +    { +        // Populate table +        S32 conflicts = 0; +        joint_override_data_map_t::iterator joint_iter = mJointOverrides[display_lod].begin(); +        joint_override_data_map_t::iterator joint_end = mJointOverrides[display_lod].end(); +        while (joint_iter != joint_end) +        { +            const std::string& listName = joint_iter->first; + +            LLScrollListItem::Params item_params; +            item_params.value(listName); + +            LLScrollListCell::Params cell_params; +            cell_params.font = LLFontGL::getFontSansSerif(); +            cell_params.value = listName; +            if (joint_iter->second.mHasConflicts) +            { +                cell_params.color = LLColor4::orange; +                conflicts++; +            } + +            item_params.columns.add(cell_params); + +            joints_list->addRow(item_params, ADD_BOTTOM); +            joint_iter++; +        } +        joints_list->selectFirstItem(); + +        LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("conflicts_description"); +        joint_pos_descr->setTextArg("[CONFLICTS]", llformat("%d", conflicts)); +        joint_pos_descr->setTextArg("[JOINTS_COUNT]", llformat("%d", mJointOverrides[display_lod].size())); +    } +} + +void LLFloaterModelPreview::hideOverridesTab() +{ +    LLPanel *panel = mTabContainer->getPanelByName("overrides_panel"); +    S32 index = mTabContainer->getIndexForPanel(panel); +    mTabContainer->enableTabButton(index, false); +} + +//----------------------------------------------------------------------------- +// addStringToLogTab() +//----------------------------------------------------------------------------- +void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash) +{ +    if (str.empty()) +    { +        return; +    } + +    LLWString text = utf8str_to_wstring(str); +    S32 add_text_len = text.length() + 1; // newline +    S32 editor_max_len = mUploadLogText->getMaxTextLength(); +    if (add_text_len > editor_max_len) +    { +        return; +    } + +    LLPanel* panel = mTabContainer->getPanelByName("logs_panel"); +    S32 index = mTabContainer->getIndexForPanel(panel); +    mTabContainer->enableTabButton(index, true); + +    // Make sure we have space for new string +    S32 editor_text_len = mUploadLogText->getLength(); +    while (editor_max_len < (editor_text_len + add_text_len)) +    { +        editor_text_len -= mUploadLogText->removeFirstLine(); +    } + +    mUploadLogText->appendText(str, true); + +    if (flash && mTabContainer->getCurrentPanel() != panel) +    { +        mTabContainer->setTabPanelFlashing(panel, true); +    } +} + +//-----------------------------------------------------------------------------  // LLModelPreview  //----------------------------------------------------------------------------- @@ -1224,12 +1596,13 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)  , mLodsQuery()  , mLodsWithParsingError()  , mPelvisZOffset( 0.0f ) -, mLegacyRigValid( false ) +, mLegacyRigFlags( U32_MAX )  , mRigValidJointUpload( false )  , mPhysicsSearchLOD( LLModel::LOD_PHYSICS )  , mResetJoints( false )  , mModelNoErrors( true )  , mLastJointUpdate( false ) +, mHasDegenerate( false )  {  	mNeedsUpdate = TRUE;  	mCameraDistance = 0.f; @@ -1644,8 +2017,14 @@ void LLModelPreview::rebuildUploadData()                      LLQuaternion identity;                      if (!bind_rot.isEqualEps(identity,0.01))                      { -                        LL_WARNS() << "non-identity bind shape rot. mat is " << high_lod_model->mSkinInfo.mBindShapeMatrix  -                                   << " bind_rot " << bind_rot << LL_ENDL; +                        std::ostringstream out; +                        out << "non-identity bind shape rot. mat is "; +                        out << high_lod_model->mSkinInfo.mBindShapeMatrix; +                        out << " bind_rot "; +                        out << bind_rot; +                        LL_WARNS() << out.str() << LL_ENDL; + +                        LLFloaterModelPreview::addStringToLog(out, false);                          setLoadState( LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION );                      }                  } @@ -1816,7 +2195,11 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable  	if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1)  	{ -		LL_WARNS() << "Invalid level of detail: " << lod << LL_ENDL; +		std::ostringstream out; +		out << "Invalid level of detail: "; +		out << lod; +		LL_WARNS() << out.str() << LL_ENDL; +		LLFloaterModelPreview::addStringToLog(out, true);  		assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);  		return;  	} @@ -2003,7 +2386,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)  	// Copy determinations about rig so UI will reflect them  	//  	setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload()); -	setLegacyRigValid(mModelLoader->isLegacyRigValid()); +	setLegacyRigFlags(mModelLoader->getLegacyRigFlags());  	mModelLoader->loadTextures() ; @@ -2013,7 +2396,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)  		mBaseScene.clear();  		bool skin_weights = false; -		bool joint_positions = false; +		bool joint_overrides = false;  		bool lock_scale_if_joint_position = false;  		for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) @@ -2060,7 +2443,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)  							if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty())  							{ -								joint_positions = true; +								joint_overrides = true;  							}  							if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition)  							{ @@ -2083,12 +2466,19 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)  				fmp->childSetValue("upload_skin", true);  			} -			if (joint_positions) -			{  +			if (joint_overrides) +			{ +				fmp->enableViewOption("show_joint_overrides"); +				mViewOption["show_joint_overrides"] = true;  				fmp->enableViewOption("show_joint_positions");  				mViewOption["show_joint_positions"] = true;  				fmp->childSetValue("upload_joints", true);  			} +            else +            { +                fmp->resetOverridesTab(); +                fmp->hideOverridesTab(); +            }  			if (lock_scale_if_joint_position)  			{ @@ -2193,7 +2583,12 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)  								if (importerDebug)  								{ -									LL_WARNS() << "Loded model name " << mModel[loaded_lod][idx]->mLabel << " for LOD " << loaded_lod << " doesn't match the base model. Renaming to " << name << LL_ENDL; +									std::ostringstream out; +									out << "Loded model name " << mModel[loaded_lod][idx]->mLabel; +									out << " for LOD " << loaded_lod; +									out << " doesn't match the base model. Renaming to " << name; +									LL_WARNS() << out.str() << LL_ENDL; +									LLFloaterModelPreview::addStringToLog(out, false);  								}  								mModel[loaded_lod][idx]->mLabel = name; @@ -2217,7 +2612,6 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)  	mLoading = false;  	if (mFMP)  	{ -		mFMP->getChild<LLCheckBoxCtrl>("confirm_checkbox")->set(FALSE);  		if (!mBaseModel.empty())  		{  			const std::string& model_name = mBaseModel[0]->getName(); @@ -2351,7 +2745,10 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim  	// Allow LoD from -1 to LLModel::LOD_PHYSICS  	if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1)  	{ -		LL_WARNS() << "Invalid level of detail: " << which_lod << LL_ENDL; +		std::ostringstream out; +		out << "Invalid level of detail: " << which_lod; +		LL_WARNS() << out.str() << LL_ENDL; +		LLFloaterModelPreview::addStringToLog(out, false);  		assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS);  		return;  	} @@ -2719,8 +3116,20 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim  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]; @@ -2810,43 +3219,57 @@ void LLModelPreview::updateStatusMessages()  		mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH];  	} -	bool has_degenerate = false; - +	mHasDegenerate = false;  	{//check for degenerate triangles in physics mesh  		U32 lod = LLModel::LOD_PHYSICS;  		const LLVector4a scale(0.5f); -		for (U32 i = 0; i < mModel[lod].size() && !has_degenerate; ++i) +		for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i)  		{ //for each model in the lod  			if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty())  			{ //no decomp exists  				S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); -				for (S32 j = 0; j < cur_submeshes && !has_degenerate; ++j) +				for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j)  				{ //for each submesh (face), add triangles and vertices to current total  					LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); -					for (S32 k = 0; (k < face.mNumIndices) && !has_degenerate; ) +					for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate; )  					{ -						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); +						U16 index_a = face.mIndices[k + 0]; +						U16 index_b = face.mIndices[k + 1]; +						U16 index_c = face.mIndices[k + 2]; -						if (ll_is_degenerate(v1,v2,v3)) +						if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case  						{ -							has_degenerate = true; +							LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL;  						}  						else  						{ -							k += 3; +							LLVector4a v1; v1.setMul(face.mPositions[index_a], scale); +							LLVector4a v2; v2.setMul(face.mPositions[index_b], scale); +							LLVector4a v3; v3.setMul(face.mPositions[index_c], scale); +							if (ll_is_degenerate(v1, v2, v3)) +							{ +								mHasDegenerate = true; +							}  						} +						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"); @@ -2971,14 +3394,22 @@ void LLModelPreview::updateStatusMessages()  			}  		}  	} -	mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit); -	LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon"); -	physStatusIcon->setVisible(physExceededVertexLimit); +  	if (physExceededVertexLimit)  	{ -		mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); -		LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); -		physStatusIcon->setImage(img); +		has_physics_error |= PhysicsError::TOOMANYVERTSINHULL; +	} + +	if (!(has_physics_error & PhysicsError::DEGENERATE)){ // only update this field (incluides clearing it) if it is not already in use. +		mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit); +		LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon"); +		physStatusIcon->setVisible(physExceededVertexLimit); +		if (physExceededVertexLimit) +		{ +			mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); +			LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); +			physStatusIcon->setImage(img); +		}  	}  	if (getLoadState() >= LLModelLoader::ERROR_PARSING) @@ -3008,11 +3439,15 @@ void LLModelPreview::updateStatusMessages()  		}  	} -	// 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) +	if (!mModelNoErrors || mHasDegenerate)  	{  		mFMP->childDisable("ok_btn"); +		mFMP->childDisable("calculate_btn"); +	} +	else +	{ +		mFMP->childEnable("ok_btn"); +		mFMP->childEnable("calculate_btn");  	}      if (mModelNoErrors && mLodsWithParsingError.empty()) @@ -3193,7 +3628,10 @@ void LLModelPreview::updateLodControls(S32 lod)  {  	if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH)  	{ -		LL_WARNS() << "Invalid level of detail: " << lod << LL_ENDL; +		std::ostringstream out; +		out << "Invalid level of detail: " << lod; +		LL_WARNS() << out.str() << LL_ENDL; +		LLFloaterModelPreview::addStringToLog(out, false);  		assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH);  		return;  	} @@ -3389,9 +3827,12 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)  			if (!vb->allocateBuffer(num_vertices, num_indices, TRUE))  			{  				// We are likely to crash due this failure, if this happens, find a way to gracefully stop preview -				LL_WARNS() << "Failed to allocate Vertex Buffer for model preview " -					<< num_vertices << " vertices and " -					<< num_indices << " indices" << LL_ENDL; +									std::ostringstream out; +									out << "Failed to allocate Vertex Buffer for model preview "; +									out << num_vertices << " vertices and "; +									out << num_indices << " indices"; +									LL_WARNS() << out.str() << LL_ENDL; +									LLFloaterModelPreview::addStringToLog(out, true);  			}  			LLStrider<LLVector3> vertex_strider; @@ -3539,8 +3980,21 @@ void LLModelPreview::loadedCallback(  	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);  	if (pPreview && !LLModelPreview::sIgnoreLoadedCallback)  	{ -		pPreview->loadModelCallback(lod); -	}	 +        // Load loader's warnings into floater's log tab +        const LLSD out = pPreview->mModelLoader->logOut(); +        LLSD::array_const_iterator iter_out = out.beginArray(); +        LLSD::array_const_iterator end_out = out.endArray(); +        for (; iter_out != end_out; ++iter_out) +        { +            if (iter_out->has("Message")) +            { +                LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod); +            } +        } +        pPreview->mModelLoader->clearLog(); +        pPreview->loadModelCallback(lod); // removes mModelLoader in some cases +	} +  }  void LLModelPreview::stateChangedCallback(U32 state,void* opaque) @@ -3617,11 +4071,13 @@ void LLModelPreview::addEmptyFace( LLModel* pTarget )  	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(); @@ -3632,16 +4088,32 @@ BOOL LLModelPreview::render()  	bool use_shaders = LLGLSLShader::sNoFixedFunction;  	bool edges = mViewOption["show_edges"]; +	bool joint_overrides = mViewOption["show_joint_overrides"];  	bool joint_positions = mViewOption["show_joint_positions"];  	bool skin_weight = mViewOption["show_skin_weight"];  	bool textures = mViewOption["show_textures"];  	bool physics = mViewOption["show_physics"]; +	// Extra configurability, to be exposed later as controls? +	static LLCachedControl<LLColor4> canvas_col(gSavedSettings, "MeshPreviewCanvasColor"); +	static LLCachedControl<LLColor4> edge_col(gSavedSettings, "MeshPreviewEdgeColor"); +	static LLCachedControl<LLColor4> base_col(gSavedSettings, "MeshPreviewBaseColor"); +	static LLCachedControl<LLColor3> brightness(gSavedSettings, "MeshPreviewBrightnessColor"); +	static LLCachedControl<F32> edge_width(gSavedSettings, "MeshPreviewEdgeWidth"); +	static LLCachedControl<LLColor4> phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor"); +	static LLCachedControl<LLColor4> phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor"); +	static LLCachedControl<F32> phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth"); +	static LLCachedControl<LLColor4> deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor"); +	static LLCachedControl<LLColor4> deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor");	 +	static LLCachedControl<F32> deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth"); +	static LLCachedControl<F32> deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize"); +  	S32 width = getWidth();  	S32 height = getHeight();  	LLGLSUIDefault def;  	LLGLDisable no_blend(GL_BLEND); +  	LLGLEnable cull(GL_CULL_FACE);  	LLGLDepthTest depth(GL_TRUE);  	LLGLDisable fog(GL_FOG); @@ -3661,8 +4133,7 @@ BOOL LLModelPreview::render()  		gGL.pushMatrix();  		gGL.loadIdentity(); -		gGL.color4f(0.169f, 0.169f, 0.169f, 1.f); - +		gGL.color4fv(static_cast<LLColor4>(canvas_col).mV);  		gl_rect_2d_simple( width, height );  		gGL.matrixMode(LLRender::MM_PROJECTION); @@ -3703,13 +4174,26 @@ BOOL LLModelPreview::render()  	if (has_skin_weights && lodsReady())  	{ //model has skin weights, enable view options for skin weights and joint positions -		if (fmp && isLegacyRigValid() ) -		{ -			fmp->enableViewOption("show_skin_weight"); -			fmp->setViewOptionEnabled("show_joint_positions", skin_weight);	 -			mFMP->childEnable("upload_skin"); -			mFMP->childSetValue("show_skin_weight", skin_weight); -		} +        U32 flags = getLegacyRigFlags(); +        if (fmp) +        { +            if (flags == LEGACY_RIG_OK) +            { +                fmp->enableViewOption("show_skin_weight"); +                fmp->setViewOptionEnabled("show_joint_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  	{ @@ -3718,6 +4202,7 @@ BOOL LLModelPreview::render()  		{  			mViewOption["show_skin_weight"] = false;  			fmp->disableViewOption("show_skin_weight"); +			fmp->disableViewOption("show_joint_overrides");  			fmp->disableViewOption("show_joint_positions");  			skin_weight = false; @@ -3741,11 +4226,19 @@ BOOL LLModelPreview::render()      if (upload_skin && upload_joints)      {          mFMP->childEnable("lock_scale_if_joint_position"); +        if (fmp) +        { +            fmp->showOverridesTab(); +        }      }      else      {          mFMP->childDisable("lock_scale_if_joint_position");          mFMP->childSetValue("lock_scale_if_joint_position", false); +        if (fmp) +        { +            fmp->hideOverridesTab(); +        }      }  	//Only enable joint offsets if it passed the earlier critiquing @@ -3776,10 +4269,9 @@ BOOL LLModelPreview::render()  	if (skin_weight)  	{ -		target_pos = getPreviewAvatar()->getPositionAgent(); +		target_pos = getPreviewAvatar()->getPositionAgent() + offset;  		z_near = 0.01f;  		z_far = 1024.f; -		mCameraDistance = 16.f;  		//render avatar previews every frame  		refresh(); @@ -3797,8 +4289,9 @@ BOOL LLModelPreview::render()  	LLQuaternion(mCameraYaw, LLVector3::z_axis);  	LLQuaternion av_rot = camera_rot; +	F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance;  	LLViewerCamera::getInstance()->setOriginAndLookAt( -													  target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot),		// camera +													  target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera  													  LLVector3::z_axis,																	// up  													  target_pos);											// point of interest @@ -3810,8 +4303,7 @@ BOOL LLModelPreview::render()  	stop_glerror();  	gGL.pushMatrix(); -	const F32 BRIGHTNESS = 0.9f; -	gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); +	gGL.color4fv(edge_col().mV);  	const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; @@ -3896,16 +4388,15 @@ BOOL LLModelPreview::render()  						}  						else  						{ -							gGL.diffuseColor4f(1,1,1,1); +							gGL.diffuseColor4fv(static_cast<LLColor4>(base_col).mV);  						}  						buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);  						gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); -						gGL.diffuseColor3f(0.4f, 0.4f, 0.4f); - +						gGL.diffuseColor4fv(static_cast<LLColor4>(edge_col).mV);  						if (edges)  						{ -							glLineWidth(3.f); +							glLineWidth(edge_width);  							glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);  							buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);  							glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -3918,10 +4409,9 @@ BOOL LLModelPreview::render()  			if (physics)  			{  				glClear(GL_DEPTH_BUFFER_BIT); -				 -				for (U32 i = 0; i < 2; i++) +				for (U32 pass = 0; pass < 2; pass++)  				{ -					if (i == 0) +					if (pass == 0)  					{ //depth only pass  						gGL.setColorMask(false, false);  					} @@ -3931,8 +4421,8 @@ BOOL LLModelPreview::render()  					}  					//enable alpha blending on second pass but not first pass -					LLGLState blend(GL_BLEND, i);  -					 +					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) @@ -3941,184 +4431,201 @@ BOOL LLModelPreview::render()  						LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; -							if (!model) -							{ -								continue; -							} +						if (!model) +						{ +							continue; +						} -							gGL.pushMatrix(); -							LLMatrix4 mat = instance.mTransform; +						gGL.pushMatrix(); +						LLMatrix4 mat = instance.mTransform; + +						gGL.multMatrix((GLfloat*)mat.mMatrix); -						gGL.multMatrix((GLfloat*) mat.mMatrix); +						bool render_mesh = true; +						LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; +						if (decomp) +						{ +							LLMutexLock(decomp->mMutex); -							bool render_mesh = true; +							LLModel::Decomposition& physics = model->mPhysics; -							LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; -							if (decomp) +							if (!physics.mHull.empty())  							{ -								LLMutexLock(decomp->mMutex); +								render_mesh = false; -								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()) -									{ //build vertex buffer for physics mesh -										gMeshRepo.buildPhysicsMesh(physics); -									} -						 -									if (!physics.mMesh.empty()) -									{ //render hull instead of mesh -										for (U32 i = 0; i < physics.mMesh.size(); ++i) +								if (!physics.mMesh.empty()) +								{ //render hull instead of mesh +									for (U32 i = 0; i < physics.mMesh.size(); ++i) +									{ +										if (explode > 0.f)  										{ -											if (explode > 0.f) -											{ -												gGL.pushMatrix(); +											gGL.pushMatrix(); -												LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters; -												offset *= explode; +											LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; +											offset *= explode; -												gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); -											} +											gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); +										} -											static std::vector<LLColor4U> hull_colors; +										static std::vector<LLColor4U> hull_colors; -											if (i+1 >= hull_colors.size()) -											{ -												hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 128)); -											} +										if (i + 1 >= hull_colors.size()) +										{ +											hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128)); +										} -											gGL.diffuseColor4ubv(hull_colors[i].mV); -											LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); +										gGL.diffuseColor4ubv(hull_colors[i].mV); +										LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); -											if (explode > 0.f) -											{ -												gGL.popMatrix(); -											} +										if (explode > 0.f) +										{ +											gGL.popMatrix();  										}  									}  								}  							} -						 -							if (render_mesh) +						} + +						if (render_mesh) +						{ +							if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())  							{ -								if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) -								{ -									genBuffers(LLModel::LOD_PHYSICS, false); -								} +								genBuffers(LLModel::LOD_PHYSICS, false); +							} -								U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); +							U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); +							if (pass > 0){  								for (U32 i = 0; i < num_models; ++i)  								{  									LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];  									gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); -									gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f); +									gGL.diffuseColor4fv(phys_fill_col().mV);  									buffer->setBuffer(type_mask & buffer->getTypeMask()); -									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - -									gGL.diffuseColor3f(1.f, 1.f, 0.f); +									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); -									glLineWidth(2.f); +									gGL.diffuseColor4fv(phys_edge_col().mV); +									glLineWidth(phys_edge_width);  									glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); -									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); +									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0);  									glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);  									glLineWidth(1.f);  								}  							} - -							gGL.popMatrix();  						} -					glLineWidth(3.f); -					glPointSize(8.f); -					gPipeline.enableLightsFullbright(LLColor4::white); -					//show degenerate triangles -					LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS); -					LLGLDisable cull(GL_CULL_FACE); -					gGL.diffuseColor4f(1.f,0.f,0.f,1.f); -					const LLVector4a scale(0.5f); +						gGL.popMatrix(); +					} -					for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) +					// only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks] +					if (pass > 0 && mHasDegenerate)  					{ -						LLModelInstance& instance = *iter; - -						LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - -						if (!model) +						glLineWidth(deg_edge_width); +						glPointSize(deg_point_size); +						//show degenerate triangles +						LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS); +						LLGLDisable cull(GL_CULL_FACE); +						const LLVector4a scale(0.5f); + +						for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)  						{ -							continue; -						} +							LLModelInstance& instance = *iter; -						gGL.pushMatrix(); -						LLMatrix4 mat = instance.mTransform; +							LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; -						gGL.multMatrix((GLfloat*) mat.mMatrix); +							if (!model) +							{ +								continue; +							} +							gGL.pushMatrix(); +							LLMatrix4 mat = instance.mTransform; -						LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; -						if (decomp) -						{ -							LLMutexLock(decomp->mMutex); +							gGL.multMatrix((GLfloat*)mat.mMatrix); -							LLModel::Decomposition& physics = model->mPhysics; -							if (physics.mHull.empty()) +							LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; +							if (decomp)  							{ -								if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) -								{ -									genBuffers(LLModel::LOD_PHYSICS, false); -								} -							 -								for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i) -								{ -									LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; +								LLMutexLock(decomp->mMutex); -									buffer->setBuffer(type_mask & buffer->getTypeMask()); +								LLModel::Decomposition& physics = model->mPhysics; -									LLStrider<LLVector3> pos_strider;  -									buffer->getVertexStrider(pos_strider, 0); -									LLVector4a* pos = (LLVector4a*) pos_strider.get(); -							 -									LLStrider<U16> idx; -									buffer->getIndexStrider(idx, 0); +								if (physics.mHull.empty()) +								{ +									if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) +									{ +										genBuffers(LLModel::LOD_PHYSICS, false); +									} -									for (U32 i = 0; i < buffer->getNumIndices(); i += 3) +									auto num_degenerate = 0; +									auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); +									for (U32 v = 0; v < num_models; ++v)  									{ -										LLVector4a v1; v1.setMul(pos[*idx++], scale); -										LLVector4a v2; v2.setMul(pos[*idx++], scale); -										LLVector4a v3; v3.setMul(pos[*idx++], scale); +										LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v]; +										if(buffer->getNumVerts() < 3)continue; + +										buffer->setBuffer(type_mask & buffer->getTypeMask()); + +										LLStrider<LLVector3> pos_strider; +										buffer->getVertexStrider(pos_strider, 0); +										LLVector4a* pos = (LLVector4a*)pos_strider.get(); -										if (ll_is_degenerate(v1,v2,v3)) +										LLStrider<U16> idx; +										buffer->getIndexStrider(idx, 0); + +										LLVector4a v1, v2, v3; +										for (U32 indices_offset = 0; indices_offset < buffer->getNumIndices(); indices_offset += 3)  										{ -											buffer->draw(LLRender::LINE_LOOP, 3, i); -											buffer->draw(LLRender::POINTS, 3, i); +											v1.setMul(pos[*idx++], scale); +											v2.setMul(pos[*idx++], scale); +											v3.setMul(pos[*idx++], scale); + +											if (ll_is_degenerate(v1, v2, v3)) +											{ +												num_degenerate++; +												glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +												gGL.diffuseColor3fv(deg_edge_col().mV); +												buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset); +												buffer->drawRange(LLRender::POINTS, 0, 2, 3, indices_offset); +												glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +												gGL.diffuseColor3fv(deg_fill_col().mV); +												buffer->drawRange(LLRender::TRIANGLES, 0, 2, 3, indices_offset); +											}  										}  									}  								}  							} -						} -						gGL.popMatrix(); +							gGL.popMatrix(); +						} +						glLineWidth(1.f); +						glPointSize(1.f); +						gPipeline.enableLightsPreview(); +						gGL.setSceneBlendType(LLRender::BT_ALPHA);  					} -					glLineWidth(1.f); -					glPointSize(1.f); -					gPipeline.enableLightsPreview(); -					gGL.setSceneBlendType(LLRender::BT_ALPHA);  				}  			}  		}  		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(mCameraDistance, 0.f, 0.f) + offset) * av_rot),		// camera +															  target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot),		// camera  															  LLVector3::z_axis,																	// up  															  target_pos);											// point of interest @@ -4131,6 +4638,51 @@ BOOL LLModelPreview::render()  					if (!model->mSkinWeights.empty())  					{ +                        const LLMeshSkinInfo *skin = &model->mSkinInfo; +                        LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary +                        U32 count = LLSkinningUtil::getMeshJointCount(skin); + +                        if (joint_overrides && skin->mAlternateBindMatrix.size() > 0) +                        { +                            // mesh_id is used to determine which mesh gets to +                            // set the joint offset, in the event of a conflict. Since +                            // we don't know the mesh id yet, we can't guarantee that +                            // joint offsets will be applied with the same priority as +                            // in the uploaded model. If the file contains multiple +                            // meshes with conflicting joint offsets, preview may be +                            // incorrect. +                            LLUUID fake_mesh_id; +                            fake_mesh_id.generate(); +                            for (U32 j = 0; j < count; ++j) +                            { +                                LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]); +                                if (joint) +                                { +                                    const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation(); +                                    if (joint->aboveJointPosThreshold(jointPos)) +                                    { +                                        bool override_changed; +                                        joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed); + +                                        if (override_changed) +                                        { +                                            //If joint is a pelvis then handle old/new pelvis to foot values +                                            if (joint->getName() == "mPelvis")// or skin->mJointNames[j] +                                            { +                                                pelvis_recalc = true; +                                            } +                                        } +                                        if (skin->mLockScaleIfJointPosition) +                                        { +                                            // Note that unlike positions, there's no threshold check here, +                                            // just a lock at the default value. +                                            joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model"); +                                        } +                                    } +                                } +                            } +                        } +  						for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i)  						{  							LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; @@ -4148,10 +4700,9 @@ BOOL LLModelPreview::render()  							//build matrix palette  							LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT]; -                            const LLMeshSkinInfo *skin = &model->mSkinInfo; -							U32 count = LLSkinningUtil::getMeshJointCount(skin);                              LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count,                                                                          skin, getPreviewAvatar()); +                              LLMatrix4a bind_shape_matrix;                              bind_shape_matrix.loadu(skin->mBindShapeMatrix);                              U32 max_joints = LLSkinningUtil::getMaxJointCount(); @@ -4191,11 +4742,11 @@ BOOL LLModelPreview::render()  							}  							buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); -							gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);  							if (edges)  							{ -								glLineWidth(3.f); +								gGL.diffuseColor4fv(edge_col().mV); +								glLineWidth(edge_width);  								glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);  								buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);  								glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -4221,6 +4772,11 @@ BOOL LLModelPreview::render()  				}  			} +            if (pelvis_recalc) +            { +                // size/scale recalculation +                getPreviewAvatar()->postPelvisSetRecalc(); +            }  		}  	} @@ -4258,14 +4814,17 @@ void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians)  void LLModelPreview::zoom(F32 zoom_amt)  {  	F32 new_zoom = mCameraZoom+zoom_amt; - -	mCameraZoom	= llclamp(new_zoom, 1.f, 10.f); +	// TODO: stop clamping in render +	static LLCachedControl<F32> zoom_limit(gSavedSettings, "MeshPreviewZoomLimit"); +	mCameraZoom	= llclamp(new_zoom, 1.f, zoom_limit());  }  void LLModelPreview::pan(F32 right, F32 up)  { -	mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); -	mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); +	bool skin_weight = mViewOption["show_skin_weight"]; +	F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; +	mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f); +	mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f);  }  void LLModelPreview::setPreviewLOD(S32 lod) @@ -4280,12 +4839,6 @@ void LLModelPreview::setPreviewLOD(S32 lod)  		combo_box->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order  		mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]); -		LLComboBox* combo_box2 = mFMP->getChild<LLComboBox>("preview_lod_combo2"); -		combo_box2->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order -		 -		LLComboBox* combo_box3 = mFMP->getChild<LLComboBox>("preview_lod_combo3"); -		combo_box3->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order -  		LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor");  		LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); @@ -4298,6 +4851,13 @@ void LLModelPreview::setPreviewLOD(S32 lod)  			mFMP->childSetColor(lod_triangles_name[i], color);  			mFMP->childSetColor(lod_vertices_name[i], color);  		} + +        LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; +        if (fmp) +        { +            // make preview repopulate tab +            fmp->clearOverridesTab(); +        }  	}  	refresh();  	updateStatusMessages(); @@ -4315,12 +4875,16 @@ void LLFloaterModelPreview::onReset(void* user_data)  {  	assert_main_thread(); +  	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;  	fmp->childDisable("reset_btn"); +	fmp->clearLogTab(); +	fmp->resetOverridesTab();  	LLModelPreview* mp = fmp->mModelPreview;  	std::string filename = mp->mLODFile[LLModel::LOD_HIGH];   	fmp->resetDisplayOptions(); +	fmp->resetUploadOptions();  	//reset model preview  	fmp->initModelPreview(); @@ -4334,6 +4898,7 @@ void LLFloaterModelPreview::onUpload(void* user_data)  	assert_main_thread();  	LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; +	mp->clearLogTab();  	mp->mUploadBtn->setEnabled(false); @@ -4474,11 +5039,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);  	}  } @@ -4505,6 +5074,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; @@ -4528,6 +5135,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()); @@ -4535,7 +5152,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..c359722431 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -57,7 +57,19 @@ class domController;  class domSkin;  class domMesh;  class LLMenuButton; +class LLTabContainer;  class LLToggleableMenu; +class LLViewerTextEditor; + + +class LLJointOverrideData +{ +public: +    LLJointOverrideData() : mHasConflicts(false) {}; +    std::map<std::string, LLVector3> mPosOverrides; +    bool mHasConflicts; +}; +typedef std::map<std::string, LLJointOverrideData> joint_override_data_map_t;  class LLFloaterModelPreview : public LLFloaterModelUploadBase  { @@ -93,6 +105,13 @@ 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 clearOverridesTab(); // clears table +	void resetOverridesTab(); // clears table and cleans all data +	void showOverridesTab(); // populates table and data as nessesary +	void hideOverridesTab();  	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);  	void setPreviewLOD(S32 lod); @@ -109,11 +128,13 @@ public:  	void 			loadModel(S32 lod, const std::string& file_name, bool force_disable_slm = false);  	void onViewOptionChecked(LLUICtrl* ctrl); +	void onUploadOptionChecked(LLUICtrl* ctrl);  	bool isViewOptionChecked(const LLSD& userdata);  	bool isViewOptionEnabled(const LLSD& userdata);  	void setViewOptionEnabled(const std::string& option, bool enabled);  	void enableViewOption(const std::string& option);  	void disableViewOption(const std::string& option); +	void onShowSkinWeightChecked(LLUICtrl* ctrl);  	bool isModelLoading(); @@ -154,6 +175,7 @@ protected:  	static void		onAutoFillCommit(LLUICtrl*,void*);  	void onLODParamCommit(S32 lod, bool enforce_tri_limit); +	void draw3dPreview();  	static void		onExplodeCommit(LLUICtrl*, void*); @@ -175,11 +197,13 @@ protected:      // FIXME - this function and mStatusMessage have no visible effect, and the      // actual status messages are managed by directly manipulation of      // the UI element. -	void setStatusMessage(const std::string& msg); +    void setStatusMessage(const std::string& msg); +    void addStringToLogTab(const std::string& str, bool flash);  	LLModelPreview*	mModelPreview;  	LLPhysicsDecomp::decomp_params mDecompParams; +	LLPhysicsDecomp::decomp_params mDefaultDecompParams;  	S32				mLastMouseX;  	S32				mLastMouseY; @@ -203,23 +227,31 @@ 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; +	LLViewerTextEditor* mUploadLogText; +	LLTabContainer* mTabContainer; + +	joint_override_data_map_t mJointOverrides[LLModel::NUM_LODS];  };  class LLMeshFilePicker : public LLFilePickerThread @@ -298,8 +330,9 @@ public:  	void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }  	//Accessors for the legacy rigs -	const bool isLegacyRigValid( void ) const { return mLegacyRigValid; } -	void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }		 +	const bool isLegacyRigValid( void ) const { return mLegacyRigFlags == 0; } +	U32 getLegacyRigFlags() const { return mLegacyRigFlags; } +	void setLegacyRigFlags( U32 rigFlags ) { mLegacyRigFlags = rigFlags; }  	static void	textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata );      static bool lodQueryCallback(); @@ -314,6 +347,7 @@ public:  	static bool 		sIgnoreLoadedCallback;      std::vector<S32> mLodsQuery;      std::vector<S32> mLodsWithParsingError; +	bool mHasDegenerate;  protected: @@ -412,7 +446,7 @@ private:  	float		mPelvisZOffset;  	bool		mRigValidJointUpload; -	bool		mLegacyRigValid; +	U32			mLegacyRigFlags;  	bool		mLastJointUpdate; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index bec33790bd..fe7b6f06f3 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -6589,7 +6589,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); @@ -6600,7 +6600,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); @@ -6610,7 +6610,7 @@ void LLPipeline::enableLightsPreview()  	light->enable();  	light->setPosition(light_pos);  	light->setDiffuse(diffuse2); -	light->setAmbient(LLColor4::black); +	light->setAmbient(ambient);  	light->setSpecular(specular2);  	light->setSpotExponent(0.f);  	light->setSpotCutoff(180.f); diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml index 5a86eb06fb..9ebcb0c09d 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -2,10 +2,10 @@  <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="600" + min_height="600"   width="980"   min_width="980"   name="Model Preview" @@ -33,19 +33,26 @@    <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 from model loader--> +  <string name="TooManyJoint">Skinning disabled due to too many joints: [JOINTS], maximum: [MAX]</string> +  <string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string> +  <string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string> +   -<panel -  follows="top|left" -  height="455" -  layout="topleft" -  left="3" -  name="left_panel" -  top_pad="10" -  width="630"> +  <panel +    follows="top|left" +    height="580" +    layout="topleft" +    left="3" +    name="left_panel" +    top_pad="0" +    width="630">      <panel        follows="all"        height="50" @@ -81,7 +88,8 @@        height="300"        width="635"        name="import_tab" -      tab_position="top"> +      tab_position="top" +      enable_tabs_flashing="true">        <!-- LOD PANEL -->          <panel           help_topic="upload_model_lod" @@ -97,7 +105,7 @@               left="3"               name="lod_tab_border"               top_pad="0" -             width="629" /> +             width="619" />            <text             follows="left|top"             height="18" @@ -755,8 +763,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 +807,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 +816,7 @@                    follows="top|left"                    left="18"                    name="physics analysis" -                  top_pad="15" +                  top_pad="10"                    visible="true"                    width="589">                      <text @@ -819,7 +828,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 +914,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 +923,7 @@                    height="66"                    left="18"                    name="physics simplification" -                  top_pad="15" +                  top_pad="10"                    width="589">                      <text                        text_color="White" @@ -1013,7 +1022,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 +1084,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 +1101,7 @@                        layout="topleft"                        left_pad="2"                        name="physics_status_message_text" -                      width="252" +                      width="573"                        top_delta="3"/>                  </panel>          </panel> @@ -1182,20 +1190,41 @@               label_text.text_color="White"               name="upload_skin"               top_pad="15"/> +           <text +             follows="top|left" +             height="17" +             left="40" +             name="skin_too_many_joints" +             text_color="Orange" +             top_pad="-2" +             width="150"> +             Too many skinned joints +           </text> +           <text +             follows="top|left" +             height="17" +             left="40" +             name="skin_unknown_joint" +             text_color="Orange" +             top_pad="-17" +             width="150"> +             Model has an unknown joint(s) +           </text>             <check_box               follows="top|left"               height="15"               label="Include joint positions"               label_text.text_color="White"               name="upload_joints" -             top_pad="15"/> +             left_delta="-20" +             top_pad="1"/>             <check_box               follows="top|left"               height="15"               label="Lock scale if joint position defined"               label_text.text_color="White"               name="lock_scale_if_joint_position" -             top_pad="15"/> +             top_pad="16"/>             <text               follows="top|left"               height="15" @@ -1217,15 +1246,137 @@               value="0.0"               width="80"/>       </panel> +      <panel +       label="Overrides" +       layout="topleft" +       name="overrides_panel" +       title="Overrides"> +        <view_border +         bevel_style="none" +         follows="top|left" +         height="275" +         layout="topleft" +         left="3" +         name="log_tab_border" +         top_pad="0" +         width="619" /> +        <text +          layout="topleft" +          follows="top|left" +          height="15" +          left="6" +          name="joints_descr" +          top="4" +          width="300"> +          Joints: +        </text> +        <scroll_list +         layout="topleft" +         follows="top|left" +         name="joints_list" +         column_padding="0" +         draw_heading="false" +         draw_stripes="false" +         commit_on_selection_change="true" +         heading_height="23" +         height="238" +         left="6" +         top_pad="0" +         width="200"/> +        <text +          layout="topleft" +          follows="top|left" +          height="15" +          left="6" +          name="conflicts_description" +          top_pad="2" +          width="200"> +          [CONFLICTS] conflicts in [JOINTS_COUNT] joints +        </text> +        <text +          layout="topleft" +          follows="top|left" +          height="15" +          left_pad="5" +          name="pos_overrides_descr" +          top="4" +          width="300"> +          Position overrides for joint '[JOINT]': +        </text> +        <scroll_list +         layout="topleft" +         follows="top|left" +         name="pos_overrides_list" +         column_padding="0" +         draw_heading="true" +         draw_stripes="false" +         heading_height="23" +         height="100" +         left_delta="0" +         top_pad="0" +         width="330"> +          <scroll_list.columns +           label="Model" +           name="model_name" +           relative_width="0.40"  /> +          <scroll_list.columns +           label="X" +           name="axis_x" +           relative_width="0.20"  /> +          <scroll_list.columns +           label="Y" +           name="axis_y" +           relative_width="0.20"  /> +          <scroll_list.columns +           label="Z" +           name="axis_z" +           relative_width="0.20"  /> +        </scroll_list> +      </panel> +      <panel +       label="Log" +       layout="topleft" +       name="logs_panel" +       title="Log"> +        <view_border +         bevel_style="none" +         follows="top|left" +         height="275" +         layout="topleft" +         left="3" +         name="log_tab_border" +         top_pad="0" +         width="619" /> +        <text_editor +         type="string" +         length="1" +         embedded_items="false" +         follows="top|left" +         font="SansSerif" +         ignore_tab="false" +         layout="topleft" +         height="275" +         left="4" +         top="0" +         right="-11" +         max_length="65536" +         name="log_text" +         parse_urls="true" +         spellcheck="false" +         read_only="true" +         word_wrap="true"> +        </text_editor> +      </panel>      </tab_container>      <panel -     follows="top|left" -     height="80" -     layout="top|left" -     left="0" +     follows="top|left|bottom" +     layout="topleft" +     height="197" +     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 +1416,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 +1438,7 @@           left_pad="0"           name="prim_weight"           top_delta="0" -         width="120" +         width="130"           word_wrap="true">           Land impact: [EQ]         </text> @@ -1297,7 +1448,7 @@           left_pad="0"           name="download_weight"           top_delta="0" -         width="100" +         width="130"           word_wrap="true">           Download: [ST]         </text> @@ -1307,7 +1458,7 @@           layout="topleft"           left_pad="0"           name="physics_weight" -         width="90" +         width="130"           word_wrap="true">           Physics: [PH]         </text> @@ -1317,17 +1468,148 @@           layout="topleft"           left_pad="0"           name="server_weight" -         width="83" +         width="130"           word_wrap="true">           Server: [SIM]         </text> -       <!-- ========== NOTE MESSAGE ========== --> +       <!-- =========== Cost breakdown ======== --> +      <panel +        border="true" +        top_pad="5" +        layout="topleft" +        left="6" +        name="price_breakdown_panel" +        width="120" +        height="100"> +        <text +          layout="topleft" +          left="3"> +          Price Breakdown +        </text> +        <view_border +          bevel_style="none" +          follows="top|left" +          height="0" +          layout="topleft" +          left="3" +          name="price_breakdown_border" +          top_pad="5" +          width="110"/> +        <text +          height="80" +          top_pad="5" +          layout="topleft" +          left="3" +          name="price_breakdown_labels" +          width="70" +          word_wrap="false"> +Download: +Physics: +Instances: +Textures: +Model: +        </text> +        <text +          height="80" +          top_delta="0" +          layout="topleft" +          halign="right" +          left_pad="0" +          name="price_breakdown" +          width="40" +          word_wrap="false"> +[STREAMING] +[PHYSICS] +[INSTANCES] +[TEXTURES] +[MODEL] +        </text> +      </panel> +       <!--  +       Streaming breakdown numbers are available but not fully understood +       uncommenting the following sections will display the numbers for debugging purposes +       <text +        height="80" +        top_delta="0" +        layout="topleft" +        left="130" +        name="streaming_breakdown_labels" +        width="65" +        word_wrap="true"> +Streaming/Download: +High: +Medium: +Low: +Lowest: +      </text>         <text +        height="80" +        top_delta="0" +        layout="topleft" +        left_pad="0" +        name="streaming_breakdown" +        width="95" +        word_wrap="true"> +[STR_TOTAL] +[STR_HIGH] +[STR_MED] +[STR_LOW] +[STR_LOWEST] +      </text>--> +      <panel +        border="true" +        layout="topleft" +        left_pad="265" +        name="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"           width="40"> @@ -1345,39 +1627,41 @@           width="462"           visible="false">           You dont have rights to upload mesh models. [[VURL] Find out how] to get certified. +       </text>  +       <text text_color="Yellow" layout="topleft" top_pad="-1" 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="4" +   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 +1670,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 +1720,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 +1754,5 @@        max_val="3.0"        height="20"        width="150"/> -</panel> +  </panel>  </floater> | 
