From 2772a7747029214f8ebde664f7b4ffc4dae77793 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 19 Nov 2010 16:59:35 -0600 Subject: Don't use llwarns/llinfos from a background thread. --- indra/llcommon/llthread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 59e28948f5..84da54f26d 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -145,7 +145,7 @@ void LLThread::shutdown() // First, set the flag that indicates that we're ready to die setQuitting(); - llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl; + //llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl; // Now wait a bit for the thread to exit // It's unclear whether I should even bother doing this - this destructor // should netver get called unless we're already stopped, really... @@ -167,7 +167,7 @@ void LLThread::shutdown() if (!isStopped()) { // This thread just wouldn't stop, even though we gave it time - llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl; + //llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl; return; } mAPRThreadp = NULL; -- cgit v1.2.3 From 3a1fbcab2a745e8e0d85720d6df9c83d7be2c1d8 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 19 Nov 2010 17:07:01 -0600 Subject: SH-448 Initial draft of implementing UI spec for "advanced" importer. --- indra/llprimitive/llmodel.cpp | 4 +- indra/llprimitive/llmodel.h | 2 +- indra/newview/llfloatermodelpreview.cpp | 1333 +++++++++----------- indra/newview/llfloatermodelpreview.h | 56 +- indra/newview/skins/default/colors.xml | 6 + .../skins/default/xui/en/floater_model_preview.xml | 371 +++++- 6 files changed, 996 insertions(+), 776 deletions(-) (limited to 'indra') diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index f7eafb2fff..eacf2377fe 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -1056,9 +1056,9 @@ void LLModel::addFace(const LLVolumeFace& face) } -void LLModel::smoothNormals(F32 angle_cutoff) +void LLModel::generateNormals(F32 angle_cutoff) { - //smooth normals for all faces by: + //generate normals for all faces by: // 1 - Create faceted copy of face with no texture coordinates // 2 - Weld vertices in faceted copy that are shared between triangles with less than "angle_cutoff" difference between normals // 3 - Generate smoothed set of normals based on welding results diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index b45bca626a..b81bc662a0 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -114,7 +114,7 @@ public: U32 num_verts, U32 num_indices); - void smoothNormals(F32 angle_cutoff); + void generateNormals(F32 angle_cutoff); void addFace(const LLVolumeFace& face); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 1c73a6cb31..b52e54275a 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -105,28 +105,54 @@ const S32 PREVIEW_TEXTURE_HEIGHT = 300; void drawBoxOutline(const LLVector3& pos, const LLVector3& size); -std::string limit_name[] = + +std::string lod_name[] = +{ + "lowest", + "low", + "medium", + "high", + "I went off the end of the lod_name array. Me so smart." +}; + +std::string lod_triangles_name[] = { - "lowest limit", - "low limit", - "medium limit", - "high limit", - "physics limit", + "lowest_triangles", + "low_triangles", + "medium_triangles", + "high_triangles", + "I went off the end of the lod_triangles_name array. Me so smart." +}; - "I went off the end of the limit_name array. Me so smart." +std::string lod_vertices_name[] = +{ + "lowest_vertices", + "low_vertices", + "medium_vertices", + "high_vertices", + "I went off the end of the lod_vertices_name array. Me so smart." }; -std::string info_name[] = +std::string lod_status_name[] = { - "lowest info", - "low info", - "medium info", - "high info", - "physics info", + "lowest_status", + "low_status", + "medium_status", + "high_status", + "I went off the end of the lod_status_name array. Me so smart." +}; - "I went off the end of the info_name array. Me so smart." +std::string lod_label_name[] = +{ + "lowest_label", + "low_label", + "medium_label", + "high_label", + "I went off the end of the lod_label_name array. Me so smart." }; + + bool validate_face(const LLVolumeFace& face) { for (U32 i = 0; i < face.mNumIndices; ++i) @@ -185,20 +211,6 @@ BOOL stop_gloderror() return FALSE; } -LLPhysicsDecompFloater::LLPhysicsDecompFloater(LLSD& key) -: LLFloater(key) -{ - -} - -LLPhysicsDecompFloater::~LLPhysicsDecompFloater() -{ - if (LLFloaterModelPreview::sInstance && LLFloaterModelPreview::sInstance->mDecompFloater) - { - LLFloaterModelPreview::sInstance->mDecompFloater = NULL; - } -} - class LLMeshFilePicker : public LLFilePickerThread { public: @@ -230,7 +242,6 @@ LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) : mLastMouseY = 0; mGLName = 0; mLoading = FALSE; - mDecompFloater = NULL; } //----------------------------------------------------------------------------- @@ -243,75 +254,73 @@ BOOL LLFloaterModelPreview::postBuild() return FALSE; } - childSetCommitCallback("high detail combo", onHighLODCommit, this); - childSetCommitCallback("medium detail combo", onMediumLODCommit, this); - childSetCommitCallback("low detail combo", onLowLODCommit, this); - childSetCommitCallback("lowest detail combo", onLowestLODCommit, this); - childSetCommitCallback("physics detail combo", onPhysicsLODCommit, this); - - - childSetCommitCallback("high limit", onHighLimitCommit, this); - childSetCommitCallback("medium limit", onMediumLimitCommit, this); - childSetCommitCallback("low limit", onLowLimitCommit, this); - childSetCommitCallback("lowest limit", onLowestLimitCommit, this); - childSetCommitCallback("physics limit", onPhysicsLimitCommit, this); - - childSetCommitCallback("smooth normals", onSmoothNormalsCommit, this); + childSetAction("lod_browse", onBrowseLOD, this); + childSetCommitCallback("lod_triangle_limit", onTriangleLimitCommit, this); + + childSetCommitCallback("crease_angle", onGenerateNormalsCommit, this); + childSetCommitCallback("generate_normals", onGenerateNormalsCommit, this); childSetCommitCallback("show edges", onShowEdgesCommit, this); - childSetCommitCallback("auto fill", onAutoFillCommit, this); + childSetCommitCallback("lod_generate", onAutoFillCommit, this); childSetTextArg("status", "[STATUS]", getString("status_idle")); - for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) - { - if (lod == LLModel::LOD_PHYSICS) - { - childSetTextArg(info_name[lod], "[TRIANGLES]", std::string("0")); - childSetTextArg(info_name[lod], "[HULLS]", std::string("0")); - childSetTextArg(info_name[lod], "[POINTS]", std::string("0")); - } - else - { - childSetTextArg(info_name[lod], "[TRIANGLES]", std::string("0")); - childSetTextArg(info_name[lod], "[VERTICES]", std::string("0")); - childSetTextArg(info_name[lod], "[SUBMESHES]", std::string("0")); - std::string msg = getString("required"); - childSetTextArg(info_name[lod], "[MESSAGE]", msg); - } - - childSetVisible(limit_name[lod], FALSE); - } - //childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount)); childSetAction("ok_btn", onUpload, this); childSetAction("consolidate", onConsolidate, this); - childSetAction("scrub materials", onScrubMaterials, this); - - childSetAction("decompose_btn", onDecompose, this); + childSetAction("clear_materials", onClearMaterials, this); childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this); childSetCommitCallback("upload_skin", onUploadSkinCommit, this); childSetCommitCallback("upload_joints", onUploadJointsCommit, this); - childSetCommitCallback("debug scale", onDebugScaleCommit, this); + childSetCommitCallback("import_scale", onImportScaleCommit, this); + + childSetCommitCallback("lod_file_or_limit", refresh, this); + childSetCommitCallback("physics_load_radio", refresh, this); childDisable("upload_skin"); childDisable("upload_joints"); + + initDecompControls(); - const U32 width = 512; - const U32 height = 512; + LLView* preview_panel = getChild("preview_panel"); - mPreviewRect.set(getRect().getWidth()-PREVIEW_HPAD-width, - PREVIEW_HPAD+height, - getRect().getWidth()-PREVIEW_HPAD, - PREVIEW_HPAD); + mPreviewRect = preview_panel->getRect(); mModelPreview = new LLModelPreview(512, 512, this); mModelPreview->setPreviewTarget(16.f); + //set callbacks for left click on line editor rows + for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) + { + LLTextBox* text = getChild(lod_label_name[i]); + if (text) + { + text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); + } + + text = getChild(lod_triangles_name[i]); + if (text) + { + text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); + } + + text = getChild(lod_vertices_name[i]); + if (text) + { + text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); + } + + text = getChild(lod_status_name[i]); + if (text) + { + text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); + } + } + return TRUE; } @@ -333,12 +342,6 @@ LLFloaterModelPreview::~LLFloaterModelPreview() { LLImageGL::deleteTextures(1, &mGLName ); } - - if (mDecompFloater) - { - mDecompFloater->closeFloater(); - mDecompFloater = NULL; - } } void LLFloaterModelPreview::loadModel(S32 lod) @@ -348,58 +351,6 @@ void LLFloaterModelPreview::loadModel(S32 lod) (new LLMeshFilePicker(this, lod))->getFile(); } -void LLFloaterModelPreview::setLODMode(S32 lod, S32 mode) -{ - if (mode == 0) - { - if (lod != LLModel::LOD_PHYSICS) - { - for (S32 i = lod; i >= 0; i--) - { - mModelPreview->clearModel(i); - } - } - else - { - mModelPreview->clearModel(lod); - } - - mModelPreview->refresh(); - mModelPreview->calcResourceCost(); - } - else if (mode == 1) - { - loadModel(lod); - } - else if (mode != mModelPreview->mLODMode[lod]) - { - mModelPreview->mLODMode[lod] = mode; - mModelPreview->genLODs(lod); - } - - mModelPreview->setPreviewLOD(lod); - - - LLSpinCtrl* lim = getChild(limit_name[lod], TRUE); - - if (mode == 2) //triangle count - { - U32 tri_count = 0; - for (LLModelLoader::model_list::iterator iter = mModelPreview->mBaseModel.begin(); - iter != mModelPreview->mBaseModel.end(); ++iter) - { - tri_count += (*iter)->getNumTriangles(); - } - - lim->setMaxValue(tri_count); - lim->setVisible(TRUE); - } - else - { - lim->setVisible(FALSE); - } -} - void LLFloaterModelPreview::setLimit(S32 lod, S32 limit) { if (limit != mModelPreview->mLimit[lod]) @@ -411,7 +362,7 @@ void LLFloaterModelPreview::setLimit(S32 lod, S32 limit) } //static -void LLFloaterModelPreview::onDebugScaleCommit(LLUICtrl*,void* userdata) +void LLFloaterModelPreview::onImportScaleCommit(LLUICtrl*,void* userdata) { LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; @@ -421,6 +372,7 @@ void LLFloaterModelPreview::onDebugScaleCommit(LLUICtrl*,void* userdata) } fp->mModelPreview->calcResourceCost(); + fp->mModelPreview->refresh(); } //static @@ -471,68 +423,6 @@ void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata) fp->mModelPreview->setPreviewLOD(which_mode); } -//static -void LLFloaterModelPreview::setLODMode(S32 lod, void* userdata) -{ - LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; - - if (!fp->mModelPreview) - { - return; - } - - S32 which_mode = 0; - - std::string combo_name[] = - { - "lowest detail combo", - "low detail combo", - "medium detail combo", - "high detail combo", - "physics detail combo", - - "I went off the end of the combo_name array. Me so smart." - }; - - LLCtrlSelectionInterface* iface = fp->childGetSelectionInterface(combo_name[lod]); - if (iface) - { - which_mode = iface->getFirstSelectedIndex(); - } - - fp->setLODMode(lod, which_mode); -} - -//static -void LLFloaterModelPreview::onHighLODCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLODMode(3, userdata); -} - -//static -void LLFloaterModelPreview::onMediumLODCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLODMode(2, userdata); -} - -//static -void LLFloaterModelPreview::onLowLODCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLODMode(1, userdata); -} - -//static -void LLFloaterModelPreview::onLowestLODCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLODMode(0, userdata); -} - -//static -void LLFloaterModelPreview::onPhysicsLODCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLODMode(4, userdata); -} - //static void LLFloaterModelPreview::setLimit(S32 lod, void* userdata) { @@ -543,48 +433,26 @@ void LLFloaterModelPreview::setLimit(S32 lod, void* userdata) return; } - S32 limit = fp->childGetValue(limit_name[lod]).asInteger(); + S32 limit = fp->childGetValue("lod_triangle_limit").asInteger(); fp->setLimit(lod, limit); } //static -void LLFloaterModelPreview::onHighLimitCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLimit(3, userdata); -} - -//static -void LLFloaterModelPreview::onMediumLimitCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLimit(2, userdata); -} - -//static -void LLFloaterModelPreview::onLowLimitCommit(LLUICtrl* ctrl, void* userdata) +void LLFloaterModelPreview::onTriangleLimitCommit(LLUICtrl* ctrl, void* userdata) { - LLFloaterModelPreview::setLimit(1, userdata); -} - -//static -void LLFloaterModelPreview::onLowestLimitCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLimit(0, userdata); -} + LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; -//static -void LLFloaterModelPreview::onPhysicsLimitCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview::setLimit(4, userdata); + LLFloaterModelPreview::setLimit(fp->mModelPreview->mPreviewLOD, userdata); } //static -void LLFloaterModelPreview::onSmoothNormalsCommit(LLUICtrl* ctrl, void* userdata) +void LLFloaterModelPreview::onGenerateNormalsCommit(LLUICtrl* ctrl, void* userdata) { LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; - fp->mModelPreview->smoothNormals(); + fp->mModelPreview->generateNormals(); } //static @@ -627,22 +495,14 @@ void LLFloaterModelPreview::draw() childSetTextArg("status", "[STATUS]", getString("status_idle")); } - childSetTextArg("description_label", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost)); + childSetTextArg("prim_cost", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost)); childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size())); - if (mDecompFloater) + if (mCurRequest.notNull()) { - if (mCurRequest.notNull()) - { - mDecompFloater->childSetText("status", mCurRequest->mStatusMessage); - } - else - { - const std::string idle("Idle."); - mDecompFloater->childSetText("status", idle); - } + childSetTextArg("status", "[STATUS]", mCurRequest->mStatusMessage); } - + U32 resource_cost = mModelPreview->mResourceCost*10; if (childGetValue("upload_textures").asBoolean()) @@ -658,6 +518,16 @@ void LLFloaterModelPreview::draw() gGL.getTexUnit(0)->bind(mModelPreview); + + LLView* preview_panel = getChild("preview_panel"); + + LLRect rect = preview_panel->getRect(); + if (rect != mPreviewRect) + { + mModelPreview->refresh(); + mPreviewRect = preview_panel->getRect(); + } + gGL.begin( LLRender::QUADS ); { gGL.texCoord2f(0.f, 1.f); @@ -807,13 +677,10 @@ void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data) if (sInstance->mModelPreview) { - if (sInstance->mDecompFloater) + S32 idx = sInstance->childGetValue("physics_layer").asInteger(); + if (idx >= 0 && idx < sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS].size()) { - S32 idx = sInstance->mDecompFloater->childGetValue("model").asInteger(); - if (idx >= 0 && idx < sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS].size()) - { - mdl = sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS][idx]; - } + mdl = sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS][idx]; } } } @@ -823,276 +690,198 @@ void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data) sInstance->mCurRequest = new DecompRequest(stage->mName, mdl); gMeshRepo.mDecompThread->submitRequest(sInstance->mCurRequest); } + + const std::string decompose("Decompose"); + + if (decompose == stage->mName) + { //hide decompose panel and show simplify panel + sInstance->childSetVisible("physics step 2", false); + sInstance->childSetVisible("physics step 3", true); + } } //static -void LLFloaterModelPreview::onPhysicsStageCancel(LLUICtrl* ctrl, void*data) +void LLFloaterModelPreview::onPhysicsOptimize(LLUICtrl* ctrl, void *data) { - if (sInstance && sInstance->mCurRequest.notNull()) - { - sInstance->mCurRequest->mContinue = 0; - } + //hide step 1 panel and show step 2 panel + info + sInstance->childSetVisible("physics step 1", false); + sInstance->childSetVisible("physics step 2", true); + sInstance->childSetVisible("physics info", true); +} + +//static +void LLFloaterModelPreview::onPhysicsBrowse(LLUICtrl* ctrl, void* userdata) +{ + sInstance->loadModel(LLModel::LOD_PHYSICS); } -void LLFloaterModelPreview::showDecompFloater() +//static +void LLFloaterModelPreview::onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata) { - if (!mDecompFloater) + S32 which_mode = 3; + LLCtrlSelectionInterface* iface = sInstance->childGetSelectionInterface("physics_lod_combo"); + if (iface) { - LLSD key; - mDecompFloater = new LLPhysicsDecompFloater(key); + which_mode = iface->getFirstSelectedIndex(); + } - S32 left = 20; - S32 right = 320; - - S32 cur_y = 30; + sInstance->mModelPreview->setPhysicsFromLOD(which_mode); +} - { - //add status text - LLTextBox::Params p; - p.name("status"); - p.rect(LLRect(left, cur_y, right-80, cur_y-20)); - mDecompFloater->addChild(LLUICtrlFactory::create(p)); - } +//static +void LLFloaterModelPreview::onPhysicsDecomposeBack(LLUICtrl* ctrl, void* userdata) +{ + //hide step 2 panel and info and show step 1 panel + sInstance->childSetVisible("physics step 1", true); + sInstance->childSetVisible("physics step 2", false); + sInstance->childSetVisible("physics info", false); +} +//static +void LLFloaterModelPreview::onPhysicsSimplifyBack(LLUICtrl* ctrl, void* userdata) +{ + //hide step 3 panel and show step 2 panel + sInstance->childSetVisible("physics step 3", false); + sInstance->childSetVisible("physics step 2", true); +} + +//static +void LLFloaterModelPreview::onPhysicsStageCancel(LLUICtrl* ctrl, void*data) +{ + if (sInstance && sInstance->mCurRequest.notNull()) + { + sInstance->mCurRequest->mContinue = 0; + } +} - { //add cancel button - LLButton::Params p; - p.name("Cancel"); - p.label("Cancel"); - p.rect(LLRect(right-80, cur_y, right, cur_y-20)); - LLButton* button = LLUICtrlFactory::create(p); - button->setCommitCallback(onPhysicsStageCancel, NULL); - mDecompFloater->addChild(button); - } +void LLFloaterModelPreview::initDecompControls() +{ + LLSD key; - cur_y += 30; + childSetCommitCallback("cancel_btn", onPhysicsStageCancel, NULL); + childSetCommitCallback("physics_lod_combo", onPhysicsUseLOD, NULL); + childSetCommitCallback("physics_browse", onPhysicsBrowse, NULL); + childSetCommitCallback("physics_optimize", onPhysicsOptimize, NULL); + childSetCommitCallback("decompose_back", onPhysicsDecomposeBack, NULL); + childSetCommitCallback("simplify_back", onPhysicsSimplifyBack, NULL); + childSetCommitCallback("physics_layer", refresh, NULL); + static const LLCDStageData* stage = NULL; + static S32 stage_count = 0; - static const LLCDStageData* stage = NULL; - static S32 stage_count = 0; + if (!stage && LLConvexDecomposition::getInstance() != NULL) + { + stage_count = LLConvexDecomposition::getInstance()->getStages(&stage); + } - if (!stage && LLConvexDecomposition::getInstance() != NULL) - { - stage_count = LLConvexDecomposition::getInstance()->getStages(&stage); - } + static const LLCDParam* param = NULL; + static S32 param_count = 0; + if (!param && LLConvexDecomposition::getInstance() != NULL) + { + param_count = LLConvexDecomposition::getInstance()->getParameters(¶m); + } - static const LLCDParam* param = NULL; - static S32 param_count = 0; - if (!param && LLConvexDecomposition::getInstance() != NULL) + for (S32 j = stage_count-1; j >= 0; --j) + { + LLButton* button = getChild(stage[j].mName); + if (button) { - param_count = LLConvexDecomposition::getInstance()->getParameters(¶m); + button->setCommitCallback(onPhysicsStageExecute, (void*) &stage[j]); } - for (S32 j = stage_count-1; j >= 0; --j) - { - LLButton::Params p; - p.name(stage[j].mName); - p.label(stage[j].mName); - p.rect(LLRect(left, cur_y, right, cur_y-20)); - LLButton* button = LLUICtrlFactory::create(p); - button->setCommitCallback(onPhysicsStageExecute, (void*) &stage[j]); - mDecompFloater->addChild(button); - gMeshRepo.mDecompThread->mStageID[stage[j].mName] = j; - cur_y += 30; - // protected against stub by stage_count being 0 for stub above - LLConvexDecomposition::getInstance()->registerCallback(j, LLPhysicsDecomp::llcdCallback); + gMeshRepo.mDecompThread->mStageID[stage[j].mName] = j; + // protected against stub by stage_count being 0 for stub above + LLConvexDecomposition::getInstance()->registerCallback(j, LLPhysicsDecomp::llcdCallback); - llinfos << "Physics decomp stage " << stage[j].mName << " (" << j << ") parameters:" << llendl; - llinfos << "------------------------------------" << llendl; + //llinfos << "Physics decomp stage " << stage[j].mName << " (" << j << ") parameters:" << llendl; + //llinfos << "------------------------------------" << llendl; - for (S32 i = 0; i < param_count; ++i) + for (S32 i = 0; i < param_count; ++i) + { + if (param[i].mStage != j) { - if (param[i].mStage != j) - { - continue; - } + continue; + } - std::string name(param[i].mName ? param[i].mName : ""); - std::string description(param[i].mDescription ? param[i].mDescription : ""); + std::string name(param[i].mName ? param[i].mName : ""); + std::string description(param[i].mDescription ? param[i].mDescription : ""); - std::string type = "unknown"; + std::string type = "unknown"; - llinfos << name << " - " << description << llendl; + llinfos << name << " - " << description << llendl; - if (param[i].mType == LLCDParam::LLCD_FLOAT) + if (param[i].mType == LLCDParam::LLCD_FLOAT) + { + mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mFloat); + //llinfos << "Type: float, Default: " << param[i].mDefault.mFloat << llendl; + + LLSliderCtrl* slider = getChild(name); + if (slider) { - mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mFloat); - llinfos << "Type: float, Default: " << param[i].mDefault.mFloat << llendl; - - LLSliderCtrl::Params p; - p.name(name); - p.label(name); - p.rect(LLRect(left, cur_y, right, cur_y-20)); - p.min_value(param[i].mDetails.mRange.mLow.mFloat); - p.max_value(param[i].mDetails.mRange.mHigh.mFloat); - p.increment(param[i].mDetails.mRange.mDelta.mFloat); - p.tool_tip(description); - p.decimal_digits(3); - p.initial_value(param[i].mDefault.mFloat); - LLSliderCtrl* slider = LLUICtrlFactory::create(p); + slider->setMinValue(param[i].mDetails.mRange.mLow.mFloat); + slider->setMaxValue(param[i].mDetails.mRange.mHigh.mFloat); + slider->setIncrement(param[i].mDetails.mRange.mDelta.mFloat); + slider->setValue(param[i].mDefault.mFloat); slider->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); - mDecompFloater->addChild(slider); - cur_y += 30; } - else if (param[i].mType == LLCDParam::LLCD_INTEGER) + } + else if (param[i].mType == LLCDParam::LLCD_INTEGER) + { + mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue); + //llinfos << "Type: integer, Default: " << param[i].mDefault.mIntOrEnumValue << llendl; + + LLSliderCtrl* slider = getChild(name); + if (slider) { - mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue); - llinfos << "Type: integer, Default: " << param[i].mDefault.mIntOrEnumValue << llendl; - LLSliderCtrl::Params p; - p.name(name); - p.label(name); - p.rect(LLRect(left, cur_y, right, cur_y-20)); - p.min_value(param[i].mDetails.mRange.mLow.mIntOrEnumValue); - p.max_value(param[i].mDetails.mRange.mHigh.mIntOrEnumValue); - p.increment(param[i].mDetails.mRange.mDelta.mIntOrEnumValue); - p.tool_tip(description); - p.initial_value(param[i].mDefault.mIntOrEnumValue); - LLSliderCtrl* slider = LLUICtrlFactory::create(p); + slider->setMinValue(param[i].mDetails.mRange.mLow.mIntOrEnumValue); + slider->setMaxValue(param[i].mDetails.mRange.mHigh.mIntOrEnumValue); + slider->setIncrement(param[i].mDetails.mRange.mDelta.mIntOrEnumValue); + slider->setValue(param[i].mDefault.mIntOrEnumValue); slider->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); - mDecompFloater->addChild(slider); - cur_y += 30; } - else if (param[i].mType == LLCDParam::LLCD_BOOLEAN) + } + else if (param[i].mType == LLCDParam::LLCD_BOOLEAN) + { + mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mBool); + //llinfos << "Type: boolean, Default: " << (param[i].mDefault.mBool ? "True" : "False") << llendl; + + LLCheckBoxCtrl* check_box = getChild(name); + if (check_box) { - mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mBool); - llinfos << "Type: boolean, Default: " << (param[i].mDefault.mBool ? "True" : "False") << llendl; - - LLCheckBoxCtrl::Params p; - p.rect(LLRect(left, cur_y, right, cur_y-20)); - p.name(name); - p.label(name); - p.initial_value(param[i].mDefault.mBool); - p.tool_tip(description); - LLCheckBoxCtrl* check_box = LLUICtrlFactory::create(p); + check_box->setValue(param[i].mDefault.mBool); check_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); - mDecompFloater->addChild(check_box); - cur_y += 30; } - else if (param[i].mType == LLCDParam::LLCD_ENUM) - { - S32 cur_x = left; - mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue); - llinfos << "Type: enum, Default: " << param[i].mDefault.mIntOrEnumValue << llendl; - - { //add label - LLTextBox::Params p; - const LLFontGL* font = (LLFontGL*) p.font(); - - p.rect(LLRect(left, cur_y, left+font->getWidth(name), cur_y-20)); - p.name(name); - p.label(name); - p.initial_value(name); - LLTextBox* text_box = LLUICtrlFactory::create(p); - mDecompFloater->addChild(text_box); - cur_x += text_box->getRect().getWidth(); - } - - { //add combo_box - LLComboBox::Params p; - p.rect(LLRect(cur_x, cur_y, right-right/4, cur_y-20)); - p.name(name); - p.label(name); - p.tool_tip(description); - - llinfos << "Accepted values: " << llendl; - LLComboBox* combo_box = LLUICtrlFactory::create(p); - for (S32 k = 0; k < param[i].mDetails.mEnumValues.mNumEnums; ++k) - { - llinfos << param[i].mDetails.mEnumValues.mEnumsArray[k].mValue - << " - " << param[i].mDetails.mEnumValues.mEnumsArray[k].mName << llendl; + } + else if (param[i].mType == LLCDParam::LLCD_ENUM) + { + mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue); + //llinfos << "Type: enum, Default: " << param[i].mDefault.mIntOrEnumValue << llendl; - combo_box->add(param[i].mDetails.mEnumValues.mEnumsArray[k].mName, - LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue)); - } - combo_box->setValue(param[i].mDefault.mIntOrEnumValue); - combo_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); - mDecompFloater->addChild(combo_box); - cur_y += 30; + { //plug into combo box + + //llinfos << "Accepted values: " << llendl; + LLComboBox* combo_box = getChild(name); + for (S32 k = 0; k < param[i].mDetails.mEnumValues.mNumEnums; ++k) + { + //llinfos << param[i].mDetails.mEnumValues.mEnumsArray[k].mValue + // << " - " << param[i].mDetails.mEnumValues.mEnumsArray[k].mName << llendl; + combo_box->add(param[i].mDetails.mEnumValues.mEnumsArray[k].mName, + LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue)); } - - llinfos << "----" << llendl; + combo_box->setValue(param[i].mDefault.mIntOrEnumValue); + combo_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); } - llinfos << "-----------------------------" << llendl; - } - } - - cur_y += 30; - //explode slider - { - LLSliderCtrl::Params p; - p.initial_value(0); - p.min_value(0); - p.max_value(1); - p.decimal_digits(2); - p.increment(0.05f); - p.label("Explode"); - p.name("explode"); - p.rect(LLRect(left, cur_y, right, cur_y-20)); - LLSliderCtrl* slider = LLUICtrlFactory::create(p); - - mDecompFloater->addChild(slider); - cur_y += 30; - } - - //mesh render checkbox - { - LLCheckBoxCtrl::Params p; - p.label("Mesh: "); - p.name("render_mesh"); - p.rect(LLRect(left, cur_y, right/4, cur_y-20)); - LLCheckBoxCtrl* check = LLUICtrlFactory::create(p); - check->setValue(true); - mDecompFloater->addChild(check); - } - //hull render checkbox - { - LLCheckBoxCtrl::Params p; - p.label("Hull: "); - p.name("render_hull"); - p.rect(LLRect(right/4, cur_y, right/2, cur_y-20)); - LLCheckBoxCtrl* check = LLUICtrlFactory::create(p); - check->setValue(true); - mDecompFloater->addChild(check); - } - - { //submesh combo box label - LLTextBox::Params p; - p.label("Model"); - p.name("model label"); - p.rect(LLRect(right/2, cur_y, right-right/3, cur_y-20)); - LLTextBox* text_box = LLUICtrlFactory::create(p); - text_box->setValue("Model"); - mDecompFloater->addChild(text_box); - } - - { - //add submesh combo box - LLComboBox::Params p; - p.rect(LLRect(right-right/2+p.font()->getWidth("Model"), cur_y, right, cur_y-20)); - p.name("model"); - LLComboBox* combo_box = LLUICtrlFactory::create(p); - for (S32 i = 0; i < mModelPreview->mBaseModel.size(); ++i) - { - LLModel* mdl = mModelPreview->mBaseModel[i]; - combo_box->add(mdl->mLabel, i); + //llinfos << "----" << llendl; } - combo_box->setValue(0); - mDecompFloater->addChild(combo_box); - cur_y += 30; + //llinfos << "-----------------------------" << llendl; } - - mDecompFloater->childSetCommitCallback("model", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance); - mDecompFloater->childSetCommitCallback("render_mesh", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance); - mDecompFloater->childSetCommitCallback("render_hull", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance); - - mDecompFloater->setRect(LLRect(10, cur_y+20, right+20, 10)); } - mDecompFloater->childSetCommitCallback("explode", LLFloaterModelPreview::onExplodeCommit, this); - - mDecompFloater->openFloater(); + childSetCommitCallback("physics_layer", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance); + childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this); + childSetCommitCallback("show physics", LLFloaterModelPreview::refresh, this); } //----------------------------------------------------------------------------- @@ -2080,18 +1869,15 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloaterModelPreview* fmp mCameraPitch = 0.f; mCameraZoom = 1.f; mTextureName = 0; - mPreviewLOD = 3; + mPreviewLOD = 0; mModelLoader = NULL; mDirty = false; for (U32 i = 0; i < LLModel::NUM_LODS; i++) { - mLODMode[i] = 1; mLimit[i] = 0; } - mLODMode[0] = 0; - mFMP = fmp; glodInit(); @@ -2118,10 +1904,9 @@ U32 LLModelPreview::calcResourceCost() U32 num_points = 0; U32 num_hulls = 0; - F32 debug_scale = mFMP->childGetValue("debug scale").asReal(); + F32 debug_scale = mFMP->childGetValue("import_scale").asReal(); F32 streaming_cost = 0.f; - for (U32 i = 0; i < mUploadData.size(); ++i) { LLModelInstance& instance = mUploadData[i]; @@ -2173,13 +1958,13 @@ U32 LLModelPreview::calcResourceCost() } } - mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",num_hulls)); - mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",num_points)); + //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",num_hulls)); + //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",num_points)); mFMP->childSetTextArg("streaming cost", "[COST]", llformat("%.3f", streaming_cost)); - F32 scale = mFMP->childGetValue("debug scale").asReal()*2.f; - mFMP->childSetTextArg("dimensions", "[X]", llformat("%.3f", mPreviewScale[0]*scale)); - mFMP->childSetTextArg("dimensions", "[Y]", llformat("%.3f", mPreviewScale[1]*scale)); - mFMP->childSetTextArg("dimensions", "[Z]", llformat("%.3f", mPreviewScale[2]*scale)); + F32 scale = mFMP->childGetValue("import_scale").asReal()*2.f; + mFMP->childSetTextArg("import_dimensions", "[X]", llformat("%.3f", mPreviewScale[0]*scale)); + mFMP->childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", mPreviewScale[1]*scale)); + mFMP->childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", mPreviewScale[2]*scale)); updateStatusMessages(); @@ -2193,11 +1978,11 @@ void LLModelPreview::rebuildUploadData() //fill uploaddata instance vectors from scene data - LLSpinCtrl* scale_spinner = mFMP->getChild("debug scale"); + LLSpinCtrl* scale_spinner = mFMP->getChild("import_scale"); if (!scale_spinner) { - llerrs << "floater_model_preview.xml MUST contain debug scale spinner." << llendl; + llerrs << "floater_model_preview.xml MUST contain import_scale spinner." << llendl; } F32 scale = scale_spinner->getValue().asReal(); @@ -2266,6 +2051,21 @@ void LLModelPreview::rebuildUploadData() { scale_spinner->setValue(max_import_scale); } + + //refill "layer" combo in physics panel + LLComboBox* combo_box = mFMP->getChild("physics_layer"); + if (combo_box) + { + S32 current = combo_box->getCurrentIndex(); + combo_box->removeall(); + + for (S32 i = 0; i < mBaseModel.size(); ++i) + { + LLModel* mdl = mBaseModel[i]; + combo_box->add(mdl->mLabel, i); + } + combo_box->setCurrentByIndex(current); + } } @@ -2304,6 +2104,8 @@ void LLModelPreview::loadModel(std::string filename, S32 lod) return; } + mLODFile[lod] = filename; + if (lod == 3 && !mGroup.empty()) { for (std::map, U32>::iterator iter = mGroup.begin(); iter != mGroup.end(); ++iter) @@ -2327,16 +2129,35 @@ void LLModelPreview::loadModel(std::string filename, S32 lod) mModelLoader->start(); mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); + + setPreviewLOD(lod); - if (mFMP->childGetValue("description_form").asString().empty()) + if (lod == mPreviewLOD) + { + mFMP->childSetText("lod_file", mLODFile[mPreviewLOD]); + } + else if (lod == LLModel::LOD_PHYSICS) { - std::string name = gDirUtilp->getBaseFileName(filename, true); - mFMP->childSetValue("description_form", name); + mFMP->childSetText("physics_file", mLODFile[lod]); } mFMP->openFloater(); } +void LLModelPreview::setPhysicsFromLOD(S32 lod) +{ + if (lod >= 0 && lod <= 3) + { + mModel[LLModel::LOD_PHYSICS] = mModel[lod]; + mScene[LLModel::LOD_PHYSICS] = mScene[lod]; + mLODFile[LLModel::LOD_PHYSICS].clear(); + mFMP->childSetText("physics_file", mLODFile[LLModel::LOD_PHYSICS]); + mVertexBuffer[LLModel::LOD_PHYSICS].clear(); + rebuildUploadData(); + refresh(); + } +} + void LLModelPreview::clearIncompatible(S32 lod) { for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) @@ -2405,7 +2226,7 @@ void LLModelPreview::resetPreviewTarget() setPreviewTarget(mPreviewScale.magVec()*2.f); } -void LLModelPreview::smoothNormals() +void LLModelPreview::generateNormals() { S32 which_lod = mPreviewLOD; @@ -2416,7 +2237,7 @@ void LLModelPreview::smoothNormals() return; } - F32 angle_cutoff = mFMP->childGetValue("edge threshold").asReal(); + F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); angle_cutoff *= DEG_TO_RAD; @@ -2424,7 +2245,7 @@ void LLModelPreview::smoothNormals() { for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) { - (*iter)->smoothNormals(angle_cutoff); + (*iter)->generateNormals(angle_cutoff); } mVertexBuffer[5].clear(); @@ -2432,7 +2253,7 @@ void LLModelPreview::smoothNormals() for (LLModelLoader::model_list::iterator iter = mModel[which_lod].begin(); iter != mModel[which_lod].end(); ++iter) { - (*iter)->smoothNormals(angle_cutoff); + (*iter)->generateNormals(angle_cutoff); } mVertexBuffer[which_lod].clear(); @@ -2621,7 +2442,7 @@ void LLModelPreview::consolidate() refresh(); } -void LLModelPreview::scrubMaterials() +void LLModelPreview::clearMaterials() { for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) { //for each transform in current scene @@ -2644,7 +2465,6 @@ void LLModelPreview::scrubMaterials() } } - mVertexBuffer[mPreviewLOD].clear(); if (mPreviewLOD == LLModel::LOD_HIGH) @@ -2808,25 +2628,9 @@ void LLModelPreview::genLODs(S32 which_lod) start = end = which_lod; } + LLSpinCtrl* lim = mFMP->getChild("lod_triangle_limit", TRUE); + lim->setMaxValue(base_triangle_count); - std::string combo_name[] = - { - "lowest detail combo", - "low detail combo", - "medium detail combo", - "high detail combo", - "physics detail combo" - }; - - std::string limit_name[] = - { - "lowest limit", - "low limit", - "medium limit", - "high limit", - "physics limit" - }; - for (S32 lod = start; lod >= end; --lod) { if (which_lod == -1) @@ -2841,13 +2645,6 @@ void LLModelPreview::genLODs(S32 which_lod) triangle_count = limit; } - LLComboBox* combo_box = mFMP->findChild(combo_name[lod]); - combo_box->setCurrentByIndex(2); - - LLSpinCtrl* lim = mFMP->getChild(limit_name[lod], TRUE); - lim->setMaxValue(base_triangle_count); - lim->setVisible(true); - mModel[lod].clear(); mModel[lod].resize(mBaseModel.size()); mVertexBuffer[lod].clear(); @@ -3037,32 +2834,44 @@ void LLModelPreview::updateStatusMessages() } } + mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH])); + + std::string mesh_status_good = mFMP->getString("mesh_status_good"); + std::string mesh_status_bad = mFMP->getString("mesh_status_bad"); + std::string mesh_status_na = mFMP->getString("mesh_status_na"); + std::string mesh_status_none = mFMP->getString("mesh_status_none"); - std::string upload_message; - - mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[TRIANGLES]", llformat("%d", total_tris[LLModel::LOD_PHYSICS])); + bool upload_ok = true; for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod) { - mFMP->childSetTextArg(info_name[lod], "[TRIANGLES]", llformat("%d", total_tris[lod])); - mFMP->childSetTextArg(info_name[lod], "[VERTICES]", llformat("%d", total_verts[lod])); - mFMP->childSetTextArg(info_name[lod], "[SUBMESHES]", llformat("%d", total_submeshes[lod])); + if (total_tris[lod] > 0) + { + mFMP->childSetText(lod_triangles_name[lod], llformat("%d", total_tris[lod])); + mFMP->childSetText(lod_vertices_name[lod], llformat("%d", total_verts[lod])); + } + else + { + mFMP->childSetText(lod_triangles_name[lod], mesh_status_na); + mFMP->childSetText(lod_vertices_name[lod], mesh_status_na); + } - std::string message = "good"; + + std::string message = mesh_status_good; const U32 lod_high = LLModel::LOD_HIGH; if (lod != lod_high) { if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) - { - message = "mesh_mismatch"; - upload_message = "bad_lod"; + { //number of submeshes is different + message = mesh_status_bad; + upload_ok = false; } else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) - { - message = "model_mismatch"; - upload_message = "bad_lod"; + { //number of meshes is different + message = mesh_status_bad; + upload_ok = false; } else if (!verts[lod].empty()) { @@ -3071,27 +2880,119 @@ void LLModelPreview::updateStatusMessages() S32 max_verts = verts[lod+1][i]; if (verts[lod][i] > max_verts) - { - message = "too_heavy"; - upload_message = "bad_lod"; + { //too many vertices in this lod + message = mesh_status_bad; + upload_ok = false; } } } + else + { //no mesh + message = mesh_status_none; + } } - mFMP->childSetTextArg(info_name[lod], "[MESSAGE]", mFMP->getString(message)); + mFMP->childSetText(lod_status_name[lod], message); } - if (upload_message.empty()) + if (upload_ok) { - mFMP->childSetTextArg("upload_message", "[MESSAGE]", std::string("")); mFMP->childEnable("ok_btn"); } else { - mFMP->childSetTextArg("upload_message", "[MESSAGE]", mFMP->getString(upload_message)); mFMP->childDisable("ok_btn"); } + + //add up physics triangles etc + S32 start = 0; + S32 end = mModel[LLModel::LOD_PHYSICS].size(); + + S32 idx = mFMP->childGetValue("physics_layer").asInteger(); + + if (idx >= 0 && idx < mModel[LLModel::LOD_PHYSICS].size()) + { + start = idx; + end = idx+1; + } + + S32 phys_tris = 0; + S32 phys_hulls = 0; + S32 phys_points = 0; + + for (S32 i = start; i < end; ++i) + { //add up hulls and points and triangles for selected mesh(es) + LLModel* model = mModel[LLModel::LOD_PHYSICS][i]; + S32 cur_submeshes = model->getNumVolumeFaces(); + + LLModel::convex_hull_decomposition& decomp = model->mConvexHullDecomp; + + if (!decomp.empty()) + { + phys_hulls += decomp.size(); + for (U32 i = 0; i < decomp.size(); ++i) + { + phys_points += decomp[i].size(); + } + } + else + { //choose physics shape OR decomposition, can't use both + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = model->getVolumeFace(j); + phys_tris += face.mNumIndices/3; + } + } + } + + if (phys_tris > 0) + { + mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", llformat("%d", phys_tris)); + } + else + { + mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", mesh_status_na); + } + + if (phys_hulls > 0) + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls)); + mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points)); + } + else + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na); + mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na); + } + + //enable/disable controls based on radio groups + if (mFMP->childGetValue("lod_from_file").asBoolean()) + { + mFMP->childDisable("lod_triangle_limit"); + mFMP->childDisable("lod_generate"); + mFMP->childEnable("lod_file"); + mFMP->childEnable("lod_browse"); + } + else + { + mFMP->childEnable("lod_triangle_limit"); + mFMP->childEnable("lod_generate"); + mFMP->childDisable("lod_file"); + mFMP->childDisable("lod_browse"); + } + + if (mFMP->childGetValue("physics_load_from_file").asBoolean()) + { + mFMP->childDisable("physics_lod_combo"); + mFMP->childEnable("physics_file"); + mFMP->childEnable("physics_browse"); + } + else + { + mFMP->childEnable("physics_lod_combo"); + mFMP->childDisable("physics_file"); + mFMP->childDisable("physics_browse"); + } } void LLModelPreview::setPreviewTarget(F32 distance) @@ -3243,6 +3144,7 @@ void LLModelPreview::update() { mDirty = false; mResourceCost = calcResourceCost(); + refresh(); } } @@ -3263,18 +3165,27 @@ BOOL LLModelPreview::render() LLGLDepthTest depth(GL_TRUE); LLGLDisable fog(GL_FOG); - glMatrixMode(GL_PROJECTION); - gGL.pushMatrix(); - glLoadIdentity(); - glOrtho(0.0f, width, 0.0f, height, -1.0f, 1.0f); + { + //clear background to blue + glMatrixMode(GL_PROJECTION); + gGL.pushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, width, 0.0f, height, -1.0f, 1.0f); - glMatrixMode(GL_MODELVIEW); - gGL.pushMatrix(); - glLoadIdentity(); - - gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); + glMatrixMode(GL_MODELVIEW); + gGL.pushMatrix(); + glLoadIdentity(); + + gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); + + gl_rect_2d_simple( width, height ); - gl_rect_2d_simple( width, height ); + glMatrixMode(GL_PROJECTION); + gGL.popMatrix(); + + glMatrixMode(GL_MODELVIEW); + gGL.popMatrix(); + } bool avatar_preview = false; bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); @@ -3328,24 +3239,20 @@ BOOL LLModelPreview::render() mFMP->childSetEnabled("consolidate", !avatar_preview); - F32 explode = mFMP->mDecompFloater ? mFMP->mDecompFloater->childGetValue("explode").asReal() : 0.f; - - glMatrixMode(GL_PROJECTION); - gGL.popMatrix(); - - glMatrixMode(GL_MODELVIEW); - gGL.popMatrix(); + F32 explode = mFMP->childGetValue("physics_explode").asReal(); glClear(GL_DEPTH_BUFFER_BIT); - LLViewerCamera::getInstance()->setAspect((F32) width / height ); + F32 aspect = (F32) mFMP->mPreviewRect.getWidth()/mFMP->mPreviewRect.getHeight(); + + LLViewerCamera::getInstance()->setAspect(aspect); LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); - LLVector3 target_pos = mPreviewTarget; LLVector3 offset = mCameraOffset; + LLVector3 target_pos = mPreviewTarget+offset; - F32 z_near = llmax(mCameraDistance-mPreviewScale.magVec(), 0.001f); - F32 z_far = mCameraDistance+mPreviewScale.magVec(); + F32 z_near = 0.001f; + F32 z_far = mCameraDistance+mPreviewScale.magVec()+mCameraOffset.magVec(); if (avatar_preview) { @@ -3386,21 +3293,10 @@ BOOL LLModelPreview::render() //genBuffers(3); //genLODs(); } - - bool physics = (mPreviewLOD == LLModel::LOD_PHYSICS); - - S32 physics_idx = -1; - - bool render_mesh = true; - bool render_hull = false; - - if (physics && mFMP->mDecompFloater) - { - physics_idx = mFMP->mDecompFloater->childGetValue("model").asInteger(); - render_mesh = mFMP->mDecompFloater->childGetValue("render_mesh").asBoolean(); - render_hull = mFMP->mDecompFloater->childGetValue("render_hull").asBoolean(); - } - + + bool physics = mFMP->childGetValue("show physics").asBoolean(); + S32 physics_idx = mFMP->childGetValue("physics_layer").asInteger(); + if (!mModel[mPreviewLOD].empty()) { if (mVertexBuffer[mPreviewLOD].empty()) @@ -3410,68 +3306,175 @@ BOOL LLModelPreview::render() if (!avatar_preview) { - //for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) { LLModelInstance& instance = *iter; + LLModel* model = instance.mLOD[mPreviewLOD]; + + if (!model) + { + continue; + } + gGL.pushMatrix(); LLMatrix4 mat = instance.mTransform; - glMultMatrixf((GLfloat*) mat.mMatrix); + glMultMatrixf((GLfloat*) mat.mMatrix); + + for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + + glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); + if (i < instance.mMaterial.size() && instance.mMaterial[i].mDiffuseMap.notNull()) + { + gGL.getTexUnit(0)->bind(instance.mMaterial[i].mDiffuseMap, true); + if (instance.mMaterial[i].mDiffuseMap->getDiscardLevel() > -1) + { + mTextureSet.insert(instance.mMaterial[i].mDiffuseMap); + } + } + + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + glColor3f(0.4f, 0.4f, 0.4f); + + if (mFMP->childGetValue("show edges").asBoolean()) + { + glLineWidth(3.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + gGL.popMatrix(); + } + + if (physics) + { + glClear(GL_DEPTH_BUFFER_BIT); + LLGLEnable blend(GL_BLEND); + gGL.blendFunc(LLRender::BF_ONE, LLRender::BF_ZERO); + + LLModel* physics_model = NULL; - //for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + if (physics_idx >= 0 && physics_idx < mModel[LLModel::LOD_PHYSICS].size() ) { - //LLModelInstance& instance = *model_iter; - LLModel* model = instance.mLOD[mPreviewLOD]; + physics_model = mModel[LLModel::LOD_PHYSICS][physics_idx]; + } + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; if (!model) { continue; } - //if (instance.mTransform != mat) - //{ - // llerrs << "WTF?" << llendl; - //} + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; - if (render_mesh) + glMultMatrixf((GLfloat*) mat.mMatrix); + + + bool render_mesh = true; + + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) { - for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i) + LLMutexLock(decomp->mMutex); + + std::map, std::vector > >::iterator iter = + mPhysicsMesh.find(model); + if (iter != mPhysicsMesh.end()) { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); - if (physics) + render_mesh = false; + for (U32 i = 0; i < iter->second.size(); ++i) { - if (physics_idx > -1 && model == mModel[mPreviewLOD][physics_idx]) + if (explode > 0.f) { - glColor4f(1,0,0,1); + gGL.pushMatrix(); + + LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters; + offset *= explode; + + gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); } - else + + static std::vector hull_colors; + + if (i+1 >= hull_colors.size()) { - glColor4f(0.75f, 0.75f, 0.75f, 1.f); + hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 255)); } - } - else - { - glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); - if (i < instance.mMaterial.size() && instance.mMaterial[i].mDiffuseMap.notNull()) + + LLVertexBuffer* buff = iter->second[i]; + if (buff) { - gGL.getTexUnit(0)->bind(instance.mMaterial[i].mDiffuseMap, true); - if (instance.mMaterial[i].mDiffuseMap->getDiscardLevel() > -1) + buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL); + + glColor4ubv(hull_colors[i].mV); + buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts()); + + if (mFMP->childGetValue("show edges").asBoolean()) { - mTextureSet.insert(instance.mMaterial[i].mDiffuseMap); - } + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glLineWidth(3.f); + glColor4ub(hull_colors[i].mV[0]/2, hull_colors[i].mV[1]/2, hull_colors[i].mV[2]/2, 255); + buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts()); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + + if (explode > 0.f) + { + gGL.popMatrix(); } } + } + } + + if (render_mesh) + { + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; + buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + + glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); + if (i < instance.mMaterial.size() && instance.mMaterial[i].mDiffuseMap.notNull()) + { + gGL.getTexUnit(0)->bind(instance.mMaterial[i].mDiffuseMap, true); + if (instance.mMaterial[i].mDiffuseMap->getDiscardLevel() > -1) + { + mTextureSet.insert(instance.mMaterial[i].mDiffuseMap); + } + } + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); glColor3f(0.4f, 0.4f, 0.4f); - if (mFMP->childGetValue("show edges").asBoolean()) + if (mFMP->childGetValue("show edges").asBoolean() || model == physics_model) { + if (model == physics_model) + { + glColor3f(1.f, 1.f, 0.f); + } + glLineWidth(3.f); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); @@ -3481,74 +3484,10 @@ BOOL LLModelPreview::render() } } - if (render_hull) - { - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); - - std::map, std::vector > >::iterator iter = - mPhysicsMesh.find(model); - if (iter != mPhysicsMesh.end()) - { - for (U32 i = 0; i < iter->second.size(); ++i) - { - if (explode > 0.f) - { - gGL.pushMatrix(); - - LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters; - offset *= explode; - - gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); - } - - static std::vector hull_colors; - - if (i+1 >= hull_colors.size()) - { - hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 255)); - } - - LLVertexBuffer* buff = iter->second[i]; - if (buff) - { - buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL); - - glColor4ubv(hull_colors[i].mV); - buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts()); - - if (mFMP->childGetValue("show edges").asBoolean()) - { - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glLineWidth(3.f); - glColor4ub(hull_colors[i].mV[0]/2, hull_colors[i].mV[1]/2, hull_colors[i].mV[2]/2, 255); - buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts()); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); - } - } - - if (explode > 0.f) - { - gGL.popMatrix(); - } - } - } - } - - //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",decomp->mHulls.size())); - //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",decomp->mTotalPoints)); - } + gGL.popMatrix(); } - gGL.popMatrix(); - } - - if (physics) - { - mPreviewLOD = LLModel::LOD_PHYSICS; + gGL.setSceneBlendType(LLRender::BT_ALPHA); } } else @@ -3702,36 +3641,38 @@ void LLModelPreview::pan(F32 right, F32 up) void LLModelPreview::setPreviewLOD(S32 lod) { - mPreviewLOD = llclamp(lod, 0, 4); - refresh(); -} + lod = llclamp(lod, 0, (S32) LLModel::LOD_HIGH); -//static -void LLFloaterModelPreview::onBrowseHighLOD(void* data) -{ - LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; - mp->loadModel(3); -} + if (lod != mPreviewLOD) + { + mPreviewLOD = lod; + + LLComboBox* combo_box = mFMP->getChild("preview_lod_combo"); + combo_box->setCurrentByIndex(mPreviewLOD); + mFMP->childSetTextArg("lod_table_footer", "[DETAIL]", mFMP->getString(lod_name[mPreviewLOD])); + mFMP->childSetText("lod_file", mLODFile[mPreviewLOD]); -//static -void LLFloaterModelPreview::onBrowseMediumLOD(void* data) -{ - LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; - mp->loadModel(2); -} + LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); + LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); -//static -void LLFloaterModelPreview::onBrowseLowLOD(void* data) -{ - LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; - mp->loadModel(1); + for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i) + { + const LLColor4& color = (i == lod) ? highlight_color : normal_color; + + mFMP->childSetColor(lod_status_name[i], color); + mFMP->childSetColor(lod_label_name[i], color); + mFMP->childSetColor(lod_triangles_name[i], color); + mFMP->childSetColor(lod_vertices_name[i], color); + } + } + refresh(); } //static -void LLFloaterModelPreview::onBrowseLowestLOD(void* data) +void LLFloaterModelPreview::onBrowseLOD(void* data) { LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; - mp->loadModel(0); + mp->loadModel(mp->mModelPreview->mPreviewLOD); } //static @@ -3739,11 +3680,6 @@ void LLFloaterModelPreview::onUpload(void* user_data) { LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; - if (mp->mDecompFloater) - { - mp->mDecompFloater->closeFloater(); - } - mp->mModelPreview->rebuildUploadData(); gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale, @@ -3760,24 +3696,17 @@ void LLFloaterModelPreview::onConsolidate(void* user_data) } //static -void LLFloaterModelPreview::onScrubMaterials(void* user_data) -{ - LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; - mp->mModelPreview->scrubMaterials(); -} - -//static -void LLFloaterModelPreview::onDecompose(void* user_data) +void LLFloaterModelPreview::onClearMaterials(void* user_data) { LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; - mp->showDecompFloater(); + mp->mModelPreview->clearMaterials(); } //static void LLFloaterModelPreview::refresh(LLUICtrl* ctrl, void* user_data) { LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; - mp->mModelPreview->refresh(); + sInstance->mModelPreview->mDirty = true; } void LLFloaterModelPreview::updateResourceCost() diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index a7e87ba9a1..958a8f583a 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -47,14 +47,6 @@ class daeElement; class domProfile_COMMON; class domInstance_geometry; -class LLPhysicsDecompFloater : public LLFloater -{ -public: - - LLPhysicsDecompFloater(LLSD& key); - ~LLPhysicsDecompFloater(); -}; - class LLModelLoader : public LLThread { public: @@ -136,16 +128,12 @@ public: static void onMouseCaptureLostModelPreview(LLMouseHandler*); static void setUploadAmount(S32 amount) { sUploadAmount = amount; } - static void onBrowseHighLOD(void* data); - static void onBrowseMediumLOD(void* data); - static void onBrowseLowLOD(void* data); - static void onBrowseLowestLOD(void* data); + static void onBrowseLOD(void* data); static void onUpload(void* data); static void onConsolidate(void* data); - static void onScrubMaterials(void* data); - static void onDecompose(void* data); + static void onClearMaterials(void* data); static void onModelDecompositionComplete(LLModel* model, std::vector >& physics_mesh); static void refresh(LLUICtrl* ctrl, void* data); @@ -158,27 +146,16 @@ protected: friend class LLModelPreview; friend class LLMeshFilePicker; friend class LLPhysicsDecomp; - friend class LLPhysicsDecompFloater; - static void onDebugScaleCommit(LLUICtrl*, void*); + static void onImportScaleCommit(LLUICtrl*, void*); static void onUploadJointsCommit(LLUICtrl*,void*); static void onUploadSkinCommit(LLUICtrl*,void*); static void onPreviewLODCommit(LLUICtrl*,void*); - static void onHighLODCommit(LLUICtrl*,void*); - static void onMediumLODCommit(LLUICtrl*,void*); - static void onLowLODCommit(LLUICtrl*,void*); - static void onLowestLODCommit(LLUICtrl*,void*); - static void onPhysicsLODCommit(LLUICtrl*,void*); - - static void onHighLimitCommit(LLUICtrl*,void*); - static void onMediumLimitCommit(LLUICtrl*,void*); - static void onLowLimitCommit(LLUICtrl*,void*); - static void onLowestLimitCommit(LLUICtrl*,void*); - static void onPhysicsLimitCommit(LLUICtrl*,void*); + static void onTriangleLimitCommit(LLUICtrl*,void*); - static void onSmoothNormalsCommit(LLUICtrl*,void*); + static void onGenerateNormalsCommit(LLUICtrl*,void*); static void onAutoFillCommit(LLUICtrl*,void*); static void onShowEdgesCommit(LLUICtrl*,void*); @@ -188,20 +165,24 @@ protected: static void onPhysicsParamCommit(LLUICtrl* ctrl, void* userdata); static void onPhysicsStageExecute(LLUICtrl* ctrl, void* userdata); static void onPhysicsStageCancel(LLUICtrl* ctrl, void* userdata); - static void onClosePhysicsFloater(LLUICtrl* ctrl, void* userdata); + + static void onPhysicsBrowse(LLUICtrl* ctrl, void* userdata); + static void onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata); + static void onPhysicsOptimize(LLUICtrl* ctrl, void* userdata); + static void onPhysicsDecomposeBack(LLUICtrl* ctrl, void* userdata); + static void onPhysicsSimplifyBack(LLUICtrl* ctrl, void* userdata); + + void draw(); - static void setLODMode(S32 lod, void* userdata); - void setLODMode(S32 lod, S32 which_mode); static void setLimit(S32 lod, void* userdata); void setLimit(S32 lod, S32 limit); - void showDecompFloater(); + void initDecompControls(); LLModelPreview* mModelPreview; - LLFloater* mDecompFloater; LLPhysicsDecomp::decomp_params mDecompParams; S32 mLastMouseX; @@ -227,6 +208,7 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex void setPreviewTarget(F32 distance); void setTexture(U32 name) { mTextureName = name; } + void setPhysicsFromLOD(S32 lod); BOOL render(); void update(); void genBuffers(S32 lod, bool skinned); @@ -241,9 +223,9 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex void loadModel(std::string filename, S32 lod); void loadModelCallback(S32 lod); void genLODs(S32 which_lod = -1); - void smoothNormals(); + void generateNormals(); void consolidate(); - void scrubMaterials(); + void clearMaterials(); U32 calcResourceCost(); void rebuildUploadData(); void clearIncompatible(S32 lod); @@ -271,9 +253,9 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex LLVector3 mPreviewScale; S32 mPreviewLOD; U32 mResourceCost; - S32 mLODMode[LLModel::NUM_LODS]; S32 mLimit[LLModel::NUM_LODS]; - + std::string mLODFile[LLModel::NUM_LODS]; + LLModelLoader* mModelLoader; diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index ddd2ff196b..2a5d034789 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -778,4 +778,10 @@ + + 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 c7433af226..f1b6024ff7 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -1,49 +1,358 @@ - - - Status: [STATUS] - + can_resize="true" height="520" min_height="520" min_width="630" + name="Model Preview" title="Upload Model" width="630"> Idle Loading... Generating Meshes... - Cannot upload, missing required level of detail. - Cannot upload, supplied levels of detail invalid." - Too many vertices. - Required - Good - Submesh count mismatch. - Model count mismatch. - - - + High + Medium + Low + Lowest + Good + Bad" + N/A + None + All + + + Name: + + + + Preview: + + + + + + + Prim Cost: [COST] + + + [STATUS] + + + Level of Detail: + + + Lowest - + Low - + Medium - + High - - Physics - - + + + + +