From fb7a9106cd13167caa4f7dfe6ba239e050b04d5e Mon Sep 17 00:00:00 2001 From: Zi Ree Date: Thu, 27 May 2021 14:01:48 +0200 Subject: [PATCH 01/14] FIRE-30967 - When clearing inventory cache, also clear library cache --- indra/newview/llinventorymodel.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index d3cc03914f..e8542dcaf6 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -2234,6 +2234,22 @@ bool LLInventoryModel::loadSkeleton( LLFile::remove(inventory_filename); } + // also delete library cache if inventory cache is purged, so issues with EEP settings going missing + // and bridge objects not being found can be resolved + inventory_filename = getInvCacheAddres(ALEXANDRIA_LINDEN_ID); + if (LLFile::isfile(inventory_filename)) + { + LL_INFOS("LLInventoryModel") << "Purging library cache file: " << inventory_filename << LL_ENDL; + LLFile::remove(inventory_filename); + } + + inventory_filename.append(".gz"); + if (LLFile::isfile(inventory_filename)) + { + LL_INFOS("LLInventoryModel") << "Purging library cache file: " << inventory_filename << LL_ENDL; + LLFile::remove(inventory_filename); + } + LL_INFOS("LLInventoryModel") << "Clear inventory cache marker removed: " << delete_cache_marker << LL_ENDL; LLFile::remove(delete_cache_marker); } From f7e2f750d890f302655916f6d3521bb3d1c59b86 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Thu, 27 May 2021 19:39:40 +0200 Subject: [PATCH 02/14] FIRE-30962: Fix animation details not being displayed for animations in task inventory --- indra/newview/llpreviewanim.cpp | 24 ++++++++++++++++++++++++ indra/newview/llpreviewanim.h | 1 + 2 files changed, 25 insertions(+) diff --git a/indra/newview/llpreviewanim.cpp b/indra/newview/llpreviewanim.cpp index 29accdb074..b9a27dc799 100644 --- a/indra/newview/llpreviewanim.cpp +++ b/indra/newview/llpreviewanim.cpp @@ -235,4 +235,28 @@ void LLPreviewAnim::onClose(bool app_quitting) // } // } //} + +// virtual +void LLPreviewAnim::refreshFromItem() +{ + LLPreview::refreshFromItem(); + + const LLInventoryItem* item = getItem(); + if (item) + { + pMotion = gAgentAvatarp->createMotion(item->getAssetUUID()); // preload the animation + + if (pMotion) + { + LLTextBox* stats_box_left = getChild("AdvancedStatsLeft"); + LLTextBox* stats_box_right = getChild("AdvancedStatsRight"); + stats_box_left->setTextArg("[PRIORITY]", llformat("%d", pMotion->getPriority())); + stats_box_left->setTextArg("[DURATION]", llformat("%.2f", pMotion->getDuration())); + stats_box_left->setTextArg("[IS_LOOP]", (pMotion->getLoop() ? LLTrans::getString("PermYes") : LLTrans::getString("PermNo"))); + stats_box_right->setTextArg("[EASE_IN]", llformat("%.2f", pMotion->getEaseInDuration())); + stats_box_right->setTextArg("[EASE_OUT]", llformat("%.2f", pMotion->getEaseOutDuration())); + stats_box_right->setTextArg("[NUM_JOINTS]", llformat("%d", pMotion->getNumJointMotions())); + } + } +} // diff --git a/indra/newview/llpreviewanim.h b/indra/newview/llpreviewanim.h index df5ad99187..7ce6cfc371 100644 --- a/indra/newview/llpreviewanim.h +++ b/indra/newview/llpreviewanim.h @@ -45,6 +45,7 @@ public: void play(const LLSD& param); // Improved animation preview //void showAdvanced(); + /*virtual*/ void refreshFromItem(); protected: From 20cd74917572eb3cce199ee5139396bb0d411caa Mon Sep 17 00:00:00 2001 From: Beq Date: Fri, 28 May 2021 02:22:53 +0100 Subject: [PATCH 03/14] Instrument threaded texture decode includes fix for timeout in slow decoded bakes --- indra/llimage/llimageworker.cpp | 34 +++++++++++++++++-- indra/llimagej2coj/llimagej2coj.cpp | 4 ++- indra/llkdu/llimagej2ckdu.cpp | 5 ++- indra/newview/llappviewer.cpp | 8 ++++- .../newview/skins/default/xui/en/strings.xml | 1 + 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp index 0f44df12f2..53e02ee07f 100644 --- a/indra/llimage/llimageworker.cpp +++ b/indra/llimage/llimageworker.cpp @@ -25,7 +25,7 @@ */ #include "linden_common.h" - +#include "fstelemetry.h" // add telemetry support. #include "llimageworker.h" #include "llimagedxt.h" @@ -135,7 +135,12 @@ LLImageDecodeThread::~LLImageDecodeThread() // virtual S32 LLImageDecodeThread::update(F32 max_time_ms) { + FSZoneC(tracy::Color::Blue); // instrument image decodes LLMutexLock lock(mCreationMutex); + // instrument image decodes + { + FSZoneC(tracy::Color::Blue1); + // for (creation_list_t::iterator iter = mCreationList.begin(); iter != mCreationList.end(); ++iter) { @@ -155,13 +160,21 @@ S32 LLImageDecodeThread::update(F32 max_time_ms) } } mCreationList.clear(); + // instrument image decodes + } + { + FSZoneC(tracy::Color::Blue2); + // S32 res = LLQueuedThread::update(max_time_ms); + // FSPlot("img_decode_pending", (int64_t)res); // instrument image decodes return res; + } // instrument image decodes } LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image, U32 priority, S32 discard, BOOL needs_aux, Responder* responder) { + FSZoneC(tracy::Color::Orange); // instrument the image decode pipeline LLMutexLock lock(mCreationMutex); handle_t handle = generateHandle(); mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder)); @@ -230,10 +243,19 @@ bool LLImageDecodeThread::ImageRequest::processRequest() bool LLImageDecodeThread::ImageRequest::processRequestIntern() { - const F32 decode_time_slice = .1f; + // allow longer timeout for async and add instrumentation + // const F32 decode_time_slice = .1f; + FSZoneC(tracy::Color::DarkOrange); + F32 decode_time_slice = .1f; + if(mFlags & FLAG_ASYNC) + { + decode_time_slice = 10.0f;// long time out as this is not an issue with async + } + // bool done = true; if (!mDecodedRaw && mFormattedImage.notNull()) { + FSZoneC(tracy::Color::DarkOrange1); // instrument the image decode pipeline // Decode primary channels if (mDecodedImageRaw.isNull()) { @@ -272,6 +294,7 @@ bool LLImageDecodeThread::ImageRequest::processRequestIntern() } if (done && mNeedsAux && !mDecodedAux && mFormattedImage.notNull()) { + FSZoneC(tracy::Color::DarkOrange2); // instrument the image decode pipeline // Decode aux channel if (!mDecodedImageAux) { @@ -282,7 +305,12 @@ bool LLImageDecodeThread::ImageRequest::processRequestIntern() done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms mDecodedAux = done && mDecodedImageAux->getData(); } - + // report timeout on async thread (which leads to worker abort errors) + if(!done) + { + LL_WARNS("ImageDecode") << "Image decoding failed to complete with time slice=" << decode_time_slice << LL_ENDL; + } + // // Image thread pool from CoolVL if (mFlags & FLAG_ASYNC) { diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 0e605cf8b2..f46bcacb64 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -25,6 +25,7 @@ */ #include "linden_common.h" +#include "fstelemetry.h" // instrument image decodes #include "llimagej2coj.h" #define OPENJPEG2 @@ -258,10 +259,11 @@ bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int block bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) { // texture comment metadata reader + FSZone; // instrument image decodes U8* c_data = base.getData(); S32 c_size = base.getDataSize(); S32 position = 0; - + while (position < 1024 && position < (c_size - 7)) // the comment field should be in the first 1024 bytes. { if (c_data[position] == 0xff && c_data[position + 1] == 0x64) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 300a60c551..e1e7863cca 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -32,7 +32,7 @@ #include "llpointer.h" #include "llmath.h" #include "llkdumem.h" - +#include "fstelemetry.h" // instrument image decodes #define kdu_xxxx "kdu_block_coding.h" #include "include_kdu_xxxx.h" @@ -287,6 +287,7 @@ void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision); // as well, when that still existed, with keep_codestream true and MODE_FAST. void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECodeStreamMode mode) { + FSZone; // instrument image decodes S32 data_size = base.getDataSize(); S32 max_bytes = (base.getMaxBytes() ? base.getMaxBytes() : data_size); @@ -436,6 +437,7 @@ bool LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int bloc // decodeImpl() usage matters for production. bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level, int* region) { + FSZone; // instrument image decodes base.resetLastError(); // *FIX: kdu calls our callback function if there's an error, and then bombs. @@ -519,6 +521,7 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco // Returns true to mean done, whether successful or not. bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) { + FSZone; // instrument image decodes ECodeStreamMode mode = MODE_FAST; LLTimer decode_timer; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e3d743e792..fc5ec952da 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1882,8 +1882,13 @@ bool LLAppViewer::doFrame() S32 work_pending = 0; S32 io_pending = 0; F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f); - + // instrument image decodes + { + FSZoneN("updateTextureThreads"); + // FSPlot("max_time_ms",max_time); + // work_pending += updateTextureThreads(max_time); + } // instrument image decodes { LL_RECORD_BLOCK_TIME(FTM_LFS); @@ -3822,6 +3827,7 @@ LLSD LLAppViewer::getViewerInfo() const // CPU info["CPU"] = gSysCPU.getCPUString(); info["MEMORY_MB"] = LLSD::Integer(gSysMemory.getPhysicalMemoryKB().valueInUnits()); + info["CONCURRENCY"] = LLSD::Integer((S32)boost::thread::hardware_concurrency()); // Add hardware concurrency to info // Moved hack adjustment to Windows memory size into llsys.cpp info["OS_VERSION"] = LLOSInfo::instance().getOSString(); info["GRAPHICS_CARD_VENDOR"] = ll_safe_string((const char*)(glGetString(GL_VENDOR))); diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 152d45b599..61377bc02a 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -51,6 +51,7 @@ You are in [REGION] CPU: [CPU] Memory: [MEMORY_MB] MB +Concurrency: [CONCURRENCY] OS Version: [OS_VERSION] Graphics Card Vendor: [GRAPHICS_CARD_VENDOR] Graphics Card: [GRAPHICS_CARD] From 99027205df37cc50d9c47f80dde2193193b53523 Mon Sep 17 00:00:00 2001 From: Beq Date: Fri, 28 May 2021 03:07:14 +0100 Subject: [PATCH 04/14] FIRE-30965 Improve mesh upload error + rewrite subset checks Rewrite material parsing to properly implement material subset with the NoGeometry tag. --- indra/llprimitive/llmodel.cpp | 123 +- indra/llprimitive/llmodel.h | 2 +- indra/llprimitive/llmodelloader.h | 3 +- indra/newview/llfloatermodelpreview.cpp | 2 +- indra/newview/llmodelpreview.cpp | 6766 ++++++++++++----------- indra/newview/llmodelpreview.h | 2 +- 6 files changed, 3513 insertions(+), 3385 deletions(-) diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index a2596335c2..ef008e0a81 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -1220,7 +1220,13 @@ bool LLModel::isMaterialListSubset( LLModel* ref ) { int refCnt = ref->mMaterialList.size(); int modelCnt = mMaterialList.size(); - + // FIRE-30965 Cleanup braindead mesh parsing error handlers + if(modelCnt > refCnt) + { + // this model cannot be a strict subset if it has more materials than the reference + return FALSE; + } + // for (U32 src = 0; src < modelCnt; ++src) { bool foundRef = false; @@ -1264,77 +1270,80 @@ bool LLModel::needToAddFaces( LLModel* ref, int& refFaceCnt, int& modelFaceCnt ) return changed; } -bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt ) -{ - //Is this a subset? - //LODs cannot currently add new materials, e.g. - //1. ref = a,b,c lod1 = d,e => This is not permitted - //2. ref = a,b,c lod1 = c => This would be permitted +// FIRE-30965 Improve mesh upload error handling +// function moved to llmodelpreview +// bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt ) +// { +// //Is this a subset? +// //LODs cannot currently add new materials, e.g. +// //1. ref = a,b,c lod1 = d,e => This is not permitted +// //2. ref = a,b,c lod1 = c => This would be permitted - bool isASubset = isMaterialListSubset( ref ); - if ( !isASubset ) - { - LL_INFOS("MESHSKININFO")<<"Material of model is not a subset of reference."< ref->mMaterialList.size()) - { - LL_INFOS("MESHSKININFO") << "Material of model has more materials than a reference." << LL_ENDL; - // We passed isMaterialListSubset, so materials are a subset, but subset isn't supposed to be - // larger than original and if we keep going, reordering will cause a crash - return false; - } +// if (mMaterialList.size() > ref->mMaterialList.size()) +// { +// LL_INFOS("MESHSKININFO") << "Material of model has more materials than a reference." << LL_ENDL; +// // We passed isMaterialListSubset, so materials are a subset, but subset isn't supposed to be +// // larger than original and if we keep going, reordering will cause a crash +// return false; +// } - std::map index_map; +// std::map index_map; - //build a map of material slot names to face indexes - bool reorder = false; +// //build a map of material slot names to face indexes +// bool reorder = false; - std::set base_mat; - std::set cur_mat; +// std::set base_mat; +// std::set cur_mat; - for (U32 i = 0; i < mMaterialList.size(); i++) - { - index_map[ref->mMaterialList[i]] = i; - //if any material name does not match reference, we need to reorder - reorder |= ref->mMaterialList[i] != mMaterialList[i]; - base_mat.insert(ref->mMaterialList[i]); - cur_mat.insert(mMaterialList[i]); - } +// for (U32 i = 0; i < mMaterialList.size(); i++) +// { +// index_map[ref->mMaterialList[i]] = i; +// //if any material name does not match reference, we need to reorder +// reorder |= ref->mMaterialList[i] != mMaterialList[i]; +// base_mat.insert(ref->mMaterialList[i]); +// cur_mat.insert(mMaterialList[i]); +// } - if (reorder && (base_mat == cur_mat)) //don't reorder if material name sets don't match - { - std::vector new_face_list; - new_face_list.resize(mMaterialList.size()); +// if (reorder && (base_mat == cur_mat)) //don't reorder if material name sets don't match +// { +// std::vector new_face_list; +// new_face_list.resize(mMaterialList.size()); - std::vector new_material_list; - new_material_list.resize(mMaterialList.size()); +// std::vector new_material_list; +// new_material_list.resize(mMaterialList.size()); - //rebuild face list so materials have the same order - //as the reference model - for (U32 i = 0; i < mMaterialList.size(); ++i) - { - U32 ref_idx = index_map[mMaterialList[i]]; +// //rebuild face list so materials have the same order +// //as the reference model +// for (U32 i = 0; i < mMaterialList.size(); ++i) +// { +// U32 ref_idx = index_map[mMaterialList[i]]; - if (i < mVolumeFaces.size()) - { - new_face_list[ref_idx] = mVolumeFaces[i]; - } - new_material_list[ref_idx] = mMaterialList[i]; - } +// if (i < mVolumeFaces.size()) +// { +// new_face_list[ref_idx] = mVolumeFaces[i]; +// } +// new_material_list[ref_idx] = mMaterialList[i]; +// } - llassert(new_material_list == ref->mMaterialList); +// llassert(new_material_list == ref->mMaterialList); - mVolumeFaces = new_face_list; +// mVolumeFaces = new_face_list; - //override material list with reference model ordering - mMaterialList = ref->mMaterialList; - } +// //override material list with reference model ordering +// mMaterialList = ref->mMaterialList; +// } - return true; -} +// return true; +// } +// bool LLModel::loadSkinInfo(LLSD& header, std::istream &is) { diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index c2a5389b6c..d77a13f145 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -187,7 +187,7 @@ public: //reorder face list based on mMaterialList in this and reference so //order matches that of reference (material ordering touchup) - bool matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); + // bool matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); // FIRE-30965 error handling improvements (function relocated) bool isMaterialListSubset( LLModel* ref ); bool needToAddFaces( LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h index 0bd7a05217..20278dcdb5 100644 --- a/indra/llprimitive/llmodelloader.h +++ b/indra/llprimitive/llmodelloader.h @@ -87,7 +87,7 @@ public: DONE, WARNING_BIND_SHAPE_ORIENTATION, ERROR_PARSING, //basically loading failed - ERROR_MATERIALS, + ERROR_MATERIALS_NOT_A_SUBSET, // FIRE-30965 - better error differentiation ERROR_PASSWORD_REQUIRED, ERROR_NEED_MORE_MEMORY, ERROR_INVALID_FILE, @@ -96,6 +96,7 @@ public: ERROR_OUT_OF_RANGE, ERROR_FILE_VERSION_INVALID, ERROR_LOD_MODEL_MISMATCH, // clean up and improve error reporting + ERROR_HIGH_LOD_MODEL_MISSING, // clean up and improve error reporting ERROR_MODEL // this error should always be last in this list, error code is passed as ERROR_MODEL+error_code } eLoadState; diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 9f34b59bf6..8370d36a14 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -864,7 +864,7 @@ void LLFloaterModelPreview::draw() if (!mModelPreview->mLoading) { - if ( mModelPreview->getLoadState() == LLModelLoader::ERROR_MATERIALS ) + if ( mModelPreview->getLoadState() == LLModelLoader::ERROR_MATERIALS_NOT_A_SUBSET )// Improve error reporting { // cleanup/improve errors - this error is effectively duplicated, the unused one was actually better // childSetTextArg("status", "[STATUS]", getString("status_material_mismatch")); diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index c6b41ca2c0..3e782468ba 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -94,81 +94,81 @@ const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f; BOOL stop_gloderror() { - GLuint error = glodGetError(); + GLuint error = glodGetError(); - if (error != GLOD_NO_ERROR) - { - LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; - return TRUE; - } + if (error != GLOD_NO_ERROR) + { + LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; + return TRUE; + } - return FALSE; + return FALSE; } LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material) { - LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); + LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); - if (texture) - { - if (texture->getDiscardLevel() > -1) - { - gGL.getTexUnit(0)->bind(texture, true); - return texture; - } - } + if (texture) + { + if (texture->getDiscardLevel() > -1) + { + gGL.getTexUnit(0)->bind(texture, true); + return texture; + } + } - return NULL; + return NULL; } std::string stripSuffix(std::string name) { - // Bug fixes in mesh importer by Drake Arconis - //if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1)) - if ((name.find("_LOD") != std::string::npos) || (name.find("_PHYS") != std::string::npos)) - // - { - return name.substr(0, name.rfind('_')); - } - return name; + // Bug fixes in mesh importer by Drake Arconis + //if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1)) + if ((name.find("_LOD") != std::string::npos) || (name.find("_PHYS") != std::string::npos)) + // + { + return name.substr(0, name.rfind('_')); + } + return name; } std::string getLodSuffix(S32 lod) { - std::string suffix; - switch (lod) - { - case LLModel::LOD_IMPOSTOR: suffix = "_LOD0"; break; - case LLModel::LOD_LOW: suffix = "_LOD1"; break; - case LLModel::LOD_MEDIUM: suffix = "_LOD2"; break; - case LLModel::LOD_PHYSICS: suffix = "_PHYS"; break; - case LLModel::LOD_HIGH: break; - } - return suffix; + std::string suffix; + switch (lod) + { + case LLModel::LOD_IMPOSTOR: suffix = "_LOD0"; break; + case LLModel::LOD_LOW: suffix = "_LOD1"; break; + case LLModel::LOD_MEDIUM: suffix = "_LOD2"; break; + case LLModel::LOD_PHYSICS: suffix = "_PHYS"; break; + case LLModel::LOD_HIGH: break; + } + return suffix; } void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut) { - LLModelLoader::scene::iterator base_iter = scene.begin(); - bool found = false; - while (!found && (base_iter != scene.end())) - { - matOut = base_iter->first; + LLModelLoader::scene::iterator base_iter = scene.begin(); + bool found = false; + while (!found && (base_iter != scene.end())) + { + matOut = base_iter->first; - LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); - while (!found && (base_instance_iter != base_iter->second.end())) - { - LLModelInstance& base_instance = *base_instance_iter++; - LLModel* base_model = base_instance.mModel; + LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); + while (!found && (base_instance_iter != base_iter->second.end())) + { + LLModelInstance& base_instance = *base_instance_iter++; + LLModel* base_model = base_instance.mModel; - if (base_model && (base_model->mLabel == name_to_match)) - { - baseModelOut = base_model; - return; - } - } - base_iter++; - } + if (base_model && (base_model->mLabel == name_to_match)) + { + baseModelOut = base_model; + return; + } + } + base_iter++; + } } //----------------------------------------------------------------------------- @@ -176,2627 +176,2745 @@ void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LL //----------------------------------------------------------------------------- LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) - : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex() - , mLodsQuery() - , mLodsWithParsingError() - , mPelvisZOffset(0.0f) - , mLegacyRigFlags(U32_MAX) - , mRigValidJointUpload(false) - , mPhysicsSearchLOD(LLModel::LOD_PHYSICS) - , mResetJoints(false) - , mModelNoErrors(true) - , mLastJointUpdate(false) - , mFirstSkinUpdate(true) - , mHasDegenerate(false) - , mImporterDebug(LLCachedControl(gSavedSettings, "ImporterDebug", false)) + : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex() + , mLodsQuery() + , mLodsWithParsingError() + , mPelvisZOffset(0.0f) + , mLegacyRigFlags(U32_MAX) + , mRigValidJointUpload(false) + , mPhysicsSearchLOD(LLModel::LOD_PHYSICS) + , mResetJoints(false) + , mModelNoErrors(true) + , mLastJointUpdate(false) + , mFirstSkinUpdate(true) + , mHasDegenerate(false) + , mImporterDebug(LLCachedControl(gSavedSettings, "ImporterDebug", false)) { - mNeedsUpdate = TRUE; - mCameraDistance = 0.f; - mCameraYaw = 0.f; - mCameraPitch = 0.f; - mCameraZoom = 1.f; - mTextureName = 0; - mPreviewLOD = 0; - mModelLoader = NULL; - mMaxTriangleLimit = 0; - mDirty = false; - mGenLOD = false; - mLoading = false; - mLookUpLodFiles = false; - mLoadState = LLModelLoader::STARTING; - mGroup = 0; - mLODFrozen = false; - mBuildShareTolerance = 0.f; - mBuildQueueMode = GLOD_QUEUE_GREEDY; - mBuildBorderMode = GLOD_BORDER_UNLOCK; - mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE; - mUVGuideTexture = LLViewerTextureManager::getFetchedTextureFromFile(gSavedSettings.getString("FSMeshPreviewUVGuideFile"), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); // - Add UV guide overlay to pmesh preview + mNeedsUpdate = TRUE; + mCameraDistance = 0.f; + mCameraYaw = 0.f; + mCameraPitch = 0.f; + mCameraZoom = 1.f; + mTextureName = 0; + mPreviewLOD = 0; + mModelLoader = NULL; + mMaxTriangleLimit = 0; + mDirty = false; + mGenLOD = false; + mLoading = false; + mLookUpLodFiles = false; + mLoadState = LLModelLoader::STARTING; + mGroup = 0; + mLODFrozen = false; + mBuildShareTolerance = 0.f; + mBuildQueueMode = GLOD_QUEUE_GREEDY; + mBuildBorderMode = GLOD_BORDER_UNLOCK; + mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE; + mUVGuideTexture = LLViewerTextureManager::getFetchedTextureFromFile(gSavedSettings.getString("FSMeshPreviewUVGuideFile"), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); // - Add UV guide overlay to pmesh preview - for (U32 i = 0; i < LLModel::NUM_LODS; ++i) - { - mRequestedTriangleCount[i] = 0; - mRequestedCreaseAngle[i] = -1.f; - mRequestedLoDMode[i] = 0; - mRequestedErrorThreshold[i] = 0.f; - mRequestedBuildOperator[i] = 0; - mRequestedQueueMode[i] = 0; - mRequestedBorderMode[i] = 0; - mRequestedShareTolerance[i] = 0.f; - } + for (U32 i = 0; i < LLModel::NUM_LODS; ++i) + { + mRequestedTriangleCount[i] = 0; + mRequestedCreaseAngle[i] = -1.f; + mRequestedLoDMode[i] = 0; + mRequestedErrorThreshold[i] = 0.f; + mRequestedBuildOperator[i] = 0; + mRequestedQueueMode[i] = 0; + mRequestedBorderMode[i] = 0; + mRequestedShareTolerance[i] = 0.f; + } - mViewOption["show_textures"] = false; - mFMP = fmp; + mViewOption["show_textures"] = false; + mFMP = fmp; - mHasPivot = false; - mModelPivot = LLVector3(0.0f, 0.0f, 0.0f); + mHasPivot = false; + mModelPivot = LLVector3(0.0f, 0.0f, 0.0f); - glodInit(); + glodInit(); - createPreviewAvatar(); + createPreviewAvatar(); } LLModelPreview::~LLModelPreview() { - // glod apparently has internal mem alignment issues that are angering - // the heap-check code in windows, these should be hunted down in that - // TP code, if possible - // - // kernel32.dll!HeapFree() + 0x14 bytes - // msvcr100.dll!free(void * pBlock) Line 51 C - // glod.dll!glodGetGroupParameteriv() + 0x119 bytes - // glod.dll!glodShutdown() + 0x77 bytes - // - //glodShutdown(); - if (mModelLoader) - { - mModelLoader->shutdown(); - } + // glod apparently has internal mem alignment issues that are angering + // the heap-check code in windows, these should be hunted down in that + // TP code, if possible + // + // kernel32.dll!HeapFree() + 0x14 bytes + // msvcr100.dll!free(void * pBlock) Line 51 C + // glod.dll!glodGetGroupParameteriv() + 0x119 bytes + // glod.dll!glodShutdown() + 0x77 bytes + // + //glodShutdown(); + if (mModelLoader) + { + mModelLoader->shutdown(); + } - if (mPreviewAvatar) - { - mPreviewAvatar->markDead(); - mPreviewAvatar = NULL; - } + if (mPreviewAvatar) + { + mPreviewAvatar->markDead(); + mPreviewAvatar = NULL; + } } U32 LLModelPreview::calcResourceCost() { - assert_main_thread(); + assert_main_thread(); - rebuildUploadData(); + rebuildUploadData(); - //Upload skin is selected BUT check to see if the joints coming in from the asset were malformed. - if (mFMP && mFMP->childGetValue("upload_skin").asBoolean()) - { - bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); - if (uploadingJointPositions && !isRigValidForJointPositionUpload()) - { - mFMP->childDisable("ok_btn"); - } - } + //Upload skin is selected BUT check to see if the joints coming in from the asset were malformed. + if (mFMP && mFMP->childGetValue("upload_skin").asBoolean()) + { + bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); + if (uploadingJointPositions && !isRigValidForJointPositionUpload()) + { + mFMP->childDisable("ok_btn"); + } + } - std::set accounted; - U32 num_points = 0; - U32 num_hulls = 0; + std::set accounted; + U32 num_points = 0; + U32 num_hulls = 0; - F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f; - mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f; + F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f; + mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f; - if (mFMP && mFMP->childGetValue("upload_joints").asBoolean()) - { - // FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail. - // see also call to addAttachmentPosOverride. - LLUUID fake_mesh_id; - fake_mesh_id.generate(); - getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); - } + if (mFMP && mFMP->childGetValue("upload_joints").asBoolean()) + { + // FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail. + // see also call to addAttachmentPosOverride. + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); + } - F32 streaming_cost = 0.f; - F32 physics_cost = 0.f; - for (U32 i = 0; i < mUploadData.size(); ++i) - { - LLModelInstance& instance = mUploadData[i]; + F32 streaming_cost = 0.f; + F32 physics_cost = 0.f; + for (U32 i = 0; i < mUploadData.size(); ++i) + { + LLModelInstance& instance = mUploadData[i]; - if (accounted.find(instance.mModel) == accounted.end()) - { - accounted.insert(instance.mModel); + if (accounted.find(instance.mModel) == accounted.end()) + { + accounted.insert(instance.mModel); - LLModel::Decomposition& decomp = - instance.mLOD[LLModel::LOD_PHYSICS] ? - instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : - instance.mModel->mPhysics; + LLModel::Decomposition& decomp = + instance.mLOD[LLModel::LOD_PHYSICS] ? + instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : + instance.mModel->mPhysics; - //update instance skin info for each lods pelvisZoffset - for (int j = 0; jmSkinInfo.mPelvisOffset = mPelvisZOffset; - } - } + //update instance skin info for each lods pelvisZoffset + for (int j = 0; jmSkinInfo.mPelvisOffset = mPelvisZOffset; + } + } - std::stringstream ostr; - LLSD ret = LLModel::writeModel(ostr, - instance.mLOD[4], - instance.mLOD[3], - instance.mLOD[2], - instance.mLOD[1], - instance.mLOD[0], - decomp, - mFMP->childGetValue("upload_skin").asBoolean(), - mFMP->childGetValue("upload_joints").asBoolean(), - mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(), - TRUE, - FALSE, - instance.mModel->mSubmodelID); + std::stringstream ostr; + LLSD ret = LLModel::writeModel(ostr, + instance.mLOD[4], + instance.mLOD[3], + instance.mLOD[2], + instance.mLOD[1], + instance.mLOD[0], + decomp, + mFMP->childGetValue("upload_skin").asBoolean(), + mFMP->childGetValue("upload_joints").asBoolean(), + mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(), + TRUE, + FALSE, + instance.mModel->mSubmodelID); - num_hulls += decomp.mHull.size(); - for (U32 i = 0; i < decomp.mHull.size(); ++i) - { - num_points += decomp.mHull[i].size(); - } + num_hulls += decomp.mHull.size(); + for (U32 i = 0; i < decomp.mHull.size(); ++i) + { + num_points += decomp.mHull[i].size(); + } - //calculate streaming cost - LLMatrix4 transformation = instance.mTransform; + //calculate streaming cost + LLMatrix4 transformation = instance.mTransform; - LLVector3 position = LLVector3(0, 0, 0) * transformation; + LLVector3 position = LLVector3(0, 0, 0) * transformation; - LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - LLVector3 scale = LLVector3(x_length, y_length, z_length); + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); - F32 radius = scale.length()*0.5f*debug_scale; + F32 radius = scale.length()*0.5f*debug_scale; - LLMeshCostData costs; - if (gMeshRepo.getCostData(ret, costs)) - { - streaming_cost += costs.getRadiusBasedStreamingCost(radius); - } - } - } + LLMeshCostData costs; + if (gMeshRepo.getCostData(ret, costs)) + { + streaming_cost += costs.getRadiusBasedStreamingCost(radius); + } + } + } - F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f; + F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f; - mDetailsSignal(mPreviewScale[0] * scale, mPreviewScale[1] * scale, mPreviewScale[2] * scale, streaming_cost, physics_cost); + mDetailsSignal(mPreviewScale[0] * scale, mPreviewScale[1] * scale, mPreviewScale[2] * scale, streaming_cost, physics_cost); - updateStatusMessages(); + updateStatusMessages(); - return (U32)streaming_cost; + return (U32)streaming_cost; } +// relocate from llmodel and rewrite so it does what it is meant to +bool LLModelPreview::matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFaceCnt, int& modelFaceCnt ) +{ + //Is this a subset? + //LODs cannot currently add new materials, e.g. + //1. ref = a,b,c lod1 = d,e => This is not permitted + //2. ref = a,b,c lod1 = c => This would be permitted + + LL_INFOS("MESHSKININFO") << "In matchMaterialOrder." << LL_ENDL; + bool isASubset = lod->isMaterialListSubset( ref ); + if ( !isASubset ) + { + LL_INFOS("MESHSKININFO")<<"Material of model is not a subset of reference."<getName() << "'s materials are not a subset of the High LOD (reference) model " << ref->getName(); + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + return false; + } + + LL_DEBUGS("MESHSKININFO") << "subset check passed." << LL_ENDL; + std::map index_map; + + //build a map of material slot names to face indexes + bool reorder = false; + auto max_lod_mats = lod->mMaterialList.size(); + + for ( U32 i = 0; i < ref->mMaterialList.size(); i++ ) + { + // create the reference map for later + index_map[ref->mMaterialList[i]] = i; + LL_DEBUGS("MESHSKININFO") << "setting reference material " << ref->mMaterialList[i] << " as index " << i << LL_ENDL; + if( i >= max_lod_mats || lod->mMaterialList[i] != ref->mMaterialList[i] ) + { + // i is already out of range of the original material sets in this LOD OR is not matching. + LL_DEBUGS("MESHSKININFO") << "mismatch at " << i << " " << ref->mMaterialList[i] + << " != " << ((i >= max_lod_mats)? "Out-of-range":lod->mMaterialList[i]) << LL_ENDL; + // we have a misalignment/ordering + // check that ref[i] is in cur and if not add a blank + U32 j{0}; + for ( ; j < max_lod_mats; j++ ) + { + if( i != j && lod->mMaterialList[j] == ref->mMaterialList[i] ) + { + LL_DEBUGS("MESHSKININFO") << "material " << ref->mMaterialList[i] + << " found at " << j << LL_ENDL; + // we found it but in the wrong place. + reorder = true; + break; + } + } + if( j >= max_lod_mats ) + { + std::ostringstream out; + out << "material " << ref->mMaterialList[i] + << " not found in lod adding placeholder"; + LL_DEBUGS("MESHSKININFO") << out << LL_ENDL; + if (mImporterDebug) + { + LLFloaterModelPreview::addStringToLog(out, false); + } + // The material is not in the submesh, add a placeholder. + // this is appended to the existing data so we'll need to reorder + // Note that this placeholder will be eliminated on the writeData (upload) and replaced with + // "NoGeometry" in the LLSD + reorder = true; + LLVolumeFace face; + + face.resizeIndices(3); + face.resizeVertices(1); + face.mPositions->clear(); + face.mNormals->clear(); + face.mTexCoords->setZero(); + memset(face.mIndices, 0, sizeof(U16)*3); + lod->addFace(face); + lod->mMaterialList.push_back( ref->mMaterialList[i] ); + } + } + //if any material name does not match reference, we need to reorder + } + LL_DEBUGS("MESHSKININFO") << "finished parsing materials" << LL_ENDL; + for ( U32 i = 0; i < lod->mMaterialList.size(); i++ ) + { + LL_DEBUGS("MESHSKININFO") << "lod material " << lod->mMaterialList[i] << " has index " << i << LL_ENDL; + } + // Sanity check. We have added placeholders for any mats in ref that are not in this. + // the mat count MUST be equal now. + if (lod->mMaterialList.size() != ref->mMaterialList.size()) + { + std::ostringstream out; + out << "Material of LOD model " << lod->getName() << " has more materials than a reference" << ref->getName() << "."; + LL_INFOS("MESHSKININFO") << out << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + return false; + } + + + // Fix up material matching badness + // if (reorder && (base_mat == cur_mat)) //don't reorder if material name sets don't match + if ( reorder ) + { + LL_INFOS("MESHSKININFO") << "re-ordering." << LL_ENDL; + lod->sortVolumeFacesByMaterialName(); + lod->mMaterialList = ref->mMaterialList; + } + + return true; +} +// + void LLModelPreview::rebuildUploadData() { - assert_main_thread(); + assert_main_thread(); - mUploadData.clear(); - mTextureSet.clear(); + mUploadData.clear(); + mTextureSet.clear(); - //fill uploaddata instance vectors from scene data + //fill uploaddata instance vectors from scene data - std::string requested_name = mFMP->getChild("description_form")->getValue().asString(); + std::string requested_name = mFMP->getChild("description_form")->getValue().asString(); - LLSpinCtrl* scale_spinner = mFMP->getChild("import_scale"); + LLSpinCtrl* scale_spinner = mFMP->getChild("import_scale"); - F32 scale = scale_spinner->getValue().asReal(); + F32 scale = scale_spinner->getValue().asReal(); - LLMatrix4 scale_mat; - scale_mat.initScale(LLVector3(scale, scale, scale)); + LLMatrix4 scale_mat; + scale_mat.initScale(LLVector3(scale, scale, scale)); - F32 max_scale = 0.f; + F32 max_scale = 0.f; - BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); - U32 load_state = 0; + BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); + U32 load_state = 0; - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) - { //for each transform in scene - LLMatrix4 mat = iter->first; + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) + { //for each transform in scene + LLMatrix4 mat = iter->first; - // compute position - LLVector3 position = LLVector3(0, 0, 0) * mat; + // compute position + LLVector3 position = LLVector3(0, 0, 0) * mat; - // compute scale - LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); - max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length); + max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length); - mat *= scale_mat; + mat *= scale_mat; - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) - { //for each instance with said transform applied - LLModelInstance instance = *model_iter++; + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) + { //for each instance with said transform applied + LLModelInstance instance = *model_iter++; - LLModel* base_model = instance.mModel; + LLModel* base_model = instance.mModel; - if (base_model && !requested_name.empty()) - { - base_model->mRequestedLabel = requested_name; - } + if (base_model && !requested_name.empty()) + { + base_model->mRequestedLabel = requested_name; + } - for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--) - { - LLModel* lod_model = NULL; - if (!legacyMatching) - { - // Fill LOD slots by finding matching meshes by label with name extensions - // in the appropriate scene for each LOD. This fixes all kinds of issues - // where the indexed method below fails in spectacular fashion. - // If you don't take the time to name your LOD and PHYS meshes - // with the name of their corresponding mesh in the HIGH LOD, - // then the indexed method will be attempted below. + for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--) + { + LLModel* lod_model = NULL; + if (!legacyMatching) + { + // Fill LOD slots by finding matching meshes by label with name extensions + // in the appropriate scene for each LOD. This fixes all kinds of issues + // where the indexed method below fails in spectacular fashion. + // If you don't take the time to name your LOD and PHYS meshes + // with the name of their corresponding mesh in the HIGH LOD, + // then the indexed method will be attempted below. - LLMatrix4 transform; + LLMatrix4 transform; - std::string name_to_match = instance.mLabel; - llassert(!name_to_match.empty()); + std::string name_to_match = instance.mLabel; + llassert(!name_to_match.empty()); - int extensionLOD; - if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty()) - { - extensionLOD = i; - } - else - { - //Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for - extensionLOD = mPhysicsSearchLOD; - } + int extensionLOD; + if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty()) + { + extensionLOD = i; + } + else + { + //Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for + extensionLOD = mPhysicsSearchLOD; + } - std::string toAdd = getLodSuffix(extensionLOD); + std::string toAdd = getLodSuffix(extensionLOD); - // Bug fixes in mesh importer by Drake Arconis - //if (name_to_match.find(toAdd) == -1) - if (name_to_match.find(toAdd) == std::string::npos) - // - { - name_to_match += toAdd; - } + // Bug fixes in mesh importer by Drake Arconis + //if (name_to_match.find(toAdd) == -1) + if (name_to_match.find(toAdd) == std::string::npos) + // + { + name_to_match += toAdd; + } - FindModel(mScene[i], name_to_match, lod_model, transform); + FindModel(mScene[i], name_to_match, lod_model, transform); - if (!lod_model && i != LLModel::LOD_PHYSICS) - { - if (mImporterDebug) - { - std::ostringstream out; - out << "Search of " << name_to_match; - out << " in LOD" << i; - out << " list failed. Searching for alternative among LOD lists."; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } + if (!lod_model && i != LLModel::LOD_PHYSICS) + { + if (mImporterDebug) + { + std::ostringstream out; + out << "Search of " << name_to_match; + out << " in LOD" << i; + out << " list failed. Searching for alternative among LOD lists."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } - int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i; - while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model) - { - std::string name_to_match = instance.mLabel; - llassert(!name_to_match.empty()); + int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i; + while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model) + { + std::string name_to_match = instance.mLabel; + llassert(!name_to_match.empty()); - std::string toAdd = getLodSuffix(searchLOD); + std::string toAdd = getLodSuffix(searchLOD); - // Bug fixes in mesh importer by Drake Arconis - //if (name_to_match.find(toAdd) == -1) - if (name_to_match.find(toAdd) == std::string::npos) - // - { - name_to_match += toAdd; - } + // Bug fixes in mesh importer by Drake Arconis + //if (name_to_match.find(toAdd) == -1) + if (name_to_match.find(toAdd) == std::string::npos) + // + { + name_to_match += toAdd; + } - // See if we can find an appropriately named model in LOD 'searchLOD' - // - FindModel(mScene[searchLOD], name_to_match, lod_model, transform); - searchLOD++; - } - } - } - else - { - // Use old method of index-based association - U32 idx = 0; - for (idx = 0; idx < mBaseModel.size(); ++idx) - { - // find reference instance for this model - if (mBaseModel[idx] == base_model) - { - if (mImporterDebug) - { - std::ostringstream out; - out << "Attempting to use model index " << idx; - out << " for LOD" << i; - out << " of " << instance.mLabel; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - break; - } - } + // See if we can find an appropriately named model in LOD 'searchLOD' + // + FindModel(mScene[searchLOD], name_to_match, lod_model, transform); + searchLOD++; + } + } + } + else + { + // Use old method of index-based association + U32 idx = 0; + for (idx = 0; idx < mBaseModel.size(); ++idx) + { + // find reference instance for this model + if (mBaseModel[idx] == base_model) + { + if (mImporterDebug) + { + std::ostringstream out; + out << "Attempting to use model index " << idx; + out << " for LOD" << i; + out << " of " << instance.mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + break; + } + } - // If the model list for the current LOD includes that index... - // - if (mModel[i].size() > idx) - { - // Assign that index from the model list for our LOD as the LOD model for this instance - // - lod_model = mModel[i][idx]; - if (mImporterDebug) - { - std::ostringstream out; - out << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - } - else if (mImporterDebug) - { - std::ostringstream out; - out << "LOD" << i << ": List of models does not include index " << idx << " scene is missing a LOD model"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - } + // If the model list for the current LOD includes that index... + // + if (mModel[i].size() > idx) + { + // Assign that index from the model list for our LOD as the LOD model for this instance + // + lod_model = mModel[i][idx]; + if (mImporterDebug) + { + std::ostringstream out; + out << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } + else if (mImporterDebug) + { + std::ostringstream out; + out << "LOD" << i << ": List of models does not include index " << idx << " scene is missing a LOD model"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } - if (lod_model) - { - if (mImporterDebug) - { - std::ostringstream out; - if (i == LLModel::LOD_PHYSICS) - { - out << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel; - } - else - { - out << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel; - } - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - instance.mLOD[i] = lod_model; - } - else - { - if (i < LLModel::LOD_HIGH && !lodsReady()) - { - // assign a placeholder from previous LOD until lod generation is complete. - // Note: we might need to assign it regardless of conditions like named search does, to prevent crashes. - instance.mLOD[i] = instance.mLOD[i + 1]; - } - if (mImporterDebug) - { - std::ostringstream out; - out << "LOD" << i << ": List of models does not include " << instance.mLabel; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - } - } + if (lod_model) + { + if (mImporterDebug) + { + std::ostringstream out; + if (i == LLModel::LOD_PHYSICS) + { + out << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel; + } + else + { + out << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel; + } + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + instance.mLOD[i] = lod_model; + } + else + { + if (i < LLModel::LOD_HIGH && !lodsReady()) + { + // assign a placeholder from previous LOD until lod generation is complete. + // Note: we might need to assign it regardless of conditions like named search does, to prevent crashes. + instance.mLOD[i] = instance.mLOD[i + 1]; + } + if (mImporterDebug) + { + std::ostringstream out; + out << "LOD" << i << ": List of models does not include " << instance.mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } + } - LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH]; - if (!high_lod_model) - { - LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has no High Lod (LOD3).", true); - load_state = LLModelLoader::ERROR_MATERIALS; - mFMP->childDisable("calculate_btn"); - } - else - { - for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) - { - int refFaceCnt = 0; - int modelFaceCnt = 0; - llassert(instance.mLOD[i]); - if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt)) - { - LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has mismatching materials between lods." , true); - load_state = LLModelLoader::ERROR_MATERIALS; - mFMP->childDisable("calculate_btn"); - } - } - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; - bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean(); - if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0) - { - LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix); - LLQuaternion identity; - if (!bind_rot.isEqualEps(identity, 0.01f)) - { - // Bind shape matrix is not in standard X-forward orientation. - // Might be good idea to only show this once. It can be spammy. - std::ostringstream out; - out << "non-identity bind shape rot. mat is "; - out << high_lod_model->mSkinInfo.mBindShapeMatrix; - out << " bind_rot "; - out << bind_rot; - LL_WARNS() << out.str() << LL_ENDL; + LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH]; + if (!high_lod_model) + { + LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has no High Lod (LOD3).", true); + load_state = LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING; // FIRE-30965 Cleanup braindead mesh parsing error handlers + mFMP->childDisable("calculate_btn"); + } + else + { + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + int refFaceCnt = 0; + int modelFaceCnt = 0; + llassert(instance.mLOD[i]); + // Fix material matching algorithm to work as per design + // if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt)) + if (instance.mLOD[i] && !matchMaterialOrder(instance.mLOD[i],high_lod_model, refFaceCnt, modelFaceCnt)) + // + { + LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has mismatching materials between lods." , true); + load_state = LLModelLoader::ERROR_MATERIALS_NOT_A_SUBSET; // more descriptive errors + mFMP->childDisable("calculate_btn"); + } + } + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean(); + if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0) + { + LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix); + LLQuaternion identity; + if (!bind_rot.isEqualEps(identity, 0.01f)) + { + // Bind shape matrix is not in standard X-forward orientation. + // Might be good idea to only show this once. It can be spammy. + std::ostringstream out; + out << "non-identity bind shape rot. mat is "; + out << high_lod_model->mSkinInfo.mBindShapeMatrix; + out << " bind_rot "; + out << bind_rot; + LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, getLoadState() != LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION); - load_state = LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION; - } - } - } - instance.mTransform = mat; - mUploadData.push_back(instance); - } - } + LLFloaterModelPreview::addStringToLog(out, getLoadState() != LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION); + load_state = LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION; + } + } + } + instance.mTransform = mat; + mUploadData.push_back(instance); + } + } - for (U32 lod = 0; lod < LLModel::NUM_LODS - 1; lod++) - { - // Search for models that are not included into upload data - // If we found any, that means something we loaded is not a sub-model. - for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind) - { - bool found_model = false; - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - if (instance.mLOD[lod] == mModel[lod][model_ind]) - { - found_model = true; - break; - } - } - if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID) - { - // this is not debug, this is an important/useful error advisory - // if (mImporterDebug) - { - std::ostringstream out; - out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models."; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - } - load_state = LLModelLoader::ERROR_LOD_MODEL_MISMATCH; - mFMP->childDisable("calculate_btn"); - } - } - } + for (U32 lod = 0; lod < LLModel::NUM_LODS - 1; lod++) + { + // Search for models that are not included into upload data + // If we found any, that means something we loaded is not a sub-model. + for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind) + { + bool found_model = false; + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + if (instance.mLOD[lod] == mModel[lod][model_ind]) + { + found_model = true; + break; + } + } + if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID) + { + // this is not debug, this is an important/useful error advisory + // if (mImporterDebug) + { + std::ostringstream out; + out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + } + load_state = LLModelLoader::ERROR_LOD_MODEL_MISMATCH; + mFMP->childDisable("calculate_btn"); + } + } + } - // Update state for notifications - if (load_state > 0) - { - // encountered issues - setLoadState(load_state); - } - else if (getLoadState() == LLModelLoader::ERROR_MATERIALS - || getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION) - { - // This is only valid for these two error types because they are - // only used inside rebuildUploadData() and updateStatusMessages() - // updateStatusMessages() is called after rebuildUploadData() - setLoadState(LLModelLoader::DONE); - } + // Update state for notifications + if (load_state > 0) + { + // encountered issues + setLoadState(load_state); + } + // FIRE-30965 Cleanup braindead mesh parsing error handlers + // else if (getLoadState() == LLModelLoader::ERROR_MATERIALS + else if (getLoadState() == LLModelLoader::ERROR_MATERIALS_NOT_A_SUBSET + || getLoadState() == LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING + || getLoadState() == LLModelLoader::ERROR_LOD_MODEL_MISMATCH + // + || getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION) + { + // This is only valid for these two error types because they are + // only used inside rebuildUploadData() and updateStatusMessages() + // updateStatusMessages() is called after rebuildUploadData() + setLoadState(LLModelLoader::DONE); + } - // OpenSim limits - //F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE - 0.1f) / max_scale; - F32 region_max_prim_scale = LLWorld::getInstance()->getRegionMaxPrimScale(); - F32 max_import_scale = region_max_prim_scale/max_scale; - // + // OpenSim limits + //F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE - 0.1f) / max_scale; + F32 region_max_prim_scale = LLWorld::getInstance()->getRegionMaxPrimScale(); + F32 max_import_scale = region_max_prim_scale/max_scale; + // - F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]); - max_axis = llmax(max_axis, mPreviewScale.mV[2]); - max_axis *= 2.f; + F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]); + max_axis = llmax(max_axis, mPreviewScale.mV[2]); + max_axis *= 2.f; - //clamp scale so that total imported model bounding box is smaller than 240m on a side - max_import_scale = llmin(max_import_scale, 240.f / max_axis); + //clamp scale so that total imported model bounding box is smaller than 240m on a side + max_import_scale = llmin(max_import_scale, 240.f / max_axis); - scale_spinner->setMaxValue(max_import_scale); + scale_spinner->setMaxValue(max_import_scale); - if (max_import_scale < scale) - { - scale_spinner->setValue(max_import_scale); - } + if (max_import_scale < scale) + { + scale_spinner->setValue(max_import_scale); + } } void LLModelPreview::saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) { - if (!mLODFile[LLModel::LOD_HIGH].empty()) - { - std::string filename = mLODFile[LLModel::LOD_HIGH]; - std::string slm_filename; + if (!mLODFile[LLModel::LOD_HIGH].empty()) + { + std::string filename = mLODFile[LLModel::LOD_HIGH]; + std::string slm_filename; - if (LLModelLoader::getSLMFilename(filename, slm_filename)) - { - saveUploadData(slm_filename, save_skinweights, save_joint_positions, lock_scale_if_joint_position); - } - } + if (LLModelLoader::getSLMFilename(filename, slm_filename)) + { + saveUploadData(slm_filename, save_skinweights, save_joint_positions, lock_scale_if_joint_position); + } + } } void LLModelPreview::saveUploadData(const std::string& filename, - bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) + bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) { - std::set > meshes; - std::map mesh_binary; + std::set > meshes; + std::map mesh_binary; - LLModel::hull empty_hull; + LLModel::hull empty_hull; - LLSD data; + LLSD data; - data["version"] = SLM_SUPPORTED_VERSION; - if (!mBaseModel.empty()) - { - data["name"] = mBaseModel[0]->getName(); - } + data["version"] = SLM_SUPPORTED_VERSION; + if (!mBaseModel.empty()) + { + data["name"] = mBaseModel[0]->getName(); + } - S32 mesh_id = 0; + S32 mesh_id = 0; - //build list of unique models and initialize local id - for (U32 i = 0; i < mUploadData.size(); ++i) - { - LLModelInstance& instance = mUploadData[i]; + //build list of unique models and initialize local id + for (U32 i = 0; i < mUploadData.size(); ++i) + { + LLModelInstance& instance = mUploadData[i]; - if (meshes.find(instance.mModel) == meshes.end()) - { - instance.mModel->mLocalID = mesh_id++; - meshes.insert(instance.mModel); + if (meshes.find(instance.mModel) == meshes.end()) + { + instance.mModel->mLocalID = mesh_id++; + meshes.insert(instance.mModel); - std::stringstream str; - LLModel::Decomposition& decomp = - instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? - instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : - instance.mModel->mPhysics; + std::stringstream str; + LLModel::Decomposition& decomp = + instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? + instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : + instance.mModel->mPhysics; - LLModel::writeModel(str, - instance.mLOD[LLModel::LOD_PHYSICS], - instance.mLOD[LLModel::LOD_HIGH], - instance.mLOD[LLModel::LOD_MEDIUM], - instance.mLOD[LLModel::LOD_LOW], - instance.mLOD[LLModel::LOD_IMPOSTOR], - decomp, - save_skinweights, - save_joint_positions, - lock_scale_if_joint_position, - FALSE, TRUE, instance.mModel->mSubmodelID); + LLModel::writeModel(str, + instance.mLOD[LLModel::LOD_PHYSICS], + instance.mLOD[LLModel::LOD_HIGH], + instance.mLOD[LLModel::LOD_MEDIUM], + instance.mLOD[LLModel::LOD_LOW], + instance.mLOD[LLModel::LOD_IMPOSTOR], + decomp, + save_skinweights, + save_joint_positions, + lock_scale_if_joint_position, + FALSE, TRUE, instance.mModel->mSubmodelID); - data["mesh"][instance.mModel->mLocalID] = str.str(); - } + data["mesh"][instance.mModel->mLocalID] = str.str(); + } - data["instance"][i] = instance.asLLSD(); - } + data["instance"][i] = instance.asLLSD(); + } - llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary); - LLSDSerialize::toBinary(data, out); - out.flush(); - out.close(); + llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary); + LLSDSerialize::toBinary(data, out); + out.flush(); + out.close(); } void LLModelPreview::clearModel(S32 lod) { - if (lod < 0 || lod > LLModel::LOD_PHYSICS) - { - return; - } + if (lod < 0 || lod > LLModel::LOD_PHYSICS) + { + return; + } - mVertexBuffer[lod].clear(); - mModel[lod].clear(); - mScene[lod].clear(); + mVertexBuffer[lod].clear(); + mModel[lod].clear(); + mScene[lod].clear(); } void LLModelPreview::getJointAliases(JointMap& joint_map) { - // Get all standard skeleton joints from the preview avatar. - LLVOAvatar *av = getPreviewAvatar(); + // Get all standard skeleton joints from the preview avatar. + LLVOAvatar *av = getPreviewAvatar(); - //Joint names and aliases come from avatar_skeleton.xml + //Joint names and aliases come from avatar_skeleton.xml - joint_map = av->getJointAliases(); + joint_map = av->getJointAliases(); - std::vector cv_names, attach_names; - av->getSortedJointNames(1, cv_names); - av->getSortedJointNames(2, attach_names); - for (std::vector::iterator it = cv_names.begin(); it != cv_names.end(); ++it) - { - joint_map[*it] = *it; - } - for (std::vector::iterator it = attach_names.begin(); it != attach_names.end(); ++it) - { - joint_map[*it] = *it; - } + std::vector cv_names, attach_names; + av->getSortedJointNames(1, cv_names); + av->getSortedJointNames(2, attach_names); + for (std::vector::iterator it = cv_names.begin(); it != cv_names.end(); ++it) + { + joint_map[*it] = *it; + } + for (std::vector::iterator it = attach_names.begin(); it != attach_names.end(); ++it) + { + joint_map[*it] = *it; + } } void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable_slm) { - assert_main_thread(); + assert_main_thread(); - LLMutexLock lock(this); + LLMutexLock lock(this); - if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1) - { - std::ostringstream out; - out << "Invalid level of detail: "; - out << lod; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS); - return; - } + if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1) + { + std::ostringstream out; + out << "Invalid level of detail: "; + out << lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS); + return; + } - // This triggers if you bring up the file picker and then hit CANCEL. - // Just use the previous model (if any) and ignore that you brought up - // the file picker. + // This triggers if you bring up the file picker and then hit CANCEL. + // Just use the previous model (if any) and ignore that you brought up + // the file picker. - if (filename.empty()) - { - if (mBaseModel.empty()) - { - // this is the initial file picking. Close the whole floater - // if we don't have a base model to show for high LOD. - mFMP->closeFloater(false); - } - mLoading = false; - return; - } + if (filename.empty()) + { + if (mBaseModel.empty()) + { + // this is the initial file picking. Close the whole floater + // if we don't have a base model to show for high LOD. + mFMP->closeFloater(false); + } + mLoading = false; + return; + } - if (mModelLoader) - { - LL_WARNS() << "Incompleted model load operation pending." << LL_ENDL; - return; - } + if (mModelLoader) + { + LL_WARNS() << "Incompleted model load operation pending." << LL_ENDL; + return; + } - mLODFile[lod] = filename; + mLODFile[lod] = filename; - if (lod == LLModel::LOD_HIGH) - { - clearGLODGroup(); - } + if (lod == LLModel::LOD_HIGH) + { + clearGLODGroup(); + } - std::map joint_alias_map; - getJointAliases(joint_alias_map); + std::map joint_alias_map; + getJointAliases(joint_alias_map); - mModelLoader = new LLDAELoader( - filename, - lod, - &LLModelPreview::loadedCallback, - &LLModelPreview::lookupJointByName, - &LLModelPreview::loadTextures, - &LLModelPreview::stateChangedCallback, - this, - mJointTransformMap, - mJointsFromNode, - joint_alias_map, - LLSkinningUtil::getMaxJointCount(), - gSavedSettings.getU32("ImporterModelLimit"), - gSavedSettings.getBOOL("ImporterPreprocessDAE")); + mModelLoader = new LLDAELoader( + filename, + lod, + &LLModelPreview::loadedCallback, + &LLModelPreview::lookupJointByName, + &LLModelPreview::loadTextures, + &LLModelPreview::stateChangedCallback, + this, + mJointTransformMap, + mJointsFromNode, + joint_alias_map, + LLSkinningUtil::getMaxJointCount(), + gSavedSettings.getU32("ImporterModelLimit"), + gSavedSettings.getBOOL("ImporterPreprocessDAE")); - if (force_disable_slm) - { - mModelLoader->mTrySLM = false; - } - else - { - // For MAINT-6647, we have set force_disable_slm to true, - // which means this code path will never be taken. Trying to - // re-use SLM files has never worked properly; in particular, - // it tends to force the UI into strange checkbox options - // which cannot be altered. + if (force_disable_slm) + { + mModelLoader->mTrySLM = false; + } + else + { + // For MAINT-6647, we have set force_disable_slm to true, + // which means this code path will never be taken. Trying to + // re-use SLM files has never worked properly; in particular, + // it tends to force the UI into strange checkbox options + // which cannot be altered. - //only try to load from slm if viewer is configured to do so and this is the - //initial model load (not an LoD or physics shape) - mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty(); - } - mModelLoader->start(); + //only try to load from slm if viewer is configured to do so and this is the + //initial model load (not an LoD or physics shape) + mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty(); + } + mModelLoader->start(); - mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); + mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); - setPreviewLOD(lod); + setPreviewLOD(lod); - if (getLoadState() >= LLModelLoader::ERROR_PARSING) - { - mFMP->childDisable("ok_btn"); - mFMP->childDisable("calculate_btn"); - } + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mFMP->childDisable("ok_btn"); + mFMP->childDisable("calculate_btn"); + } - if (lod == mPreviewLOD) - { - mFMP->childSetValue("lod_file_" + lod_name[lod], mLODFile[lod]); - } - else if (lod == LLModel::LOD_PHYSICS) - { - mFMP->childSetValue("physics_file", mLODFile[lod]); - } + if (lod == mPreviewLOD) + { + mFMP->childSetValue("lod_file_" + lod_name[lod], mLODFile[lod]); + } + else if (lod == LLModel::LOD_PHYSICS) + { + mFMP->childSetValue("physics_file", mLODFile[lod]); + } - mFMP->openFloater(); + mFMP->openFloater(); } void LLModelPreview::setPhysicsFromLOD(S32 lod) { - assert_main_thread(); + assert_main_thread(); - if (lod >= 0 && lod <= 3) - { - mPhysicsSearchLOD = lod; - mModel[LLModel::LOD_PHYSICS] = mModel[lod]; - mScene[LLModel::LOD_PHYSICS] = mScene[lod]; - mLODFile[LLModel::LOD_PHYSICS].clear(); - mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); - mVertexBuffer[LLModel::LOD_PHYSICS].clear(); - rebuildUploadData(); - refresh(); - updateStatusMessages(); - } + if (lod >= 0 && lod <= 3) + { + mPhysicsSearchLOD = lod; + mModel[LLModel::LOD_PHYSICS] = mModel[lod]; + mScene[LLModel::LOD_PHYSICS] = mScene[lod]; + mLODFile[LLModel::LOD_PHYSICS].clear(); + mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); + mVertexBuffer[LLModel::LOD_PHYSICS].clear(); + rebuildUploadData(); + refresh(); + updateStatusMessages(); + } } // FIRE-30963 - better physics defaults void LLModelPreview::setPhysicsFromPreset(S32 preset) { - assert_main_thread(); + assert_main_thread(); - mPhysicsSearchLOD = -1; - mLODFile[LLModel::LOD_PHYSICS].clear(); - mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); - mVertexBuffer[LLModel::LOD_PHYSICS].clear(); - if(preset == 1) - { - mPhysicsSearchLOD = LLModel::LOD_PHYSICS; - loadModel( gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, "cube_phys.dae"), LLModel::LOD_PHYSICS); - } - else if(preset == 2) - { - mPhysicsSearchLOD = LLModel::LOD_PHYSICS; - loadModel( gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, "hex_phys.dae"), LLModel::LOD_PHYSICS); - } - else if(preset == 3) - { - auto ud_physics = gSavedSettings.getString("FSPhysicsPresetUser1"); - LL_INFOS() << "Loading User defined Physics Preset [" << ud_physics << "]" << LL_ENDL; - if (ud_physics != "" && gDirUtilp->fileExists(ud_physics)) - { - // loading physics from file - mPhysicsSearchLOD = LLModel::LOD_PHYSICS; - loadModel( gDirUtilp->getExpandedFilename(LL_PATH_NONE, gDirUtilp->getDirName(ud_physics), gDirUtilp->getBaseFileName(ud_physics, false)), LLModel::LOD_PHYSICS); - } - } + mPhysicsSearchLOD = -1; + mLODFile[LLModel::LOD_PHYSICS].clear(); + mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); + mVertexBuffer[LLModel::LOD_PHYSICS].clear(); + if(preset == 1) + { + mPhysicsSearchLOD = LLModel::LOD_PHYSICS; + loadModel( gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, "cube_phys.dae"), LLModel::LOD_PHYSICS); + } + else if(preset == 2) + { + mPhysicsSearchLOD = LLModel::LOD_PHYSICS; + loadModel( gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, "hex_phys.dae"), LLModel::LOD_PHYSICS); + } + else if(preset == 3) + { + auto ud_physics = gSavedSettings.getString("FSPhysicsPresetUser1"); + LL_INFOS() << "Loading User defined Physics Preset [" << ud_physics << "]" << LL_ENDL; + if (ud_physics != "" && gDirUtilp->fileExists(ud_physics)) + { + // loading physics from file + mPhysicsSearchLOD = LLModel::LOD_PHYSICS; + loadModel( gDirUtilp->getExpandedFilename(LL_PATH_NONE, gDirUtilp->getDirName(ud_physics), gDirUtilp->getBaseFileName(ud_physics, false)), LLModel::LOD_PHYSICS); + } + } } // void LLModelPreview::clearIncompatible(S32 lod) { - //Don't discard models if specified model is the physic rep - if (lod == LLModel::LOD_PHYSICS) - { - return; - } + //Don't discard models if specified model is the physic rep + if (lod == LLModel::LOD_PHYSICS) + { + return; + } - // at this point we don't care about sub-models, - // different amount of sub-models means face count mismatch, not incompatibility - U32 lod_size = countRootModels(mModel[lod]); - for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) - { //clear out any entries that aren't compatible with this model - if (i != lod) - { - if (countRootModels(mModel[i]) != lod_size) - { - mModel[i].clear(); - mScene[i].clear(); - mVertexBuffer[i].clear(); + // at this point we don't care about sub-models, + // different amount of sub-models means face count mismatch, not incompatibility + U32 lod_size = countRootModels(mModel[lod]); + for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) + { //clear out any entries that aren't compatible with this model + if (i != lod) + { + if (countRootModels(mModel[i]) != lod_size) + { + mModel[i].clear(); + mScene[i].clear(); + mVertexBuffer[i].clear(); - if (i == LLModel::LOD_HIGH) - { - mBaseModel = mModel[lod]; - clearGLODGroup(); - mBaseScene = mScene[lod]; - mVertexBuffer[5].clear(); - } - } - } - } + if (i == LLModel::LOD_HIGH) + { + mBaseModel = mModel[lod]; + clearGLODGroup(); + mBaseScene = mScene[lod]; + mVertexBuffer[5].clear(); + } + } + } + } } void LLModelPreview::clearGLODGroup() { - if (mGroup) - { - for (std::map, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) - { - glodDeleteObject(iter->second); - stop_gloderror(); - } - mObject.clear(); + if (mGroup) + { + for (std::map, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) + { + glodDeleteObject(iter->second); + stop_gloderror(); + } + mObject.clear(); - glodDeleteGroup(mGroup); - stop_gloderror(); - mGroup = 0; - } + glodDeleteGroup(mGroup); + stop_gloderror(); + mGroup = 0; + } } void LLModelPreview::loadModelCallback(S32 loaded_lod) { - assert_main_thread(); + assert_main_thread(); - LLMutexLock lock(this); - if (!mModelLoader) - { - mLoading = false; - return; - } - if (getLoadState() >= LLModelLoader::ERROR_PARSING) - { - mLoading = false; - mModelLoader = NULL; - mLodsWithParsingError.push_back(loaded_lod); - return; - } + LLMutexLock lock(this); + if (!mModelLoader) + { + mLoading = false; + return; + } + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mLoading = false; + mModelLoader = NULL; + mLodsWithParsingError.push_back(loaded_lod); + return; + } - mLodsWithParsingError.erase(std::remove(mLodsWithParsingError.begin(), mLodsWithParsingError.end(), loaded_lod), mLodsWithParsingError.end()); - if (mLodsWithParsingError.empty()) - { - mFMP->childEnable("calculate_btn"); - } + mLodsWithParsingError.erase(std::remove(mLodsWithParsingError.begin(), mLodsWithParsingError.end(), loaded_lod), mLodsWithParsingError.end()); + if (mLodsWithParsingError.empty()) + { + mFMP->childEnable("calculate_btn"); + } - // Copy determinations about rig so UI will reflect them - // - setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload()); - setLegacyRigFlags(mModelLoader->getLegacyRigFlags()); + // Copy determinations about rig so UI will reflect them + // + setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload()); + setLegacyRigFlags(mModelLoader->getLegacyRigFlags()); - mModelLoader->loadTextures(); + mModelLoader->loadTextures(); - if (loaded_lod == -1) - { //populate all LoDs from model loader scene - mBaseModel.clear(); - mBaseScene.clear(); + if (loaded_lod == -1) + { //populate all LoDs from model loader scene + mBaseModel.clear(); + mBaseScene.clear(); - bool skin_weights = false; - bool joint_overrides = false; - bool lock_scale_if_joint_position = false; + bool skin_weights = false; + bool joint_overrides = false; + bool lock_scale_if_joint_position = false; - for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) - { //for each LoD + for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) + { //for each LoD - //clear scene and model info - mScene[lod].clear(); - mModel[lod].clear(); - mVertexBuffer[lod].clear(); + //clear scene and model info + mScene[lod].clear(); + mModel[lod].clear(); + mVertexBuffer[lod].clear(); - if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull()) - { //if this LoD exists in the loaded scene + if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull()) + { //if this LoD exists in the loaded scene - //copy scene to current LoD - mScene[lod] = mModelLoader->mScene; + //copy scene to current LoD + mScene[lod] = mModelLoader->mScene; - //touch up copied scene to look like current LoD - for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) - { - LLModelLoader::model_instance_list& list = iter->second; + //touch up copied scene to look like current LoD + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + LLModelLoader::model_instance_list& list = iter->second; - for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter) - { - //override displayed model with current LoD - list_iter->mModel = list_iter->mLOD[lod]; + for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter) + { + //override displayed model with current LoD + list_iter->mModel = list_iter->mLOD[lod]; - if (!list_iter->mModel) - { - continue; - } + if (!list_iter->mModel) + { + continue; + } - //add current model to current LoD's model list (LLModel::mLocalID makes a good vector index) - S32 idx = list_iter->mModel->mLocalID; + //add current model to current LoD's model list (LLModel::mLocalID makes a good vector index) + S32 idx = list_iter->mModel->mLocalID; - if (mModel[lod].size() <= idx) - { //stretch model list to fit model at given index - mModel[lod].resize(idx + 1); - } + if (mModel[lod].size() <= idx) + { //stretch model list to fit model at given index + mModel[lod].resize(idx + 1); + } - mModel[lod][idx] = list_iter->mModel; - if (!list_iter->mModel->mSkinWeights.empty()) - { - skin_weights = true; + mModel[lod][idx] = list_iter->mModel; + if (!list_iter->mModel->mSkinWeights.empty()) + { + skin_weights = true; - if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty()) - { - joint_overrides = true; - } - if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition) - { - lock_scale_if_joint_position = true; - } - } - } - } - } - } + if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty()) + { + joint_overrides = true; + } + if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition) + { + lock_scale_if_joint_position = true; + } + } + } + } + } + } - if (mFMP) - { - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + if (mFMP) + { + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; - if (skin_weights) - { //enable uploading/previewing of skin weights if present in .slm file - fmp->enableViewOption("show_skin_weight"); - mViewOption["show_skin_weight"] = true; - fmp->childSetValue("upload_skin", true); - } + if (skin_weights) + { //enable uploading/previewing of skin weights if present in .slm file + fmp->enableViewOption("show_skin_weight"); + mViewOption["show_skin_weight"] = true; + fmp->childSetValue("upload_skin", true); + } - if (joint_overrides) - { - fmp->enableViewOption("show_joint_overrides"); - mViewOption["show_joint_overrides"] = true; - fmp->childSetValue("show_joint_overrides", true); // make sure option appears checked, when value is being forced true - fmp->enableViewOption("show_joint_positions"); - fmp->childSetValue("show_joint_positions", true); // make sure option appears checked, when value is being forced true - mViewOption["show_joint_positions"] = true; - fmp->childSetValue("upload_joints", true); - } - else - { - fmp->clearAvatarTab(); - } + if (joint_overrides) + { + fmp->enableViewOption("show_joint_overrides"); + mViewOption["show_joint_overrides"] = true; + fmp->childSetValue("show_joint_overrides", true); // make sure option appears checked, when value is being forced true + fmp->enableViewOption("show_joint_positions"); + fmp->childSetValue("show_joint_positions", true); // make sure option appears checked, when value is being forced true + mViewOption["show_joint_positions"] = true; + fmp->childSetValue("upload_joints", true); + } + else + { + fmp->clearAvatarTab(); + } - if (lock_scale_if_joint_position) - { - fmp->enableViewOption("lock_scale_if_joint_position"); - mViewOption["lock_scale_if_joint_position"] = true; - fmp->childSetValue("lock_scale_if_joint_position", true); - } - } + if (lock_scale_if_joint_position) + { + fmp->enableViewOption("lock_scale_if_joint_position"); + mViewOption["lock_scale_if_joint_position"] = true; + fmp->childSetValue("lock_scale_if_joint_position", true); + } + } - //copy high lod to base scene for LoD generation - mBaseScene = mScene[LLModel::LOD_HIGH]; - mBaseModel = mModel[LLModel::LOD_HIGH]; + //copy high lod to base scene for LoD generation + mBaseScene = mScene[LLModel::LOD_HIGH]; + mBaseModel = mModel[LLModel::LOD_HIGH]; - mDirty = true; - resetPreviewTarget(); - } - else - { //only replace given LoD - mModel[loaded_lod] = mModelLoader->mModelList; - mScene[loaded_lod] = mModelLoader->mScene; - mVertexBuffer[loaded_lod].clear(); + mDirty = true; + resetPreviewTarget(); + } + else + { //only replace given LoD + mModel[loaded_lod] = mModelLoader->mModelList; + mScene[loaded_lod] = mModelLoader->mScene; + mVertexBuffer[loaded_lod].clear(); - setPreviewLOD(loaded_lod); + setPreviewLOD(loaded_lod); - if (loaded_lod == LLModel::LOD_HIGH) - { //save a copy of the highest LOD for automatic LOD manipulation - if (mBaseModel.empty()) - { //first time we've loaded a model, auto-gen LoD - mGenLOD = true; - } + if (loaded_lod == LLModel::LOD_HIGH) + { //save a copy of the highest LOD for automatic LOD manipulation + if (mBaseModel.empty()) + { //first time we've loaded a model, auto-gen LoD + mGenLOD = true; + } - mBaseModel = mModel[loaded_lod]; - clearGLODGroup(); + mBaseModel = mModel[loaded_lod]; + clearGLODGroup(); - mBaseScene = mScene[loaded_lod]; - mVertexBuffer[5].clear(); - } - else - { - BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); - if (!legacyMatching) - { - if (!mBaseModel.empty()) - { - BOOL name_based = FALSE; - BOOL has_submodels = FALSE; - for (U32 idx = 0; idx < mBaseModel.size(); ++idx) - { - if (mBaseModel[idx]->mSubmodelID) - { // don't do index-based renaming when the base model has submodels - has_submodels = TRUE; - if (mImporterDebug) - { - std::ostringstream out; - out << "High LOD has submodels"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - break; - } - } + mBaseScene = mScene[loaded_lod]; + mVertexBuffer[5].clear(); + } + else + { + BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); + if (!legacyMatching) + { + if (!mBaseModel.empty()) + { + BOOL name_based = FALSE; + BOOL has_submodels = FALSE; + for (U32 idx = 0; idx < mBaseModel.size(); ++idx) + { + if (mBaseModel[idx]->mSubmodelID) + { // don't do index-based renaming when the base model has submodels + has_submodels = TRUE; + if (mImporterDebug) + { + std::ostringstream out; + out << "High LOD has submodels"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + break; + } + } - for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx) - { - std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); + for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx) + { + std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); - LLModel* found_model = NULL; - LLMatrix4 transform; - FindModel(mBaseScene, loaded_name, found_model, transform); - if (found_model) - { // don't rename correctly named models (even if they are placed in a wrong order) - name_based = TRUE; - } + LLModel* found_model = NULL; + LLMatrix4 transform; + FindModel(mBaseScene, loaded_name, found_model, transform); + if (found_model) + { // don't rename correctly named models (even if they are placed in a wrong order) + name_based = TRUE; + } - if (mModel[loaded_lod][idx]->mSubmodelID) - { // don't rename the models when loaded LOD model has submodels - has_submodels = TRUE; - } - } + if (mModel[loaded_lod][idx]->mSubmodelID) + { // don't rename the models when loaded LOD model has submodels + has_submodels = TRUE; + } + } - if (mImporterDebug) - { - std::ostringstream out; - out << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } + if (mImporterDebug) + { + std::ostringstream out; + out << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } - if (!name_based && !has_submodels) - { // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601) - // this actually works like "ImporterLegacyMatching" for this particular LOD - for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx) - { - std::string name = mBaseModel[idx]->mLabel; - std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); + if (!name_based && !has_submodels) + { // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601) + // this actually works like "ImporterLegacyMatching" for this particular LOD + for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx) + { + std::string name = mBaseModel[idx]->mLabel; + std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); - if (loaded_name != name) - { - name += getLodSuffix(loaded_lod); + if (loaded_name != name) + { + name += getLodSuffix(loaded_lod); - if (mImporterDebug) - { - std::ostringstream out; - out << "Loded model name " << mModel[loaded_lod][idx]->mLabel; - out << " for LOD " << loaded_lod; - out << " doesn't match the base model. Renaming to " << name; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } + if (mImporterDebug) + { + std::ostringstream out; + out << "Loded model name " << mModel[loaded_lod][idx]->mLabel; + out << " for LOD " << loaded_lod; + out << " doesn't match the base model. Renaming to " << name; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } - mModel[loaded_lod][idx]->mLabel = name; - } - } - } - } - } - } + mModel[loaded_lod][idx]->mLabel = name; + } + } + } + } + } + } - clearIncompatible(loaded_lod); + clearIncompatible(loaded_lod); - mDirty = true; + mDirty = true; - if (loaded_lod == LLModel::LOD_HIGH) - { - resetPreviewTarget(); - } - } + if (loaded_lod == LLModel::LOD_HIGH) + { + resetPreviewTarget(); + } + } - mLoading = false; - if (mFMP) - { - if (!mBaseModel.empty()) - { - const std::string& model_name = mBaseModel[0]->getName(); - LLLineEditor* description_form = mFMP->getChild("description_form"); - if (description_form->getText().empty()) - { - description_form->setText(model_name); - } - // Add info to log that loading is complete (purpose: separator between loading and other logs) - LLSD args; - args["MODEL_NAME"] = model_name; // Teoretically shouldn't be empty, but might be better idea to add filename here - LLFloaterModelPreview::addStringToLog("ModelLoaded", args, false, loaded_lod); - } - } - refresh(); + mLoading = false; + if (mFMP) + { + if (!mBaseModel.empty()) + { + const std::string& model_name = mBaseModel[0]->getName(); + LLLineEditor* description_form = mFMP->getChild("description_form"); + if (description_form->getText().empty()) + { + description_form->setText(model_name); + } + // Add info to log that loading is complete (purpose: separator between loading and other logs) + LLSD args; + args["MODEL_NAME"] = model_name; // Teoretically shouldn't be empty, but might be better idea to add filename here + LLFloaterModelPreview::addStringToLog("ModelLoaded", args, false, loaded_lod); + } + } + refresh(); - mModelLoadedSignal(); + mModelLoadedSignal(); - mModelLoader = NULL; + mModelLoader = NULL; } void LLModelPreview::resetPreviewTarget() { - if (mModelLoader) - { - mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; - mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; - } + if (mModelLoader) + { + mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; + mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; + } - setPreviewTarget(mPreviewScale.magVec()*10.f); + setPreviewTarget(mPreviewScale.magVec()*10.f); } void LLModelPreview::generateNormals() { - assert_main_thread(); + assert_main_thread(); - S32 which_lod = mPreviewLOD; + S32 which_lod = mPreviewLOD; - if (which_lod > 4 || which_lod < 0 || - mModel[which_lod].empty()) - { - return; - } + if (which_lod > 4 || which_lod < 0 || + mModel[which_lod].empty()) + { + return; + } - F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); + F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); - mRequestedCreaseAngle[which_lod] = angle_cutoff; + mRequestedCreaseAngle[which_lod] = angle_cutoff; - angle_cutoff *= DEG_TO_RAD; + angle_cutoff *= DEG_TO_RAD; - if (which_lod == 3 && !mBaseModel.empty()) - { - if (mBaseModelFacesCopy.empty()) - { - mBaseModelFacesCopy.reserve(mBaseModel.size()); - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) - { - v_LLVolumeFace_t faces; - (*it)->copyFacesTo(faces); - mBaseModelFacesCopy.push_back(faces); - } - } + if (which_lod == 3 && !mBaseModel.empty()) + { + if (mBaseModelFacesCopy.empty()) + { + mBaseModelFacesCopy.reserve(mBaseModel.size()); + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) + { + v_LLVolumeFace_t faces; + (*it)->copyFacesTo(faces); + mBaseModelFacesCopy.push_back(faces); + } + } - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) - { - (*it)->generateNormals(angle_cutoff); - } + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) + { + (*it)->generateNormals(angle_cutoff); + } - mVertexBuffer[5].clear(); - } + mVertexBuffer[5].clear(); + } - bool perform_copy = mModelFacesCopy[which_lod].empty(); - if (perform_copy) { - mModelFacesCopy[which_lod].reserve(mModel[which_lod].size()); - } + bool perform_copy = mModelFacesCopy[which_lod].empty(); + if (perform_copy) { + mModelFacesCopy[which_lod].reserve(mModel[which_lod].size()); + } - for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it) - { - if (perform_copy) - { - v_LLVolumeFace_t faces; - (*it)->copyFacesTo(faces); - mModelFacesCopy[which_lod].push_back(faces); - } + for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it) + { + if (perform_copy) + { + v_LLVolumeFace_t faces; + (*it)->copyFacesTo(faces); + mModelFacesCopy[which_lod].push_back(faces); + } - (*it)->generateNormals(angle_cutoff); - } + (*it)->generateNormals(angle_cutoff); + } - mVertexBuffer[which_lod].clear(); - refresh(); - updateStatusMessages(); + mVertexBuffer[which_lod].clear(); + refresh(); + updateStatusMessages(); } void LLModelPreview::restoreNormals() { - S32 which_lod = mPreviewLOD; + S32 which_lod = mPreviewLOD; - if (which_lod > 4 || which_lod < 0 || - mModel[which_lod].empty()) - { - return; - } + if (which_lod > 4 || which_lod < 0 || + mModel[which_lod].empty()) + { + return; + } - if (!mBaseModelFacesCopy.empty()) - { - llassert(mBaseModelFacesCopy.size() == mBaseModel.size()); + if (!mBaseModelFacesCopy.empty()) + { + llassert(mBaseModelFacesCopy.size() == mBaseModel.size()); - vv_LLVolumeFace_t::const_iterator itF = mBaseModelFacesCopy.begin(); - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it, ++itF) - { - (*it)->copyFacesFrom((*itF)); - } + vv_LLVolumeFace_t::const_iterator itF = mBaseModelFacesCopy.begin(); + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it, ++itF) + { + (*it)->copyFacesFrom((*itF)); + } - mBaseModelFacesCopy.clear(); - } + mBaseModelFacesCopy.clear(); + } - if (!mModelFacesCopy[which_lod].empty()) - { - vv_LLVolumeFace_t::const_iterator itF = mModelFacesCopy[which_lod].begin(); - for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it, ++itF) - { - (*it)->copyFacesFrom((*itF)); - } + if (!mModelFacesCopy[which_lod].empty()) + { + vv_LLVolumeFace_t::const_iterator itF = mModelFacesCopy[which_lod].begin(); + for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it, ++itF) + { + (*it)->copyFacesFrom((*itF)); + } - mModelFacesCopy[which_lod].clear(); - } + mModelFacesCopy[which_lod].clear(); + } - mVertexBuffer[which_lod].clear(); - refresh(); - updateStatusMessages(); + mVertexBuffer[which_lod].clear(); + refresh(); + updateStatusMessages(); } void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) { - // Allow LoD from -1 to LLModel::LOD_PHYSICS - if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1) - { - std::ostringstream out; - out << "Invalid level of detail: " << which_lod; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS); - return; - } - - if (mBaseModel.empty()) - { - return; - } - - LLVertexBuffer::unbind(); - - bool no_ff = LLGLSLShader::sNoFixedFunction; - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - LLGLSLShader::sNoFixedFunction = false; - - if (shader) - { - shader->unbind(); - } - - stop_gloderror(); - static U32 cur_name = 1; - - S32 limit = -1; - - U32 triangle_count = 0; - - U32 instanced_triangle_count = 0; - - //get the triangle count for the whole scene - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter) - { - for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) - { - LLModel* mdl = instance->mModel; - if (mdl) - { - instanced_triangle_count += mdl->getNumTriangles(); - } - } - } - - //get the triangle count for the non-instanced set of models - for (U32 i = 0; i < mBaseModel.size(); ++i) - { - triangle_count += mBaseModel[i]->getNumTriangles(); - } - - //get ratio of uninstanced triangles to instanced triangles - F32 triangle_ratio = (F32)triangle_count / (F32)instanced_triangle_count; - - U32 base_triangle_count = triangle_count; - - U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - U32 lod_mode = 0; - - F32 lod_error_threshold = 0; - - // The LoD should be in range from Lowest to High - if (which_lod > -1 && which_lod < NUM_LOD) - { - LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]); - if (iface) - { - lod_mode = iface->getFirstSelectedIndex(); - } - - lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal(); - } - - if (which_lod != -1) - { - mRequestedLoDMode[which_lod] = lod_mode; - } - - if (lod_mode == 0) - { - lod_mode = GLOD_TRIANGLE_BUDGET; - - // The LoD should be in range from Lowest to High - if (which_lod > -1 && which_lod < NUM_LOD) - { - limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger(); - //convert from "scene wide" to "non-instanced" triangle limit - limit = (S32)((F32)limit*triangle_ratio); - } - } - else - { - lod_mode = GLOD_ERROR_THRESHOLD; - } - - bool object_dirty = false; - - if (mGroup == 0) - { - object_dirty = true; - mGroup = cur_name++; - glodNewGroup(mGroup); - } - - if (object_dirty) - { - for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) - { //build GLOD objects for each model in base model list - LLModel* mdl = *iter; - - if (mObject[mdl] != 0) - { - glodDeleteObject(mObject[mdl]); - } - - mObject[mdl] = cur_name++; - - glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); - stop_gloderror(); - - if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) - { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation - mVertexBuffer[5].clear(); - } - - if (mVertexBuffer[5].empty()) - { - genBuffers(5, false); - } - - U32 tri_count = 0; - for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) - { - LLVertexBuffer* buff = mVertexBuffer[5][mdl][i]; - buff->setBuffer(type_mask & buff->getTypeMask()); - - U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); - if (num_indices > 2) - { - // Fix glod so it works when just using the opengl core profile - //glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); - LLStrider vertex_strider; - LLStrider normal_strider; - LLStrider tc_strider; - - LLStrider< U16 > index_strider; - buff->getIndexStrider( index_strider ); - - glodVBO vbo = {}; - - if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) - { - buff->getVertexStrider( vertex_strider ); - vbo.mV.p = vertex_strider.get(); - vbo.mV.size = 3; - vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; - vbo.mV.type = GL_FLOAT; - } - if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) - { - buff->getNormalStrider( normal_strider ); - vbo.mN.p = normal_strider.get(); - vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; - vbo.mN.type = GL_FLOAT; - } - if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) - { - buff->getTexCoord0Strider( tc_strider ); - vbo.mT.p = tc_strider.get(); - vbo.mT.size = 2; - vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; - vbo.mT.type = GL_FLOAT; - } - - glodInsertElements( mObject[ mdl ], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)index_strider.get(), 0, 0.f, &vbo ); - // - } - tri_count += num_indices / 3; - stop_gloderror(); - } - - glodBuildObject(mObject[mdl]); - stop_gloderror(); - } - } - - - S32 start = LLModel::LOD_HIGH; - S32 end = 0; - - if (which_lod != -1) - { - start = end = which_lod; - } - - mMaxTriangleLimit = base_triangle_count; - - for (S32 lod = start; lod >= end; --lod) - { - if (which_lod == -1) - { - if (lod < start) - { - triangle_count /= decimation; - } - } - else - { - if (enforce_tri_limit) - { - triangle_count = limit; - } - else - { - for (S32 j = LLModel::LOD_HIGH; j>which_lod; --j) - { - triangle_count /= decimation; - } - } - } - - mModel[lod].clear(); - mModel[lod].resize(mBaseModel.size()); - mVertexBuffer[lod].clear(); - - U32 actual_tris = 0; - U32 actual_verts = 0; - U32 submeshes = 0; - - mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); - mRequestedErrorThreshold[lod] = lod_error_threshold; - - glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); - stop_gloderror(); - - glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); - stop_gloderror(); - - glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); - stop_gloderror(); - - if (lod_mode != GLOD_TRIANGLE_BUDGET) - { - glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0); - } - else - { - //SH-632: always add 1 to desired amount to avoid decimating below desired amount - glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count + 1); - } - - stop_gloderror(); - glodAdaptGroup(mGroup); - stop_gloderror(); - - for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) - { - LLModel* base = mBaseModel[mdl_idx]; - - GLint patch_count = 0; - glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); - stop_gloderror(); - - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); - - std::string name = base->mLabel + getLodSuffix(lod); - - mModel[lod][mdl_idx]->mLabel = name; - mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; - - GLint* sizes = new GLint[patch_count * 2]; - glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); - stop_gloderror(); - - GLint* names = new GLint[patch_count]; - glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); - stop_gloderror(); - - mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); - - LLModel* target_model = mModel[lod][mdl_idx]; - - for (GLint i = 0; i < patch_count; ++i) - { - type_mask = mVertexBuffer[5][base][i]->getTypeMask(); - - LLPointer buff = new LLVertexBuffer(type_mask, 0); - - if (sizes[i * 2 + 1] > 0 && sizes[i * 2] > 0) - { - if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true)) - { - // Todo: find a way to stop preview in this case instead of crashing - LL_ERRS() << "Failed buffer allocation during preview LOD generation." - << " Vertices: " << sizes[i * 2 + 1] - << " Indices: " << sizes[i * 2] << LL_ENDL; - } - buff->setBuffer(type_mask); - // Fix glod so it works when just using the opengl core profile - //glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*)buff->getIndicesPointer()); - LLStrider vertex_strider; - LLStrider normal_strider; - LLStrider tc_strider; - - LLStrider< U16 > index_strider; - buff->getIndexStrider( index_strider ); - - glodVBO vbo = {}; - - if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) - { - buff->getVertexStrider( vertex_strider ); - vbo.mV.p = vertex_strider.get(); - vbo.mV.size = 3; - vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; - vbo.mV.type = GL_FLOAT; - } - if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) - { - buff->getNormalStrider( normal_strider ); - vbo.mN.p = normal_strider.get(); - vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; - vbo.mN.type = GL_FLOAT; - } - if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) - { - buff->getTexCoord0Strider( tc_strider ); - vbo.mT.p = tc_strider.get(); - vbo.mT.size = 2; - vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; - vbo.mT.type = GL_FLOAT; - } - - glodFillElements( mObject[ base ], names[ i ], GL_UNSIGNED_SHORT, (U8*)index_strider.get(), &vbo ); - // - stop_gloderror(); - } - else - { - // This face was eliminated or we failed to allocate buffer, - // attempt to create a dummy triangle (one vertex, 3 indices, all 0) - buff->allocateBuffer(1, 3, true); - memset((U8*)buff->getMappedData(), 0, buff->getSize()); - // Fix when running with opengl core profile - //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); - LLStrider< U16 > index_strider; - buff->getIndexStrider( index_strider ); - - memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); - // - } - - buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); - - LLStrider pos; - LLStrider norm; - LLStrider tc; - LLStrider index; - - buff->getVertexStrider(pos); - if (type_mask & LLVertexBuffer::MAP_NORMAL) - { - buff->getNormalStrider(norm); - } - if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) - { - buff->getTexCoord0Strider(tc); - } - - buff->getIndexStrider(index); - - target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); - actual_tris += buff->getNumIndices() / 3; - actual_verts += buff->getNumVerts(); - ++submeshes; - - if (!validate_face(target_model->getVolumeFace(names[i]))) - { - LL_ERRS() << "Invalid face generated during LOD generation." << LL_ENDL; - } - } - - //blind copy skin weights and just take closest skin weight to point on - //decimated mesh for now (auto-generating LODs with skin weights is still a bit - //of an open problem). - target_model->mPosition = base->mPosition; - target_model->mSkinWeights = base->mSkinWeights; - target_model->mSkinInfo = base->mSkinInfo; - //copy material list - target_model->mMaterialList = base->mMaterialList; - - if (!validate_model(target_model)) - { - LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL; - } - - delete[] sizes; - delete[] names; - } - - //rebuild scene based on mBaseScene - mScene[lod].clear(); - mScene[lod] = mBaseScene; - - for (U32 i = 0; i < mBaseModel.size(); ++i) - { - LLModel* mdl = mBaseModel[i]; - LLModel* target = mModel[lod][i]; - if (target) - { - for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) - { - for (U32 j = 0; j < iter->second.size(); ++j) - { - if (iter->second[j].mModel == mdl) - { - iter->second[j].mModel = target; - } - } - } - } - } - } - - mResourceCost = calcResourceCost(); - - LLVertexBuffer::unbind(); - LLGLSLShader::sNoFixedFunction = no_ff; - if (shader) - { - shader->bind(); - } - refresh(); // refresh once to make sure render gets called with the updated vbos + // Allow LoD from -1 to LLModel::LOD_PHYSICS + if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1) + { + std::ostringstream out; + out << "Invalid level of detail: " << which_lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS); + return; + } + + if (mBaseModel.empty()) + { + return; + } + + LLVertexBuffer::unbind(); + + bool no_ff = LLGLSLShader::sNoFixedFunction; + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + LLGLSLShader::sNoFixedFunction = false; + + if (shader) + { + shader->unbind(); + } + + stop_gloderror(); + static U32 cur_name = 1; + + S32 limit = -1; + + U32 triangle_count = 0; + + U32 instanced_triangle_count = 0; + + //get the triangle count for the whole scene + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter) + { + for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) + { + LLModel* mdl = instance->mModel; + if (mdl) + { + instanced_triangle_count += mdl->getNumTriangles(); + } + } + } + + //get the triangle count for the non-instanced set of models + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + triangle_count += mBaseModel[i]->getNumTriangles(); + } + + //get ratio of uninstanced triangles to instanced triangles + F32 triangle_ratio = (F32)triangle_count / (F32)instanced_triangle_count; + + U32 base_triangle_count = triangle_count; + + U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + U32 lod_mode = 0; + + F32 lod_error_threshold = 0; + + // The LoD should be in range from Lowest to High + if (which_lod > -1 && which_lod < NUM_LOD) + { + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } + + lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal(); + } + + if (which_lod != -1) + { + mRequestedLoDMode[which_lod] = lod_mode; + } + + if (lod_mode == 0) + { + lod_mode = GLOD_TRIANGLE_BUDGET; + + // The LoD should be in range from Lowest to High + if (which_lod > -1 && which_lod < NUM_LOD) + { + limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger(); + //convert from "scene wide" to "non-instanced" triangle limit + limit = (S32)((F32)limit*triangle_ratio); + } + } + else + { + lod_mode = GLOD_ERROR_THRESHOLD; + } + + bool object_dirty = false; + + if (mGroup == 0) + { + object_dirty = true; + mGroup = cur_name++; + glodNewGroup(mGroup); + } + + if (object_dirty) + { + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { //build GLOD objects for each model in base model list + LLModel* mdl = *iter; + + if (mObject[mdl] != 0) + { + glodDeleteObject(mObject[mdl]); + } + + mObject[mdl] = cur_name++; + + glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); + stop_gloderror(); + + if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) + { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation + mVertexBuffer[5].clear(); + } + + if (mVertexBuffer[5].empty()) + { + genBuffers(5, false); + } + + U32 tri_count = 0; + for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) + { + LLVertexBuffer* buff = mVertexBuffer[5][mdl][i]; + buff->setBuffer(type_mask & buff->getTypeMask()); + + U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); + if (num_indices > 2) + { + // Fix glod so it works when just using the opengl core profile + //glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + glodVBO vbo = {}; + + if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) + { + buff->getVertexStrider( vertex_strider ); + vbo.mV.p = vertex_strider.get(); + vbo.mV.size = 3; + vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; + vbo.mV.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) + { + buff->getNormalStrider( normal_strider ); + vbo.mN.p = normal_strider.get(); + vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; + vbo.mN.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) + { + buff->getTexCoord0Strider( tc_strider ); + vbo.mT.p = tc_strider.get(); + vbo.mT.size = 2; + vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; + vbo.mT.type = GL_FLOAT; + } + + glodInsertElements( mObject[ mdl ], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)index_strider.get(), 0, 0.f, &vbo ); + // + } + tri_count += num_indices / 3; + stop_gloderror(); + } + + glodBuildObject(mObject[mdl]); + stop_gloderror(); + } + } + + + S32 start = LLModel::LOD_HIGH; + S32 end = 0; + + if (which_lod != -1) + { + start = end = which_lod; + } + + mMaxTriangleLimit = base_triangle_count; + + for (S32 lod = start; lod >= end; --lod) + { + if (which_lod == -1) + { + if (lod < start) + { + triangle_count /= decimation; + } + } + else + { + if (enforce_tri_limit) + { + triangle_count = limit; + } + else + { + for (S32 j = LLModel::LOD_HIGH; j>which_lod; --j) + { + triangle_count /= decimation; + } + } + } + + mModel[lod].clear(); + mModel[lod].resize(mBaseModel.size()); + mVertexBuffer[lod].clear(); + + U32 actual_tris = 0; + U32 actual_verts = 0; + U32 submeshes = 0; + + mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); + mRequestedErrorThreshold[lod] = lod_error_threshold; + + glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); + stop_gloderror(); + + glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); + stop_gloderror(); + + glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); + stop_gloderror(); + + if (lod_mode != GLOD_TRIANGLE_BUDGET) + { + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0); + } + else + { + //SH-632: always add 1 to desired amount to avoid decimating below desired amount + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count + 1); + } + + stop_gloderror(); + glodAdaptGroup(mGroup); + stop_gloderror(); + + for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) + { + LLModel* base = mBaseModel[mdl_idx]; + + GLint patch_count = 0; + glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); + stop_gloderror(); + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); + + std::string name = base->mLabel + getLodSuffix(lod); + + mModel[lod][mdl_idx]->mLabel = name; + mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; + + GLint* sizes = new GLint[patch_count * 2]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); + stop_gloderror(); + + GLint* names = new GLint[patch_count]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); + stop_gloderror(); + + mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); + + LLModel* target_model = mModel[lod][mdl_idx]; + + for (GLint i = 0; i < patch_count; ++i) + { + type_mask = mVertexBuffer[5][base][i]->getTypeMask(); + + LLPointer buff = new LLVertexBuffer(type_mask, 0); + + if (sizes[i * 2 + 1] > 0 && sizes[i * 2] > 0) + { + if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true)) + { + // Todo: find a way to stop preview in this case instead of crashing + LL_ERRS() << "Failed buffer allocation during preview LOD generation." + << " Vertices: " << sizes[i * 2 + 1] + << " Indices: " << sizes[i * 2] << LL_ENDL; + } + buff->setBuffer(type_mask); + // Fix glod so it works when just using the opengl core profile + //glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*)buff->getIndicesPointer()); + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + glodVBO vbo = {}; + + if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) + { + buff->getVertexStrider( vertex_strider ); + vbo.mV.p = vertex_strider.get(); + vbo.mV.size = 3; + vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; + vbo.mV.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) + { + buff->getNormalStrider( normal_strider ); + vbo.mN.p = normal_strider.get(); + vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; + vbo.mN.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) + { + buff->getTexCoord0Strider( tc_strider ); + vbo.mT.p = tc_strider.get(); + vbo.mT.size = 2; + vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; + vbo.mT.type = GL_FLOAT; + } + + glodFillElements( mObject[ base ], names[ i ], GL_UNSIGNED_SHORT, (U8*)index_strider.get(), &vbo ); + // + stop_gloderror(); + } + else + { + // This face was eliminated or we failed to allocate buffer, + // attempt to create a dummy triangle (one vertex, 3 indices, all 0) + buff->allocateBuffer(1, 3, true); + memset((U8*)buff->getMappedData(), 0, buff->getSize()); + // Fix when running with opengl core profile + //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); + // + } + + buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); + + LLStrider pos; + LLStrider norm; + LLStrider tc; + LLStrider index; + + buff->getVertexStrider(pos); + if (type_mask & LLVertexBuffer::MAP_NORMAL) + { + buff->getNormalStrider(norm); + } + if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) + { + buff->getTexCoord0Strider(tc); + } + + buff->getIndexStrider(index); + + target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + actual_tris += buff->getNumIndices() / 3; + actual_verts += buff->getNumVerts(); + ++submeshes; + + if (!validate_face(target_model->getVolumeFace(names[i]))) + { + LL_ERRS() << "Invalid face generated during LOD generation." << LL_ENDL; + } + } + + //blind copy skin weights and just take closest skin weight to point on + //decimated mesh for now (auto-generating LODs with skin weights is still a bit + //of an open problem). + target_model->mPosition = base->mPosition; + target_model->mSkinWeights = base->mSkinWeights; + target_model->mSkinInfo = base->mSkinInfo; + //copy material list + target_model->mMaterialList = base->mMaterialList; + + if (!validate_model(target_model)) + { + LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL; + } + + delete[] sizes; + delete[] names; + } + + //rebuild scene based on mBaseScene + mScene[lod].clear(); + mScene[lod] = mBaseScene; + + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + LLModel* mdl = mBaseModel[i]; + LLModel* target = mModel[lod][i]; + if (target) + { + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + for (U32 j = 0; j < iter->second.size(); ++j) + { + if (iter->second[j].mModel == mdl) + { + iter->second[j].mModel = target; + } + } + } + } + } + } + + mResourceCost = calcResourceCost(); + + LLVertexBuffer::unbind(); + LLGLSLShader::sNoFixedFunction = no_ff; + if (shader) + { + shader->bind(); + } + refresh(); // refresh once to make sure render gets called with the updated vbos } 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 - }; + // 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(); + 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 tris[LLModel::NUM_LODS]; - std::vector verts[LLModel::NUM_LODS]; - std::vector submeshes[LLModel::NUM_LODS]; + U32 has_physics_error{ PhysicsError::NONE }; // physics error bitmap + //triangle/vertex/submesh count for each mesh asset for each lod + std::vector tris[LLModel::NUM_LODS]; + std::vector verts[LLModel::NUM_LODS]; + std::vector submeshes[LLModel::NUM_LODS]; - //total triangle/vertex/submesh count for each lod - S32 total_tris[LLModel::NUM_LODS]; - S32 total_verts[LLModel::NUM_LODS]; - S32 total_submeshes[LLModel::NUM_LODS]; + //total triangle/vertex/submesh count for each lod + S32 total_tris[LLModel::NUM_LODS]; + S32 total_verts[LLModel::NUM_LODS]; + S32 total_submeshes[LLModel::NUM_LODS]; - for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) - { - total_tris[i] = 0; - total_verts[i] = 0; - total_submeshes[i] = 0; - } + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + total_tris[i] = 0; + total_verts[i] = 0; + total_submeshes[i] = 0; + } - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; - LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH]; - if (!model_high_lod) - { - setLoadState(LLModelLoader::ERROR_MATERIALS); - mFMP->childDisable("calculate_btn"); - continue; - } + LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH]; + if (!model_high_lod) + { + setLoadState(LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING); // FIRE-30965 Cleanup braindead mesh parsing error handlers + mFMP->childDisable("calculate_btn"); + continue; + } - for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) - { - LLModel* lod_model = instance.mLOD[i]; - if (!lod_model) - { - setLoadState(LLModelLoader::ERROR_MATERIALS); - mFMP->childDisable("calculate_btn"); - } - else - { - //for each model in the lod - S32 cur_tris = 0; - S32 cur_verts = 0; - S32 cur_submeshes = lod_model->getNumVolumeFaces(); + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + LLModel* lod_model = instance.mLOD[i]; + if (!lod_model) + { + setLoadState(LLModelLoader::ERROR_LOD_MODEL_MISMATCH); // FIRE-30965 Cleanup braindead mesh parsing error handlers + mFMP->childDisable("calculate_btn"); + } + else + { + //for each model in the lod + S32 cur_tris = 0; + S32 cur_verts = 0; + S32 cur_submeshes = lod_model->getNumVolumeFaces(); - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = lod_model->getVolumeFace(j); - cur_tris += face.mNumIndices / 3; - cur_verts += face.mNumVertices; - } + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = lod_model->getVolumeFace(j); + cur_tris += face.mNumIndices / 3; + cur_verts += face.mNumVertices; + } - std::string instance_name = instance.mLabel; + std::string instance_name = instance.mLabel; - if (mImporterDebug) - { - // Useful for debugging generalized complaints below about total submeshes which don't have enough - // context to address exactly what needs to be fixed to move towards compliance with the rules. - // - std::ostringstream out; - out << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); + if (mImporterDebug) + { + // Useful for debugging generalized complaints below about total submeshes which don't have enough + // context to address exactly what needs to be fixed to move towards compliance with the rules. + // + std::ostringstream out; + out << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); - out.str(""); - out << "Instance " << lod_model->mLabel << " LOD " << i << " Tris: " << cur_tris; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); + out.str(""); + out << "Instance " << lod_model->mLabel << " LOD " << i << " Tris: " << cur_tris; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); - out.str(""); - out << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); + out.str(""); + out << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); - out.str(""); - LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin(); - while (mat_iter != lod_model->mMaterialList.end()) - { - out << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter); - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - out.str(""); - mat_iter++; - } - } + out.str(""); + LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin(); + while (mat_iter != lod_model->mMaterialList.end()) + { + out << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter); + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + out.str(""); + mat_iter++; + } + } - //add this model to the lod total - total_tris[i] += cur_tris; - total_verts[i] += cur_verts; - total_submeshes[i] += cur_submeshes; + //add this model to the lod total + total_tris[i] += cur_tris; + total_verts[i] += cur_verts; + total_submeshes[i] += cur_submeshes; - //store this model's counts to asset data - tris[i].push_back(cur_tris); - verts[i].push_back(cur_verts); - submeshes[i].push_back(cur_submeshes); - } - } - } + //store this model's counts to asset data + tris[i].push_back(cur_tris); + verts[i].push_back(cur_verts); + submeshes[i].push_back(cur_submeshes); + } + } + } - if (mMaxTriangleLimit == 0) - { - mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; - } + if (mMaxTriangleLimit == 0) + { + mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; + } - mHasDegenerate = false; - {//check for degenerate triangles in physics mesh - U32 lod = LLModel::LOD_PHYSICS; - const LLVector4a scale(0.5f); - for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i) - { //for each model in the lod - if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty()) - { //no decomp exists - S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); - for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j) - { //for each submesh (face), add triangles and vertices to current total - LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); - for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate;) - { - U16 index_a = face.mIndices[k + 0]; - U16 index_b = face.mIndices[k + 1]; - U16 index_c = face.mIndices[k + 2]; + mHasDegenerate = false; + {//check for degenerate triangles in physics mesh + U32 lod = LLModel::LOD_PHYSICS; + const LLVector4a scale(0.5f); + for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i) + { //for each model in the lod + if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty()) + { //no decomp exists + S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); + for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j) + { //for each submesh (face), add triangles and vertices to current total + LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); + for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate;) + { + U16 index_a = face.mIndices[k + 0]; + U16 index_b = face.mIndices[k + 1]; + U16 index_c = face.mIndices[k + 2]; - if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case - { - LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL; - } - else - { - LLVector4a v1; v1.setMul(face.mPositions[index_a], scale); - LLVector4a v2; v2.setMul(face.mPositions[index_b], scale); - LLVector4a v3; v3.setMul(face.mPositions[index_c], scale); - if (ll_is_degenerate(v1, v2, v3)) - { - mHasDegenerate = true; - } - } - k += 3; - } - } - } - } - } + if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case + { + LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL; + } + else + { + LLVector4a v1; v1.setMul(face.mPositions[index_a], scale); + LLVector4a v2; v2.setMul(face.mPositions[index_b], scale); + LLVector4a v3; v3.setMul(face.mPositions[index_c], scale); + if (ll_is_degenerate(v1, v2, v3)) + { + mHasDegenerate = true; + } + } + k += 3; + } + } + } + } + } - // flag degenerates here rather than deferring to a MAV error later - // - //mFMP->childSetVisible("physics_status_message_text", mHasDegenerate); //display or clear - //auto degenerateIcon = mFMP->getChild("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); - // - } + // 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("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])); + mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH])); - std::string mesh_status_na = mFMP->getString("mesh_status_na"); + std::string mesh_status_na = mFMP->getString("mesh_status_na"); - S32 upload_status[LLModel::LOD_HIGH + 1]; + S32 upload_status[LLModel::LOD_HIGH + 1]; - mModelNoErrors = true; + mModelNoErrors = true; - const U32 lod_high = LLModel::LOD_HIGH; - U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]); + const U32 lod_high = LLModel::LOD_HIGH; + U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]); - for (S32 lod = 0; lod <= lod_high; ++lod) - { - upload_status[lod] = 0; + for (S32 lod = 0; lod <= lod_high; ++lod) + { + upload_status[lod] = 0; - std::string message = "mesh_status_good"; + std::string message = "mesh_status_good"; - if (total_tris[lod] > 0) - { - mFMP->childSetValue(lod_triangles_name[lod], llformat("%d", total_tris[lod])); - mFMP->childSetValue(lod_vertices_name[lod], llformat("%d", total_verts[lod])); - } - else - { - if (lod == lod_high) - { - upload_status[lod] = 2; - message = "mesh_status_missing_lod"; - } - else - { - for (S32 i = lod - 1; i >= 0; --i) - { - if (total_tris[i] > 0) - { - upload_status[lod] = 2; - message = "mesh_status_missing_lod"; - } - } - } + if (total_tris[lod] > 0) + { + mFMP->childSetValue(lod_triangles_name[lod], llformat("%d", total_tris[lod])); + mFMP->childSetValue(lod_vertices_name[lod], llformat("%d", total_verts[lod])); + } + else + { + if (lod == lod_high) + { + upload_status[lod] = 2; + message = "mesh_status_missing_lod"; + } + else + { + for (S32 i = lod - 1; i >= 0; --i) + { + if (total_tris[i] > 0) + { + upload_status[lod] = 2; + message = "mesh_status_missing_lod"; + } + } + } - mFMP->childSetValue(lod_triangles_name[lod], mesh_status_na); - mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na); - } + mFMP->childSetValue(lod_triangles_name[lod], mesh_status_na); + mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na); + } - if (lod != lod_high) - { - if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) - { //number of submeshes is different - message = "mesh_status_submesh_mismatch"; - upload_status[lod] = 2; - } - else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count) - {//number of submodels is different, not all faces are matched correctly. - message = "mesh_status_submesh_mismatch"; - upload_status[lod] = 2; - // Note: Submodels in instance were loaded from higher LOD and as result face count - // returns same value and total_submeshes[lod] is identical to high_lod one. - } - else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) - { //number of meshes is different - message = "mesh_status_mesh_mismatch"; - upload_status[lod] = 2; - } - else if (!verts[lod].empty()) - { - S32 sum_verts_higher_lod = 0; - S32 sum_verts_this_lod = 0; - for (U32 i = 0; i < verts[lod].size(); ++i) - { - sum_verts_higher_lod += ((i < verts[lod + 1].size()) ? verts[lod + 1][i] : 0); - sum_verts_this_lod += verts[lod][i]; - } + if (lod != lod_high) + { + if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) + { //number of submeshes is different + message = "mesh_status_submesh_mismatch"; + upload_status[lod] = 2; + } + else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count) + {//number of submodels is different, not all faces are matched correctly. + message = "mesh_status_submesh_mismatch"; + upload_status[lod] = 2; + // Note: Submodels in instance were loaded from higher LOD and as result face count + // returns same value and total_submeshes[lod] is identical to high_lod one. + } + else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) + { //number of meshes is different + message = "mesh_status_mesh_mismatch"; + upload_status[lod] = 2; + } + else if (!verts[lod].empty()) + { + S32 sum_verts_higher_lod = 0; + S32 sum_verts_this_lod = 0; + for (U32 i = 0; i < verts[lod].size(); ++i) + { + sum_verts_higher_lod += ((i < verts[lod + 1].size()) ? verts[lod + 1][i] : 0); + sum_verts_this_lod += verts[lod][i]; + } - if ((sum_verts_higher_lod > 0) && - (sum_verts_this_lod > sum_verts_higher_lod)) - { - //too many vertices in this lod - message = "mesh_status_too_many_vertices"; - upload_status[lod] = 1; - } - } - } + if ((sum_verts_higher_lod > 0) && + (sum_verts_this_lod > sum_verts_higher_lod)) + { + //too many vertices in this lod + message = "mesh_status_too_many_vertices"; + upload_status[lod] = 1; + } + } + } - LLIconCtrl* icon = mFMP->getChild(lod_icon_name[lod]); - LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]); - icon->setVisible(true); - icon->setImage(img); + LLIconCtrl* icon = mFMP->getChild(lod_icon_name[lod]); + LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]); + icon->setVisible(true); + icon->setImage(img); - if (upload_status[lod] >= 2) - { - mModelNoErrors = false; - } + if (upload_status[lod] >= 2) + { + mModelNoErrors = false; + } - if (lod == mPreviewLOD) - { - mFMP->childSetValue("lod_status_message_text", mFMP->getString(message)); - icon = mFMP->getChild("lod_status_message_icon"); - icon->setImage(img); - } + if (lod == mPreviewLOD) + { + mFMP->childSetValue("lod_status_message_text", mFMP->getString(message)); + icon = mFMP->getChild("lod_status_message_icon"); + icon->setImage(img); + } - updateLodControls(lod); - } + updateLodControls(lod); + } - //warn if hulls have more than 256 points in them - BOOL physExceededVertexLimit = FALSE; - for (U32 i = 0; mModelNoErrors && (i < mModel[LLModel::LOD_PHYSICS].size()); ++i) - { - LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i]; + //warn if hulls have more than 256 points in them + BOOL physExceededVertexLimit = FALSE; + for (U32 i = 0; mModelNoErrors && (i < mModel[LLModel::LOD_PHYSICS].size()); ++i) + { + LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i]; - if (mdl) - { - // Better error handling - auto num_hulls = mdl->mPhysics.mHull.size(); - for (U32 j = 0; j < num_hulls; ++j) - { - // - if (mdl->mPhysics.mHull[j].size() > 256) - { - physExceededVertexLimit = TRUE; - // add new friendlier logging to mesh uploader - // LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL; - std::ostringstream out; - out << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations."; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - break; - } - } - // Better error handling - if (num_hulls > 256) // decomp cannot have more than 256 hulls (http://wiki.secondlife.com/wiki/Mesh/Mesh_physics) - { - // improve uploader error reporting - // LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds 256 hull limitation." << LL_ENDL; - std::ostringstream out; - out << "Physical model " << mdl->mLabel << " exceeds 256 hull limitation."; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - has_physics_error |= PhysicsError::TOOMANYHULLS; - } - // - } - } + if (mdl) + { + // Better error handling + auto num_hulls = mdl->mPhysics.mHull.size(); + for (U32 j = 0; j < num_hulls; ++j) + { + // + if (mdl->mPhysics.mHull[j].size() > 256) + { + physExceededVertexLimit = TRUE; + // add new friendlier logging to mesh uploader + // LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL; + std::ostringstream out; + out << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + break; + } + } + // Better error handling + if (num_hulls > 256) // decomp cannot have more than 256 hulls (http://wiki.secondlife.com/wiki/Mesh/Mesh_physics) + { + // improve uploader error reporting + // LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds 256 hull limitation." << LL_ENDL; + std::ostringstream out; + out << "Physical model " << mdl->mLabel << " exceeds 256 hull limitation."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + has_physics_error |= PhysicsError::TOOMANYHULLS; + } + // + } + } - if (physExceededVertexLimit) - { - has_physics_error |= PhysicsError::TOOMANYVERTSINHULL; - } + if (physExceededVertexLimit) + { + has_physics_error |= PhysicsError::TOOMANYVERTSINHULL; + } // standardise error handling - //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("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 (!(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("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); + // } + //} #ifndef HAVOK_TPV - has_physics_error |= PhysicsError::NOHAVOK; + has_physics_error |= PhysicsError::NOHAVOK; #endif - auto physStatusIcon = mFMP->getChild("physics_status_message_icon"); + auto physStatusIcon = mFMP->getChild("physics_status_message_icon"); - if (has_physics_error != PhysicsError::NONE) - { - mFMP->childSetVisible("physics_status_message_text", true); //display or clear - physStatusIcon->setVisible(true); - // The order here is important. - if (has_physics_error & PhysicsError::TOOMANYHULLS) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_hull_limit_exceeded")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); - physStatusIcon->setImage(img); - } - else if (has_physics_error & PhysicsError::TOOMANYVERTSINHULL) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); - physStatusIcon->setImage(img); - } - else if (has_physics_error & PhysicsError::DEGENERATE) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_degenerate_triangles")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); - physStatusIcon->setImage(img); - } - else if (has_physics_error & PhysicsError::NOHAVOK) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_no_havok")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); - physStatusIcon->setImage(img); - } - else - { - // This should not happen - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_unknown_error")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); - physStatusIcon->setImage(img); - } - } - else - { - mFMP->childSetVisible("physics_status_message_text", false); //display or clear - physStatusIcon->setVisible(false); - } + if (has_physics_error != PhysicsError::NONE) + { + mFMP->childSetVisible("physics_status_message_text", true); //display or clear + physStatusIcon->setVisible(true); + // The order here is important. + if (has_physics_error & PhysicsError::TOOMANYHULLS) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_hull_limit_exceeded")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); + physStatusIcon->setImage(img); + } + else if (has_physics_error & PhysicsError::TOOMANYVERTSINHULL) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); + physStatusIcon->setImage(img); + } + else if (has_physics_error & PhysicsError::DEGENERATE) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_degenerate_triangles")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); + physStatusIcon->setImage(img); + } + else if (has_physics_error & PhysicsError::NOHAVOK) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_no_havok")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); + physStatusIcon->setImage(img); + } + else + { + // This should not happen + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_unknown_error")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); + physStatusIcon->setImage(img); + } + } + else + { + mFMP->childSetVisible("physics_status_message_text", false); //display or clear + physStatusIcon->setVisible(false); + } // - if (getLoadState() >= LLModelLoader::ERROR_PARSING) - { - mModelNoErrors = false; - // improve uploader error reporting - // LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL; - std::ostringstream out; - out << "Loader returned errors, model can't be uploaded"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - } + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mModelNoErrors = false; + // improve uploader error reporting + // LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL; + std::ostringstream out; + out << "Loader returned errors, model can't be uploaded"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + } - bool uploadingSkin = mFMP->childGetValue("upload_skin").asBoolean(); - bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); + bool uploadingSkin = mFMP->childGetValue("upload_skin").asBoolean(); + bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); - if (uploadingSkin) - { - if (uploadingJointPositions && !isRigValidForJointPositionUpload()) - { - mModelNoErrors = false; - // improve uploader error reporting - // LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL; - std::ostringstream out; - out << "Invalid rig, there might be issues with uploading Joint positions"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - } - } + if (uploadingSkin) + { + if (uploadingJointPositions && !isRigValidForJointPositionUpload()) + { + mModelNoErrors = false; + // improve uploader error reporting + // LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL; + std::ostringstream out; + out << "Invalid rig, there might be issues with uploading Joint positions"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + } + } - if (mModelNoErrors && mModelLoader) - { - if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) - { - // Some textures are still loading, prevent upload until they are done - mModelNoErrors = false; - } - } + if (mModelNoErrors && mModelLoader) + { + if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) + { + // Some textures are still loading, prevent upload until they are done + mModelNoErrors = false; + } + } - // Improve the error checking the TO DO here is no longer applicable but not an FS comment so edited to stop it being picked up - //if (!mModelNoErrors || mHasDegenerate) - if (!gSavedSettings.getBOOL("FSIgnoreClientsideMeshValidation") && (!mModelNoErrors || (has_physics_error > PhysicsError::NOHAVOK))) // block for all cases of phsyics error except NOHAVOK - // - { - mFMP->childDisable("ok_btn"); - mFMP->childDisable("calculate_btn"); - } - else - { - mFMP->childEnable("ok_btn"); - mFMP->childEnable("calculate_btn"); - } + // Improve the error checking the TO DO here is no longer applicable but not an FS comment so edited to stop it being picked up + //if (!mModelNoErrors || mHasDegenerate) + if (!gSavedSettings.getBOOL("FSIgnoreClientsideMeshValidation") && (!mModelNoErrors || (has_physics_error > PhysicsError::NOHAVOK))) // block for all cases of phsyics error except NOHAVOK + // + { + mFMP->childDisable("ok_btn"); + mFMP->childDisable("calculate_btn"); + } + else + { + mFMP->childEnable("ok_btn"); + mFMP->childEnable("calculate_btn"); + } - if (mModelNoErrors && mLodsWithParsingError.empty()) - { - mFMP->childEnable("calculate_btn"); - } - else - { - mFMP->childDisable("calculate_btn"); - } + if (mModelNoErrors && mLodsWithParsingError.empty()) + { + mFMP->childEnable("calculate_btn"); + } + else + { + mFMP->childDisable("calculate_btn"); + } - //add up physics triangles etc - S32 phys_tris = 0; - S32 phys_hulls = 0; - S32 phys_points = 0; + //add up physics triangles etc + S32 phys_tris = 0; + S32 phys_hulls = 0; + S32 phys_points = 0; - //get the triangle count for the whole scene - for (LLModelLoader::scene::iterator iter = mScene[LLModel::LOD_PHYSICS].begin(), endIter = mScene[LLModel::LOD_PHYSICS].end(); iter != endIter; ++iter) - { - for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) - { - LLModel* model = instance->mModel; - if (model) - { - S32 cur_submeshes = model->getNumVolumeFaces(); + //get the triangle count for the whole scene + for (LLModelLoader::scene::iterator iter = mScene[LLModel::LOD_PHYSICS].begin(), endIter = mScene[LLModel::LOD_PHYSICS].end(); iter != endIter; ++iter) + { + for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) + { + LLModel* model = instance->mModel; + if (model) + { + S32 cur_submeshes = model->getNumVolumeFaces(); - LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull; + LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull; - if (!decomp.empty()) - { - phys_hulls += decomp.size(); - for (U32 i = 0; i < decomp.size(); ++i) - { - phys_points += decomp[i].size(); - } - } - else - { //choose physics shape OR decomposition, can't use both - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = model->getVolumeFace(j); - phys_tris += face.mNumIndices / 3; - } - } - } - } - } + if (!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_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); - } + if (phys_hulls > 0) + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls)); + mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points)); + } + else + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na); + mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na); + } - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp) - { - if (phys_tris > 0 || phys_hulls > 0) - { - if (!fmp->isViewOptionEnabled("show_physics")) - { - fmp->enableViewOption("show_physics"); - mViewOption["show_physics"] = true; - fmp->childSetValue("show_physics", true); - } - // handle hiding of hull only explode slider - //} - //else - //{ - // fmp->disableViewOption("show_physics"); - // mViewOption["show_physics"] = false; - // fmp->childSetValue("show_physics", false); - //} - - // mViewOption["show_physics"] = true; // merge LL uploader changes - if (phys_hulls > 0) - { - fmp->enableViewOption("physics_explode"); - fmp->enableViewOption("exploder_label"); - fmp->childSetVisible("physics_explode", true); - fmp->childSetVisible("exploder_label", true); - } - else - { - fmp->disableViewOption("physics_explode"); - fmp->disableViewOption("exploder_label"); - fmp->childSetVisible("physics_explode", false); - fmp->childSetVisible("exploder_label", false); - } - } - else - { - fmp->disableViewOption("show_physics"); - fmp->childSetVisible("physics_explode", false); - fmp->disableViewOption("physics_explode"); - fmp->childSetVisible("exploder_label", false); - fmp->disableViewOption("exploder_label"); - mViewOption["show_physics"] = false; - fmp->childSetValue("show_physics", false); + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + if (phys_tris > 0 || phys_hulls > 0) + { + if (!fmp->isViewOptionEnabled("show_physics")) + { + fmp->enableViewOption("show_physics"); + mViewOption["show_physics"] = true; + fmp->childSetValue("show_physics", true); + } + // handle hiding of hull only explode slider + //} + //else + //{ + // fmp->disableViewOption("show_physics"); + // mViewOption["show_physics"] = false; + // fmp->childSetValue("show_physics", false); + //} + + // mViewOption["show_physics"] = true; // merge LL uploader changes + if (phys_hulls > 0) + { + fmp->enableViewOption("physics_explode"); + fmp->enableViewOption("exploder_label"); + fmp->childSetVisible("physics_explode", true); + fmp->childSetVisible("exploder_label", true); + } + else + { + fmp->disableViewOption("physics_explode"); + fmp->disableViewOption("exploder_label"); + fmp->childSetVisible("physics_explode", false); + fmp->childSetVisible("exploder_label", false); + } + } + else + { + fmp->disableViewOption("show_physics"); + fmp->childSetVisible("physics_explode", false); + fmp->disableViewOption("physics_explode"); + fmp->childSetVisible("exploder_label", false); + fmp->disableViewOption("exploder_label"); + mViewOption["show_physics"] = false; + fmp->childSetValue("show_physics", false); - } - // + } + // - //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean(); + //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean(); - //fmp->childSetEnabled("physics_optimize", !use_hull); + //fmp->childSetEnabled("physics_optimize", !use_hull); - bool enable = (phys_tris > 0 || phys_hulls > 0) && fmp->mCurRequest.empty(); - //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); + bool enable = (phys_tris > 0 || phys_hulls > 0) && fmp->mCurRequest.empty(); + //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); - //enable/disable "analysis" UI - LLPanel* panel = fmp->getChild("physics analysis"); - LLView* child = panel->getFirstChild(); - while (child) - { - child->setEnabled(enable); - child = panel->findNextSibling(child); - } + //enable/disable "analysis" UI + LLPanel* panel = fmp->getChild("physics analysis"); + LLView* child = panel->getFirstChild(); + while (child) + { + child->setEnabled(enable); + child = panel->findNextSibling(child); + } - enable = phys_hulls > 0 && fmp->mCurRequest.empty(); - //enable/disable "simplification" UI - panel = fmp->getChild("physics simplification"); - child = panel->getFirstChild(); - while (child) - { - child->setEnabled(enable); - child = panel->findNextSibling(child); - } + enable = phys_hulls > 0 && fmp->mCurRequest.empty(); + //enable/disable "simplification" UI + panel = fmp->getChild("physics simplification"); + child = panel->getFirstChild(); + while (child) + { + child->setEnabled(enable); + child = panel->findNextSibling(child); + } - if (fmp->mCurRequest.empty()) - { - fmp->childSetVisible("Simplify", true); - fmp->childSetVisible("simplify_cancel", false); - fmp->childSetVisible("Decompose", true); - fmp->childSetVisible("decompose_cancel", false); + if (fmp->mCurRequest.empty()) + { + fmp->childSetVisible("Simplify", true); + fmp->childSetVisible("simplify_cancel", false); + fmp->childSetVisible("Decompose", true); + fmp->childSetVisible("decompose_cancel", false); - if (phys_hulls > 0) - { - fmp->childEnable("Simplify"); - } + if (phys_hulls > 0) + { + fmp->childEnable("Simplify"); + } - if (phys_tris || phys_hulls > 0) - { - fmp->childEnable("Decompose"); - } - } - else - { - fmp->childEnable("simplify_cancel"); - fmp->childEnable("decompose_cancel"); - } - // move the closing bracket for the if(fmp) to prevent possible crash - // } + if (phys_tris || phys_hulls > 0) + { + fmp->childEnable("Decompose"); + } + } + else + { + fmp->childEnable("simplify_cancel"); + fmp->childEnable("decompose_cancel"); + } + // move the closing bracket for the if(fmp) to prevent possible crash + // } - LLCtrlSelectionInterface* iface = fmp->childGetSelectionInterface("physics_lod_combo"); - S32 which_mode = 0; - S32 file_mode = 1; - if (iface) - { - which_mode = iface->getFirstSelectedIndex(); - file_mode = iface->getItemCount() - 1; - } + LLCtrlSelectionInterface* iface = fmp->childGetSelectionInterface("physics_lod_combo"); + S32 which_mode = 0; + S32 file_mode = 1; + if (iface) + { + which_mode = iface->getFirstSelectedIndex(); + file_mode = iface->getItemCount() - 1; + } - if (which_mode == file_mode) - { - mFMP->childEnable("physics_file"); - mFMP->childEnable("physics_browse"); - } - else - { - mFMP->childDisable("physics_file"); - mFMP->childDisable("physics_browse"); - } - } - // + if (which_mode == file_mode) + { + mFMP->childEnable("physics_file"); + mFMP->childEnable("physics_browse"); + } + else + { + mFMP->childDisable("physics_file"); + mFMP->childDisable("physics_browse"); + } + } + // - LLSpinCtrl* crease = mFMP->getChild("crease_angle"); + LLSpinCtrl* crease = mFMP->getChild("crease_angle"); - if (mRequestedCreaseAngle[mPreviewLOD] == -1.f) - { - mFMP->childSetColor("crease_label", LLColor4::grey); - crease->forceSetValue(75.f); - } - else - { - mFMP->childSetColor("crease_label", LLColor4::white); - crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]); - } + if (mRequestedCreaseAngle[mPreviewLOD] == -1.f) + { + mFMP->childSetColor("crease_label", LLColor4::grey); + crease->forceSetValue(75.f); + } + else + { + mFMP->childSetColor("crease_label", LLColor4::white); + crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]); + } - mModelUpdatedSignal(true); + mModelUpdatedSignal(true); } void LLModelPreview::updateLodControls(S32 lod) { - if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH) - { - std::ostringstream out; - out << "Invalid level of detail: " << lod; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH); - return; - } + if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH) + { + std::ostringstream out; + out << "Invalid level of detail: " << lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH); + return; + } - const char* lod_controls[] = - { - "lod_mode_", - "lod_triangle_limit_", - "lod_error_threshold_" - }; - const U32 num_lod_controls = sizeof(lod_controls) / sizeof(char*); + const char* lod_controls[] = + { + "lod_mode_", + "lod_triangle_limit_", + "lod_error_threshold_" + }; + const U32 num_lod_controls = sizeof(lod_controls) / sizeof(char*); - const char* file_controls[] = - { - "lod_browse_", - "lod_file_", - }; - const U32 num_file_controls = sizeof(file_controls) / sizeof(char*); + const char* file_controls[] = + { + "lod_browse_", + "lod_file_", + }; + const U32 num_file_controls = sizeof(file_controls) / sizeof(char*); - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (!fmp) return; + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (!fmp) return; - LLComboBox* lod_combo = mFMP->findChild("lod_source_" + lod_name[lod]); - if (!lod_combo) return; + LLComboBox* lod_combo = mFMP->findChild("lod_source_" + lod_name[lod]); + if (!lod_combo) return; - S32 lod_mode = lod_combo->getCurrentIndex(); - if (lod_mode == LOD_FROM_FILE) // LoD from file - { - fmp->mLODMode[lod] = 0; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childSetVisible(file_controls[i] + lod_name[lod], true); - } + S32 lod_mode = lod_combo->getCurrentIndex(); + if (lod_mode == LOD_FROM_FILE) // LoD from file + { + fmp->mLODMode[lod] = 0; + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childSetVisible(file_controls[i] + lod_name[lod], true); + } - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); - } - } - else if (lod_mode == USE_LOD_ABOVE) // use LoD above - { - fmp->mLODMode[lod] = 2; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childSetVisible(file_controls[i] + lod_name[lod], false); - } + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); + } + } + else if (lod_mode == USE_LOD_ABOVE) // use LoD above + { + fmp->mLODMode[lod] = 2; + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childSetVisible(file_controls[i] + lod_name[lod], false); + } - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); - } + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); + } - if (lod < LLModel::LOD_HIGH) - { - mModel[lod] = mModel[lod + 1]; - mScene[lod] = mScene[lod + 1]; - mVertexBuffer[lod].clear(); + if (lod < LLModel::LOD_HIGH) + { + mModel[lod] = mModel[lod + 1]; + mScene[lod] = mScene[lod + 1]; + mVertexBuffer[lod].clear(); - // Also update lower LoD - if (lod > LLModel::LOD_IMPOSTOR) - { - updateLodControls(lod - 1); - } - } - } - else // auto generate, the default case for all LoDs except High - { - fmp->mLODMode[lod] = 1; + // Also update lower LoD + if (lod > LLModel::LOD_IMPOSTOR) + { + updateLodControls(lod - 1); + } + } + } + else // auto generate, the default case for all LoDs except High + { + fmp->mLODMode[lod] = 1; - //don't actually regenerate lod when refreshing UI - mLODFrozen = true; + //don't actually regenerate lod when refreshing UI + mLODFrozen = true; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->getChildView(file_controls[i] + lod_name[lod])->setVisible(false); - } + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->getChildView(file_controls[i] + lod_name[lod])->setVisible(false); + } - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->getChildView(lod_controls[i] + lod_name[lod])->setVisible(true); - } + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->getChildView(lod_controls[i] + lod_name[lod])->setVisible(true); + } - LLSpinCtrl* threshold = mFMP->getChild("lod_error_threshold_" + lod_name[lod]); - LLSpinCtrl* limit = mFMP->getChild("lod_triangle_limit_" + lod_name[lod]); + LLSpinCtrl* threshold = mFMP->getChild("lod_error_threshold_" + lod_name[lod]); + LLSpinCtrl* limit = mFMP->getChild("lod_triangle_limit_" + lod_name[lod]); - limit->setMaxValue(mMaxTriangleLimit); - limit->forceSetValue(mRequestedTriangleCount[lod]); + limit->setMaxValue(mMaxTriangleLimit); + limit->forceSetValue(mRequestedTriangleCount[lod]); - threshold->forceSetValue(mRequestedErrorThreshold[lod]); + threshold->forceSetValue(mRequestedErrorThreshold[lod]); - mFMP->getChild("lod_mode_" + lod_name[lod])->selectNthItem(mRequestedLoDMode[lod]); + mFMP->getChild("lod_mode_" + lod_name[lod])->selectNthItem(mRequestedLoDMode[lod]); - if (mRequestedLoDMode[lod] == 0) - { - limit->setVisible(true); - threshold->setVisible(false); + if (mRequestedLoDMode[lod] == 0) + { + limit->setVisible(true); + threshold->setVisible(false); - limit->setMaxValue(mMaxTriangleLimit); - limit->setIncrement(mMaxTriangleLimit / 32); - } - else - { - limit->setVisible(false); - threshold->setVisible(true); - } + limit->setMaxValue(mMaxTriangleLimit); + limit->setIncrement(mMaxTriangleLimit / 32); + } + else + { + limit->setVisible(false); + threshold->setVisible(true); + } - mLODFrozen = false; - } + mLODFrozen = false; + } } void LLModelPreview::setPreviewTarget(F32 distance) { - mCameraDistance = distance; - mCameraZoom = 1.f; - mCameraPitch = 0.f; - mCameraYaw = 0.f; - mCameraOffset.clearVec(); + mCameraDistance = distance; + mCameraZoom = 1.f; + mCameraPitch = 0.f; + mCameraYaw = 0.f; + mCameraOffset.clearVec(); } void LLModelPreview::clearBuffers() { - for (U32 i = 0; i < 6; i++) - { - mVertexBuffer[i].clear(); - } + for (U32 i = 0; i < 6; i++) + { + mVertexBuffer[i].clear(); + } } void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) { - U32 tri_count = 0; - U32 vertex_count = 0; - U32 mesh_count = 0; + U32 tri_count = 0; + U32 vertex_count = 0; + U32 mesh_count = 0; - LLModelLoader::model_list* model = NULL; + LLModelLoader::model_list* model = NULL; - if (lod < 0 || lod > 4) - { - model = &mBaseModel; - lod = 5; - } - else - { - model = &(mModel[lod]); - } + if (lod < 0 || lod > 4) + { + model = &mBaseModel; + lod = 5; + } + else + { + model = &(mModel[lod]); + } - if (!mVertexBuffer[lod].empty()) - { - mVertexBuffer[lod].clear(); - } + if (!mVertexBuffer[lod].empty()) + { + mVertexBuffer[lod].clear(); + } - mVertexBuffer[lod].clear(); + mVertexBuffer[lod].clear(); - LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); + LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); - for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) - { - LLModel* mdl = *iter; - if (!mdl) - { - continue; - } + for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) + { + LLModel* mdl = *iter; + if (!mdl) + { + continue; + } - LLModel* base_mdl = *base_iter; - base_iter++; + LLModel* base_mdl = *base_iter; + base_iter++; - S32 num_faces = mdl->getNumVolumeFaces(); - for (S32 i = 0; i < num_faces; ++i) - { - const LLVolumeFace &vf = mdl->getVolumeFace(i); - U32 num_vertices = vf.mNumVertices; - U32 num_indices = vf.mNumIndices; + S32 num_faces = mdl->getNumVolumeFaces(); + for (S32 i = 0; i < num_faces; ++i) + { + const LLVolumeFace &vf = mdl->getVolumeFace(i); + U32 num_vertices = vf.mNumVertices; + U32 num_indices = vf.mNumIndices; - if (!num_vertices || !num_indices) - { - continue; - } + if (!num_vertices || !num_indices) + { + continue; + } - LLVertexBuffer* vb = NULL; + LLVertexBuffer* vb = NULL; - bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); + bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); - U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - if (skinned) - { - mask |= LLVertexBuffer::MAP_WEIGHT4; - } + if (skinned) + { + mask |= LLVertexBuffer::MAP_WEIGHT4; + } - vb = new LLVertexBuffer(mask, 0); + vb = new LLVertexBuffer(mask, 0); - if (!vb->allocateBuffer(num_vertices, num_indices, TRUE)) - { - // We are likely to crash due this failure, if this happens, find a way to gracefully stop preview - std::ostringstream out; - out << "Failed to allocate Vertex Buffer for model preview "; - out << num_vertices << " vertices and "; - out << num_indices << " indices"; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - } + if (!vb->allocateBuffer(num_vertices, num_indices, TRUE)) + { + // We are likely to crash due this failure, if this happens, find a way to gracefully stop preview + std::ostringstream out; + out << "Failed to allocate Vertex Buffer for model preview "; + out << num_vertices << " vertices and "; + out << num_indices << " indices"; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + } - LLStrider vertex_strider; - LLStrider normal_strider; - LLStrider tc_strider; - LLStrider index_strider; - // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis - //LLStrider weights_strider; - LLStrider weights_strider; + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + LLStrider index_strider; + // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis + //LLStrider weights_strider; + LLStrider weights_strider; - vb->getVertexStrider(vertex_strider); - vb->getIndexStrider(index_strider); + vb->getVertexStrider(vertex_strider); + vb->getIndexStrider(index_strider); - if (skinned) - { - vb->getWeight4Strider(weights_strider); - } + if (skinned) + { + vb->getWeight4Strider(weights_strider); + } - LLVector4a::memcpyNonAliased16((F32*)vertex_strider.get(), (F32*)vf.mPositions, num_vertices * 4 * sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*)vertex_strider.get(), (F32*)vf.mPositions, num_vertices * 4 * sizeof(F32)); - if (vf.mTexCoords) - { - vb->getTexCoord0Strider(tc_strider); - S32 tex_size = (num_vertices * 2 * sizeof(F32) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)tc_strider.get(), (F32*)vf.mTexCoords, tex_size); - } + if (vf.mTexCoords) + { + vb->getTexCoord0Strider(tc_strider); + S32 tex_size = (num_vertices * 2 * sizeof(F32) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)tc_strider.get(), (F32*)vf.mTexCoords, tex_size); + } - if (vf.mNormals) - { - vb->getNormalStrider(normal_strider); - LLVector4a::memcpyNonAliased16((F32*)normal_strider.get(), (F32*)vf.mNormals, num_vertices * 4 * sizeof(F32)); - } + if (vf.mNormals) + { + vb->getNormalStrider(normal_strider); + LLVector4a::memcpyNonAliased16((F32*)normal_strider.get(), (F32*)vf.mNormals, num_vertices * 4 * sizeof(F32)); + } - if (skinned) - { - for (U32 i = 0; i < num_vertices; i++) - { - //find closest weight to vf.mVertices[i].mPosition - LLVector3 pos(vf.mPositions[i].getF32ptr()); + if (skinned) + { + for (U32 i = 0; i < num_vertices; i++) + { + //find closest weight to vf.mVertices[i].mPosition + LLVector3 pos(vf.mPositions[i].getF32ptr()); - const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); - llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this + const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); + llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this - LLVector4 w(0, 0, 0, 0); + LLVector4 w(0, 0, 0, 0); - for (U32 i = 0; i < weight_list.size(); ++i) - { - F32 wght = llclamp(weight_list[i].mWeight, 0.001f, 0.999f); - F32 joint = (F32)weight_list[i].mJointIdx; - w.mV[i] = joint + wght; - llassert(w.mV[i] - (S32)w.mV[i]>0.0f); // because weights are non-zero, and range of wt values - //should not cause floating point precision issues. - } + for (U32 i = 0; i < weight_list.size(); ++i) + { + F32 wght = llclamp(weight_list[i].mWeight, 0.001f, 0.999f); + F32 joint = (F32)weight_list[i].mJointIdx; + w.mV[i] = joint + wght; + llassert(w.mV[i] - (S32)w.mV[i]>0.0f); // because weights are non-zero, and range of wt values + //should not cause floating point precision issues. + } - // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis - //*(weights_strider++) = w; - (*(weights_strider++)).loadua(w.mV); - } - } + // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis + //*(weights_strider++) = w; + (*(weights_strider++)).loadua(w.mV); + } + } - // build indices - for (U32 i = 0; i < num_indices; i++) - { - *(index_strider++) = vf.mIndices[i]; - } + // build indices + for (U32 i = 0; i < num_indices; i++) + { + *(index_strider++) = vf.mIndices[i]; + } - mVertexBuffer[lod][mdl].push_back(vb); + mVertexBuffer[lod][mdl].push_back(vb); - vertex_count += num_vertices; - tri_count += num_indices / 3; - ++mesh_count; + vertex_count += num_vertices; + tri_count += num_indices / 3; + ++mesh_count; - } - } + } + } } void LLModelPreview::update() { - if (mGenLOD) - { - bool subscribe_for_generation = mLodsQuery.empty(); - mGenLOD = false; - mDirty = true; - mLodsQuery.clear(); + if (mGenLOD) + { + bool subscribe_for_generation = mLodsQuery.empty(); + mGenLOD = false; + mDirty = true; + mLodsQuery.clear(); - for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod) - { - // adding all lods into query for generation - mLodsQuery.push_back(lod); - } + for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod) + { + // adding all lods into query for generation + mLodsQuery.push_back(lod); + } - if (subscribe_for_generation) - { - doOnIdleRepeating(lodQueryCallback); - } - } + if (subscribe_for_generation) + { + doOnIdleRepeating(lodQueryCallback); + } + } - if (mDirty && mLodsQuery.empty()) - { - mDirty = false; - mResourceCost = calcResourceCost(); - refresh(); - updateStatusMessages(); - } + if (mDirty && mLodsQuery.empty()) + { + mDirty = false; + mResourceCost = calcResourceCost(); + refresh(); + updateStatusMessages(); + } } //----------------------------------------------------------------------------- @@ -2804,189 +2922,189 @@ void LLModelPreview::update() //----------------------------------------------------------------------------- void LLModelPreview::createPreviewAvatar(void) { - mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR); - if (mPreviewAvatar) - { - mPreviewAvatar->createDrawable(&gPipeline); - mPreviewAvatar->mSpecialRenderMode = 1; - mPreviewAvatar->startMotion(ANIM_AGENT_STAND); - mPreviewAvatar->hideSkirt(); - } - else - { - // improve uploader error reporting - // LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL; - std::ostringstream out; - out << "Failed to create preview avatar for upload model window"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - } + mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR); + if (mPreviewAvatar) + { + mPreviewAvatar->createDrawable(&gPipeline); + mPreviewAvatar->mSpecialRenderMode = 1; + mPreviewAvatar->startMotion(ANIM_AGENT_STAND); + mPreviewAvatar->hideSkirt(); + } + else + { + // improve uploader error reporting + // LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL; + std::ostringstream out; + out << "Failed to create preview avatar for upload model window"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + } } //static U32 LLModelPreview::countRootModels(LLModelLoader::model_list models) { - U32 root_models = 0; - model_list::iterator model_iter = models.begin(); - while (model_iter != models.end()) - { - LLModel* mdl = *model_iter; - if (mdl && mdl->mSubmodelID == 0) - { - root_models++; - } - model_iter++; - } - return root_models; + U32 root_models = 0; + model_list::iterator model_iter = models.begin(); + while (model_iter != models.end()) + { + LLModel* mdl = *model_iter; + if (mdl && mdl->mSubmodelID == 0) + { + root_models++; + } + model_iter++; + } + return root_models; } void LLModelPreview::loadedCallback( - LLModelLoader::scene& scene, - LLModelLoader::model_list& model_list, - S32 lod, - void* opaque) + LLModelLoader::scene& scene, + LLModelLoader::model_list& model_list, + S32 lod, + void* opaque) { - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) - { - // Load loader's warnings into floater's log tab - const LLSD out = pPreview->mModelLoader->logOut(); - LLSD::array_const_iterator iter_out = out.beginArray(); - LLSD::array_const_iterator end_out = out.endArray(); - for (; iter_out != end_out; ++iter_out) - { - if (iter_out->has("Message")) - { - LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod); - } - } - pPreview->mModelLoader->clearLog(); - pPreview->loadModelCallback(lod); // removes mModelLoader in some cases - if (pPreview->mLookUpLodFiles && (lod != LLModel::LOD_HIGH)) - { - pPreview->lookupLODModelFiles(lod); - } - } + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) + { + // Load loader's warnings into floater's log tab + const LLSD out = pPreview->mModelLoader->logOut(); + LLSD::array_const_iterator iter_out = out.beginArray(); + LLSD::array_const_iterator end_out = out.endArray(); + for (; iter_out != end_out; ++iter_out) + { + if (iter_out->has("Message")) + { + LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod); + } + } + pPreview->mModelLoader->clearLog(); + pPreview->loadModelCallback(lod); // removes mModelLoader in some cases + if (pPreview->mLookUpLodFiles && (lod != LLModel::LOD_HIGH)) + { + pPreview->lookupLODModelFiles(lod); + } + } } void LLModelPreview::lookupLODModelFiles(S32 lod) { - if (lod == LLModel::LOD_PHYSICS) - { - mLookUpLodFiles = false; - return; - } - S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS; + if (lod == LLModel::LOD_PHYSICS) + { + mLookUpLodFiles = false; + return; + } + S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS; - std::string lod_filename = mLODFile[LLModel::LOD_HIGH]; - std::string ext = ".dae"; - std::string::size_type i = lod_filename.rfind(ext); - if (i != std::string::npos) - { - lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext); - } - if (gDirUtilp->fileExists(lod_filename)) - { - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp) - { - fmp->setCtrlLoadFromFile(next_lod); - } - loadModel(lod_filename, next_lod); - } - else - { - lookupLODModelFiles(next_lod); - } + std::string lod_filename = mLODFile[LLModel::LOD_HIGH]; + std::string ext = ".dae"; + std::string::size_type i = lod_filename.rfind(ext); + if (i != std::string::npos) + { + lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext); + } + if (gDirUtilp->fileExists(lod_filename)) + { + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + fmp->setCtrlLoadFromFile(next_lod); + } + loadModel(lod_filename, next_lod); + } + else + { + lookupLODModelFiles(next_lod); + } } void LLModelPreview::stateChangedCallback(U32 state, void* opaque) { - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview) - { - pPreview->setLoadState(state); - } + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview) + { + pPreview->setLoadState(state); + } } LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque) { - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview) - { + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview) + { // Query by JointKey rather than just a string, the key can be a U32 index for faster lookup // return pPreview->getPreviewAvatar()->getJoint(str); - return pPreview->getPreviewAvatar()->getJoint( JointKey::construct( str ) ); + return pPreview->getPreviewAvatar()->getJoint( JointKey::construct( str ) ); // - } - return NULL; + } + return NULL; } U32 LLModelPreview::loadTextures(LLImportMaterial& material, void* opaque) { - (void)opaque; + (void)opaque; - if (material.mDiffuseMapFilename.size()) - { - material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >; - LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData)); + if (material.mDiffuseMapFilename.size()) + { + material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >; + LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData)); - tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + LLURI::unescape(material.mDiffuseMapFilename), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); - tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE); - tex->forceToSaveRawImage(0, F32_MAX); - material.setDiffuseMap(tex->getID()); // record tex ID - return 1; - } + tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + LLURI::unescape(material.mDiffuseMapFilename), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); + tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE); + tex->forceToSaveRawImage(0, F32_MAX); + material.setDiffuseMap(tex->getID()); // record tex ID + return 1; + } - material.mOpaqueData = NULL; - return 0; + material.mOpaqueData = NULL; + return 0; } void LLModelPreview::addEmptyFace(LLModel* pTarget) { - U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - LLPointer buff = new LLVertexBuffer(type_mask, 0); + LLPointer buff = new LLVertexBuffer(type_mask, 0); - buff->allocateBuffer(1, 3, true); - memset((U8*)buff->getMappedData(), 0, buff->getSize()); - // Fix when running with opengl core profile - //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); - { - LLStrider< U16 > index_strider; - buff->getIndexStrider( index_strider ); + buff->allocateBuffer(1, 3, true); + memset((U8*)buff->getMappedData(), 0, buff->getSize()); + // Fix when running with opengl core profile + //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); + { + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); - memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); - } - // + memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); + } + // - buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); + buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); - LLStrider pos; - LLStrider norm; - LLStrider tc; - LLStrider index; + LLStrider pos; + LLStrider norm; + LLStrider tc; + LLStrider index; - buff->getVertexStrider(pos); + buff->getVertexStrider(pos); - if (type_mask & LLVertexBuffer::MAP_NORMAL) - { - buff->getNormalStrider(norm); - } - if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) - { - buff->getTexCoord0Strider(tc); - } + if (type_mask & LLVertexBuffer::MAP_NORMAL) + { + buff->getNormalStrider(norm); + } + if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) + { + buff->getTexCoord0Strider(tc); + } - buff->getIndexStrider(index); + buff->getIndexStrider(index); - //resize face array - int faceCnt = pTarget->getNumVolumeFaces(); - pTarget->setNumVolumeFaces(faceCnt + 1); - pTarget->setVolumeFaceData(faceCnt + 1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + //resize face array + int faceCnt = pTarget->getNumVolumeFaces(); + pTarget->setNumVolumeFaces(faceCnt + 1); + pTarget->setVolumeFaceData(faceCnt + 1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); } @@ -2997,823 +3115,823 @@ void LLModelPreview::addEmptyFace(LLModel* pTarget) // Note: Render happens each frame with skinned avatars BOOL LLModelPreview::render() { - assert_main_thread(); - - LLMutexLock lock(this); - mNeedsUpdate = FALSE; - - bool use_shaders = LLGLSLShader::sNoFixedFunction; - - bool edges = mViewOption["show_edges"]; - bool joint_overrides = mViewOption["show_joint_overrides"]; - bool joint_positions = mViewOption["show_joint_positions"]; - bool skin_weight = mViewOption["show_skin_weight"]; - bool textures = mViewOption["show_textures"]; - bool physics = mViewOption["show_physics"]; - bool uv_guide = mViewOption["show_uv_guide"]; // Add UV guide overlay in mesh preview - - // restore things lost by the lab during importer work - // Extra configurability, to be exposed later as controls? - static LLCachedControl canvas_col(gSavedSettings, "MeshPreviewCanvasColor"); - static LLCachedControl edge_col(gSavedSettings, "MeshPreviewEdgeColor"); - static LLCachedControl base_col(gSavedSettings, "MeshPreviewBaseColor"); - static LLCachedControl brightness(gSavedSettings, "MeshPreviewBrightnessColor"); - static LLCachedControl edge_width(gSavedSettings, "MeshPreviewEdgeWidth"); - static LLCachedControl phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor"); - static LLCachedControl phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor"); - static LLCachedControl phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth"); - static LLCachedControl deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor"); - static LLCachedControl deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor"); - static LLCachedControl deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth"); - static LLCachedControl deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize"); - static LLCachedControl auto_enable_weight_upload(gSavedSettings, "FSMeshUploadAutoEnableWeights"); - static LLCachedControl auto_enable_show_weights(gSavedSettings, "FSMeshUploadAutoShowWeightsWhenEnabled"); - // - - S32 width = getWidth(); - S32 height = getHeight(); - - LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test - LLGLDisable no_blend(GL_BLEND); - LLGLEnable cull(GL_CULL_FACE); - LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color - LLGLDisable fog(GL_FOG); - - { - if (use_shaders) - { - gUIProgram.bind(); - } - //clear background to grey - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.ortho(0.0f, width, 0.0f, height, -1.0f, 1.0f); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.pushMatrix(); - gGL.loadIdentity(); - - gGL.color4fv(canvas_col().mV); // restore changes removed by the lab - gl_rect_2d_simple(width, height); - - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.popMatrix(); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); - if (use_shaders) - { - gUIProgram.unbind(); - } - } - - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - - bool has_skin_weights = false; - bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); - bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); - - if (upload_joints != mLastJointUpdate) - { - mLastJointUpdate = upload_joints; - if (fmp) - { - fmp->clearAvatarTab(); - } - } - - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - model->mPelvisOffset = mPelvisZOffset; - if (!model->mSkinWeights.empty()) - { - has_skin_weights = true; - } - } - } - - if (has_skin_weights && lodsReady()) - { //model has skin weights, enable view options for skin weights and joint positions - U32 flags = getLegacyRigFlags(); - if (fmp) - { - if (flags == LEGACY_RIG_OK) - { - if (mFirstSkinUpdate) - { - // auto enable weight upload if weights are present - // (note: all these UI updates need to be somewhere that is not render) - // BUG-229632 auto enable weights slows manual workflow - // mViewOption["show_skin_weight"] = true; - // skin_weight = true; - // fmp->childSetValue("upload_skin", true); - LL_DEBUGS("MeshUpload") << "FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; - LL_DEBUGS("MeshUpload") << "FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; - upload_skin=auto_enable_weight_upload(); - fmp->childSetValue("upload_skin", upload_skin); - - skin_weight = upload_skin && auto_enable_show_weights(); - mViewOption["show_skin_weight"] = skin_weight; - mFMP->childSetValue("show_skin_weight", skin_weight); - fmp->setViewOptionEnabled("show_skin_weight", upload_skin); - fmp->setViewOptionEnabled("show_joint_overrides", upload_skin); - fmp->setViewOptionEnabled("show_joint_positions", upload_skin); - // - mFirstSkinUpdate = false; - } - // BUG-229632 auto enable weights slows manual workflow - // fmp->enableViewOption("show_skin_weight"); - // fmp->setViewOptionEnabled("show_joint_overrides", skin_weight); - // fmp->setViewOptionEnabled("show_joint_posi - else - { - LL_DEBUGS("MeshUpload") << "NOT FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; - LL_DEBUGS("MeshUpload") << "NOT FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; - fmp->setViewOptionEnabled("show_skin_weight", upload_skin); - fmp->setViewOptionEnabled("show_joint_overrides", upload_skin); - fmp->setViewOptionEnabled("show_joint_positions", upload_skin); - } - // - mFMP->childEnable("upload_skin"); - // mFMP->childSetValue("show_skin_weight", skin_weight); // BUG-229632 - - } - 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); - } - // defensive code to wanr for incorrect flags - no behavioural change - else - { - LL_WARNS("MeshUpload") << "Unexpected flags combination on weights check" << LL_ENDL; - } - // - } - } - else - { - mFMP->childDisable("upload_skin"); - if (fmp) - { - mViewOption["show_skin_weight"] = false; - fmp->disableViewOption("show_skin_weight"); - fmp->disableViewOption("show_joint_overrides"); - fmp->disableViewOption("show_joint_positions"); - - skin_weight = false; - mFMP->childSetValue("show_skin_weight", false); - fmp->setViewOptionEnabled("show_skin_weight", skin_weight); - } - } - - if (upload_skin && !has_skin_weights) - { //can't upload skin weights if model has no skin weights - mFMP->childSetValue("upload_skin", false); - upload_skin = false; - } - - if (!upload_skin && upload_joints) - { //can't upload joints if not uploading skin weights - mFMP->childSetValue("upload_joints", false); - upload_joints = false; - } - - if (fmp) - { - if (upload_skin) - { - // will populate list of joints - fmp->updateAvatarTab(upload_joints); - } - else - { - fmp->clearAvatarTab(); - } - } - - if (upload_skin && upload_joints) - { - mFMP->childEnable("lock_scale_if_joint_position"); - } - else - { - mFMP->childDisable("lock_scale_if_joint_position"); - mFMP->childSetValue("lock_scale_if_joint_position", false); - } - - //Only enable joint offsets if it passed the earlier critiquing - if (isRigValidForJointPositionUpload()) - { - mFMP->childSetEnabled("upload_joints", upload_skin); - } - - F32 explode = mFMP->childGetValue("physics_explode").asReal(); - - LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview - - LLRect preview_rect; - - preview_rect = mFMP->getChildView("preview_panel")->getRect(); - - F32 aspect = (F32)preview_rect.getWidth() / preview_rect.getHeight(); - - LLViewerCamera::getInstance()->setAspect(aspect); - - LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); - - LLVector3 offset = mCameraOffset; - LLVector3 target_pos = mPreviewTarget + offset; - - F32 z_near = 0.001f; - F32 z_far = mCameraDistance*10.0f + mPreviewScale.magVec() + mCameraOffset.magVec(); - - if (skin_weight) - { - target_pos = getPreviewAvatar()->getPositionAgent() + offset; - z_near = 0.01f; - z_far = 1024.f; - - //render avatar previews every frame - refresh(); - } - - if (use_shaders) - { - gObjectPreviewProgram.bind(); - } - - gGL.loadIdentity(); - gPipeline.enableLightsPreview(); - - LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * - LLQuaternion(mCameraYaw, LLVector3::z_axis); - - LLQuaternion av_rot = camera_rot; - F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest - - - z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f); - - LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); - - stop_glerror(); - - gGL.pushMatrix(); - gGL.color4fv(edge_col().mV); // restore changes removed by the lab - - const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - LLGLEnable normalize(GL_NORMALIZE); - - if (!mBaseModel.empty() && mVertexBuffer[5].empty()) - { - genBuffers(-1, skin_weight); - //genBuffers(3); - //genLODs(); - } - - if (!mModel[mPreviewLOD].empty()) - { - mFMP->childEnable("reset_btn"); - - bool regen = mVertexBuffer[mPreviewLOD].empty(); - if (!regen) - { - const std::vector >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second; - if (!vb_vec.empty()) - { - const LLVertexBuffer* buff = vb_vec[0]; - regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; - } - else - { - LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL; - regen = TRUE; - } - } - - if (regen) - { - genBuffers(mPreviewLOD, skin_weight); - } - - if (!skin_weight) - { - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[mPreviewLOD]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*)mat.mMatrix); - - - U32 num_models = mVertexBuffer[mPreviewLOD][model].size(); - for (U32 i = 0; i < num_models; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - - if (textures) - { - int materialCnt = instance.mModel->mMaterialList.size(); - if (i < materialCnt) - { - const std::string& binding = instance.mModel->mMaterialList[i]; - const LLImportMaterial& material = instance.mMaterial[binding]; - - gGL.diffuseColor4fv(material.mDiffuseColor.mV); - - // Find the tex for this material, bind it, and add it to our set - // - LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); - if (tex) - { - mTextureSet.insert(tex); - } - } - } - // improved mesh uploader - else if (uv_guide) - { - if(mUVGuideTexture) - { - if (mUVGuideTexture->getDiscardLevel() > -1) - { - gGL.getTexUnit(0)->bind(mUVGuideTexture, true); - } - } - gGL.diffuseColor4fv(base_col().mV); // restore changes removed by the lab - - } - // - else - { - gGL.diffuseColor4fv(base_col().mV); // restore changes removed by the lab - } - - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.diffuseColor4fv(edge_col().mV); // restore changes removed by the lab - if (edges) - { - gGL.setLineWidth(edge_width()); // restore changes removed by the lab - 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); - gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt - } - } - gGL.popMatrix(); - } - - if (physics) - { - glClear(GL_DEPTH_BUFFER_BIT); - - for (U32 pass = 0; pass < 2; pass++) - { - if (pass == 0) - { //depth only pass - gGL.setColorMask(false, false); - } - else - { - gGL.setColorMask(true, true); - } - - //enable alpha blending on second pass but not first pass - LLGLState blend(GL_BLEND, pass); - - gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA); - - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*)mat.mMatrix); - - - bool render_mesh = true; - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); - - LLModel::Decomposition& physics = model->mPhysics; - - if (!physics.mHull.empty()) - { - render_mesh = false; - - if (physics.mMesh.empty()) - { //build vertex buffer for physics mesh - gMeshRepo.buildPhysicsMesh(physics); - } - - if (!physics.mMesh.empty()) - { //render hull instead of mesh - for (U32 i = 0; i < physics.mMesh.size(); ++i) - { - if (explode > 0.f) - { - gGL.pushMatrix(); - - LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; - offset *= explode; - - gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); - } - - static std::vector hull_colors; - - if (i + 1 >= hull_colors.size()) - { - hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128)); - } - - gGL.diffuseColor4ubv(hull_colors[i].mV); - LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); - - if (explode > 0.f) - { - gGL.popMatrix(); - } - } - } - } - } - - if (render_mesh) - { - if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) - { - genBuffers(LLModel::LOD_PHYSICS, false); - } - - U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); - if (pass > 0){ - for (U32 i = 0; i < num_models; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.diffuseColor4fv(phys_fill_col().mV); // restore changes removed by the lab - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); - // restore changes removed by the lab - // gGL.diffuseColor4fv(PREVIEW_PSYH_EDGE_COL.mV); - // gGL.setLineWidth(PREVIEW_PSYH_EDGE_WIDTH); // Line width OGL core profile fix by Rye Mutt - gGL.diffuseColor4fv(phys_edge_col().mV); - gGL.setLineWidth(phys_edge_width()); - // - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt - } - } - } - gGL.popMatrix(); - } - - // only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks] - if (mHasDegenerate) - { - // restore older functionality lost in lab importer - // gGL.setLineWidth(PREVIEW_DEG_EDGE_WIDTH); // Line width OGL core profile fix by Rye Mutt - // glPointSize(PREVIEW_DEG_POINT_SIZE); - // gPipeline.enableLightsFullbright(); - gGL.setLineWidth(deg_edge_width()); - glPointSize(deg_point_size()); - // gPipeline.enableLightsFullbright(); // This may need to be restored when I fined the cause of the black rendering - // - //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); // restore proper functionality - const LLVector4a scale(0.5f); - - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*)mat.mMatrix); - - - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); - - LLModel::Decomposition& physics = model->mPhysics; - - if (physics.mHull.empty()) - { - if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) - { - genBuffers(LLModel::LOD_PHYSICS, false); - } - - U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); - for (U32 v = 0; v < num_models; ++v) - { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - - LLStrider pos_strider; - buffer->getVertexStrider(pos_strider, 0); - LLVector4a* pos = (LLVector4a*)pos_strider.get(); - - LLStrider idx; - buffer->getIndexStrider(idx, 0); - - for (U32 i = 0; i < buffer->getNumIndices(); i += 3) - { - LLVector4a v1; v1.setMul(pos[*idx++], scale); - LLVector4a v2; v2.setMul(pos[*idx++], scale); - LLVector4a v3; v3.setMul(pos[*idx++], scale); - - if (ll_is_degenerate(v1, v2, v3)) - { - // restore (configurable) coloured overlay - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - gGL.diffuseColor4fv(deg_fill_col().mV); - buffer->draw(LLRender::TRIANGLES, 3, i); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - gGL.diffuseColor3fv(deg_edge_col().mV); - gGL.color3fv(deg_edge_col().mV); - // - buffer->draw(LLRender::LINE_LOOP, 3, i); - buffer->draw(LLRender::POINTS, 3, i); - } - } - } - } - } - - gGL.popMatrix(); - } - gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt - glPointSize(1.f); - gPipeline.enableLightsPreview(); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - } - } - } - else - { - target_pos = getPreviewAvatar()->getPositionAgent(); - getPreviewAvatar()->clearAttachmentOverrides(); // removes pelvis fixup - LLUUID fake_mesh_id; - fake_mesh_id.generate(); - getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); - bool pelvis_recalc = false; - - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest - - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - - if (!model->mSkinWeights.empty()) - { - const LLMeshSkinInfo *skin = &model->mSkinInfo; - LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary - U32 joint_count = LLSkinningUtil::getMeshJointCount(skin); - U32 bind_count = skin->mAlternateBindMatrix.size(); - - if (joint_overrides - && bind_count > 0 - && joint_count == bind_count) - { - // mesh_id is used to determine which mesh gets to - // set the joint offset, in the event of a conflict. Since - // we don't know the mesh id yet, we can't guarantee that - // joint offsets will be applied with the same priority as - // in the uploaded model. If the file contains multiple - // meshes with conflicting joint offsets, preview may be - // incorrect. - LLUUID fake_mesh_id; - fake_mesh_id.generate(); - for (U32 j = 0; j < joint_count; ++j) - { - LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]); - if (joint) - { - const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation(); - if (joint->aboveJointPosThreshold(jointPos)) - { - bool override_changed; - joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed); - - if (override_changed) - { - //If joint is a pelvis then handle old/new pelvis to foot values - if (joint->getName() == "mPelvis")// or skin->mJointNames[j] - { - pelvis_recalc = true; - } - } - if (skin->mLockScaleIfJointPosition) - { - // Note that unlike positions, there's no threshold check here, - // just a lock at the default value. - joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model"); - } - } - } - } - } - - for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - const LLVolumeFace& face = model->getVolumeFace(i); - - LLStrider position; - buffer->getVertexStrider(position); - - // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis - //LLStrider weight; - LLStrider weight; - buffer->getWeight4Strider(weight); - - //quick 'n dirty software vertex skinning - - //build matrix palette - - LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT]; - // use Mat4a part of the caching changes, no point in using the cache itself in the preview though. - //LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, joint_count, - // skin, getPreviewAvatar()); - LLSkinningUtil::initSkinningMatrixPalette(mat, joint_count, - skin, getPreviewAvatar()); - // - - LLMatrix4a bind_shape_matrix; - bind_shape_matrix.loadu(skin->mBindShapeMatrix); - U32 max_joints = LLSkinningUtil::getMaxJointCount(); - for (U32 j = 0; j < buffer->getNumVerts(); ++j) - { - LLMatrix4a final_mat; - // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis - //F32 *wptr = weight[j].mV; - F32 *wptr = weight[j].getF32ptr(); - // - LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints); - - //VECTORIZE THIS - LLVector4a& v = face.mPositions[j]; - - LLVector4a t; - LLVector4a dst; - bind_shape_matrix.affineTransform(v, t); - final_mat.affineTransform(t, dst); - - position[j][0] = dst[0]; - position[j][1] = dst[1]; - position[j][2] = dst[2]; - } - - // FIRE-13465 Make sure there's a material set before dereferencing it - if( instance.mModel->mMaterialList.size() > i && - instance.mMaterial.end() != instance.mMaterial.find( instance.mModel->mMaterialList[ i ] ) ) - { - // - llassert(model->mMaterialList.size() > i); - const std::string& binding = instance.mModel->mMaterialList[i]; - const LLImportMaterial& material = instance.mMaterial[binding]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - gGL.diffuseColor4fv(material.mDiffuseColor.mV); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - // Find the tex for this material, bind it, and add it to our set - // - LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); - if (tex) - { - mTextureSet.insert(tex); - } - } else // FIRE-13465 Make sure there's a material set before dereferencing it, if none, set buffer type and unbind texture. - { - buffer->setBuffer(type_mask & buffer->getTypeMask()); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } // - - buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - - if (edges) - { - // restore behaviour removed by lab - // gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); - // gGL.setLineWidth(PREVIEW_EDGE_WIDTH); - gGL.diffuseColor4fv(edge_col().mV); - gGL.setLineWidth(edge_width()); - // - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt - } - } - } - } - } - - if (joint_positions) - { - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - if (shader) - { - gDebugProgram.bind(); - } - getPreviewAvatar()->renderCollisionVolumes(); - if (fmp->mTabContainer->getCurrentPanelIndex() == fmp->mAvatarTabIndex) - { - getPreviewAvatar()->renderBones(fmp->mSelectedJointName); - } - else - { - getPreviewAvatar()->renderBones(); - } - if (shader) - { - shader->bind(); - } - } - - if (pelvis_recalc) - { - // size/scale recalculation - getPreviewAvatar()->postPelvisSetRecalc(); - } - } - } - - if (use_shaders) - { - gObjectPreviewProgram.unbind(); - } - - gGL.popMatrix(); - - return TRUE; + assert_main_thread(); + + LLMutexLock lock(this); + mNeedsUpdate = FALSE; + + bool use_shaders = LLGLSLShader::sNoFixedFunction; + + bool edges = mViewOption["show_edges"]; + bool joint_overrides = mViewOption["show_joint_overrides"]; + bool joint_positions = mViewOption["show_joint_positions"]; + bool skin_weight = mViewOption["show_skin_weight"]; + bool textures = mViewOption["show_textures"]; + bool physics = mViewOption["show_physics"]; + bool uv_guide = mViewOption["show_uv_guide"]; // Add UV guide overlay in mesh preview + + // restore things lost by the lab during importer work + // Extra configurability, to be exposed later as controls? + static LLCachedControl canvas_col(gSavedSettings, "MeshPreviewCanvasColor"); + static LLCachedControl edge_col(gSavedSettings, "MeshPreviewEdgeColor"); + static LLCachedControl base_col(gSavedSettings, "MeshPreviewBaseColor"); + static LLCachedControl brightness(gSavedSettings, "MeshPreviewBrightnessColor"); + static LLCachedControl edge_width(gSavedSettings, "MeshPreviewEdgeWidth"); + static LLCachedControl phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor"); + static LLCachedControl phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor"); + static LLCachedControl phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth"); + static LLCachedControl deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor"); + static LLCachedControl deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor"); + static LLCachedControl deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth"); + static LLCachedControl deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize"); + static LLCachedControl auto_enable_weight_upload(gSavedSettings, "FSMeshUploadAutoEnableWeights"); + static LLCachedControl auto_enable_show_weights(gSavedSettings, "FSMeshUploadAutoShowWeightsWhenEnabled"); + // + + S32 width = getWidth(); + S32 height = getHeight(); + + LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test + LLGLDisable no_blend(GL_BLEND); + LLGLEnable cull(GL_CULL_FACE); + LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color + LLGLDisable fog(GL_FOG); + + { + if (use_shaders) + { + gUIProgram.bind(); + } + //clear background to grey + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(0.0f, width, 0.0f, height, -1.0f, 1.0f); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.color4fv(canvas_col().mV); // restore changes removed by the lab + gl_rect_2d_simple(width, height); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + if (use_shaders) + { + gUIProgram.unbind(); + } + } + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + + bool has_skin_weights = false; + bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); + bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); + + if (upload_joints != mLastJointUpdate) + { + mLastJointUpdate = upload_joints; + if (fmp) + { + fmp->clearAvatarTab(); + } + } + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + model->mPelvisOffset = mPelvisZOffset; + if (!model->mSkinWeights.empty()) + { + has_skin_weights = true; + } + } + } + + if (has_skin_weights && lodsReady()) + { //model has skin weights, enable view options for skin weights and joint positions + U32 flags = getLegacyRigFlags(); + if (fmp) + { + if (flags == LEGACY_RIG_OK) + { + if (mFirstSkinUpdate) + { + // auto enable weight upload if weights are present + // (note: all these UI updates need to be somewhere that is not render) + // BUG-229632 auto enable weights slows manual workflow + // mViewOption["show_skin_weight"] = true; + // skin_weight = true; + // fmp->childSetValue("upload_skin", true); + LL_DEBUGS("MeshUpload") << "FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; + LL_DEBUGS("MeshUpload") << "FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; + upload_skin=auto_enable_weight_upload(); + fmp->childSetValue("upload_skin", upload_skin); + + skin_weight = upload_skin && auto_enable_show_weights(); + mViewOption["show_skin_weight"] = skin_weight; + mFMP->childSetValue("show_skin_weight", skin_weight); + fmp->setViewOptionEnabled("show_skin_weight", upload_skin); + fmp->setViewOptionEnabled("show_joint_overrides", upload_skin); + fmp->setViewOptionEnabled("show_joint_positions", upload_skin); + // + mFirstSkinUpdate = false; + } + // BUG-229632 auto enable weights slows manual workflow + // fmp->enableViewOption("show_skin_weight"); + // fmp->setViewOptionEnabled("show_joint_overrides", skin_weight); + // fmp->setViewOptionEnabled("show_joint_posi + else + { + LL_DEBUGS("MeshUpload") << "NOT FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; + LL_DEBUGS("MeshUpload") << "NOT FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; + fmp->setViewOptionEnabled("show_skin_weight", upload_skin); + fmp->setViewOptionEnabled("show_joint_overrides", upload_skin); + fmp->setViewOptionEnabled("show_joint_positions", upload_skin); + } + // + mFMP->childEnable("upload_skin"); + // mFMP->childSetValue("show_skin_weight", skin_weight); // BUG-229632 + + } + 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); + } + // defensive code to wanr for incorrect flags - no behavioural change + else + { + LL_WARNS("MeshUpload") << "Unexpected flags combination on weights check" << LL_ENDL; + } + // + } + } + else + { + mFMP->childDisable("upload_skin"); + if (fmp) + { + mViewOption["show_skin_weight"] = false; + fmp->disableViewOption("show_skin_weight"); + fmp->disableViewOption("show_joint_overrides"); + fmp->disableViewOption("show_joint_positions"); + + skin_weight = false; + mFMP->childSetValue("show_skin_weight", false); + fmp->setViewOptionEnabled("show_skin_weight", skin_weight); + } + } + + if (upload_skin && !has_skin_weights) + { //can't upload skin weights if model has no skin weights + mFMP->childSetValue("upload_skin", false); + upload_skin = false; + } + + if (!upload_skin && upload_joints) + { //can't upload joints if not uploading skin weights + mFMP->childSetValue("upload_joints", false); + upload_joints = false; + } + + if (fmp) + { + if (upload_skin) + { + // will populate list of joints + fmp->updateAvatarTab(upload_joints); + } + else + { + fmp->clearAvatarTab(); + } + } + + if (upload_skin && upload_joints) + { + mFMP->childEnable("lock_scale_if_joint_position"); + } + else + { + mFMP->childDisable("lock_scale_if_joint_position"); + mFMP->childSetValue("lock_scale_if_joint_position", false); + } + + //Only enable joint offsets if it passed the earlier critiquing + if (isRigValidForJointPositionUpload()) + { + mFMP->childSetEnabled("upload_joints", upload_skin); + } + + F32 explode = mFMP->childGetValue("physics_explode").asReal(); + + LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview + + LLRect preview_rect; + + preview_rect = mFMP->getChildView("preview_panel")->getRect(); + + F32 aspect = (F32)preview_rect.getWidth() / preview_rect.getHeight(); + + LLViewerCamera::getInstance()->setAspect(aspect); + + LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); + + LLVector3 offset = mCameraOffset; + LLVector3 target_pos = mPreviewTarget + offset; + + F32 z_near = 0.001f; + F32 z_far = mCameraDistance*10.0f + mPreviewScale.magVec() + mCameraOffset.magVec(); + + if (skin_weight) + { + target_pos = getPreviewAvatar()->getPositionAgent() + offset; + z_near = 0.01f; + z_far = 1024.f; + + //render avatar previews every frame + refresh(); + } + + if (use_shaders) + { + gObjectPreviewProgram.bind(); + } + + gGL.loadIdentity(); + gPipeline.enableLightsPreview(); + + LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * + LLQuaternion(mCameraYaw, LLVector3::z_axis); + + LLQuaternion av_rot = camera_rot; + F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + + z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f); + + LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); + + stop_glerror(); + + gGL.pushMatrix(); + gGL.color4fv(edge_col().mV); // restore changes removed by the lab + + const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + LLGLEnable normalize(GL_NORMALIZE); + + if (!mBaseModel.empty() && mVertexBuffer[5].empty()) + { + genBuffers(-1, skin_weight); + //genBuffers(3); + //genLODs(); + } + + if (!mModel[mPreviewLOD].empty()) + { + mFMP->childEnable("reset_btn"); + + bool regen = mVertexBuffer[mPreviewLOD].empty(); + if (!regen) + { + const std::vector >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second; + if (!vb_vec.empty()) + { + const LLVertexBuffer* buff = vb_vec[0]; + regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; + } + else + { + LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL; + regen = TRUE; + } + } + + if (regen) + { + genBuffers(mPreviewLOD, skin_weight); + } + + if (!skin_weight) + { + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[mPreviewLOD]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + U32 num_models = mVertexBuffer[mPreviewLOD][model].size(); + for (U32 i = 0; i < num_models; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + + if (textures) + { + int materialCnt = instance.mModel->mMaterialList.size(); + if (i < materialCnt) + { + const std::string& binding = instance.mModel->mMaterialList[i]; + const LLImportMaterial& material = instance.mMaterial[binding]; + + gGL.diffuseColor4fv(material.mDiffuseColor.mV); + + // Find the tex for this material, bind it, and add it to our set + // + LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); + if (tex) + { + mTextureSet.insert(tex); + } + } + } + // improved mesh uploader + else if (uv_guide) + { + if(mUVGuideTexture) + { + if (mUVGuideTexture->getDiscardLevel() > -1) + { + gGL.getTexUnit(0)->bind(mUVGuideTexture, true); + } + } + gGL.diffuseColor4fv(base_col().mV); // restore changes removed by the lab + + } + // + else + { + gGL.diffuseColor4fv(base_col().mV); // restore changes removed by the lab + } + + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.diffuseColor4fv(edge_col().mV); // restore changes removed by the lab + if (edges) + { + gGL.setLineWidth(edge_width()); // restore changes removed by the lab + 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); + gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt + } + } + gGL.popMatrix(); + } + + if (physics) + { + glClear(GL_DEPTH_BUFFER_BIT); + + for (U32 pass = 0; pass < 2; pass++) + { + if (pass == 0) + { //depth only pass + gGL.setColorMask(false, false); + } + else + { + gGL.setColorMask(true, true); + } + + //enable alpha blending on second pass but not first pass + LLGLState blend(GL_BLEND, pass); + + gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA); + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + bool render_mesh = true; + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); + + LLModel::Decomposition& physics = model->mPhysics; + + if (!physics.mHull.empty()) + { + render_mesh = false; + + if (physics.mMesh.empty()) + { //build vertex buffer for physics mesh + gMeshRepo.buildPhysicsMesh(physics); + } + + if (!physics.mMesh.empty()) + { //render hull instead of mesh + for (U32 i = 0; i < physics.mMesh.size(); ++i) + { + if (explode > 0.f) + { + gGL.pushMatrix(); + + LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; + offset *= explode; + + gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); + } + + static std::vector hull_colors; + + if (i + 1 >= hull_colors.size()) + { + hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128)); + } + + gGL.diffuseColor4ubv(hull_colors[i].mV); + LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); + + if (explode > 0.f) + { + gGL.popMatrix(); + } + } + } + } + } + + if (render_mesh) + { + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + + U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); + if (pass > 0){ + for (U32 i = 0; i < num_models; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.diffuseColor4fv(phys_fill_col().mV); // restore changes removed by the lab + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + // restore changes removed by the lab + // gGL.diffuseColor4fv(PREVIEW_PSYH_EDGE_COL.mV); + // gGL.setLineWidth(PREVIEW_PSYH_EDGE_WIDTH); // Line width OGL core profile fix by Rye Mutt + gGL.diffuseColor4fv(phys_edge_col().mV); + gGL.setLineWidth(phys_edge_width()); + // + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt + } + } + } + gGL.popMatrix(); + } + + // only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks] + if (mHasDegenerate) + { + // restore older functionality lost in lab importer + // gGL.setLineWidth(PREVIEW_DEG_EDGE_WIDTH); // Line width OGL core profile fix by Rye Mutt + // glPointSize(PREVIEW_DEG_POINT_SIZE); + // gPipeline.enableLightsFullbright(); + gGL.setLineWidth(deg_edge_width()); + glPointSize(deg_point_size()); + // gPipeline.enableLightsFullbright(); // This may need to be restored when I fined the cause of the black rendering + // + //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); // restore proper functionality + const LLVector4a scale(0.5f); + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); + + LLModel::Decomposition& physics = model->mPhysics; + + if (physics.mHull.empty()) + { + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + + U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); + for (U32 v = 0; v < num_models; ++v) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + + LLStrider pos_strider; + buffer->getVertexStrider(pos_strider, 0); + LLVector4a* pos = (LLVector4a*)pos_strider.get(); + + LLStrider idx; + buffer->getIndexStrider(idx, 0); + + for (U32 i = 0; i < buffer->getNumIndices(); i += 3) + { + LLVector4a v1; v1.setMul(pos[*idx++], scale); + LLVector4a v2; v2.setMul(pos[*idx++], scale); + LLVector4a v3; v3.setMul(pos[*idx++], scale); + + if (ll_is_degenerate(v1, v2, v3)) + { + // restore (configurable) coloured overlay + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.diffuseColor4fv(deg_fill_col().mV); + buffer->draw(LLRender::TRIANGLES, 3, i); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + gGL.diffuseColor3fv(deg_edge_col().mV); + gGL.color3fv(deg_edge_col().mV); + // + buffer->draw(LLRender::LINE_LOOP, 3, i); + buffer->draw(LLRender::POINTS, 3, i); + } + } + } + } + } + + gGL.popMatrix(); + } + gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt + glPointSize(1.f); + gPipeline.enableLightsPreview(); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + } + } + } + else + { + target_pos = getPreviewAvatar()->getPositionAgent(); + getPreviewAvatar()->clearAttachmentOverrides(); // removes pelvis fixup + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); + bool pelvis_recalc = false; + + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + + if (!model->mSkinWeights.empty()) + { + const LLMeshSkinInfo *skin = &model->mSkinInfo; + LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary + U32 joint_count = LLSkinningUtil::getMeshJointCount(skin); + U32 bind_count = skin->mAlternateBindMatrix.size(); + + if (joint_overrides + && bind_count > 0 + && joint_count == bind_count) + { + // mesh_id is used to determine which mesh gets to + // set the joint offset, in the event of a conflict. Since + // we don't know the mesh id yet, we can't guarantee that + // joint offsets will be applied with the same priority as + // in the uploaded model. If the file contains multiple + // meshes with conflicting joint offsets, preview may be + // incorrect. + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + for (U32 j = 0; j < joint_count; ++j) + { + LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]); + if (joint) + { + const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation(); + if (joint->aboveJointPosThreshold(jointPos)) + { + bool override_changed; + joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed); + + if (override_changed) + { + //If joint is a pelvis then handle old/new pelvis to foot values + if (joint->getName() == "mPelvis")// or skin->mJointNames[j] + { + pelvis_recalc = true; + } + } + if (skin->mLockScaleIfJointPosition) + { + // Note that unlike positions, there's no threshold check here, + // just a lock at the default value. + joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model"); + } + } + } + } + } + + for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + const LLVolumeFace& face = model->getVolumeFace(i); + + LLStrider position; + buffer->getVertexStrider(position); + + // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis + //LLStrider weight; + LLStrider weight; + buffer->getWeight4Strider(weight); + + //quick 'n dirty software vertex skinning + + //build matrix palette + + LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT]; + // use Mat4a part of the caching changes, no point in using the cache itself in the preview though. + //LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, joint_count, + // skin, getPreviewAvatar()); + LLSkinningUtil::initSkinningMatrixPalette(mat, joint_count, + skin, getPreviewAvatar()); + // + + LLMatrix4a bind_shape_matrix; + bind_shape_matrix.loadu(skin->mBindShapeMatrix); + U32 max_joints = LLSkinningUtil::getMaxJointCount(); + for (U32 j = 0; j < buffer->getNumVerts(); ++j) + { + LLMatrix4a final_mat; + // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis + //F32 *wptr = weight[j].mV; + F32 *wptr = weight[j].getF32ptr(); + // + LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints); + + //VECTORIZE THIS + LLVector4a& v = face.mPositions[j]; + + LLVector4a t; + LLVector4a dst; + bind_shape_matrix.affineTransform(v, t); + final_mat.affineTransform(t, dst); + + position[j][0] = dst[0]; + position[j][1] = dst[1]; + position[j][2] = dst[2]; + } + + // FIRE-13465 Make sure there's a material set before dereferencing it + if( instance.mModel->mMaterialList.size() > i && + instance.mMaterial.end() != instance.mMaterial.find( instance.mModel->mMaterialList[ i ] ) ) + { + // + llassert(model->mMaterialList.size() > i); + const std::string& binding = instance.mModel->mMaterialList[i]; + const LLImportMaterial& material = instance.mMaterial[binding]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + gGL.diffuseColor4fv(material.mDiffuseColor.mV); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // Find the tex for this material, bind it, and add it to our set + // + LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); + if (tex) + { + mTextureSet.insert(tex); + } + } else // FIRE-13465 Make sure there's a material set before dereferencing it, if none, set buffer type and unbind texture. + { + buffer->setBuffer(type_mask & buffer->getTypeMask()); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } // + + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + + if (edges) + { + // restore behaviour removed by lab + // gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); + // gGL.setLineWidth(PREVIEW_EDGE_WIDTH); + gGL.diffuseColor4fv(edge_col().mV); + gGL.setLineWidth(edge_width()); + // + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt + } + } + } + } + } + + if (joint_positions) + { + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + if (shader) + { + gDebugProgram.bind(); + } + getPreviewAvatar()->renderCollisionVolumes(); + if (fmp->mTabContainer->getCurrentPanelIndex() == fmp->mAvatarTabIndex) + { + getPreviewAvatar()->renderBones(fmp->mSelectedJointName); + } + else + { + getPreviewAvatar()->renderBones(); + } + if (shader) + { + shader->bind(); + } + } + + if (pelvis_recalc) + { + // size/scale recalculation + getPreviewAvatar()->postPelvisSetRecalc(); + } + } + } + + if (use_shaders) + { + gObjectPreviewProgram.unbind(); + } + + gGL.popMatrix(); + + return TRUE; } //----------------------------------------------------------------------------- @@ -3821,7 +3939,7 @@ BOOL LLModelPreview::render() //----------------------------------------------------------------------------- void LLModelPreview::refresh() { - mNeedsUpdate = TRUE; + mNeedsUpdate = TRUE; } //----------------------------------------------------------------------------- @@ -3829,9 +3947,9 @@ void LLModelPreview::refresh() //----------------------------------------------------------------------------- void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) { - mCameraYaw = mCameraYaw + yaw_radians; + mCameraYaw = mCameraYaw + yaw_radians; - mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); + mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); } //----------------------------------------------------------------------------- @@ -3839,115 +3957,115 @@ void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) //----------------------------------------------------------------------------- void LLModelPreview::zoom(F32 zoom_amt) { - F32 new_zoom = mCameraZoom + zoom_amt; - // TODO: stop clamping in render - // restore settings control - // mCameraZoom = llclamp(new_zoom, 1.f, PREVIEW_ZOOM_LIMIT); - static LLCachedControl zoom_limit(gSavedSettings, "MeshPreviewZoomLimit"); - mCameraZoom = llclamp(new_zoom, 1.f, zoom_limit()); - // + F32 new_zoom = mCameraZoom + zoom_amt; + // TODO: stop clamping in render + // restore settings control + // mCameraZoom = llclamp(new_zoom, 1.f, PREVIEW_ZOOM_LIMIT); + static LLCachedControl zoom_limit(gSavedSettings, "MeshPreviewZoomLimit"); + mCameraZoom = llclamp(new_zoom, 1.f, zoom_limit()); + // } void LLModelPreview::pan(F32 right, F32 up) { - bool skin_weight = mViewOption["show_skin_weight"]; - F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; - mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f); - mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f); + bool skin_weight = mViewOption["show_skin_weight"]; + F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; + mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f); + mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f); } void LLModelPreview::setPreviewLOD(S32 lod) { - lod = llclamp(lod, 0, (S32)LLModel::LOD_HIGH); + lod = llclamp(lod, 0, (S32)LLModel::LOD_HIGH); - if (lod != mPreviewLOD) - { - mPreviewLOD = lod; + if (lod != mPreviewLOD) + { + mPreviewLOD = lod; - LLComboBox* combo_box = mFMP->getChild("preview_lod_combo"); - combo_box->setCurrentByIndex((NUM_LOD - 1) - mPreviewLOD); // combo box list of lods is in reverse order - mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]); + LLComboBox* combo_box = mFMP->getChild("preview_lod_combo"); + combo_box->setCurrentByIndex((NUM_LOD - 1) - mPreviewLOD); // combo box list of lods is in reverse order + mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]); - LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); - LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); + LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); + LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); - for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i) - { - const LLColor4& color = (i == lod) ? highlight_color : normal_color; + 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); - } + mFMP->childSetColor(lod_status_name[i], color); + mFMP->childSetColor(lod_label_name[i], color); + mFMP->childSetColor(lod_triangles_name[i], color); + mFMP->childSetColor(lod_vertices_name[i], color); + } - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; - if (fmp) - { - // make preview repopulate tab - fmp->clearAvatarTab(); - } - } - refresh(); - updateStatusMessages(); + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + if (fmp) + { + // make preview repopulate tab + fmp->clearAvatarTab(); + } + } + refresh(); + updateStatusMessages(); } //static void LLModelPreview::textureLoadedCallback( - BOOL success, - LLViewerFetchedTexture *src_vi, - LLImageRaw* src, - LLImageRaw* src_aux, - S32 discard_level, - BOOL final, - void* userdata) + BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* src_aux, + S32 discard_level, + BOOL final, + void* userdata) { - LLModelPreview* preview = (LLModelPreview*)userdata; - preview->refresh(); + LLModelPreview* preview = (LLModelPreview*)userdata; + preview->refresh(); - if (final && preview->mModelLoader) - { - if (preview->mModelLoader->mNumOfFetchingTextures > 0) - { - preview->mModelLoader->mNumOfFetchingTextures--; - } - } + if (final && preview->mModelLoader) + { + if (preview->mModelLoader->mNumOfFetchingTextures > 0) + { + preview->mModelLoader->mNumOfFetchingTextures--; + } + } } // static bool LLModelPreview::lodQueryCallback() { - // not the best solution, but model preview belongs to floater - // so it is an easy way to check that preview still exists. - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp && fmp->mModelPreview) - { - LLModelPreview* preview = fmp->mModelPreview; - if (preview->mLodsQuery.size() > 0) - { - S32 lod = preview->mLodsQuery.back(); - preview->mLodsQuery.pop_back(); - preview->genLODs(lod); + // not the best solution, but model preview belongs to floater + // so it is an easy way to check that preview still exists. + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp && fmp->mModelPreview) + { + LLModelPreview* preview = fmp->mModelPreview; + if (preview->mLodsQuery.size() > 0) + { + S32 lod = preview->mLodsQuery.back(); + preview->mLodsQuery.pop_back(); + preview->genLODs(lod); - if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) - { - preview->lookupLODModelFiles(LLModel::LOD_HIGH); - } + if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) + { + preview->lookupLODModelFiles(LLModel::LOD_HIGH); + } - // return false to continue cycle - return false; - } - } - // nothing to process - return true; + // return false to continue cycle + return false; + } + } + // nothing to process + return true; } void LLModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) { - if (!mLODFrozen) - { - genLODs(lod, 3, enforce_tri_limit); - refresh(); - } + if (!mLODFrozen) + { + genLODs(lod, 3, enforce_tri_limit); + refresh(); + } } diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index 90a3fccf81..b8fa419e3a 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -200,7 +200,7 @@ public: bool mHasDegenerate; protected: - + bool LLModelPreview::matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); // FIRE-30965 Cleanup mesh material parsing static void loadedCallback(LLModelLoader::scene& scene, LLModelLoader::model_list& model_list, S32 lod, void* opaque); static void stateChangedCallback(U32 state, void* opaque); From b979ddef7d6b84f6eec533877f1393ae2322867f Mon Sep 17 00:00:00 2001 From: Beq Date: Fri, 28 May 2021 03:58:37 +0100 Subject: [PATCH 05/14] FIRE-30979 Fix Ambient Lighting in Mesh preview and a couple of typo fixes --- .../app_settings/shaders/class1/objects/previewV.glsl | 3 ++- indra/newview/llmodelpreview.cpp | 7 ++++--- indra/newview/llviewershadermgr.cpp | 2 +- .../skins/default/xui/en/floater_model_preview.xml | 10 ++++++++++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl index 4bb588335a..3945160db6 100644 --- a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl @@ -25,6 +25,7 @@ uniform mat3 normal_matrix; uniform mat4 texture_matrix0; +uniform vec4 ambient_color; // add ambient color to preview shader uniform mat4 modelview_matrix; uniform mat4 modelview_projection_matrix; @@ -87,7 +88,7 @@ void main() vec3 norm = normalize(normal_matrix * normal); - vec4 col = vec4(0,0,0,1); + vec4 col = ambient_color; // add ambient color to preview shader // 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); diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 3e782468ba..6d83ddbdc9 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -424,7 +424,7 @@ bool LLModelPreview::matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFace std::ostringstream out; out << "material " << ref->mMaterialList[i] << " not found in lod adding placeholder"; - LL_DEBUGS("MESHSKININFO") << out << LL_ENDL; + LL_DEBUGS("MESHSKININFO") << out.str() << LL_ENDL; if (mImporterDebug) { LLFloaterModelPreview::addStringToLog(out, false); @@ -458,8 +458,8 @@ bool LLModelPreview::matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFace if (lod->mMaterialList.size() != ref->mMaterialList.size()) { std::ostringstream out; - out << "Material of LOD model " << lod->getName() << " has more materials than a reference" << ref->getName() << "."; - LL_INFOS("MESHSKININFO") << out << LL_ENDL; + out << "Material of LOD model " << lod->getName() << " has more materials than the reference " << ref->getName() << "."; + LL_INFOS("MESHSKININFO") << out.str() << LL_ENDL; LLFloaterModelPreview::addStringToLog(out, true); return false; } @@ -3371,6 +3371,7 @@ BOOL LLModelPreview::render() gGL.loadIdentity(); gPipeline.enableLightsPreview(); + gObjectPreviewProgram.uniform4fv(LLShaderMgr::AMBIENT, 1, LLPipeline::PreviewAmbientColor.mV); // pass ambient setting to shader LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * LLQuaternion(mCameraYaw, LLVector3::z_axis); diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 3ca714fd46..525387af51 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -3219,7 +3219,7 @@ BOOL LLViewerShaderMgr::loadShadersObject() if (success) { - gObjectPreviewProgram.mName = "Simple Shader"; + gObjectPreviewProgram.mName = "Preview Shader"; // update preview shader name gObjectPreviewProgram.mFeatures.calculatesLighting = false; gObjectPreviewProgram.mFeatures.calculatesAtmospherics = false; gObjectPreviewProgram.mFeatures.hasGamma = false; 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 684dbaa81b..d32a06bcac 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -1492,6 +1492,16 @@ label="Model Edge" tool_tip="Edge color for the model in preview window on the Mesh uploader" name="mesh_preview_edge_color"/> + Date: Fri, 28 May 2021 11:08:32 +0100 Subject: [PATCH 06/14] Restore the "proper" mismatched indents cos Beq was sleep coding --- indra/newview/llmodelpreview.cpp | 6668 +++++++++++++++--------------- 1 file changed, 3335 insertions(+), 3333 deletions(-) diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 6d83ddbdc9..83e4fe3326 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -94,81 +94,81 @@ const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f; BOOL stop_gloderror() { - GLuint error = glodGetError(); + GLuint error = glodGetError(); - if (error != GLOD_NO_ERROR) - { - LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; - return TRUE; - } + if (error != GLOD_NO_ERROR) + { + LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; + return TRUE; + } - return FALSE; + return FALSE; } LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material) { - LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); + LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); - if (texture) - { - if (texture->getDiscardLevel() > -1) - { - gGL.getTexUnit(0)->bind(texture, true); - return texture; - } - } + if (texture) + { + if (texture->getDiscardLevel() > -1) + { + gGL.getTexUnit(0)->bind(texture, true); + return texture; + } + } - return NULL; + return NULL; } std::string stripSuffix(std::string name) { - // Bug fixes in mesh importer by Drake Arconis - //if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1)) - if ((name.find("_LOD") != std::string::npos) || (name.find("_PHYS") != std::string::npos)) - // - { - return name.substr(0, name.rfind('_')); - } - return name; + // Bug fixes in mesh importer by Drake Arconis + //if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1)) + if ((name.find("_LOD") != std::string::npos) || (name.find("_PHYS") != std::string::npos)) + // + { + return name.substr(0, name.rfind('_')); + } + return name; } std::string getLodSuffix(S32 lod) { - std::string suffix; - switch (lod) - { - case LLModel::LOD_IMPOSTOR: suffix = "_LOD0"; break; - case LLModel::LOD_LOW: suffix = "_LOD1"; break; - case LLModel::LOD_MEDIUM: suffix = "_LOD2"; break; - case LLModel::LOD_PHYSICS: suffix = "_PHYS"; break; - case LLModel::LOD_HIGH: break; - } - return suffix; + std::string suffix; + switch (lod) + { + case LLModel::LOD_IMPOSTOR: suffix = "_LOD0"; break; + case LLModel::LOD_LOW: suffix = "_LOD1"; break; + case LLModel::LOD_MEDIUM: suffix = "_LOD2"; break; + case LLModel::LOD_PHYSICS: suffix = "_PHYS"; break; + case LLModel::LOD_HIGH: break; + } + return suffix; } void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut) { - LLModelLoader::scene::iterator base_iter = scene.begin(); - bool found = false; - while (!found && (base_iter != scene.end())) - { - matOut = base_iter->first; + LLModelLoader::scene::iterator base_iter = scene.begin(); + bool found = false; + while (!found && (base_iter != scene.end())) + { + matOut = base_iter->first; - LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); - while (!found && (base_instance_iter != base_iter->second.end())) - { - LLModelInstance& base_instance = *base_instance_iter++; - LLModel* base_model = base_instance.mModel; + LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); + while (!found && (base_instance_iter != base_iter->second.end())) + { + LLModelInstance& base_instance = *base_instance_iter++; + LLModel* base_model = base_instance.mModel; - if (base_model && (base_model->mLabel == name_to_match)) - { - baseModelOut = base_model; - return; - } - } - base_iter++; - } + if (base_model && (base_model->mLabel == name_to_match)) + { + baseModelOut = base_model; + return; + } + } + base_iter++; + } } //----------------------------------------------------------------------------- @@ -176,199 +176,201 @@ void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LL //----------------------------------------------------------------------------- LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) - : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex() - , mLodsQuery() - , mLodsWithParsingError() - , mPelvisZOffset(0.0f) - , mLegacyRigFlags(U32_MAX) - , mRigValidJointUpload(false) - , mPhysicsSearchLOD(LLModel::LOD_PHYSICS) - , mResetJoints(false) - , mModelNoErrors(true) - , mLastJointUpdate(false) - , mFirstSkinUpdate(true) - , mHasDegenerate(false) - , mImporterDebug(LLCachedControl(gSavedSettings, "ImporterDebug", false)) + : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex() + , mLodsQuery() + , mLodsWithParsingError() + , mPelvisZOffset(0.0f) + , mLegacyRigFlags(U32_MAX) + , mRigValidJointUpload(false) + , mPhysicsSearchLOD(LLModel::LOD_PHYSICS) + , mResetJoints(false) + , mModelNoErrors(true) + , mLastJointUpdate(false) + , mFirstSkinUpdate(true) + , mHasDegenerate(false) + , mImporterDebug(LLCachedControl(gSavedSettings, "ImporterDebug", false)) { - mNeedsUpdate = TRUE; - mCameraDistance = 0.f; - mCameraYaw = 0.f; - mCameraPitch = 0.f; - mCameraZoom = 1.f; - mTextureName = 0; - mPreviewLOD = 0; - mModelLoader = NULL; - mMaxTriangleLimit = 0; - mDirty = false; - mGenLOD = false; - mLoading = false; - mLookUpLodFiles = false; - mLoadState = LLModelLoader::STARTING; - mGroup = 0; - mLODFrozen = false; - mBuildShareTolerance = 0.f; - mBuildQueueMode = GLOD_QUEUE_GREEDY; - mBuildBorderMode = GLOD_BORDER_UNLOCK; - mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE; - mUVGuideTexture = LLViewerTextureManager::getFetchedTextureFromFile(gSavedSettings.getString("FSMeshPreviewUVGuideFile"), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); // - Add UV guide overlay to pmesh preview + mNeedsUpdate = TRUE; + mCameraDistance = 0.f; + mCameraYaw = 0.f; + mCameraPitch = 0.f; + mCameraZoom = 1.f; + mTextureName = 0; + mPreviewLOD = 0; + mModelLoader = NULL; + mMaxTriangleLimit = 0; + mDirty = false; + mGenLOD = false; + mLoading = false; + mLookUpLodFiles = false; + mLoadState = LLModelLoader::STARTING; + mGroup = 0; + mLODFrozen = false; + mBuildShareTolerance = 0.f; + mBuildQueueMode = GLOD_QUEUE_GREEDY; + mBuildBorderMode = GLOD_BORDER_UNLOCK; + mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE; + mUVGuideTexture = LLViewerTextureManager::getFetchedTextureFromFile(gSavedSettings.getString("FSMeshPreviewUVGuideFile"), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); // - Add UV guide overlay to pmesh preview - for (U32 i = 0; i < LLModel::NUM_LODS; ++i) - { - mRequestedTriangleCount[i] = 0; - mRequestedCreaseAngle[i] = -1.f; - mRequestedLoDMode[i] = 0; - mRequestedErrorThreshold[i] = 0.f; - mRequestedBuildOperator[i] = 0; - mRequestedQueueMode[i] = 0; - mRequestedBorderMode[i] = 0; - mRequestedShareTolerance[i] = 0.f; - } + for (U32 i = 0; i < LLModel::NUM_LODS; ++i) + { + mRequestedTriangleCount[i] = 0; + mRequestedCreaseAngle[i] = -1.f; + mRequestedLoDMode[i] = 0; + mRequestedErrorThreshold[i] = 0.f; + mRequestedBuildOperator[i] = 0; + mRequestedQueueMode[i] = 0; + mRequestedBorderMode[i] = 0; + mRequestedShareTolerance[i] = 0.f; + } - mViewOption["show_textures"] = false; - mFMP = fmp; + mViewOption["show_textures"] = false; + mFMP = fmp; - mHasPivot = false; - mModelPivot = LLVector3(0.0f, 0.0f, 0.0f); + mHasPivot = false; + mModelPivot = LLVector3(0.0f, 0.0f, 0.0f); - glodInit(); + glodInit(); - createPreviewAvatar(); + createPreviewAvatar(); } LLModelPreview::~LLModelPreview() { - // glod apparently has internal mem alignment issues that are angering - // the heap-check code in windows, these should be hunted down in that - // TP code, if possible - // - // kernel32.dll!HeapFree() + 0x14 bytes - // msvcr100.dll!free(void * pBlock) Line 51 C - // glod.dll!glodGetGroupParameteriv() + 0x119 bytes - // glod.dll!glodShutdown() + 0x77 bytes - // - //glodShutdown(); - if (mModelLoader) - { - mModelLoader->shutdown(); - } + // glod apparently has internal mem alignment issues that are angering + // the heap-check code in windows, these should be hunted down in that + // TP code, if possible + // + // kernel32.dll!HeapFree() + 0x14 bytes + // msvcr100.dll!free(void * pBlock) Line 51 C + // glod.dll!glodGetGroupParameteriv() + 0x119 bytes + // glod.dll!glodShutdown() + 0x77 bytes + // + //glodShutdown(); + if (mModelLoader) + { + mModelLoader->shutdown(); + } - if (mPreviewAvatar) - { - mPreviewAvatar->markDead(); - mPreviewAvatar = NULL; - } + if (mPreviewAvatar) + { + mPreviewAvatar->markDead(); + mPreviewAvatar = NULL; + } } U32 LLModelPreview::calcResourceCost() { - assert_main_thread(); + assert_main_thread(); - rebuildUploadData(); + rebuildUploadData(); - //Upload skin is selected BUT check to see if the joints coming in from the asset were malformed. - if (mFMP && mFMP->childGetValue("upload_skin").asBoolean()) - { - bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); - if (uploadingJointPositions && !isRigValidForJointPositionUpload()) - { - mFMP->childDisable("ok_btn"); - } - } + //Upload skin is selected BUT check to see if the joints coming in from the asset were malformed. + if (mFMP && mFMP->childGetValue("upload_skin").asBoolean()) + { + bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); + if (uploadingJointPositions && !isRigValidForJointPositionUpload()) + { + mFMP->childDisable("ok_btn"); + } + } - std::set accounted; - U32 num_points = 0; - U32 num_hulls = 0; + std::set accounted; + U32 num_points = 0; + U32 num_hulls = 0; - F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f; - mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f; + F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f; + mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f; - if (mFMP && mFMP->childGetValue("upload_joints").asBoolean()) - { - // FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail. - // see also call to addAttachmentPosOverride. - LLUUID fake_mesh_id; - fake_mesh_id.generate(); - getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); - } + if (mFMP && mFMP->childGetValue("upload_joints").asBoolean()) + { + // FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail. + // see also call to addAttachmentPosOverride. + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); + } - F32 streaming_cost = 0.f; - F32 physics_cost = 0.f; - for (U32 i = 0; i < mUploadData.size(); ++i) - { - LLModelInstance& instance = mUploadData[i]; + F32 streaming_cost = 0.f; + F32 physics_cost = 0.f; + for (U32 i = 0; i < mUploadData.size(); ++i) + { + LLModelInstance& instance = mUploadData[i]; - if (accounted.find(instance.mModel) == accounted.end()) - { - accounted.insert(instance.mModel); + if (accounted.find(instance.mModel) == accounted.end()) + { + accounted.insert(instance.mModel); - LLModel::Decomposition& decomp = - instance.mLOD[LLModel::LOD_PHYSICS] ? - instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : - instance.mModel->mPhysics; + LLModel::Decomposition& decomp = + instance.mLOD[LLModel::LOD_PHYSICS] ? + instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : + instance.mModel->mPhysics; - //update instance skin info for each lods pelvisZoffset - for (int j = 0; jmSkinInfo.mPelvisOffset = mPelvisZOffset; - } - } + //update instance skin info for each lods pelvisZoffset + for (int j = 0; jmSkinInfo.mPelvisOffset = mPelvisZOffset; + } + } - std::stringstream ostr; - LLSD ret = LLModel::writeModel(ostr, - instance.mLOD[4], - instance.mLOD[3], - instance.mLOD[2], - instance.mLOD[1], - instance.mLOD[0], - decomp, - mFMP->childGetValue("upload_skin").asBoolean(), - mFMP->childGetValue("upload_joints").asBoolean(), - mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(), - TRUE, - FALSE, - instance.mModel->mSubmodelID); + std::stringstream ostr; + LLSD ret = LLModel::writeModel(ostr, + instance.mLOD[4], + instance.mLOD[3], + instance.mLOD[2], + instance.mLOD[1], + instance.mLOD[0], + decomp, + mFMP->childGetValue("upload_skin").asBoolean(), + mFMP->childGetValue("upload_joints").asBoolean(), + mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(), + TRUE, + FALSE, + instance.mModel->mSubmodelID); - num_hulls += decomp.mHull.size(); - for (U32 i = 0; i < decomp.mHull.size(); ++i) - { - num_points += decomp.mHull[i].size(); - } + num_hulls += decomp.mHull.size(); + for (U32 i = 0; i < decomp.mHull.size(); ++i) + { + num_points += decomp.mHull[i].size(); + } - //calculate streaming cost - LLMatrix4 transformation = instance.mTransform; + //calculate streaming cost + LLMatrix4 transformation = instance.mTransform; - LLVector3 position = LLVector3(0, 0, 0) * transformation; + LLVector3 position = LLVector3(0, 0, 0) * transformation; - LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - LLVector3 scale = LLVector3(x_length, y_length, z_length); + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); - F32 radius = scale.length()*0.5f*debug_scale; + F32 radius = scale.length()*0.5f*debug_scale; - LLMeshCostData costs; - if (gMeshRepo.getCostData(ret, costs)) - { - streaming_cost += costs.getRadiusBasedStreamingCost(radius); - } - } - } + LLMeshCostData costs; + if (gMeshRepo.getCostData(ret, costs)) + { + streaming_cost += costs.getRadiusBasedStreamingCost(radius); + } + } + } - F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f; + F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f; - mDetailsSignal(mPreviewScale[0] * scale, mPreviewScale[1] * scale, mPreviewScale[2] * scale, streaming_cost, physics_cost); + mDetailsSignal(mPreviewScale[0] * scale, mPreviewScale[1] * scale, mPreviewScale[2] * scale, streaming_cost, physics_cost); - updateStatusMessages(); + updateStatusMessages(); - return (U32)streaming_cost; + return (U32)streaming_cost; } // relocate from llmodel and rewrite so it does what it is meant to +// Material matching should work as the comment below states (subsets are allowed) +// prior to this a mess in multiple places meant that all LODs are forced to carry unwanted triangles for unused materials bool LLModelPreview::matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFaceCnt, int& modelFaceCnt ) { //Is this a subset? @@ -480,2441 +482,2441 @@ bool LLModelPreview::matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFace void LLModelPreview::rebuildUploadData() { - assert_main_thread(); + assert_main_thread(); - mUploadData.clear(); - mTextureSet.clear(); + mUploadData.clear(); + mTextureSet.clear(); - //fill uploaddata instance vectors from scene data + //fill uploaddata instance vectors from scene data - std::string requested_name = mFMP->getChild("description_form")->getValue().asString(); + std::string requested_name = mFMP->getChild("description_form")->getValue().asString(); - LLSpinCtrl* scale_spinner = mFMP->getChild("import_scale"); + LLSpinCtrl* scale_spinner = mFMP->getChild("import_scale"); - F32 scale = scale_spinner->getValue().asReal(); + F32 scale = scale_spinner->getValue().asReal(); - LLMatrix4 scale_mat; - scale_mat.initScale(LLVector3(scale, scale, scale)); + LLMatrix4 scale_mat; + scale_mat.initScale(LLVector3(scale, scale, scale)); - F32 max_scale = 0.f; + F32 max_scale = 0.f; - BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); - U32 load_state = 0; + BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); + U32 load_state = 0; - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) - { //for each transform in scene - LLMatrix4 mat = iter->first; + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) + { //for each transform in scene + LLMatrix4 mat = iter->first; - // compute position - LLVector3 position = LLVector3(0, 0, 0) * mat; + // compute position + LLVector3 position = LLVector3(0, 0, 0) * mat; - // compute scale - LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); - max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length); + max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length); - mat *= scale_mat; + mat *= scale_mat; - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) - { //for each instance with said transform applied - LLModelInstance instance = *model_iter++; + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) + { //for each instance with said transform applied + LLModelInstance instance = *model_iter++; - LLModel* base_model = instance.mModel; + LLModel* base_model = instance.mModel; - if (base_model && !requested_name.empty()) - { - base_model->mRequestedLabel = requested_name; - } + if (base_model && !requested_name.empty()) + { + base_model->mRequestedLabel = requested_name; + } - for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--) - { - LLModel* lod_model = NULL; - if (!legacyMatching) - { - // Fill LOD slots by finding matching meshes by label with name extensions - // in the appropriate scene for each LOD. This fixes all kinds of issues - // where the indexed method below fails in spectacular fashion. - // If you don't take the time to name your LOD and PHYS meshes - // with the name of their corresponding mesh in the HIGH LOD, - // then the indexed method will be attempted below. + for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--) + { + LLModel* lod_model = NULL; + if (!legacyMatching) + { + // Fill LOD slots by finding matching meshes by label with name extensions + // in the appropriate scene for each LOD. This fixes all kinds of issues + // where the indexed method below fails in spectacular fashion. + // If you don't take the time to name your LOD and PHYS meshes + // with the name of their corresponding mesh in the HIGH LOD, + // then the indexed method will be attempted below. - LLMatrix4 transform; + LLMatrix4 transform; - std::string name_to_match = instance.mLabel; - llassert(!name_to_match.empty()); + std::string name_to_match = instance.mLabel; + llassert(!name_to_match.empty()); - int extensionLOD; - if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty()) - { - extensionLOD = i; - } - else - { - //Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for - extensionLOD = mPhysicsSearchLOD; - } + int extensionLOD; + if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty()) + { + extensionLOD = i; + } + else + { + //Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for + extensionLOD = mPhysicsSearchLOD; + } - std::string toAdd = getLodSuffix(extensionLOD); + std::string toAdd = getLodSuffix(extensionLOD); - // Bug fixes in mesh importer by Drake Arconis - //if (name_to_match.find(toAdd) == -1) - if (name_to_match.find(toAdd) == std::string::npos) - // - { - name_to_match += toAdd; - } + // Bug fixes in mesh importer by Drake Arconis + //if (name_to_match.find(toAdd) == -1) + if (name_to_match.find(toAdd) == std::string::npos) + // + { + name_to_match += toAdd; + } - FindModel(mScene[i], name_to_match, lod_model, transform); + FindModel(mScene[i], name_to_match, lod_model, transform); - if (!lod_model && i != LLModel::LOD_PHYSICS) - { - if (mImporterDebug) - { - std::ostringstream out; - out << "Search of " << name_to_match; - out << " in LOD" << i; - out << " list failed. Searching for alternative among LOD lists."; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } + if (!lod_model && i != LLModel::LOD_PHYSICS) + { + if (mImporterDebug) + { + std::ostringstream out; + out << "Search of " << name_to_match; + out << " in LOD" << i; + out << " list failed. Searching for alternative among LOD lists."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } - int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i; - while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model) - { - std::string name_to_match = instance.mLabel; - llassert(!name_to_match.empty()); + int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i; + while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model) + { + std::string name_to_match = instance.mLabel; + llassert(!name_to_match.empty()); - std::string toAdd = getLodSuffix(searchLOD); + std::string toAdd = getLodSuffix(searchLOD); - // Bug fixes in mesh importer by Drake Arconis - //if (name_to_match.find(toAdd) == -1) - if (name_to_match.find(toAdd) == std::string::npos) - // - { - name_to_match += toAdd; - } + // Bug fixes in mesh importer by Drake Arconis + //if (name_to_match.find(toAdd) == -1) + if (name_to_match.find(toAdd) == std::string::npos) + // + { + name_to_match += toAdd; + } - // See if we can find an appropriately named model in LOD 'searchLOD' - // - FindModel(mScene[searchLOD], name_to_match, lod_model, transform); - searchLOD++; - } - } - } - else - { - // Use old method of index-based association - U32 idx = 0; - for (idx = 0; idx < mBaseModel.size(); ++idx) - { - // find reference instance for this model - if (mBaseModel[idx] == base_model) - { - if (mImporterDebug) - { - std::ostringstream out; - out << "Attempting to use model index " << idx; - out << " for LOD" << i; - out << " of " << instance.mLabel; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - break; - } - } + // See if we can find an appropriately named model in LOD 'searchLOD' + // + FindModel(mScene[searchLOD], name_to_match, lod_model, transform); + searchLOD++; + } + } + } + else + { + // Use old method of index-based association + U32 idx = 0; + for (idx = 0; idx < mBaseModel.size(); ++idx) + { + // find reference instance for this model + if (mBaseModel[idx] == base_model) + { + if (mImporterDebug) + { + std::ostringstream out; + out << "Attempting to use model index " << idx; + out << " for LOD" << i; + out << " of " << instance.mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + break; + } + } - // If the model list for the current LOD includes that index... - // - if (mModel[i].size() > idx) - { - // Assign that index from the model list for our LOD as the LOD model for this instance - // - lod_model = mModel[i][idx]; - if (mImporterDebug) - { - std::ostringstream out; - out << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - } - else if (mImporterDebug) - { - std::ostringstream out; - out << "LOD" << i << ": List of models does not include index " << idx << " scene is missing a LOD model"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - } + // If the model list for the current LOD includes that index... + // + if (mModel[i].size() > idx) + { + // Assign that index from the model list for our LOD as the LOD model for this instance + // + lod_model = mModel[i][idx]; + if (mImporterDebug) + { + std::ostringstream out; + out << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } + else if (mImporterDebug) + { + std::ostringstream out; + out << "LOD" << i << ": List of models does not include index " << idx << " scene is missing a LOD model"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } - if (lod_model) - { - if (mImporterDebug) - { - std::ostringstream out; - if (i == LLModel::LOD_PHYSICS) - { - out << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel; - } - else - { - out << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel; - } - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - instance.mLOD[i] = lod_model; - } - else - { - if (i < LLModel::LOD_HIGH && !lodsReady()) - { - // assign a placeholder from previous LOD until lod generation is complete. - // Note: we might need to assign it regardless of conditions like named search does, to prevent crashes. - instance.mLOD[i] = instance.mLOD[i + 1]; - } - if (mImporterDebug) - { - std::ostringstream out; - out << "LOD" << i << ": List of models does not include " << instance.mLabel; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - } - } + if (lod_model) + { + if (mImporterDebug) + { + std::ostringstream out; + if (i == LLModel::LOD_PHYSICS) + { + out << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel; + } + else + { + out << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel; + } + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + instance.mLOD[i] = lod_model; + } + else + { + if (i < LLModel::LOD_HIGH && !lodsReady()) + { + // assign a placeholder from previous LOD until lod generation is complete. + // Note: we might need to assign it regardless of conditions like named search does, to prevent crashes. + instance.mLOD[i] = instance.mLOD[i + 1]; + } + if (mImporterDebug) + { + std::ostringstream out; + out << "LOD" << i << ": List of models does not include " << instance.mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } + } - LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH]; - if (!high_lod_model) - { - LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has no High Lod (LOD3).", true); - load_state = LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING; // FIRE-30965 Cleanup braindead mesh parsing error handlers - mFMP->childDisable("calculate_btn"); - } - else - { - for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) - { - int refFaceCnt = 0; - int modelFaceCnt = 0; - llassert(instance.mLOD[i]); - // Fix material matching algorithm to work as per design - // if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt)) - if (instance.mLOD[i] && !matchMaterialOrder(instance.mLOD[i],high_lod_model, refFaceCnt, modelFaceCnt)) - // - { - LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has mismatching materials between lods." , true); - load_state = LLModelLoader::ERROR_MATERIALS_NOT_A_SUBSET; // more descriptive errors - mFMP->childDisable("calculate_btn"); - } - } - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; - bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean(); - if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0) - { - LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix); - LLQuaternion identity; - if (!bind_rot.isEqualEps(identity, 0.01f)) - { - // Bind shape matrix is not in standard X-forward orientation. - // Might be good idea to only show this once. It can be spammy. - std::ostringstream out; - out << "non-identity bind shape rot. mat is "; - out << high_lod_model->mSkinInfo.mBindShapeMatrix; - out << " bind_rot "; - out << bind_rot; - LL_WARNS() << out.str() << LL_ENDL; + LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH]; + if (!high_lod_model) + { + LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has no High Lod (LOD3).", true); + load_state = LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING; // FIRE-30965 Cleanup braindead mesh parsing error handlers + mFMP->childDisable("calculate_btn"); + } + else + { + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + int refFaceCnt = 0; + int modelFaceCnt = 0; + llassert(instance.mLOD[i]); + // Fix material matching algorithm to work as per design + // if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt)) + if (instance.mLOD[i] && !matchMaterialOrder(instance.mLOD[i],high_lod_model, refFaceCnt, modelFaceCnt)) + // + { + LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has mismatching materials between lods." , true); + load_state = LLModelLoader::ERROR_MATERIALS_NOT_A_SUBSET; // more descriptive errors + mFMP->childDisable("calculate_btn"); + } + } + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean(); + if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0) + { + LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix); + LLQuaternion identity; + if (!bind_rot.isEqualEps(identity, 0.01f)) + { + // Bind shape matrix is not in standard X-forward orientation. + // Might be good idea to only show this once. It can be spammy. + std::ostringstream out; + out << "non-identity bind shape rot. mat is "; + out << high_lod_model->mSkinInfo.mBindShapeMatrix; + out << " bind_rot "; + out << bind_rot; + LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, getLoadState() != LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION); - load_state = LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION; - } - } - } - instance.mTransform = mat; - mUploadData.push_back(instance); - } - } + LLFloaterModelPreview::addStringToLog(out, getLoadState() != LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION); + load_state = LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION; + } + } + } + instance.mTransform = mat; + mUploadData.push_back(instance); + } + } - for (U32 lod = 0; lod < LLModel::NUM_LODS - 1; lod++) - { - // Search for models that are not included into upload data - // If we found any, that means something we loaded is not a sub-model. - for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind) - { - bool found_model = false; - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - if (instance.mLOD[lod] == mModel[lod][model_ind]) - { - found_model = true; - break; - } - } - if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID) - { - // this is not debug, this is an important/useful error advisory - // if (mImporterDebug) - { - std::ostringstream out; - out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models."; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - } - load_state = LLModelLoader::ERROR_LOD_MODEL_MISMATCH; - mFMP->childDisable("calculate_btn"); - } - } - } + for (U32 lod = 0; lod < LLModel::NUM_LODS - 1; lod++) + { + // Search for models that are not included into upload data + // If we found any, that means something we loaded is not a sub-model. + for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind) + { + bool found_model = false; + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + if (instance.mLOD[lod] == mModel[lod][model_ind]) + { + found_model = true; + break; + } + } + if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID) + { + // this is not debug, this is an important/useful error advisory + // if (mImporterDebug) + { + std::ostringstream out; + out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + } + load_state = LLModelLoader::ERROR_LOD_MODEL_MISMATCH; + mFMP->childDisable("calculate_btn"); + } + } + } - // Update state for notifications - if (load_state > 0) - { - // encountered issues - setLoadState(load_state); - } - // FIRE-30965 Cleanup braindead mesh parsing error handlers - // else if (getLoadState() == LLModelLoader::ERROR_MATERIALS - else if (getLoadState() == LLModelLoader::ERROR_MATERIALS_NOT_A_SUBSET - || getLoadState() == LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING - || getLoadState() == LLModelLoader::ERROR_LOD_MODEL_MISMATCH - // - || getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION) - { - // This is only valid for these two error types because they are - // only used inside rebuildUploadData() and updateStatusMessages() - // updateStatusMessages() is called after rebuildUploadData() - setLoadState(LLModelLoader::DONE); - } + // Update state for notifications + if (load_state > 0) + { + // encountered issues + setLoadState(load_state); + } + // FIRE-30965 Cleanup braindead mesh parsing error handlers + // else if (getLoadState() == LLModelLoader::ERROR_MATERIALS + else if (getLoadState() == LLModelLoader::ERROR_MATERIALS_NOT_A_SUBSET + || getLoadState() == LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING + || getLoadState() == LLModelLoader::ERROR_LOD_MODEL_MISMATCH + // + || getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION) + { + // This is only valid for these two error types because they are + // only used inside rebuildUploadData() and updateStatusMessages() + // updateStatusMessages() is called after rebuildUploadData() + setLoadState(LLModelLoader::DONE); + } - // OpenSim limits - //F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE - 0.1f) / max_scale; - F32 region_max_prim_scale = LLWorld::getInstance()->getRegionMaxPrimScale(); - F32 max_import_scale = region_max_prim_scale/max_scale; - // + // OpenSim limits + //F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE - 0.1f) / max_scale; + F32 region_max_prim_scale = LLWorld::getInstance()->getRegionMaxPrimScale(); + F32 max_import_scale = region_max_prim_scale/max_scale; + // - F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]); - max_axis = llmax(max_axis, mPreviewScale.mV[2]); - max_axis *= 2.f; + F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]); + max_axis = llmax(max_axis, mPreviewScale.mV[2]); + max_axis *= 2.f; - //clamp scale so that total imported model bounding box is smaller than 240m on a side - max_import_scale = llmin(max_import_scale, 240.f / max_axis); + //clamp scale so that total imported model bounding box is smaller than 240m on a side + max_import_scale = llmin(max_import_scale, 240.f / max_axis); - scale_spinner->setMaxValue(max_import_scale); + scale_spinner->setMaxValue(max_import_scale); - if (max_import_scale < scale) - { - scale_spinner->setValue(max_import_scale); - } + if (max_import_scale < scale) + { + scale_spinner->setValue(max_import_scale); + } } void LLModelPreview::saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) { - if (!mLODFile[LLModel::LOD_HIGH].empty()) - { - std::string filename = mLODFile[LLModel::LOD_HIGH]; - std::string slm_filename; + if (!mLODFile[LLModel::LOD_HIGH].empty()) + { + std::string filename = mLODFile[LLModel::LOD_HIGH]; + std::string slm_filename; - if (LLModelLoader::getSLMFilename(filename, slm_filename)) - { - saveUploadData(slm_filename, save_skinweights, save_joint_positions, lock_scale_if_joint_position); - } - } + if (LLModelLoader::getSLMFilename(filename, slm_filename)) + { + saveUploadData(slm_filename, save_skinweights, save_joint_positions, lock_scale_if_joint_position); + } + } } void LLModelPreview::saveUploadData(const std::string& filename, - bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) + bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) { - std::set > meshes; - std::map mesh_binary; + std::set > meshes; + std::map mesh_binary; - LLModel::hull empty_hull; + LLModel::hull empty_hull; - LLSD data; + LLSD data; - data["version"] = SLM_SUPPORTED_VERSION; - if (!mBaseModel.empty()) - { - data["name"] = mBaseModel[0]->getName(); - } + data["version"] = SLM_SUPPORTED_VERSION; + if (!mBaseModel.empty()) + { + data["name"] = mBaseModel[0]->getName(); + } - S32 mesh_id = 0; + S32 mesh_id = 0; - //build list of unique models and initialize local id - for (U32 i = 0; i < mUploadData.size(); ++i) - { - LLModelInstance& instance = mUploadData[i]; + //build list of unique models and initialize local id + for (U32 i = 0; i < mUploadData.size(); ++i) + { + LLModelInstance& instance = mUploadData[i]; - if (meshes.find(instance.mModel) == meshes.end()) - { - instance.mModel->mLocalID = mesh_id++; - meshes.insert(instance.mModel); + if (meshes.find(instance.mModel) == meshes.end()) + { + instance.mModel->mLocalID = mesh_id++; + meshes.insert(instance.mModel); - std::stringstream str; - LLModel::Decomposition& decomp = - instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? - instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : - instance.mModel->mPhysics; + std::stringstream str; + LLModel::Decomposition& decomp = + instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? + instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : + instance.mModel->mPhysics; - LLModel::writeModel(str, - instance.mLOD[LLModel::LOD_PHYSICS], - instance.mLOD[LLModel::LOD_HIGH], - instance.mLOD[LLModel::LOD_MEDIUM], - instance.mLOD[LLModel::LOD_LOW], - instance.mLOD[LLModel::LOD_IMPOSTOR], - decomp, - save_skinweights, - save_joint_positions, - lock_scale_if_joint_position, - FALSE, TRUE, instance.mModel->mSubmodelID); + LLModel::writeModel(str, + instance.mLOD[LLModel::LOD_PHYSICS], + instance.mLOD[LLModel::LOD_HIGH], + instance.mLOD[LLModel::LOD_MEDIUM], + instance.mLOD[LLModel::LOD_LOW], + instance.mLOD[LLModel::LOD_IMPOSTOR], + decomp, + save_skinweights, + save_joint_positions, + lock_scale_if_joint_position, + FALSE, TRUE, instance.mModel->mSubmodelID); - data["mesh"][instance.mModel->mLocalID] = str.str(); - } + data["mesh"][instance.mModel->mLocalID] = str.str(); + } - data["instance"][i] = instance.asLLSD(); - } + data["instance"][i] = instance.asLLSD(); + } - llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary); - LLSDSerialize::toBinary(data, out); - out.flush(); - out.close(); + llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary); + LLSDSerialize::toBinary(data, out); + out.flush(); + out.close(); } void LLModelPreview::clearModel(S32 lod) { - if (lod < 0 || lod > LLModel::LOD_PHYSICS) - { - return; - } + if (lod < 0 || lod > LLModel::LOD_PHYSICS) + { + return; + } - mVertexBuffer[lod].clear(); - mModel[lod].clear(); - mScene[lod].clear(); + mVertexBuffer[lod].clear(); + mModel[lod].clear(); + mScene[lod].clear(); } void LLModelPreview::getJointAliases(JointMap& joint_map) { - // Get all standard skeleton joints from the preview avatar. - LLVOAvatar *av = getPreviewAvatar(); + // Get all standard skeleton joints from the preview avatar. + LLVOAvatar *av = getPreviewAvatar(); - //Joint names and aliases come from avatar_skeleton.xml + //Joint names and aliases come from avatar_skeleton.xml - joint_map = av->getJointAliases(); + joint_map = av->getJointAliases(); - std::vector cv_names, attach_names; - av->getSortedJointNames(1, cv_names); - av->getSortedJointNames(2, attach_names); - for (std::vector::iterator it = cv_names.begin(); it != cv_names.end(); ++it) - { - joint_map[*it] = *it; - } - for (std::vector::iterator it = attach_names.begin(); it != attach_names.end(); ++it) - { - joint_map[*it] = *it; - } + std::vector cv_names, attach_names; + av->getSortedJointNames(1, cv_names); + av->getSortedJointNames(2, attach_names); + for (std::vector::iterator it = cv_names.begin(); it != cv_names.end(); ++it) + { + joint_map[*it] = *it; + } + for (std::vector::iterator it = attach_names.begin(); it != attach_names.end(); ++it) + { + joint_map[*it] = *it; + } } void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable_slm) { - assert_main_thread(); + assert_main_thread(); - LLMutexLock lock(this); + LLMutexLock lock(this); - if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1) - { - std::ostringstream out; - out << "Invalid level of detail: "; - out << lod; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS); - return; - } + if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1) + { + std::ostringstream out; + out << "Invalid level of detail: "; + out << lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS); + return; + } - // This triggers if you bring up the file picker and then hit CANCEL. - // Just use the previous model (if any) and ignore that you brought up - // the file picker. + // This triggers if you bring up the file picker and then hit CANCEL. + // Just use the previous model (if any) and ignore that you brought up + // the file picker. - if (filename.empty()) - { - if (mBaseModel.empty()) - { - // this is the initial file picking. Close the whole floater - // if we don't have a base model to show for high LOD. - mFMP->closeFloater(false); - } - mLoading = false; - return; - } + if (filename.empty()) + { + if (mBaseModel.empty()) + { + // this is the initial file picking. Close the whole floater + // if we don't have a base model to show for high LOD. + mFMP->closeFloater(false); + } + mLoading = false; + return; + } - if (mModelLoader) - { - LL_WARNS() << "Incompleted model load operation pending." << LL_ENDL; - return; - } + if (mModelLoader) + { + LL_WARNS() << "Incompleted model load operation pending." << LL_ENDL; + return; + } - mLODFile[lod] = filename; + mLODFile[lod] = filename; - if (lod == LLModel::LOD_HIGH) - { - clearGLODGroup(); - } + if (lod == LLModel::LOD_HIGH) + { + clearGLODGroup(); + } - std::map joint_alias_map; - getJointAliases(joint_alias_map); + std::map joint_alias_map; + getJointAliases(joint_alias_map); - mModelLoader = new LLDAELoader( - filename, - lod, - &LLModelPreview::loadedCallback, - &LLModelPreview::lookupJointByName, - &LLModelPreview::loadTextures, - &LLModelPreview::stateChangedCallback, - this, - mJointTransformMap, - mJointsFromNode, - joint_alias_map, - LLSkinningUtil::getMaxJointCount(), - gSavedSettings.getU32("ImporterModelLimit"), - gSavedSettings.getBOOL("ImporterPreprocessDAE")); + mModelLoader = new LLDAELoader( + filename, + lod, + &LLModelPreview::loadedCallback, + &LLModelPreview::lookupJointByName, + &LLModelPreview::loadTextures, + &LLModelPreview::stateChangedCallback, + this, + mJointTransformMap, + mJointsFromNode, + joint_alias_map, + LLSkinningUtil::getMaxJointCount(), + gSavedSettings.getU32("ImporterModelLimit"), + gSavedSettings.getBOOL("ImporterPreprocessDAE")); - if (force_disable_slm) - { - mModelLoader->mTrySLM = false; - } - else - { - // For MAINT-6647, we have set force_disable_slm to true, - // which means this code path will never be taken. Trying to - // re-use SLM files has never worked properly; in particular, - // it tends to force the UI into strange checkbox options - // which cannot be altered. + if (force_disable_slm) + { + mModelLoader->mTrySLM = false; + } + else + { + // For MAINT-6647, we have set force_disable_slm to true, + // which means this code path will never be taken. Trying to + // re-use SLM files has never worked properly; in particular, + // it tends to force the UI into strange checkbox options + // which cannot be altered. - //only try to load from slm if viewer is configured to do so and this is the - //initial model load (not an LoD or physics shape) - mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty(); - } - mModelLoader->start(); + //only try to load from slm if viewer is configured to do so and this is the + //initial model load (not an LoD or physics shape) + mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty(); + } + mModelLoader->start(); - mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); + mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); - setPreviewLOD(lod); + setPreviewLOD(lod); - if (getLoadState() >= LLModelLoader::ERROR_PARSING) - { - mFMP->childDisable("ok_btn"); - mFMP->childDisable("calculate_btn"); - } + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mFMP->childDisable("ok_btn"); + mFMP->childDisable("calculate_btn"); + } - if (lod == mPreviewLOD) - { - mFMP->childSetValue("lod_file_" + lod_name[lod], mLODFile[lod]); - } - else if (lod == LLModel::LOD_PHYSICS) - { - mFMP->childSetValue("physics_file", mLODFile[lod]); - } + if (lod == mPreviewLOD) + { + mFMP->childSetValue("lod_file_" + lod_name[lod], mLODFile[lod]); + } + else if (lod == LLModel::LOD_PHYSICS) + { + mFMP->childSetValue("physics_file", mLODFile[lod]); + } - mFMP->openFloater(); + mFMP->openFloater(); } void LLModelPreview::setPhysicsFromLOD(S32 lod) { - assert_main_thread(); + assert_main_thread(); - if (lod >= 0 && lod <= 3) - { - mPhysicsSearchLOD = lod; - mModel[LLModel::LOD_PHYSICS] = mModel[lod]; - mScene[LLModel::LOD_PHYSICS] = mScene[lod]; - mLODFile[LLModel::LOD_PHYSICS].clear(); - mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); - mVertexBuffer[LLModel::LOD_PHYSICS].clear(); - rebuildUploadData(); - refresh(); - updateStatusMessages(); - } + if (lod >= 0 && lod <= 3) + { + mPhysicsSearchLOD = lod; + mModel[LLModel::LOD_PHYSICS] = mModel[lod]; + mScene[LLModel::LOD_PHYSICS] = mScene[lod]; + mLODFile[LLModel::LOD_PHYSICS].clear(); + mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); + mVertexBuffer[LLModel::LOD_PHYSICS].clear(); + rebuildUploadData(); + refresh(); + updateStatusMessages(); + } } // FIRE-30963 - better physics defaults void LLModelPreview::setPhysicsFromPreset(S32 preset) { - assert_main_thread(); + assert_main_thread(); - mPhysicsSearchLOD = -1; - mLODFile[LLModel::LOD_PHYSICS].clear(); - mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); - mVertexBuffer[LLModel::LOD_PHYSICS].clear(); - if(preset == 1) - { - mPhysicsSearchLOD = LLModel::LOD_PHYSICS; - loadModel( gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, "cube_phys.dae"), LLModel::LOD_PHYSICS); - } - else if(preset == 2) - { - mPhysicsSearchLOD = LLModel::LOD_PHYSICS; - loadModel( gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, "hex_phys.dae"), LLModel::LOD_PHYSICS); - } - else if(preset == 3) - { - auto ud_physics = gSavedSettings.getString("FSPhysicsPresetUser1"); - LL_INFOS() << "Loading User defined Physics Preset [" << ud_physics << "]" << LL_ENDL; - if (ud_physics != "" && gDirUtilp->fileExists(ud_physics)) - { - // loading physics from file - mPhysicsSearchLOD = LLModel::LOD_PHYSICS; - loadModel( gDirUtilp->getExpandedFilename(LL_PATH_NONE, gDirUtilp->getDirName(ud_physics), gDirUtilp->getBaseFileName(ud_physics, false)), LLModel::LOD_PHYSICS); - } - } + mPhysicsSearchLOD = -1; + mLODFile[LLModel::LOD_PHYSICS].clear(); + mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); + mVertexBuffer[LLModel::LOD_PHYSICS].clear(); + if(preset == 1) + { + mPhysicsSearchLOD = LLModel::LOD_PHYSICS; + loadModel( gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, "cube_phys.dae"), LLModel::LOD_PHYSICS); + } + else if(preset == 2) + { + mPhysicsSearchLOD = LLModel::LOD_PHYSICS; + loadModel( gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, "hex_phys.dae"), LLModel::LOD_PHYSICS); + } + else if(preset == 3) + { + auto ud_physics = gSavedSettings.getString("FSPhysicsPresetUser1"); + LL_INFOS() << "Loading User defined Physics Preset [" << ud_physics << "]" << LL_ENDL; + if (ud_physics != "" && gDirUtilp->fileExists(ud_physics)) + { + // loading physics from file + mPhysicsSearchLOD = LLModel::LOD_PHYSICS; + loadModel( gDirUtilp->getExpandedFilename(LL_PATH_NONE, gDirUtilp->getDirName(ud_physics), gDirUtilp->getBaseFileName(ud_physics, false)), LLModel::LOD_PHYSICS); + } + } } // void LLModelPreview::clearIncompatible(S32 lod) { - //Don't discard models if specified model is the physic rep - if (lod == LLModel::LOD_PHYSICS) - { - return; - } + //Don't discard models if specified model is the physic rep + if (lod == LLModel::LOD_PHYSICS) + { + return; + } - // at this point we don't care about sub-models, - // different amount of sub-models means face count mismatch, not incompatibility - U32 lod_size = countRootModels(mModel[lod]); - for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) - { //clear out any entries that aren't compatible with this model - if (i != lod) - { - if (countRootModels(mModel[i]) != lod_size) - { - mModel[i].clear(); - mScene[i].clear(); - mVertexBuffer[i].clear(); + // at this point we don't care about sub-models, + // different amount of sub-models means face count mismatch, not incompatibility + U32 lod_size = countRootModels(mModel[lod]); + for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) + { //clear out any entries that aren't compatible with this model + if (i != lod) + { + if (countRootModels(mModel[i]) != lod_size) + { + mModel[i].clear(); + mScene[i].clear(); + mVertexBuffer[i].clear(); - if (i == LLModel::LOD_HIGH) - { - mBaseModel = mModel[lod]; - clearGLODGroup(); - mBaseScene = mScene[lod]; - mVertexBuffer[5].clear(); - } - } - } - } + if (i == LLModel::LOD_HIGH) + { + mBaseModel = mModel[lod]; + clearGLODGroup(); + mBaseScene = mScene[lod]; + mVertexBuffer[5].clear(); + } + } + } + } } void LLModelPreview::clearGLODGroup() { - if (mGroup) - { - for (std::map, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) - { - glodDeleteObject(iter->second); - stop_gloderror(); - } - mObject.clear(); + if (mGroup) + { + for (std::map, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) + { + glodDeleteObject(iter->second); + stop_gloderror(); + } + mObject.clear(); - glodDeleteGroup(mGroup); - stop_gloderror(); - mGroup = 0; - } + glodDeleteGroup(mGroup); + stop_gloderror(); + mGroup = 0; + } } void LLModelPreview::loadModelCallback(S32 loaded_lod) { - assert_main_thread(); + assert_main_thread(); - LLMutexLock lock(this); - if (!mModelLoader) - { - mLoading = false; - return; - } - if (getLoadState() >= LLModelLoader::ERROR_PARSING) - { - mLoading = false; - mModelLoader = NULL; - mLodsWithParsingError.push_back(loaded_lod); - return; - } + LLMutexLock lock(this); + if (!mModelLoader) + { + mLoading = false; + return; + } + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mLoading = false; + mModelLoader = NULL; + mLodsWithParsingError.push_back(loaded_lod); + return; + } - mLodsWithParsingError.erase(std::remove(mLodsWithParsingError.begin(), mLodsWithParsingError.end(), loaded_lod), mLodsWithParsingError.end()); - if (mLodsWithParsingError.empty()) - { - mFMP->childEnable("calculate_btn"); - } + mLodsWithParsingError.erase(std::remove(mLodsWithParsingError.begin(), mLodsWithParsingError.end(), loaded_lod), mLodsWithParsingError.end()); + if (mLodsWithParsingError.empty()) + { + mFMP->childEnable("calculate_btn"); + } - // Copy determinations about rig so UI will reflect them - // - setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload()); - setLegacyRigFlags(mModelLoader->getLegacyRigFlags()); + // Copy determinations about rig so UI will reflect them + // + setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload()); + setLegacyRigFlags(mModelLoader->getLegacyRigFlags()); - mModelLoader->loadTextures(); + mModelLoader->loadTextures(); - if (loaded_lod == -1) - { //populate all LoDs from model loader scene - mBaseModel.clear(); - mBaseScene.clear(); + if (loaded_lod == -1) + { //populate all LoDs from model loader scene + mBaseModel.clear(); + mBaseScene.clear(); - bool skin_weights = false; - bool joint_overrides = false; - bool lock_scale_if_joint_position = false; + bool skin_weights = false; + bool joint_overrides = false; + bool lock_scale_if_joint_position = false; - for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) - { //for each LoD + for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) + { //for each LoD - //clear scene and model info - mScene[lod].clear(); - mModel[lod].clear(); - mVertexBuffer[lod].clear(); + //clear scene and model info + mScene[lod].clear(); + mModel[lod].clear(); + mVertexBuffer[lod].clear(); - if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull()) - { //if this LoD exists in the loaded scene + if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull()) + { //if this LoD exists in the loaded scene - //copy scene to current LoD - mScene[lod] = mModelLoader->mScene; + //copy scene to current LoD + mScene[lod] = mModelLoader->mScene; - //touch up copied scene to look like current LoD - for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) - { - LLModelLoader::model_instance_list& list = iter->second; + //touch up copied scene to look like current LoD + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + LLModelLoader::model_instance_list& list = iter->second; - for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter) - { - //override displayed model with current LoD - list_iter->mModel = list_iter->mLOD[lod]; + for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter) + { + //override displayed model with current LoD + list_iter->mModel = list_iter->mLOD[lod]; - if (!list_iter->mModel) - { - continue; - } + if (!list_iter->mModel) + { + continue; + } - //add current model to current LoD's model list (LLModel::mLocalID makes a good vector index) - S32 idx = list_iter->mModel->mLocalID; + //add current model to current LoD's model list (LLModel::mLocalID makes a good vector index) + S32 idx = list_iter->mModel->mLocalID; - if (mModel[lod].size() <= idx) - { //stretch model list to fit model at given index - mModel[lod].resize(idx + 1); - } + if (mModel[lod].size() <= idx) + { //stretch model list to fit model at given index + mModel[lod].resize(idx + 1); + } - mModel[lod][idx] = list_iter->mModel; - if (!list_iter->mModel->mSkinWeights.empty()) - { - skin_weights = true; + mModel[lod][idx] = list_iter->mModel; + if (!list_iter->mModel->mSkinWeights.empty()) + { + skin_weights = true; - if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty()) - { - joint_overrides = true; - } - if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition) - { - lock_scale_if_joint_position = true; - } - } - } - } - } - } + if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty()) + { + joint_overrides = true; + } + if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition) + { + lock_scale_if_joint_position = true; + } + } + } + } + } + } - if (mFMP) - { - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + if (mFMP) + { + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; - if (skin_weights) - { //enable uploading/previewing of skin weights if present in .slm file - fmp->enableViewOption("show_skin_weight"); - mViewOption["show_skin_weight"] = true; - fmp->childSetValue("upload_skin", true); - } + if (skin_weights) + { //enable uploading/previewing of skin weights if present in .slm file + fmp->enableViewOption("show_skin_weight"); + mViewOption["show_skin_weight"] = true; + fmp->childSetValue("upload_skin", true); + } - if (joint_overrides) - { - fmp->enableViewOption("show_joint_overrides"); - mViewOption["show_joint_overrides"] = true; - fmp->childSetValue("show_joint_overrides", true); // make sure option appears checked, when value is being forced true - fmp->enableViewOption("show_joint_positions"); - fmp->childSetValue("show_joint_positions", true); // make sure option appears checked, when value is being forced true - mViewOption["show_joint_positions"] = true; - fmp->childSetValue("upload_joints", true); - } - else - { - fmp->clearAvatarTab(); - } + if (joint_overrides) + { + fmp->enableViewOption("show_joint_overrides"); + mViewOption["show_joint_overrides"] = true; + fmp->childSetValue("show_joint_overrides", true); // make sure option appears checked, when value is being forced true + fmp->enableViewOption("show_joint_positions"); + fmp->childSetValue("show_joint_positions", true); // make sure option appears checked, when value is being forced true + mViewOption["show_joint_positions"] = true; + fmp->childSetValue("upload_joints", true); + } + else + { + fmp->clearAvatarTab(); + } - if (lock_scale_if_joint_position) - { - fmp->enableViewOption("lock_scale_if_joint_position"); - mViewOption["lock_scale_if_joint_position"] = true; - fmp->childSetValue("lock_scale_if_joint_position", true); - } - } + if (lock_scale_if_joint_position) + { + fmp->enableViewOption("lock_scale_if_joint_position"); + mViewOption["lock_scale_if_joint_position"] = true; + fmp->childSetValue("lock_scale_if_joint_position", true); + } + } - //copy high lod to base scene for LoD generation - mBaseScene = mScene[LLModel::LOD_HIGH]; - mBaseModel = mModel[LLModel::LOD_HIGH]; + //copy high lod to base scene for LoD generation + mBaseScene = mScene[LLModel::LOD_HIGH]; + mBaseModel = mModel[LLModel::LOD_HIGH]; - mDirty = true; - resetPreviewTarget(); - } - else - { //only replace given LoD - mModel[loaded_lod] = mModelLoader->mModelList; - mScene[loaded_lod] = mModelLoader->mScene; - mVertexBuffer[loaded_lod].clear(); + mDirty = true; + resetPreviewTarget(); + } + else + { //only replace given LoD + mModel[loaded_lod] = mModelLoader->mModelList; + mScene[loaded_lod] = mModelLoader->mScene; + mVertexBuffer[loaded_lod].clear(); - setPreviewLOD(loaded_lod); + setPreviewLOD(loaded_lod); - if (loaded_lod == LLModel::LOD_HIGH) - { //save a copy of the highest LOD for automatic LOD manipulation - if (mBaseModel.empty()) - { //first time we've loaded a model, auto-gen LoD - mGenLOD = true; - } + if (loaded_lod == LLModel::LOD_HIGH) + { //save a copy of the highest LOD for automatic LOD manipulation + if (mBaseModel.empty()) + { //first time we've loaded a model, auto-gen LoD + mGenLOD = true; + } - mBaseModel = mModel[loaded_lod]; - clearGLODGroup(); + mBaseModel = mModel[loaded_lod]; + clearGLODGroup(); - mBaseScene = mScene[loaded_lod]; - mVertexBuffer[5].clear(); - } - else - { - BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); - if (!legacyMatching) - { - if (!mBaseModel.empty()) - { - BOOL name_based = FALSE; - BOOL has_submodels = FALSE; - for (U32 idx = 0; idx < mBaseModel.size(); ++idx) - { - if (mBaseModel[idx]->mSubmodelID) - { // don't do index-based renaming when the base model has submodels - has_submodels = TRUE; - if (mImporterDebug) - { - std::ostringstream out; - out << "High LOD has submodels"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } - break; - } - } + mBaseScene = mScene[loaded_lod]; + mVertexBuffer[5].clear(); + } + else + { + BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); + if (!legacyMatching) + { + if (!mBaseModel.empty()) + { + BOOL name_based = FALSE; + BOOL has_submodels = FALSE; + for (U32 idx = 0; idx < mBaseModel.size(); ++idx) + { + if (mBaseModel[idx]->mSubmodelID) + { // don't do index-based renaming when the base model has submodels + has_submodels = TRUE; + if (mImporterDebug) + { + std::ostringstream out; + out << "High LOD has submodels"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + break; + } + } - for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx) - { - std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); + for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx) + { + std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); - LLModel* found_model = NULL; - LLMatrix4 transform; - FindModel(mBaseScene, loaded_name, found_model, transform); - if (found_model) - { // don't rename correctly named models (even if they are placed in a wrong order) - name_based = TRUE; - } + LLModel* found_model = NULL; + LLMatrix4 transform; + FindModel(mBaseScene, loaded_name, found_model, transform); + if (found_model) + { // don't rename correctly named models (even if they are placed in a wrong order) + name_based = TRUE; + } - if (mModel[loaded_lod][idx]->mSubmodelID) - { // don't rename the models when loaded LOD model has submodels - has_submodels = TRUE; - } - } + if (mModel[loaded_lod][idx]->mSubmodelID) + { // don't rename the models when loaded LOD model has submodels + has_submodels = TRUE; + } + } - if (mImporterDebug) - { - std::ostringstream out; - out << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } + if (mImporterDebug) + { + std::ostringstream out; + out << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } - if (!name_based && !has_submodels) - { // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601) - // this actually works like "ImporterLegacyMatching" for this particular LOD - for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx) - { - std::string name = mBaseModel[idx]->mLabel; - std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); + if (!name_based && !has_submodels) + { // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601) + // this actually works like "ImporterLegacyMatching" for this particular LOD + for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx) + { + std::string name = mBaseModel[idx]->mLabel; + std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); - if (loaded_name != name) - { - name += getLodSuffix(loaded_lod); + if (loaded_name != name) + { + name += getLodSuffix(loaded_lod); - if (mImporterDebug) - { - std::ostringstream out; - out << "Loded model name " << mModel[loaded_lod][idx]->mLabel; - out << " for LOD " << loaded_lod; - out << " doesn't match the base model. Renaming to " << name; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - } + if (mImporterDebug) + { + std::ostringstream out; + out << "Loded model name " << mModel[loaded_lod][idx]->mLabel; + out << " for LOD " << loaded_lod; + out << " doesn't match the base model. Renaming to " << name; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } - mModel[loaded_lod][idx]->mLabel = name; - } - } - } - } - } - } + mModel[loaded_lod][idx]->mLabel = name; + } + } + } + } + } + } - clearIncompatible(loaded_lod); + clearIncompatible(loaded_lod); - mDirty = true; + mDirty = true; - if (loaded_lod == LLModel::LOD_HIGH) - { - resetPreviewTarget(); - } - } + if (loaded_lod == LLModel::LOD_HIGH) + { + resetPreviewTarget(); + } + } - mLoading = false; - if (mFMP) - { - if (!mBaseModel.empty()) - { - const std::string& model_name = mBaseModel[0]->getName(); - LLLineEditor* description_form = mFMP->getChild("description_form"); - if (description_form->getText().empty()) - { - description_form->setText(model_name); - } - // Add info to log that loading is complete (purpose: separator between loading and other logs) - LLSD args; - args["MODEL_NAME"] = model_name; // Teoretically shouldn't be empty, but might be better idea to add filename here - LLFloaterModelPreview::addStringToLog("ModelLoaded", args, false, loaded_lod); - } - } - refresh(); + mLoading = false; + if (mFMP) + { + if (!mBaseModel.empty()) + { + const std::string& model_name = mBaseModel[0]->getName(); + LLLineEditor* description_form = mFMP->getChild("description_form"); + if (description_form->getText().empty()) + { + description_form->setText(model_name); + } + // Add info to log that loading is complete (purpose: separator between loading and other logs) + LLSD args; + args["MODEL_NAME"] = model_name; // Teoretically shouldn't be empty, but might be better idea to add filename here + LLFloaterModelPreview::addStringToLog("ModelLoaded", args, false, loaded_lod); + } + } + refresh(); - mModelLoadedSignal(); + mModelLoadedSignal(); - mModelLoader = NULL; + mModelLoader = NULL; } void LLModelPreview::resetPreviewTarget() { - if (mModelLoader) - { - mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; - mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; - } + if (mModelLoader) + { + mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; + mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; + } - setPreviewTarget(mPreviewScale.magVec()*10.f); + setPreviewTarget(mPreviewScale.magVec()*10.f); } void LLModelPreview::generateNormals() { - assert_main_thread(); + assert_main_thread(); - S32 which_lod = mPreviewLOD; + S32 which_lod = mPreviewLOD; - if (which_lod > 4 || which_lod < 0 || - mModel[which_lod].empty()) - { - return; - } + if (which_lod > 4 || which_lod < 0 || + mModel[which_lod].empty()) + { + return; + } - F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); + F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); - mRequestedCreaseAngle[which_lod] = angle_cutoff; + mRequestedCreaseAngle[which_lod] = angle_cutoff; - angle_cutoff *= DEG_TO_RAD; + angle_cutoff *= DEG_TO_RAD; - if (which_lod == 3 && !mBaseModel.empty()) - { - if (mBaseModelFacesCopy.empty()) - { - mBaseModelFacesCopy.reserve(mBaseModel.size()); - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) - { - v_LLVolumeFace_t faces; - (*it)->copyFacesTo(faces); - mBaseModelFacesCopy.push_back(faces); - } - } + if (which_lod == 3 && !mBaseModel.empty()) + { + if (mBaseModelFacesCopy.empty()) + { + mBaseModelFacesCopy.reserve(mBaseModel.size()); + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) + { + v_LLVolumeFace_t faces; + (*it)->copyFacesTo(faces); + mBaseModelFacesCopy.push_back(faces); + } + } - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) - { - (*it)->generateNormals(angle_cutoff); - } + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) + { + (*it)->generateNormals(angle_cutoff); + } - mVertexBuffer[5].clear(); - } + mVertexBuffer[5].clear(); + } - bool perform_copy = mModelFacesCopy[which_lod].empty(); - if (perform_copy) { - mModelFacesCopy[which_lod].reserve(mModel[which_lod].size()); - } + bool perform_copy = mModelFacesCopy[which_lod].empty(); + if (perform_copy) { + mModelFacesCopy[which_lod].reserve(mModel[which_lod].size()); + } - for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it) - { - if (perform_copy) - { - v_LLVolumeFace_t faces; - (*it)->copyFacesTo(faces); - mModelFacesCopy[which_lod].push_back(faces); - } + for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it) + { + if (perform_copy) + { + v_LLVolumeFace_t faces; + (*it)->copyFacesTo(faces); + mModelFacesCopy[which_lod].push_back(faces); + } - (*it)->generateNormals(angle_cutoff); - } + (*it)->generateNormals(angle_cutoff); + } - mVertexBuffer[which_lod].clear(); - refresh(); - updateStatusMessages(); + mVertexBuffer[which_lod].clear(); + refresh(); + updateStatusMessages(); } void LLModelPreview::restoreNormals() { - S32 which_lod = mPreviewLOD; + S32 which_lod = mPreviewLOD; - if (which_lod > 4 || which_lod < 0 || - mModel[which_lod].empty()) - { - return; - } + if (which_lod > 4 || which_lod < 0 || + mModel[which_lod].empty()) + { + return; + } - if (!mBaseModelFacesCopy.empty()) - { - llassert(mBaseModelFacesCopy.size() == mBaseModel.size()); + if (!mBaseModelFacesCopy.empty()) + { + llassert(mBaseModelFacesCopy.size() == mBaseModel.size()); - vv_LLVolumeFace_t::const_iterator itF = mBaseModelFacesCopy.begin(); - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it, ++itF) - { - (*it)->copyFacesFrom((*itF)); - } + vv_LLVolumeFace_t::const_iterator itF = mBaseModelFacesCopy.begin(); + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it, ++itF) + { + (*it)->copyFacesFrom((*itF)); + } - mBaseModelFacesCopy.clear(); - } + mBaseModelFacesCopy.clear(); + } - if (!mModelFacesCopy[which_lod].empty()) - { - vv_LLVolumeFace_t::const_iterator itF = mModelFacesCopy[which_lod].begin(); - for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it, ++itF) - { - (*it)->copyFacesFrom((*itF)); - } + if (!mModelFacesCopy[which_lod].empty()) + { + vv_LLVolumeFace_t::const_iterator itF = mModelFacesCopy[which_lod].begin(); + for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it, ++itF) + { + (*it)->copyFacesFrom((*itF)); + } - mModelFacesCopy[which_lod].clear(); - } + mModelFacesCopy[which_lod].clear(); + } - mVertexBuffer[which_lod].clear(); - refresh(); - updateStatusMessages(); + mVertexBuffer[which_lod].clear(); + refresh(); + updateStatusMessages(); } void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) { - // Allow LoD from -1 to LLModel::LOD_PHYSICS - if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1) - { - std::ostringstream out; - out << "Invalid level of detail: " << which_lod; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS); - return; - } - - if (mBaseModel.empty()) - { - return; - } - - LLVertexBuffer::unbind(); - - bool no_ff = LLGLSLShader::sNoFixedFunction; - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - LLGLSLShader::sNoFixedFunction = false; - - if (shader) - { - shader->unbind(); - } - - stop_gloderror(); - static U32 cur_name = 1; - - S32 limit = -1; - - U32 triangle_count = 0; - - U32 instanced_triangle_count = 0; - - //get the triangle count for the whole scene - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter) - { - for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) - { - LLModel* mdl = instance->mModel; - if (mdl) - { - instanced_triangle_count += mdl->getNumTriangles(); - } - } - } - - //get the triangle count for the non-instanced set of models - for (U32 i = 0; i < mBaseModel.size(); ++i) - { - triangle_count += mBaseModel[i]->getNumTriangles(); - } - - //get ratio of uninstanced triangles to instanced triangles - F32 triangle_ratio = (F32)triangle_count / (F32)instanced_triangle_count; - - U32 base_triangle_count = triangle_count; - - U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - U32 lod_mode = 0; - - F32 lod_error_threshold = 0; - - // The LoD should be in range from Lowest to High - if (which_lod > -1 && which_lod < NUM_LOD) - { - LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]); - if (iface) - { - lod_mode = iface->getFirstSelectedIndex(); - } - - lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal(); - } - - if (which_lod != -1) - { - mRequestedLoDMode[which_lod] = lod_mode; - } - - if (lod_mode == 0) - { - lod_mode = GLOD_TRIANGLE_BUDGET; - - // The LoD should be in range from Lowest to High - if (which_lod > -1 && which_lod < NUM_LOD) - { - limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger(); - //convert from "scene wide" to "non-instanced" triangle limit - limit = (S32)((F32)limit*triangle_ratio); - } - } - else - { - lod_mode = GLOD_ERROR_THRESHOLD; - } - - bool object_dirty = false; - - if (mGroup == 0) - { - object_dirty = true; - mGroup = cur_name++; - glodNewGroup(mGroup); - } - - if (object_dirty) - { - for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) - { //build GLOD objects for each model in base model list - LLModel* mdl = *iter; - - if (mObject[mdl] != 0) - { - glodDeleteObject(mObject[mdl]); - } - - mObject[mdl] = cur_name++; - - glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); - stop_gloderror(); - - if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) - { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation - mVertexBuffer[5].clear(); - } - - if (mVertexBuffer[5].empty()) - { - genBuffers(5, false); - } - - U32 tri_count = 0; - for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) - { - LLVertexBuffer* buff = mVertexBuffer[5][mdl][i]; - buff->setBuffer(type_mask & buff->getTypeMask()); - - U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); - if (num_indices > 2) - { - // Fix glod so it works when just using the opengl core profile - //glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); - LLStrider vertex_strider; - LLStrider normal_strider; - LLStrider tc_strider; - - LLStrider< U16 > index_strider; - buff->getIndexStrider( index_strider ); - - glodVBO vbo = {}; - - if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) - { - buff->getVertexStrider( vertex_strider ); - vbo.mV.p = vertex_strider.get(); - vbo.mV.size = 3; - vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; - vbo.mV.type = GL_FLOAT; - } - if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) - { - buff->getNormalStrider( normal_strider ); - vbo.mN.p = normal_strider.get(); - vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; - vbo.mN.type = GL_FLOAT; - } - if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) - { - buff->getTexCoord0Strider( tc_strider ); - vbo.mT.p = tc_strider.get(); - vbo.mT.size = 2; - vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; - vbo.mT.type = GL_FLOAT; - } - - glodInsertElements( mObject[ mdl ], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)index_strider.get(), 0, 0.f, &vbo ); - // - } - tri_count += num_indices / 3; - stop_gloderror(); - } - - glodBuildObject(mObject[mdl]); - stop_gloderror(); - } - } - - - S32 start = LLModel::LOD_HIGH; - S32 end = 0; - - if (which_lod != -1) - { - start = end = which_lod; - } - - mMaxTriangleLimit = base_triangle_count; - - for (S32 lod = start; lod >= end; --lod) - { - if (which_lod == -1) - { - if (lod < start) - { - triangle_count /= decimation; - } - } - else - { - if (enforce_tri_limit) - { - triangle_count = limit; - } - else - { - for (S32 j = LLModel::LOD_HIGH; j>which_lod; --j) - { - triangle_count /= decimation; - } - } - } - - mModel[lod].clear(); - mModel[lod].resize(mBaseModel.size()); - mVertexBuffer[lod].clear(); - - U32 actual_tris = 0; - U32 actual_verts = 0; - U32 submeshes = 0; - - mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); - mRequestedErrorThreshold[lod] = lod_error_threshold; - - glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); - stop_gloderror(); - - glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); - stop_gloderror(); - - glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); - stop_gloderror(); - - if (lod_mode != GLOD_TRIANGLE_BUDGET) - { - glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0); - } - else - { - //SH-632: always add 1 to desired amount to avoid decimating below desired amount - glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count + 1); - } - - stop_gloderror(); - glodAdaptGroup(mGroup); - stop_gloderror(); - - for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) - { - LLModel* base = mBaseModel[mdl_idx]; - - GLint patch_count = 0; - glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); - stop_gloderror(); - - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); - - std::string name = base->mLabel + getLodSuffix(lod); - - mModel[lod][mdl_idx]->mLabel = name; - mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; - - GLint* sizes = new GLint[patch_count * 2]; - glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); - stop_gloderror(); - - GLint* names = new GLint[patch_count]; - glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); - stop_gloderror(); - - mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); - - LLModel* target_model = mModel[lod][mdl_idx]; - - for (GLint i = 0; i < patch_count; ++i) - { - type_mask = mVertexBuffer[5][base][i]->getTypeMask(); - - LLPointer buff = new LLVertexBuffer(type_mask, 0); - - if (sizes[i * 2 + 1] > 0 && sizes[i * 2] > 0) - { - if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true)) - { - // Todo: find a way to stop preview in this case instead of crashing - LL_ERRS() << "Failed buffer allocation during preview LOD generation." - << " Vertices: " << sizes[i * 2 + 1] - << " Indices: " << sizes[i * 2] << LL_ENDL; - } - buff->setBuffer(type_mask); - // Fix glod so it works when just using the opengl core profile - //glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*)buff->getIndicesPointer()); - LLStrider vertex_strider; - LLStrider normal_strider; - LLStrider tc_strider; - - LLStrider< U16 > index_strider; - buff->getIndexStrider( index_strider ); - - glodVBO vbo = {}; - - if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) - { - buff->getVertexStrider( vertex_strider ); - vbo.mV.p = vertex_strider.get(); - vbo.mV.size = 3; - vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; - vbo.mV.type = GL_FLOAT; - } - if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) - { - buff->getNormalStrider( normal_strider ); - vbo.mN.p = normal_strider.get(); - vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; - vbo.mN.type = GL_FLOAT; - } - if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) - { - buff->getTexCoord0Strider( tc_strider ); - vbo.mT.p = tc_strider.get(); - vbo.mT.size = 2; - vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; - vbo.mT.type = GL_FLOAT; - } - - glodFillElements( mObject[ base ], names[ i ], GL_UNSIGNED_SHORT, (U8*)index_strider.get(), &vbo ); - // - stop_gloderror(); - } - else - { - // This face was eliminated or we failed to allocate buffer, - // attempt to create a dummy triangle (one vertex, 3 indices, all 0) - buff->allocateBuffer(1, 3, true); - memset((U8*)buff->getMappedData(), 0, buff->getSize()); - // Fix when running with opengl core profile - //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); - LLStrider< U16 > index_strider; - buff->getIndexStrider( index_strider ); - - memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); - // - } - - buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); - - LLStrider pos; - LLStrider norm; - LLStrider tc; - LLStrider index; - - buff->getVertexStrider(pos); - if (type_mask & LLVertexBuffer::MAP_NORMAL) - { - buff->getNormalStrider(norm); - } - if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) - { - buff->getTexCoord0Strider(tc); - } - - buff->getIndexStrider(index); - - target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); - actual_tris += buff->getNumIndices() / 3; - actual_verts += buff->getNumVerts(); - ++submeshes; - - if (!validate_face(target_model->getVolumeFace(names[i]))) - { - LL_ERRS() << "Invalid face generated during LOD generation." << LL_ENDL; - } - } - - //blind copy skin weights and just take closest skin weight to point on - //decimated mesh for now (auto-generating LODs with skin weights is still a bit - //of an open problem). - target_model->mPosition = base->mPosition; - target_model->mSkinWeights = base->mSkinWeights; - target_model->mSkinInfo = base->mSkinInfo; - //copy material list - target_model->mMaterialList = base->mMaterialList; - - if (!validate_model(target_model)) - { - LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL; - } - - delete[] sizes; - delete[] names; - } - - //rebuild scene based on mBaseScene - mScene[lod].clear(); - mScene[lod] = mBaseScene; - - for (U32 i = 0; i < mBaseModel.size(); ++i) - { - LLModel* mdl = mBaseModel[i]; - LLModel* target = mModel[lod][i]; - if (target) - { - for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) - { - for (U32 j = 0; j < iter->second.size(); ++j) - { - if (iter->second[j].mModel == mdl) - { - iter->second[j].mModel = target; - } - } - } - } - } - } - - mResourceCost = calcResourceCost(); - - LLVertexBuffer::unbind(); - LLGLSLShader::sNoFixedFunction = no_ff; - if (shader) - { - shader->bind(); - } - refresh(); // refresh once to make sure render gets called with the updated vbos + // Allow LoD from -1 to LLModel::LOD_PHYSICS + if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1) + { + std::ostringstream out; + out << "Invalid level of detail: " << which_lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS); + return; + } + + if (mBaseModel.empty()) + { + return; + } + + LLVertexBuffer::unbind(); + + bool no_ff = LLGLSLShader::sNoFixedFunction; + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + LLGLSLShader::sNoFixedFunction = false; + + if (shader) + { + shader->unbind(); + } + + stop_gloderror(); + static U32 cur_name = 1; + + S32 limit = -1; + + U32 triangle_count = 0; + + U32 instanced_triangle_count = 0; + + //get the triangle count for the whole scene + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter) + { + for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) + { + LLModel* mdl = instance->mModel; + if (mdl) + { + instanced_triangle_count += mdl->getNumTriangles(); + } + } + } + + //get the triangle count for the non-instanced set of models + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + triangle_count += mBaseModel[i]->getNumTriangles(); + } + + //get ratio of uninstanced triangles to instanced triangles + F32 triangle_ratio = (F32)triangle_count / (F32)instanced_triangle_count; + + U32 base_triangle_count = triangle_count; + + U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + U32 lod_mode = 0; + + F32 lod_error_threshold = 0; + + // The LoD should be in range from Lowest to High + if (which_lod > -1 && which_lod < NUM_LOD) + { + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } + + lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal(); + } + + if (which_lod != -1) + { + mRequestedLoDMode[which_lod] = lod_mode; + } + + if (lod_mode == 0) + { + lod_mode = GLOD_TRIANGLE_BUDGET; + + // The LoD should be in range from Lowest to High + if (which_lod > -1 && which_lod < NUM_LOD) + { + limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger(); + //convert from "scene wide" to "non-instanced" triangle limit + limit = (S32)((F32)limit*triangle_ratio); + } + } + else + { + lod_mode = GLOD_ERROR_THRESHOLD; + } + + bool object_dirty = false; + + if (mGroup == 0) + { + object_dirty = true; + mGroup = cur_name++; + glodNewGroup(mGroup); + } + + if (object_dirty) + { + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { //build GLOD objects for each model in base model list + LLModel* mdl = *iter; + + if (mObject[mdl] != 0) + { + glodDeleteObject(mObject[mdl]); + } + + mObject[mdl] = cur_name++; + + glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); + stop_gloderror(); + + if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) + { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation + mVertexBuffer[5].clear(); + } + + if (mVertexBuffer[5].empty()) + { + genBuffers(5, false); + } + + U32 tri_count = 0; + for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) + { + LLVertexBuffer* buff = mVertexBuffer[5][mdl][i]; + buff->setBuffer(type_mask & buff->getTypeMask()); + + U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); + if (num_indices > 2) + { + // Fix glod so it works when just using the opengl core profile + //glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + glodVBO vbo = {}; + + if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) + { + buff->getVertexStrider( vertex_strider ); + vbo.mV.p = vertex_strider.get(); + vbo.mV.size = 3; + vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; + vbo.mV.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) + { + buff->getNormalStrider( normal_strider ); + vbo.mN.p = normal_strider.get(); + vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; + vbo.mN.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) + { + buff->getTexCoord0Strider( tc_strider ); + vbo.mT.p = tc_strider.get(); + vbo.mT.size = 2; + vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; + vbo.mT.type = GL_FLOAT; + } + + glodInsertElements( mObject[ mdl ], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)index_strider.get(), 0, 0.f, &vbo ); + // + } + tri_count += num_indices / 3; + stop_gloderror(); + } + + glodBuildObject(mObject[mdl]); + stop_gloderror(); + } + } + + + S32 start = LLModel::LOD_HIGH; + S32 end = 0; + + if (which_lod != -1) + { + start = end = which_lod; + } + + mMaxTriangleLimit = base_triangle_count; + + for (S32 lod = start; lod >= end; --lod) + { + if (which_lod == -1) + { + if (lod < start) + { + triangle_count /= decimation; + } + } + else + { + if (enforce_tri_limit) + { + triangle_count = limit; + } + else + { + for (S32 j = LLModel::LOD_HIGH; j>which_lod; --j) + { + triangle_count /= decimation; + } + } + } + + mModel[lod].clear(); + mModel[lod].resize(mBaseModel.size()); + mVertexBuffer[lod].clear(); + + U32 actual_tris = 0; + U32 actual_verts = 0; + U32 submeshes = 0; + + mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); + mRequestedErrorThreshold[lod] = lod_error_threshold; + + glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); + stop_gloderror(); + + glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); + stop_gloderror(); + + glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); + stop_gloderror(); + + if (lod_mode != GLOD_TRIANGLE_BUDGET) + { + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0); + } + else + { + //SH-632: always add 1 to desired amount to avoid decimating below desired amount + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count + 1); + } + + stop_gloderror(); + glodAdaptGroup(mGroup); + stop_gloderror(); + + for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) + { + LLModel* base = mBaseModel[mdl_idx]; + + GLint patch_count = 0; + glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); + stop_gloderror(); + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); + + std::string name = base->mLabel + getLodSuffix(lod); + + mModel[lod][mdl_idx]->mLabel = name; + mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; + + GLint* sizes = new GLint[patch_count * 2]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); + stop_gloderror(); + + GLint* names = new GLint[patch_count]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); + stop_gloderror(); + + mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); + + LLModel* target_model = mModel[lod][mdl_idx]; + + for (GLint i = 0; i < patch_count; ++i) + { + type_mask = mVertexBuffer[5][base][i]->getTypeMask(); + + LLPointer buff = new LLVertexBuffer(type_mask, 0); + + if (sizes[i * 2 + 1] > 0 && sizes[i * 2] > 0) + { + if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true)) + { + // Todo: find a way to stop preview in this case instead of crashing + LL_ERRS() << "Failed buffer allocation during preview LOD generation." + << " Vertices: " << sizes[i * 2 + 1] + << " Indices: " << sizes[i * 2] << LL_ENDL; + } + buff->setBuffer(type_mask); + // Fix glod so it works when just using the opengl core profile + //glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*)buff->getIndicesPointer()); + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + glodVBO vbo = {}; + + if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) + { + buff->getVertexStrider( vertex_strider ); + vbo.mV.p = vertex_strider.get(); + vbo.mV.size = 3; + vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; + vbo.mV.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) + { + buff->getNormalStrider( normal_strider ); + vbo.mN.p = normal_strider.get(); + vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; + vbo.mN.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) + { + buff->getTexCoord0Strider( tc_strider ); + vbo.mT.p = tc_strider.get(); + vbo.mT.size = 2; + vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; + vbo.mT.type = GL_FLOAT; + } + + glodFillElements( mObject[ base ], names[ i ], GL_UNSIGNED_SHORT, (U8*)index_strider.get(), &vbo ); + // + stop_gloderror(); + } + else + { + // This face was eliminated or we failed to allocate buffer, + // attempt to create a dummy triangle (one vertex, 3 indices, all 0) + buff->allocateBuffer(1, 3, true); + memset((U8*)buff->getMappedData(), 0, buff->getSize()); + // Fix when running with opengl core profile + //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); + // + } + + buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); + + LLStrider pos; + LLStrider norm; + LLStrider tc; + LLStrider index; + + buff->getVertexStrider(pos); + if (type_mask & LLVertexBuffer::MAP_NORMAL) + { + buff->getNormalStrider(norm); + } + if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) + { + buff->getTexCoord0Strider(tc); + } + + buff->getIndexStrider(index); + + target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + actual_tris += buff->getNumIndices() / 3; + actual_verts += buff->getNumVerts(); + ++submeshes; + + if (!validate_face(target_model->getVolumeFace(names[i]))) + { + LL_ERRS() << "Invalid face generated during LOD generation." << LL_ENDL; + } + } + + //blind copy skin weights and just take closest skin weight to point on + //decimated mesh for now (auto-generating LODs with skin weights is still a bit + //of an open problem). + target_model->mPosition = base->mPosition; + target_model->mSkinWeights = base->mSkinWeights; + target_model->mSkinInfo = base->mSkinInfo; + //copy material list + target_model->mMaterialList = base->mMaterialList; + + if (!validate_model(target_model)) + { + LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL; + } + + delete[] sizes; + delete[] names; + } + + //rebuild scene based on mBaseScene + mScene[lod].clear(); + mScene[lod] = mBaseScene; + + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + LLModel* mdl = mBaseModel[i]; + LLModel* target = mModel[lod][i]; + if (target) + { + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + for (U32 j = 0; j < iter->second.size(); ++j) + { + if (iter->second[j].mModel == mdl) + { + iter->second[j].mModel = target; + } + } + } + } + } + } + + mResourceCost = calcResourceCost(); + + LLVertexBuffer::unbind(); + LLGLSLShader::sNoFixedFunction = no_ff; + if (shader) + { + shader->bind(); + } + refresh(); // refresh once to make sure render gets called with the updated vbos } 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 - }; + // 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(); + 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 tris[LLModel::NUM_LODS]; - std::vector verts[LLModel::NUM_LODS]; - std::vector submeshes[LLModel::NUM_LODS]; + U32 has_physics_error{ PhysicsError::NONE }; // physics error bitmap + //triangle/vertex/submesh count for each mesh asset for each lod + std::vector tris[LLModel::NUM_LODS]; + std::vector verts[LLModel::NUM_LODS]; + std::vector submeshes[LLModel::NUM_LODS]; - //total triangle/vertex/submesh count for each lod - S32 total_tris[LLModel::NUM_LODS]; - S32 total_verts[LLModel::NUM_LODS]; - S32 total_submeshes[LLModel::NUM_LODS]; + //total triangle/vertex/submesh count for each lod + S32 total_tris[LLModel::NUM_LODS]; + S32 total_verts[LLModel::NUM_LODS]; + S32 total_submeshes[LLModel::NUM_LODS]; - for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) - { - total_tris[i] = 0; - total_verts[i] = 0; - total_submeshes[i] = 0; - } + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + total_tris[i] = 0; + total_verts[i] = 0; + total_submeshes[i] = 0; + } - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; - LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH]; - if (!model_high_lod) - { - setLoadState(LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING); // FIRE-30965 Cleanup braindead mesh parsing error handlers - mFMP->childDisable("calculate_btn"); - continue; - } + LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH]; + if (!model_high_lod) + { + setLoadState(LLModelLoader::ERROR_HIGH_LOD_MODEL_MISSING); // FIRE-30965 Cleanup braindead mesh parsing error handlers + mFMP->childDisable("calculate_btn"); + continue; + } - for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) - { - LLModel* lod_model = instance.mLOD[i]; - if (!lod_model) - { - setLoadState(LLModelLoader::ERROR_LOD_MODEL_MISMATCH); // FIRE-30965 Cleanup braindead mesh parsing error handlers - mFMP->childDisable("calculate_btn"); - } - else - { - //for each model in the lod - S32 cur_tris = 0; - S32 cur_verts = 0; - S32 cur_submeshes = lod_model->getNumVolumeFaces(); + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + LLModel* lod_model = instance.mLOD[i]; + if (!lod_model) + { + setLoadState(LLModelLoader::ERROR_LOD_MODEL_MISMATCH); // FIRE-30965 Cleanup braindead mesh parsing error handlers + mFMP->childDisable("calculate_btn"); + } + else + { + //for each model in the lod + S32 cur_tris = 0; + S32 cur_verts = 0; + S32 cur_submeshes = lod_model->getNumVolumeFaces(); - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = lod_model->getVolumeFace(j); - cur_tris += face.mNumIndices / 3; - cur_verts += face.mNumVertices; - } + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = lod_model->getVolumeFace(j); + cur_tris += face.mNumIndices / 3; + cur_verts += face.mNumVertices; + } - std::string instance_name = instance.mLabel; + std::string instance_name = instance.mLabel; - if (mImporterDebug) - { - // Useful for debugging generalized complaints below about total submeshes which don't have enough - // context to address exactly what needs to be fixed to move towards compliance with the rules. - // - std::ostringstream out; - out << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); + if (mImporterDebug) + { + // Useful for debugging generalized complaints below about total submeshes which don't have enough + // context to address exactly what needs to be fixed to move towards compliance with the rules. + // + std::ostringstream out; + out << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); - out.str(""); - out << "Instance " << lod_model->mLabel << " LOD " << i << " Tris: " << cur_tris; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); + out.str(""); + out << "Instance " << lod_model->mLabel << " LOD " << i << " Tris: " << cur_tris; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); - out.str(""); - out << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); + out.str(""); + out << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); - out.str(""); - LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin(); - while (mat_iter != lod_model->mMaterialList.end()) - { - out << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter); - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - out.str(""); - mat_iter++; - } - } + out.str(""); + LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin(); + while (mat_iter != lod_model->mMaterialList.end()) + { + out << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter); + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + out.str(""); + mat_iter++; + } + } - //add this model to the lod total - total_tris[i] += cur_tris; - total_verts[i] += cur_verts; - total_submeshes[i] += cur_submeshes; + //add this model to the lod total + total_tris[i] += cur_tris; + total_verts[i] += cur_verts; + total_submeshes[i] += cur_submeshes; - //store this model's counts to asset data - tris[i].push_back(cur_tris); - verts[i].push_back(cur_verts); - submeshes[i].push_back(cur_submeshes); - } - } - } + //store this model's counts to asset data + tris[i].push_back(cur_tris); + verts[i].push_back(cur_verts); + submeshes[i].push_back(cur_submeshes); + } + } + } - if (mMaxTriangleLimit == 0) - { - mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; - } + if (mMaxTriangleLimit == 0) + { + mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; + } - mHasDegenerate = false; - {//check for degenerate triangles in physics mesh - U32 lod = LLModel::LOD_PHYSICS; - const LLVector4a scale(0.5f); - for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i) - { //for each model in the lod - if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty()) - { //no decomp exists - S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); - for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j) - { //for each submesh (face), add triangles and vertices to current total - LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); - for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate;) - { - U16 index_a = face.mIndices[k + 0]; - U16 index_b = face.mIndices[k + 1]; - U16 index_c = face.mIndices[k + 2]; + mHasDegenerate = false; + {//check for degenerate triangles in physics mesh + U32 lod = LLModel::LOD_PHYSICS; + const LLVector4a scale(0.5f); + for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i) + { //for each model in the lod + if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty()) + { //no decomp exists + S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); + for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j) + { //for each submesh (face), add triangles and vertices to current total + LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); + for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate;) + { + U16 index_a = face.mIndices[k + 0]; + U16 index_b = face.mIndices[k + 1]; + U16 index_c = face.mIndices[k + 2]; - if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case - { - LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL; - } - else - { - LLVector4a v1; v1.setMul(face.mPositions[index_a], scale); - LLVector4a v2; v2.setMul(face.mPositions[index_b], scale); - LLVector4a v3; v3.setMul(face.mPositions[index_c], scale); - if (ll_is_degenerate(v1, v2, v3)) - { - mHasDegenerate = true; - } - } - k += 3; - } - } - } - } - } + if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case + { + LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL; + } + else + { + LLVector4a v1; v1.setMul(face.mPositions[index_a], scale); + LLVector4a v2; v2.setMul(face.mPositions[index_b], scale); + LLVector4a v3; v3.setMul(face.mPositions[index_c], scale); + if (ll_is_degenerate(v1, v2, v3)) + { + mHasDegenerate = true; + } + } + k += 3; + } + } + } + } + } - // flag degenerates here rather than deferring to a MAV error later - // - //mFMP->childSetVisible("physics_status_message_text", mHasDegenerate); //display or clear - //auto degenerateIcon = mFMP->getChild("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); - // - } + // 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("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])); + mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH])); - std::string mesh_status_na = mFMP->getString("mesh_status_na"); + std::string mesh_status_na = mFMP->getString("mesh_status_na"); - S32 upload_status[LLModel::LOD_HIGH + 1]; + S32 upload_status[LLModel::LOD_HIGH + 1]; - mModelNoErrors = true; + mModelNoErrors = true; - const U32 lod_high = LLModel::LOD_HIGH; - U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]); + const U32 lod_high = LLModel::LOD_HIGH; + U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]); - for (S32 lod = 0; lod <= lod_high; ++lod) - { - upload_status[lod] = 0; + for (S32 lod = 0; lod <= lod_high; ++lod) + { + upload_status[lod] = 0; - std::string message = "mesh_status_good"; + std::string message = "mesh_status_good"; - if (total_tris[lod] > 0) - { - mFMP->childSetValue(lod_triangles_name[lod], llformat("%d", total_tris[lod])); - mFMP->childSetValue(lod_vertices_name[lod], llformat("%d", total_verts[lod])); - } - else - { - if (lod == lod_high) - { - upload_status[lod] = 2; - message = "mesh_status_missing_lod"; - } - else - { - for (S32 i = lod - 1; i >= 0; --i) - { - if (total_tris[i] > 0) - { - upload_status[lod] = 2; - message = "mesh_status_missing_lod"; - } - } - } + if (total_tris[lod] > 0) + { + mFMP->childSetValue(lod_triangles_name[lod], llformat("%d", total_tris[lod])); + mFMP->childSetValue(lod_vertices_name[lod], llformat("%d", total_verts[lod])); + } + else + { + if (lod == lod_high) + { + upload_status[lod] = 2; + message = "mesh_status_missing_lod"; + } + else + { + for (S32 i = lod - 1; i >= 0; --i) + { + if (total_tris[i] > 0) + { + upload_status[lod] = 2; + message = "mesh_status_missing_lod"; + } + } + } - mFMP->childSetValue(lod_triangles_name[lod], mesh_status_na); - mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na); - } + mFMP->childSetValue(lod_triangles_name[lod], mesh_status_na); + mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na); + } - if (lod != lod_high) - { - if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) - { //number of submeshes is different - message = "mesh_status_submesh_mismatch"; - upload_status[lod] = 2; - } - else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count) - {//number of submodels is different, not all faces are matched correctly. - message = "mesh_status_submesh_mismatch"; - upload_status[lod] = 2; - // Note: Submodels in instance were loaded from higher LOD and as result face count - // returns same value and total_submeshes[lod] is identical to high_lod one. - } - else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) - { //number of meshes is different - message = "mesh_status_mesh_mismatch"; - upload_status[lod] = 2; - } - else if (!verts[lod].empty()) - { - S32 sum_verts_higher_lod = 0; - S32 sum_verts_this_lod = 0; - for (U32 i = 0; i < verts[lod].size(); ++i) - { - sum_verts_higher_lod += ((i < verts[lod + 1].size()) ? verts[lod + 1][i] : 0); - sum_verts_this_lod += verts[lod][i]; - } + if (lod != lod_high) + { + if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) + { //number of submeshes is different + message = "mesh_status_submesh_mismatch"; + upload_status[lod] = 2; + } + else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count) + {//number of submodels is different, not all faces are matched correctly. + message = "mesh_status_submesh_mismatch"; + upload_status[lod] = 2; + // Note: Submodels in instance were loaded from higher LOD and as result face count + // returns same value and total_submeshes[lod] is identical to high_lod one. + } + else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) + { //number of meshes is different + message = "mesh_status_mesh_mismatch"; + upload_status[lod] = 2; + } + else if (!verts[lod].empty()) + { + S32 sum_verts_higher_lod = 0; + S32 sum_verts_this_lod = 0; + for (U32 i = 0; i < verts[lod].size(); ++i) + { + sum_verts_higher_lod += ((i < verts[lod + 1].size()) ? verts[lod + 1][i] : 0); + sum_verts_this_lod += verts[lod][i]; + } - if ((sum_verts_higher_lod > 0) && - (sum_verts_this_lod > sum_verts_higher_lod)) - { - //too many vertices in this lod - message = "mesh_status_too_many_vertices"; - upload_status[lod] = 1; - } - } - } + if ((sum_verts_higher_lod > 0) && + (sum_verts_this_lod > sum_verts_higher_lod)) + { + //too many vertices in this lod + message = "mesh_status_too_many_vertices"; + upload_status[lod] = 1; + } + } + } - LLIconCtrl* icon = mFMP->getChild(lod_icon_name[lod]); - LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]); - icon->setVisible(true); - icon->setImage(img); + LLIconCtrl* icon = mFMP->getChild(lod_icon_name[lod]); + LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]); + icon->setVisible(true); + icon->setImage(img); - if (upload_status[lod] >= 2) - { - mModelNoErrors = false; - } + if (upload_status[lod] >= 2) + { + mModelNoErrors = false; + } - if (lod == mPreviewLOD) - { - mFMP->childSetValue("lod_status_message_text", mFMP->getString(message)); - icon = mFMP->getChild("lod_status_message_icon"); - icon->setImage(img); - } + if (lod == mPreviewLOD) + { + mFMP->childSetValue("lod_status_message_text", mFMP->getString(message)); + icon = mFMP->getChild("lod_status_message_icon"); + icon->setImage(img); + } - updateLodControls(lod); - } + updateLodControls(lod); + } - //warn if hulls have more than 256 points in them - BOOL physExceededVertexLimit = FALSE; - for (U32 i = 0; mModelNoErrors && (i < mModel[LLModel::LOD_PHYSICS].size()); ++i) - { - LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i]; + //warn if hulls have more than 256 points in them + BOOL physExceededVertexLimit = FALSE; + for (U32 i = 0; mModelNoErrors && (i < mModel[LLModel::LOD_PHYSICS].size()); ++i) + { + LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i]; - if (mdl) - { - // Better error handling - auto num_hulls = mdl->mPhysics.mHull.size(); - for (U32 j = 0; j < num_hulls; ++j) - { - // - if (mdl->mPhysics.mHull[j].size() > 256) - { - physExceededVertexLimit = TRUE; - // add new friendlier logging to mesh uploader - // LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL; - std::ostringstream out; - out << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations."; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - break; - } - } - // Better error handling - if (num_hulls > 256) // decomp cannot have more than 256 hulls (http://wiki.secondlife.com/wiki/Mesh/Mesh_physics) - { - // improve uploader error reporting - // LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds 256 hull limitation." << LL_ENDL; - std::ostringstream out; - out << "Physical model " << mdl->mLabel << " exceeds 256 hull limitation."; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - has_physics_error |= PhysicsError::TOOMANYHULLS; - } - // - } - } + if (mdl) + { + // Better error handling + auto num_hulls = mdl->mPhysics.mHull.size(); + for (U32 j = 0; j < num_hulls; ++j) + { + // + if (mdl->mPhysics.mHull[j].size() > 256) + { + physExceededVertexLimit = TRUE; + // add new friendlier logging to mesh uploader + // LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL; + std::ostringstream out; + out << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + break; + } + } + // Better error handling + if (num_hulls > 256) // decomp cannot have more than 256 hulls (http://wiki.secondlife.com/wiki/Mesh/Mesh_physics) + { + // improve uploader error reporting + // LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds 256 hull limitation." << LL_ENDL; + std::ostringstream out; + out << "Physical model " << mdl->mLabel << " exceeds 256 hull limitation."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + has_physics_error |= PhysicsError::TOOMANYHULLS; + } + // + } + } - if (physExceededVertexLimit) - { - has_physics_error |= PhysicsError::TOOMANYVERTSINHULL; - } + if (physExceededVertexLimit) + { + has_physics_error |= PhysicsError::TOOMANYVERTSINHULL; + } // standardise error handling - //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("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 (!(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("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); + // } + //} #ifndef HAVOK_TPV - has_physics_error |= PhysicsError::NOHAVOK; + has_physics_error |= PhysicsError::NOHAVOK; #endif - auto physStatusIcon = mFMP->getChild("physics_status_message_icon"); + auto physStatusIcon = mFMP->getChild("physics_status_message_icon"); - if (has_physics_error != PhysicsError::NONE) - { - mFMP->childSetVisible("physics_status_message_text", true); //display or clear - physStatusIcon->setVisible(true); - // The order here is important. - if (has_physics_error & PhysicsError::TOOMANYHULLS) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_hull_limit_exceeded")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); - physStatusIcon->setImage(img); - } - else if (has_physics_error & PhysicsError::TOOMANYVERTSINHULL) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); - physStatusIcon->setImage(img); - } - else if (has_physics_error & PhysicsError::DEGENERATE) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_degenerate_triangles")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); - physStatusIcon->setImage(img); - } - else if (has_physics_error & PhysicsError::NOHAVOK) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_no_havok")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); - physStatusIcon->setImage(img); - } - else - { - // This should not happen - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_unknown_error")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); - physStatusIcon->setImage(img); - } - } - else - { - mFMP->childSetVisible("physics_status_message_text", false); //display or clear - physStatusIcon->setVisible(false); - } + if (has_physics_error != PhysicsError::NONE) + { + mFMP->childSetVisible("physics_status_message_text", true); //display or clear + physStatusIcon->setVisible(true); + // The order here is important. + if (has_physics_error & PhysicsError::TOOMANYHULLS) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_hull_limit_exceeded")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); + physStatusIcon->setImage(img); + } + else if (has_physics_error & PhysicsError::TOOMANYVERTSINHULL) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); + physStatusIcon->setImage(img); + } + else if (has_physics_error & PhysicsError::DEGENERATE) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_degenerate_triangles")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); + physStatusIcon->setImage(img); + } + else if (has_physics_error & PhysicsError::NOHAVOK) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_no_havok")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); + physStatusIcon->setImage(img); + } + else + { + // This should not happen + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_unknown_error")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); + physStatusIcon->setImage(img); + } + } + else + { + mFMP->childSetVisible("physics_status_message_text", false); //display or clear + physStatusIcon->setVisible(false); + } // - if (getLoadState() >= LLModelLoader::ERROR_PARSING) - { - mModelNoErrors = false; - // improve uploader error reporting - // LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL; - std::ostringstream out; - out << "Loader returned errors, model can't be uploaded"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - } + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mModelNoErrors = false; + // improve uploader error reporting + // LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL; + std::ostringstream out; + out << "Loader returned errors, model can't be uploaded"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + } - bool uploadingSkin = mFMP->childGetValue("upload_skin").asBoolean(); - bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); + bool uploadingSkin = mFMP->childGetValue("upload_skin").asBoolean(); + bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); - if (uploadingSkin) - { - if (uploadingJointPositions && !isRigValidForJointPositionUpload()) - { - mModelNoErrors = false; - // improve uploader error reporting - // LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL; - std::ostringstream out; - out << "Invalid rig, there might be issues with uploading Joint positions"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - } - } + if (uploadingSkin) + { + if (uploadingJointPositions && !isRigValidForJointPositionUpload()) + { + mModelNoErrors = false; + // improve uploader error reporting + // LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL; + std::ostringstream out; + out << "Invalid rig, there might be issues with uploading Joint positions"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + } + } - if (mModelNoErrors && mModelLoader) - { - if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) - { - // Some textures are still loading, prevent upload until they are done - mModelNoErrors = false; - } - } + if (mModelNoErrors && mModelLoader) + { + if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) + { + // Some textures are still loading, prevent upload until they are done + mModelNoErrors = false; + } + } - // Improve the error checking the TO DO here is no longer applicable but not an FS comment so edited to stop it being picked up - //if (!mModelNoErrors || mHasDegenerate) - if (!gSavedSettings.getBOOL("FSIgnoreClientsideMeshValidation") && (!mModelNoErrors || (has_physics_error > PhysicsError::NOHAVOK))) // block for all cases of phsyics error except NOHAVOK - // - { - mFMP->childDisable("ok_btn"); - mFMP->childDisable("calculate_btn"); - } - else - { - mFMP->childEnable("ok_btn"); - mFMP->childEnable("calculate_btn"); - } + // Improve the error checking the TO DO here is no longer applicable but not an FS comment so edited to stop it being picked up + //if (!mModelNoErrors || mHasDegenerate) + if (!gSavedSettings.getBOOL("FSIgnoreClientsideMeshValidation") && (!mModelNoErrors || (has_physics_error > PhysicsError::NOHAVOK))) // block for all cases of phsyics error except NOHAVOK + // + { + mFMP->childDisable("ok_btn"); + mFMP->childDisable("calculate_btn"); + } + else + { + mFMP->childEnable("ok_btn"); + mFMP->childEnable("calculate_btn"); + } - if (mModelNoErrors && mLodsWithParsingError.empty()) - { - mFMP->childEnable("calculate_btn"); - } - else - { - mFMP->childDisable("calculate_btn"); - } + if (mModelNoErrors && mLodsWithParsingError.empty()) + { + mFMP->childEnable("calculate_btn"); + } + else + { + mFMP->childDisable("calculate_btn"); + } - //add up physics triangles etc - S32 phys_tris = 0; - S32 phys_hulls = 0; - S32 phys_points = 0; + //add up physics triangles etc + S32 phys_tris = 0; + S32 phys_hulls = 0; + S32 phys_points = 0; - //get the triangle count for the whole scene - for (LLModelLoader::scene::iterator iter = mScene[LLModel::LOD_PHYSICS].begin(), endIter = mScene[LLModel::LOD_PHYSICS].end(); iter != endIter; ++iter) - { - for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) - { - LLModel* model = instance->mModel; - if (model) - { - S32 cur_submeshes = model->getNumVolumeFaces(); + //get the triangle count for the whole scene + for (LLModelLoader::scene::iterator iter = mScene[LLModel::LOD_PHYSICS].begin(), endIter = mScene[LLModel::LOD_PHYSICS].end(); iter != endIter; ++iter) + { + for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) + { + LLModel* model = instance->mModel; + if (model) + { + S32 cur_submeshes = model->getNumVolumeFaces(); - LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull; + LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull; - if (!decomp.empty()) - { - phys_hulls += decomp.size(); - for (U32 i = 0; i < decomp.size(); ++i) - { - phys_points += decomp[i].size(); - } - } - else - { //choose physics shape OR decomposition, can't use both - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = model->getVolumeFace(j); - phys_tris += face.mNumIndices / 3; - } - } - } - } - } + if (!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_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); - } + if (phys_hulls > 0) + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls)); + mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points)); + } + else + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na); + mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na); + } - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp) - { - if (phys_tris > 0 || phys_hulls > 0) - { - if (!fmp->isViewOptionEnabled("show_physics")) - { - fmp->enableViewOption("show_physics"); - mViewOption["show_physics"] = true; - fmp->childSetValue("show_physics", true); - } - // handle hiding of hull only explode slider - //} - //else - //{ - // fmp->disableViewOption("show_physics"); - // mViewOption["show_physics"] = false; - // fmp->childSetValue("show_physics", false); - //} - - // mViewOption["show_physics"] = true; // merge LL uploader changes - if (phys_hulls > 0) - { - fmp->enableViewOption("physics_explode"); - fmp->enableViewOption("exploder_label"); - fmp->childSetVisible("physics_explode", true); - fmp->childSetVisible("exploder_label", true); - } - else - { - fmp->disableViewOption("physics_explode"); - fmp->disableViewOption("exploder_label"); - fmp->childSetVisible("physics_explode", false); - fmp->childSetVisible("exploder_label", false); - } - } - else - { - fmp->disableViewOption("show_physics"); - fmp->childSetVisible("physics_explode", false); - fmp->disableViewOption("physics_explode"); - fmp->childSetVisible("exploder_label", false); - fmp->disableViewOption("exploder_label"); - mViewOption["show_physics"] = false; - fmp->childSetValue("show_physics", false); + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + if (phys_tris > 0 || phys_hulls > 0) + { + if (!fmp->isViewOptionEnabled("show_physics")) + { + fmp->enableViewOption("show_physics"); + mViewOption["show_physics"] = true; + fmp->childSetValue("show_physics", true); + } + // handle hiding of hull only explode slider + //} + //else + //{ + // fmp->disableViewOption("show_physics"); + // mViewOption["show_physics"] = false; + // fmp->childSetValue("show_physics", false); + //} + + // mViewOption["show_physics"] = true; // merge LL uploader changes + if (phys_hulls > 0) + { + fmp->enableViewOption("physics_explode"); + fmp->enableViewOption("exploder_label"); + fmp->childSetVisible("physics_explode", true); + fmp->childSetVisible("exploder_label", true); + } + else + { + fmp->disableViewOption("physics_explode"); + fmp->disableViewOption("exploder_label"); + fmp->childSetVisible("physics_explode", false); + fmp->childSetVisible("exploder_label", false); + } + } + else + { + fmp->disableViewOption("show_physics"); + fmp->childSetVisible("physics_explode", false); + fmp->disableViewOption("physics_explode"); + fmp->childSetVisible("exploder_label", false); + fmp->disableViewOption("exploder_label"); + mViewOption["show_physics"] = false; + fmp->childSetValue("show_physics", false); - } - // + } + // - //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean(); + //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean(); - //fmp->childSetEnabled("physics_optimize", !use_hull); + //fmp->childSetEnabled("physics_optimize", !use_hull); - bool enable = (phys_tris > 0 || phys_hulls > 0) && fmp->mCurRequest.empty(); - //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); + bool enable = (phys_tris > 0 || phys_hulls > 0) && fmp->mCurRequest.empty(); + //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); - //enable/disable "analysis" UI - LLPanel* panel = fmp->getChild("physics analysis"); - LLView* child = panel->getFirstChild(); - while (child) - { - child->setEnabled(enable); - child = panel->findNextSibling(child); - } + //enable/disable "analysis" UI + LLPanel* panel = fmp->getChild("physics analysis"); + LLView* child = panel->getFirstChild(); + while (child) + { + child->setEnabled(enable); + child = panel->findNextSibling(child); + } - enable = phys_hulls > 0 && fmp->mCurRequest.empty(); - //enable/disable "simplification" UI - panel = fmp->getChild("physics simplification"); - child = panel->getFirstChild(); - while (child) - { - child->setEnabled(enable); - child = panel->findNextSibling(child); - } + enable = phys_hulls > 0 && fmp->mCurRequest.empty(); + //enable/disable "simplification" UI + panel = fmp->getChild("physics simplification"); + child = panel->getFirstChild(); + while (child) + { + child->setEnabled(enable); + child = panel->findNextSibling(child); + } - if (fmp->mCurRequest.empty()) - { - fmp->childSetVisible("Simplify", true); - fmp->childSetVisible("simplify_cancel", false); - fmp->childSetVisible("Decompose", true); - fmp->childSetVisible("decompose_cancel", false); + if (fmp->mCurRequest.empty()) + { + fmp->childSetVisible("Simplify", true); + fmp->childSetVisible("simplify_cancel", false); + fmp->childSetVisible("Decompose", true); + fmp->childSetVisible("decompose_cancel", false); - if (phys_hulls > 0) - { - fmp->childEnable("Simplify"); - } + if (phys_hulls > 0) + { + fmp->childEnable("Simplify"); + } - if (phys_tris || phys_hulls > 0) - { - fmp->childEnable("Decompose"); - } - } - else - { - fmp->childEnable("simplify_cancel"); - fmp->childEnable("decompose_cancel"); - } - // move the closing bracket for the if(fmp) to prevent possible crash - // } + if (phys_tris || phys_hulls > 0) + { + fmp->childEnable("Decompose"); + } + } + else + { + fmp->childEnable("simplify_cancel"); + fmp->childEnable("decompose_cancel"); + } + // move the closing bracket for the if(fmp) to prevent possible crash + // } - LLCtrlSelectionInterface* iface = fmp->childGetSelectionInterface("physics_lod_combo"); - S32 which_mode = 0; - S32 file_mode = 1; - if (iface) - { - which_mode = iface->getFirstSelectedIndex(); - file_mode = iface->getItemCount() - 1; - } + LLCtrlSelectionInterface* iface = fmp->childGetSelectionInterface("physics_lod_combo"); + S32 which_mode = 0; + S32 file_mode = 1; + if (iface) + { + which_mode = iface->getFirstSelectedIndex(); + file_mode = iface->getItemCount() - 1; + } - if (which_mode == file_mode) - { - mFMP->childEnable("physics_file"); - mFMP->childEnable("physics_browse"); - } - else - { - mFMP->childDisable("physics_file"); - mFMP->childDisable("physics_browse"); - } - } - // + if (which_mode == file_mode) + { + mFMP->childEnable("physics_file"); + mFMP->childEnable("physics_browse"); + } + else + { + mFMP->childDisable("physics_file"); + mFMP->childDisable("physics_browse"); + } + } + // - LLSpinCtrl* crease = mFMP->getChild("crease_angle"); + LLSpinCtrl* crease = mFMP->getChild("crease_angle"); - if (mRequestedCreaseAngle[mPreviewLOD] == -1.f) - { - mFMP->childSetColor("crease_label", LLColor4::grey); - crease->forceSetValue(75.f); - } - else - { - mFMP->childSetColor("crease_label", LLColor4::white); - crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]); - } + if (mRequestedCreaseAngle[mPreviewLOD] == -1.f) + { + mFMP->childSetColor("crease_label", LLColor4::grey); + crease->forceSetValue(75.f); + } + else + { + mFMP->childSetColor("crease_label", LLColor4::white); + crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]); + } - mModelUpdatedSignal(true); + mModelUpdatedSignal(true); } void LLModelPreview::updateLodControls(S32 lod) { - if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH) - { - std::ostringstream out; - out << "Invalid level of detail: " << lod; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH); - return; - } + if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH) + { + std::ostringstream out; + out << "Invalid level of detail: " << lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH); + return; + } - const char* lod_controls[] = - { - "lod_mode_", - "lod_triangle_limit_", - "lod_error_threshold_" - }; - const U32 num_lod_controls = sizeof(lod_controls) / sizeof(char*); + const char* lod_controls[] = + { + "lod_mode_", + "lod_triangle_limit_", + "lod_error_threshold_" + }; + const U32 num_lod_controls = sizeof(lod_controls) / sizeof(char*); - const char* file_controls[] = - { - "lod_browse_", - "lod_file_", - }; - const U32 num_file_controls = sizeof(file_controls) / sizeof(char*); + const char* file_controls[] = + { + "lod_browse_", + "lod_file_", + }; + const U32 num_file_controls = sizeof(file_controls) / sizeof(char*); - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (!fmp) return; + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (!fmp) return; - LLComboBox* lod_combo = mFMP->findChild("lod_source_" + lod_name[lod]); - if (!lod_combo) return; + LLComboBox* lod_combo = mFMP->findChild("lod_source_" + lod_name[lod]); + if (!lod_combo) return; - S32 lod_mode = lod_combo->getCurrentIndex(); - if (lod_mode == LOD_FROM_FILE) // LoD from file - { - fmp->mLODMode[lod] = 0; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childSetVisible(file_controls[i] + lod_name[lod], true); - } + S32 lod_mode = lod_combo->getCurrentIndex(); + if (lod_mode == LOD_FROM_FILE) // LoD from file + { + fmp->mLODMode[lod] = 0; + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childSetVisible(file_controls[i] + lod_name[lod], true); + } - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); - } - } - else if (lod_mode == USE_LOD_ABOVE) // use LoD above - { - fmp->mLODMode[lod] = 2; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childSetVisible(file_controls[i] + lod_name[lod], false); - } + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); + } + } + else if (lod_mode == USE_LOD_ABOVE) // use LoD above + { + fmp->mLODMode[lod] = 2; + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childSetVisible(file_controls[i] + lod_name[lod], false); + } - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); - } + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); + } - if (lod < LLModel::LOD_HIGH) - { - mModel[lod] = mModel[lod + 1]; - mScene[lod] = mScene[lod + 1]; - mVertexBuffer[lod].clear(); + if (lod < LLModel::LOD_HIGH) + { + mModel[lod] = mModel[lod + 1]; + mScene[lod] = mScene[lod + 1]; + mVertexBuffer[lod].clear(); - // Also update lower LoD - if (lod > LLModel::LOD_IMPOSTOR) - { - updateLodControls(lod - 1); - } - } - } - else // auto generate, the default case for all LoDs except High - { - fmp->mLODMode[lod] = 1; + // Also update lower LoD + if (lod > LLModel::LOD_IMPOSTOR) + { + updateLodControls(lod - 1); + } + } + } + else // auto generate, the default case for all LoDs except High + { + fmp->mLODMode[lod] = 1; - //don't actually regenerate lod when refreshing UI - mLODFrozen = true; + //don't actually regenerate lod when refreshing UI + mLODFrozen = true; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->getChildView(file_controls[i] + lod_name[lod])->setVisible(false); - } + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->getChildView(file_controls[i] + lod_name[lod])->setVisible(false); + } - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->getChildView(lod_controls[i] + lod_name[lod])->setVisible(true); - } + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->getChildView(lod_controls[i] + lod_name[lod])->setVisible(true); + } - LLSpinCtrl* threshold = mFMP->getChild("lod_error_threshold_" + lod_name[lod]); - LLSpinCtrl* limit = mFMP->getChild("lod_triangle_limit_" + lod_name[lod]); + LLSpinCtrl* threshold = mFMP->getChild("lod_error_threshold_" + lod_name[lod]); + LLSpinCtrl* limit = mFMP->getChild("lod_triangle_limit_" + lod_name[lod]); - limit->setMaxValue(mMaxTriangleLimit); - limit->forceSetValue(mRequestedTriangleCount[lod]); + limit->setMaxValue(mMaxTriangleLimit); + limit->forceSetValue(mRequestedTriangleCount[lod]); - threshold->forceSetValue(mRequestedErrorThreshold[lod]); + threshold->forceSetValue(mRequestedErrorThreshold[lod]); - mFMP->getChild("lod_mode_" + lod_name[lod])->selectNthItem(mRequestedLoDMode[lod]); + mFMP->getChild("lod_mode_" + lod_name[lod])->selectNthItem(mRequestedLoDMode[lod]); - if (mRequestedLoDMode[lod] == 0) - { - limit->setVisible(true); - threshold->setVisible(false); + if (mRequestedLoDMode[lod] == 0) + { + limit->setVisible(true); + threshold->setVisible(false); - limit->setMaxValue(mMaxTriangleLimit); - limit->setIncrement(mMaxTriangleLimit / 32); - } - else - { - limit->setVisible(false); - threshold->setVisible(true); - } + limit->setMaxValue(mMaxTriangleLimit); + limit->setIncrement(mMaxTriangleLimit / 32); + } + else + { + limit->setVisible(false); + threshold->setVisible(true); + } - mLODFrozen = false; - } + mLODFrozen = false; + } } void LLModelPreview::setPreviewTarget(F32 distance) { - mCameraDistance = distance; - mCameraZoom = 1.f; - mCameraPitch = 0.f; - mCameraYaw = 0.f; - mCameraOffset.clearVec(); + mCameraDistance = distance; + mCameraZoom = 1.f; + mCameraPitch = 0.f; + mCameraYaw = 0.f; + mCameraOffset.clearVec(); } void LLModelPreview::clearBuffers() { - for (U32 i = 0; i < 6; i++) - { - mVertexBuffer[i].clear(); - } + for (U32 i = 0; i < 6; i++) + { + mVertexBuffer[i].clear(); + } } void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) { - U32 tri_count = 0; - U32 vertex_count = 0; - U32 mesh_count = 0; + U32 tri_count = 0; + U32 vertex_count = 0; + U32 mesh_count = 0; - LLModelLoader::model_list* model = NULL; + LLModelLoader::model_list* model = NULL; - if (lod < 0 || lod > 4) - { - model = &mBaseModel; - lod = 5; - } - else - { - model = &(mModel[lod]); - } + if (lod < 0 || lod > 4) + { + model = &mBaseModel; + lod = 5; + } + else + { + model = &(mModel[lod]); + } - if (!mVertexBuffer[lod].empty()) - { - mVertexBuffer[lod].clear(); - } + if (!mVertexBuffer[lod].empty()) + { + mVertexBuffer[lod].clear(); + } - mVertexBuffer[lod].clear(); + mVertexBuffer[lod].clear(); - LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); + LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); - for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) - { - LLModel* mdl = *iter; - if (!mdl) - { - continue; - } + for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) + { + LLModel* mdl = *iter; + if (!mdl) + { + continue; + } - LLModel* base_mdl = *base_iter; - base_iter++; + LLModel* base_mdl = *base_iter; + base_iter++; - S32 num_faces = mdl->getNumVolumeFaces(); - for (S32 i = 0; i < num_faces; ++i) - { - const LLVolumeFace &vf = mdl->getVolumeFace(i); - U32 num_vertices = vf.mNumVertices; - U32 num_indices = vf.mNumIndices; + S32 num_faces = mdl->getNumVolumeFaces(); + for (S32 i = 0; i < num_faces; ++i) + { + const LLVolumeFace &vf = mdl->getVolumeFace(i); + U32 num_vertices = vf.mNumVertices; + U32 num_indices = vf.mNumIndices; - if (!num_vertices || !num_indices) - { - continue; - } + if (!num_vertices || !num_indices) + { + continue; + } - LLVertexBuffer* vb = NULL; + LLVertexBuffer* vb = NULL; - bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); + bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); - U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - if (skinned) - { - mask |= LLVertexBuffer::MAP_WEIGHT4; - } + if (skinned) + { + mask |= LLVertexBuffer::MAP_WEIGHT4; + } - vb = new LLVertexBuffer(mask, 0); + vb = new LLVertexBuffer(mask, 0); - if (!vb->allocateBuffer(num_vertices, num_indices, TRUE)) - { - // We are likely to crash due this failure, if this happens, find a way to gracefully stop preview - std::ostringstream out; - out << "Failed to allocate Vertex Buffer for model preview "; - out << num_vertices << " vertices and "; - out << num_indices << " indices"; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - } + if (!vb->allocateBuffer(num_vertices, num_indices, TRUE)) + { + // We are likely to crash due this failure, if this happens, find a way to gracefully stop preview + std::ostringstream out; + out << "Failed to allocate Vertex Buffer for model preview "; + out << num_vertices << " vertices and "; + out << num_indices << " indices"; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + } - LLStrider vertex_strider; - LLStrider normal_strider; - LLStrider tc_strider; - LLStrider index_strider; - // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis - //LLStrider weights_strider; - LLStrider weights_strider; + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + LLStrider index_strider; + // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis + //LLStrider weights_strider; + LLStrider weights_strider; - vb->getVertexStrider(vertex_strider); - vb->getIndexStrider(index_strider); + vb->getVertexStrider(vertex_strider); + vb->getIndexStrider(index_strider); - if (skinned) - { - vb->getWeight4Strider(weights_strider); - } + if (skinned) + { + vb->getWeight4Strider(weights_strider); + } - LLVector4a::memcpyNonAliased16((F32*)vertex_strider.get(), (F32*)vf.mPositions, num_vertices * 4 * sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*)vertex_strider.get(), (F32*)vf.mPositions, num_vertices * 4 * sizeof(F32)); - if (vf.mTexCoords) - { - vb->getTexCoord0Strider(tc_strider); - S32 tex_size = (num_vertices * 2 * sizeof(F32) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)tc_strider.get(), (F32*)vf.mTexCoords, tex_size); - } + if (vf.mTexCoords) + { + vb->getTexCoord0Strider(tc_strider); + S32 tex_size = (num_vertices * 2 * sizeof(F32) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)tc_strider.get(), (F32*)vf.mTexCoords, tex_size); + } - if (vf.mNormals) - { - vb->getNormalStrider(normal_strider); - LLVector4a::memcpyNonAliased16((F32*)normal_strider.get(), (F32*)vf.mNormals, num_vertices * 4 * sizeof(F32)); - } + if (vf.mNormals) + { + vb->getNormalStrider(normal_strider); + LLVector4a::memcpyNonAliased16((F32*)normal_strider.get(), (F32*)vf.mNormals, num_vertices * 4 * sizeof(F32)); + } - if (skinned) - { - for (U32 i = 0; i < num_vertices; i++) - { - //find closest weight to vf.mVertices[i].mPosition - LLVector3 pos(vf.mPositions[i].getF32ptr()); + if (skinned) + { + for (U32 i = 0; i < num_vertices; i++) + { + //find closest weight to vf.mVertices[i].mPosition + LLVector3 pos(vf.mPositions[i].getF32ptr()); - const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); - llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this + const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); + llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this - LLVector4 w(0, 0, 0, 0); + LLVector4 w(0, 0, 0, 0); - for (U32 i = 0; i < weight_list.size(); ++i) - { - F32 wght = llclamp(weight_list[i].mWeight, 0.001f, 0.999f); - F32 joint = (F32)weight_list[i].mJointIdx; - w.mV[i] = joint + wght; - llassert(w.mV[i] - (S32)w.mV[i]>0.0f); // because weights are non-zero, and range of wt values - //should not cause floating point precision issues. - } + for (U32 i = 0; i < weight_list.size(); ++i) + { + F32 wght = llclamp(weight_list[i].mWeight, 0.001f, 0.999f); + F32 joint = (F32)weight_list[i].mJointIdx; + w.mV[i] = joint + wght; + llassert(w.mV[i] - (S32)w.mV[i]>0.0f); // because weights are non-zero, and range of wt values + //should not cause floating point precision issues. + } - // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis - //*(weights_strider++) = w; - (*(weights_strider++)).loadua(w.mV); - } - } + // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis + //*(weights_strider++) = w; + (*(weights_strider++)).loadua(w.mV); + } + } - // build indices - for (U32 i = 0; i < num_indices; i++) - { - *(index_strider++) = vf.mIndices[i]; - } + // build indices + for (U32 i = 0; i < num_indices; i++) + { + *(index_strider++) = vf.mIndices[i]; + } - mVertexBuffer[lod][mdl].push_back(vb); + mVertexBuffer[lod][mdl].push_back(vb); - vertex_count += num_vertices; - tri_count += num_indices / 3; - ++mesh_count; + vertex_count += num_vertices; + tri_count += num_indices / 3; + ++mesh_count; - } - } + } + } } void LLModelPreview::update() { - if (mGenLOD) - { - bool subscribe_for_generation = mLodsQuery.empty(); - mGenLOD = false; - mDirty = true; - mLodsQuery.clear(); + if (mGenLOD) + { + bool subscribe_for_generation = mLodsQuery.empty(); + mGenLOD = false; + mDirty = true; + mLodsQuery.clear(); - for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod) - { - // adding all lods into query for generation - mLodsQuery.push_back(lod); - } + for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod) + { + // adding all lods into query for generation + mLodsQuery.push_back(lod); + } - if (subscribe_for_generation) - { - doOnIdleRepeating(lodQueryCallback); - } - } + if (subscribe_for_generation) + { + doOnIdleRepeating(lodQueryCallback); + } + } - if (mDirty && mLodsQuery.empty()) - { - mDirty = false; - mResourceCost = calcResourceCost(); - refresh(); - updateStatusMessages(); - } + if (mDirty && mLodsQuery.empty()) + { + mDirty = false; + mResourceCost = calcResourceCost(); + refresh(); + updateStatusMessages(); + } } //----------------------------------------------------------------------------- @@ -2922,189 +2924,189 @@ void LLModelPreview::update() //----------------------------------------------------------------------------- void LLModelPreview::createPreviewAvatar(void) { - mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR); - if (mPreviewAvatar) - { - mPreviewAvatar->createDrawable(&gPipeline); - mPreviewAvatar->mSpecialRenderMode = 1; - mPreviewAvatar->startMotion(ANIM_AGENT_STAND); - mPreviewAvatar->hideSkirt(); - } - else - { - // improve uploader error reporting - // LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL; - std::ostringstream out; - out << "Failed to create preview avatar for upload model window"; - LL_INFOS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, true); - out.str(""); - // - } + mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR); + if (mPreviewAvatar) + { + mPreviewAvatar->createDrawable(&gPipeline); + mPreviewAvatar->mSpecialRenderMode = 1; + mPreviewAvatar->startMotion(ANIM_AGENT_STAND); + mPreviewAvatar->hideSkirt(); + } + else + { + // improve uploader error reporting + // LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL; + std::ostringstream out; + out << "Failed to create preview avatar for upload model window"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + out.str(""); + // + } } //static U32 LLModelPreview::countRootModels(LLModelLoader::model_list models) { - U32 root_models = 0; - model_list::iterator model_iter = models.begin(); - while (model_iter != models.end()) - { - LLModel* mdl = *model_iter; - if (mdl && mdl->mSubmodelID == 0) - { - root_models++; - } - model_iter++; - } - return root_models; + U32 root_models = 0; + model_list::iterator model_iter = models.begin(); + while (model_iter != models.end()) + { + LLModel* mdl = *model_iter; + if (mdl && mdl->mSubmodelID == 0) + { + root_models++; + } + model_iter++; + } + return root_models; } void LLModelPreview::loadedCallback( - LLModelLoader::scene& scene, - LLModelLoader::model_list& model_list, - S32 lod, - void* opaque) + LLModelLoader::scene& scene, + LLModelLoader::model_list& model_list, + S32 lod, + void* opaque) { - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) - { - // Load loader's warnings into floater's log tab - const LLSD out = pPreview->mModelLoader->logOut(); - LLSD::array_const_iterator iter_out = out.beginArray(); - LLSD::array_const_iterator end_out = out.endArray(); - for (; iter_out != end_out; ++iter_out) - { - if (iter_out->has("Message")) - { - LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod); - } - } - pPreview->mModelLoader->clearLog(); - pPreview->loadModelCallback(lod); // removes mModelLoader in some cases - if (pPreview->mLookUpLodFiles && (lod != LLModel::LOD_HIGH)) - { - pPreview->lookupLODModelFiles(lod); - } - } + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) + { + // Load loader's warnings into floater's log tab + const LLSD out = pPreview->mModelLoader->logOut(); + LLSD::array_const_iterator iter_out = out.beginArray(); + LLSD::array_const_iterator end_out = out.endArray(); + for (; iter_out != end_out; ++iter_out) + { + if (iter_out->has("Message")) + { + LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod); + } + } + pPreview->mModelLoader->clearLog(); + pPreview->loadModelCallback(lod); // removes mModelLoader in some cases + if (pPreview->mLookUpLodFiles && (lod != LLModel::LOD_HIGH)) + { + pPreview->lookupLODModelFiles(lod); + } + } } void LLModelPreview::lookupLODModelFiles(S32 lod) { - if (lod == LLModel::LOD_PHYSICS) - { - mLookUpLodFiles = false; - return; - } - S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS; + if (lod == LLModel::LOD_PHYSICS) + { + mLookUpLodFiles = false; + return; + } + S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS; - std::string lod_filename = mLODFile[LLModel::LOD_HIGH]; - std::string ext = ".dae"; - std::string::size_type i = lod_filename.rfind(ext); - if (i != std::string::npos) - { - lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext); - } - if (gDirUtilp->fileExists(lod_filename)) - { - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp) - { - fmp->setCtrlLoadFromFile(next_lod); - } - loadModel(lod_filename, next_lod); - } - else - { - lookupLODModelFiles(next_lod); - } + std::string lod_filename = mLODFile[LLModel::LOD_HIGH]; + std::string ext = ".dae"; + std::string::size_type i = lod_filename.rfind(ext); + if (i != std::string::npos) + { + lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext); + } + if (gDirUtilp->fileExists(lod_filename)) + { + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + fmp->setCtrlLoadFromFile(next_lod); + } + loadModel(lod_filename, next_lod); + } + else + { + lookupLODModelFiles(next_lod); + } } void LLModelPreview::stateChangedCallback(U32 state, void* opaque) { - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview) - { - pPreview->setLoadState(state); - } + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview) + { + pPreview->setLoadState(state); + } } LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque) { - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview) - { + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview) + { // Query by JointKey rather than just a string, the key can be a U32 index for faster lookup // return pPreview->getPreviewAvatar()->getJoint(str); - return pPreview->getPreviewAvatar()->getJoint( JointKey::construct( str ) ); + return pPreview->getPreviewAvatar()->getJoint( JointKey::construct( str ) ); // - } - return NULL; + } + return NULL; } U32 LLModelPreview::loadTextures(LLImportMaterial& material, void* opaque) { - (void)opaque; + (void)opaque; - if (material.mDiffuseMapFilename.size()) - { - material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >; - LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData)); + if (material.mDiffuseMapFilename.size()) + { + material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >; + LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData)); - tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + LLURI::unescape(material.mDiffuseMapFilename), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); - tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE); - tex->forceToSaveRawImage(0, F32_MAX); - material.setDiffuseMap(tex->getID()); // record tex ID - return 1; - } + tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + LLURI::unescape(material.mDiffuseMapFilename), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); + tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE); + tex->forceToSaveRawImage(0, F32_MAX); + material.setDiffuseMap(tex->getID()); // record tex ID + return 1; + } - material.mOpaqueData = NULL; - return 0; + material.mOpaqueData = NULL; + return 0; } void LLModelPreview::addEmptyFace(LLModel* pTarget) { - U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - LLPointer buff = new LLVertexBuffer(type_mask, 0); + LLPointer buff = new LLVertexBuffer(type_mask, 0); - buff->allocateBuffer(1, 3, true); - memset((U8*)buff->getMappedData(), 0, buff->getSize()); - // Fix when running with opengl core profile - //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); - { - LLStrider< U16 > index_strider; - buff->getIndexStrider( index_strider ); + buff->allocateBuffer(1, 3, true); + memset((U8*)buff->getMappedData(), 0, buff->getSize()); + // Fix when running with opengl core profile + //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); + { + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); - memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); - } - // + memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); + } + // - buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); + buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); - LLStrider pos; - LLStrider norm; - LLStrider tc; - LLStrider index; + LLStrider pos; + LLStrider norm; + LLStrider tc; + LLStrider index; - buff->getVertexStrider(pos); + buff->getVertexStrider(pos); - if (type_mask & LLVertexBuffer::MAP_NORMAL) - { - buff->getNormalStrider(norm); - } - if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) - { - buff->getTexCoord0Strider(tc); - } + if (type_mask & LLVertexBuffer::MAP_NORMAL) + { + buff->getNormalStrider(norm); + } + if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) + { + buff->getTexCoord0Strider(tc); + } - buff->getIndexStrider(index); + buff->getIndexStrider(index); - //resize face array - int faceCnt = pTarget->getNumVolumeFaces(); - pTarget->setNumVolumeFaces(faceCnt + 1); - pTarget->setVolumeFaceData(faceCnt + 1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + //resize face array + int faceCnt = pTarget->getNumVolumeFaces(); + pTarget->setNumVolumeFaces(faceCnt + 1); + pTarget->setVolumeFaceData(faceCnt + 1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); } @@ -3115,824 +3117,824 @@ void LLModelPreview::addEmptyFace(LLModel* pTarget) // Note: Render happens each frame with skinned avatars BOOL LLModelPreview::render() { - assert_main_thread(); - - LLMutexLock lock(this); - mNeedsUpdate = FALSE; - - bool use_shaders = LLGLSLShader::sNoFixedFunction; - - bool edges = mViewOption["show_edges"]; - bool joint_overrides = mViewOption["show_joint_overrides"]; - bool joint_positions = mViewOption["show_joint_positions"]; - bool skin_weight = mViewOption["show_skin_weight"]; - bool textures = mViewOption["show_textures"]; - bool physics = mViewOption["show_physics"]; - bool uv_guide = mViewOption["show_uv_guide"]; // Add UV guide overlay in mesh preview - - // restore things lost by the lab during importer work - // Extra configurability, to be exposed later as controls? - static LLCachedControl canvas_col(gSavedSettings, "MeshPreviewCanvasColor"); - static LLCachedControl edge_col(gSavedSettings, "MeshPreviewEdgeColor"); - static LLCachedControl base_col(gSavedSettings, "MeshPreviewBaseColor"); - static LLCachedControl brightness(gSavedSettings, "MeshPreviewBrightnessColor"); - static LLCachedControl edge_width(gSavedSettings, "MeshPreviewEdgeWidth"); - static LLCachedControl phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor"); - static LLCachedControl phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor"); - static LLCachedControl phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth"); - static LLCachedControl deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor"); - static LLCachedControl deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor"); - static LLCachedControl deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth"); - static LLCachedControl deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize"); - static LLCachedControl auto_enable_weight_upload(gSavedSettings, "FSMeshUploadAutoEnableWeights"); - static LLCachedControl auto_enable_show_weights(gSavedSettings, "FSMeshUploadAutoShowWeightsWhenEnabled"); - // - - S32 width = getWidth(); - S32 height = getHeight(); - - LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test - LLGLDisable no_blend(GL_BLEND); - LLGLEnable cull(GL_CULL_FACE); - LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color - LLGLDisable fog(GL_FOG); - - { - if (use_shaders) - { - gUIProgram.bind(); - } - //clear background to grey - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.ortho(0.0f, width, 0.0f, height, -1.0f, 1.0f); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.pushMatrix(); - gGL.loadIdentity(); - - gGL.color4fv(canvas_col().mV); // restore changes removed by the lab - gl_rect_2d_simple(width, height); - - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.popMatrix(); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); - if (use_shaders) - { - gUIProgram.unbind(); - } - } - - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - - bool has_skin_weights = false; - bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); - bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); - - if (upload_joints != mLastJointUpdate) - { - mLastJointUpdate = upload_joints; - if (fmp) - { - fmp->clearAvatarTab(); - } - } - - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - model->mPelvisOffset = mPelvisZOffset; - if (!model->mSkinWeights.empty()) - { - has_skin_weights = true; - } - } - } - - if (has_skin_weights && lodsReady()) - { //model has skin weights, enable view options for skin weights and joint positions - U32 flags = getLegacyRigFlags(); - if (fmp) - { - if (flags == LEGACY_RIG_OK) - { - if (mFirstSkinUpdate) - { - // auto enable weight upload if weights are present - // (note: all these UI updates need to be somewhere that is not render) - // BUG-229632 auto enable weights slows manual workflow - // mViewOption["show_skin_weight"] = true; - // skin_weight = true; - // fmp->childSetValue("upload_skin", true); - LL_DEBUGS("MeshUpload") << "FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; - LL_DEBUGS("MeshUpload") << "FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; - upload_skin=auto_enable_weight_upload(); - fmp->childSetValue("upload_skin", upload_skin); - - skin_weight = upload_skin && auto_enable_show_weights(); - mViewOption["show_skin_weight"] = skin_weight; - mFMP->childSetValue("show_skin_weight", skin_weight); - fmp->setViewOptionEnabled("show_skin_weight", upload_skin); - fmp->setViewOptionEnabled("show_joint_overrides", upload_skin); - fmp->setViewOptionEnabled("show_joint_positions", upload_skin); - // - mFirstSkinUpdate = false; - } - // BUG-229632 auto enable weights slows manual workflow - // fmp->enableViewOption("show_skin_weight"); - // fmp->setViewOptionEnabled("show_joint_overrides", skin_weight); - // fmp->setViewOptionEnabled("show_joint_posi - else - { - LL_DEBUGS("MeshUpload") << "NOT FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; - LL_DEBUGS("MeshUpload") << "NOT FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; - fmp->setViewOptionEnabled("show_skin_weight", upload_skin); - fmp->setViewOptionEnabled("show_joint_overrides", upload_skin); - fmp->setViewOptionEnabled("show_joint_positions", upload_skin); - } - // - mFMP->childEnable("upload_skin"); - // mFMP->childSetValue("show_skin_weight", skin_weight); // BUG-229632 - - } - 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); - } - // defensive code to wanr for incorrect flags - no behavioural change - else - { - LL_WARNS("MeshUpload") << "Unexpected flags combination on weights check" << LL_ENDL; - } - // - } - } - else - { - mFMP->childDisable("upload_skin"); - if (fmp) - { - mViewOption["show_skin_weight"] = false; - fmp->disableViewOption("show_skin_weight"); - fmp->disableViewOption("show_joint_overrides"); - fmp->disableViewOption("show_joint_positions"); - - skin_weight = false; - mFMP->childSetValue("show_skin_weight", false); - fmp->setViewOptionEnabled("show_skin_weight", skin_weight); - } - } - - if (upload_skin && !has_skin_weights) - { //can't upload skin weights if model has no skin weights - mFMP->childSetValue("upload_skin", false); - upload_skin = false; - } - - if (!upload_skin && upload_joints) - { //can't upload joints if not uploading skin weights - mFMP->childSetValue("upload_joints", false); - upload_joints = false; - } - - if (fmp) - { - if (upload_skin) - { - // will populate list of joints - fmp->updateAvatarTab(upload_joints); - } - else - { - fmp->clearAvatarTab(); - } - } - - if (upload_skin && upload_joints) - { - mFMP->childEnable("lock_scale_if_joint_position"); - } - else - { - mFMP->childDisable("lock_scale_if_joint_position"); - mFMP->childSetValue("lock_scale_if_joint_position", false); - } - - //Only enable joint offsets if it passed the earlier critiquing - if (isRigValidForJointPositionUpload()) - { - mFMP->childSetEnabled("upload_joints", upload_skin); - } - - F32 explode = mFMP->childGetValue("physics_explode").asReal(); - - LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview - - LLRect preview_rect; - - preview_rect = mFMP->getChildView("preview_panel")->getRect(); - - F32 aspect = (F32)preview_rect.getWidth() / preview_rect.getHeight(); - - LLViewerCamera::getInstance()->setAspect(aspect); - - LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); - - LLVector3 offset = mCameraOffset; - LLVector3 target_pos = mPreviewTarget + offset; - - F32 z_near = 0.001f; - F32 z_far = mCameraDistance*10.0f + mPreviewScale.magVec() + mCameraOffset.magVec(); - - if (skin_weight) - { - target_pos = getPreviewAvatar()->getPositionAgent() + offset; - z_near = 0.01f; - z_far = 1024.f; - - //render avatar previews every frame - refresh(); - } - - if (use_shaders) - { - gObjectPreviewProgram.bind(); - } - - gGL.loadIdentity(); - gPipeline.enableLightsPreview(); - gObjectPreviewProgram.uniform4fv(LLShaderMgr::AMBIENT, 1, LLPipeline::PreviewAmbientColor.mV); // pass ambient setting to shader - - LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * - LLQuaternion(mCameraYaw, LLVector3::z_axis); - - LLQuaternion av_rot = camera_rot; - F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest - - - z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f); - - LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); - - stop_glerror(); - - gGL.pushMatrix(); - gGL.color4fv(edge_col().mV); // restore changes removed by the lab - - const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - LLGLEnable normalize(GL_NORMALIZE); - - if (!mBaseModel.empty() && mVertexBuffer[5].empty()) - { - genBuffers(-1, skin_weight); - //genBuffers(3); - //genLODs(); - } - - if (!mModel[mPreviewLOD].empty()) - { - mFMP->childEnable("reset_btn"); - - bool regen = mVertexBuffer[mPreviewLOD].empty(); - if (!regen) - { - const std::vector >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second; - if (!vb_vec.empty()) - { - const LLVertexBuffer* buff = vb_vec[0]; - regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; - } - else - { - LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL; - regen = TRUE; - } - } - - if (regen) - { - genBuffers(mPreviewLOD, skin_weight); - } - - if (!skin_weight) - { - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[mPreviewLOD]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*)mat.mMatrix); - - - U32 num_models = mVertexBuffer[mPreviewLOD][model].size(); - for (U32 i = 0; i < num_models; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - - if (textures) - { - int materialCnt = instance.mModel->mMaterialList.size(); - if (i < materialCnt) - { - const std::string& binding = instance.mModel->mMaterialList[i]; - const LLImportMaterial& material = instance.mMaterial[binding]; - - gGL.diffuseColor4fv(material.mDiffuseColor.mV); - - // Find the tex for this material, bind it, and add it to our set - // - LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); - if (tex) - { - mTextureSet.insert(tex); - } - } - } - // improved mesh uploader - else if (uv_guide) - { - if(mUVGuideTexture) - { - if (mUVGuideTexture->getDiscardLevel() > -1) - { - gGL.getTexUnit(0)->bind(mUVGuideTexture, true); - } - } - gGL.diffuseColor4fv(base_col().mV); // restore changes removed by the lab - - } - // - else - { - gGL.diffuseColor4fv(base_col().mV); // restore changes removed by the lab - } - - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.diffuseColor4fv(edge_col().mV); // restore changes removed by the lab - if (edges) - { - gGL.setLineWidth(edge_width()); // restore changes removed by the lab - 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); - gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt - } - } - gGL.popMatrix(); - } - - if (physics) - { - glClear(GL_DEPTH_BUFFER_BIT); - - for (U32 pass = 0; pass < 2; pass++) - { - if (pass == 0) - { //depth only pass - gGL.setColorMask(false, false); - } - else - { - gGL.setColorMask(true, true); - } - - //enable alpha blending on second pass but not first pass - LLGLState blend(GL_BLEND, pass); - - gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA); - - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*)mat.mMatrix); - - - bool render_mesh = true; - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); - - LLModel::Decomposition& physics = model->mPhysics; - - if (!physics.mHull.empty()) - { - render_mesh = false; - - if (physics.mMesh.empty()) - { //build vertex buffer for physics mesh - gMeshRepo.buildPhysicsMesh(physics); - } - - if (!physics.mMesh.empty()) - { //render hull instead of mesh - for (U32 i = 0; i < physics.mMesh.size(); ++i) - { - if (explode > 0.f) - { - gGL.pushMatrix(); - - LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; - offset *= explode; - - gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); - } - - static std::vector hull_colors; - - if (i + 1 >= hull_colors.size()) - { - hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128)); - } - - gGL.diffuseColor4ubv(hull_colors[i].mV); - LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); - - if (explode > 0.f) - { - gGL.popMatrix(); - } - } - } - } - } - - if (render_mesh) - { - if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) - { - genBuffers(LLModel::LOD_PHYSICS, false); - } - - U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); - if (pass > 0){ - for (U32 i = 0; i < num_models; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.diffuseColor4fv(phys_fill_col().mV); // restore changes removed by the lab - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); - // restore changes removed by the lab - // gGL.diffuseColor4fv(PREVIEW_PSYH_EDGE_COL.mV); - // gGL.setLineWidth(PREVIEW_PSYH_EDGE_WIDTH); // Line width OGL core profile fix by Rye Mutt - gGL.diffuseColor4fv(phys_edge_col().mV); - gGL.setLineWidth(phys_edge_width()); - // - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt - } - } - } - gGL.popMatrix(); - } - - // only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks] - if (mHasDegenerate) - { - // restore older functionality lost in lab importer - // gGL.setLineWidth(PREVIEW_DEG_EDGE_WIDTH); // Line width OGL core profile fix by Rye Mutt - // glPointSize(PREVIEW_DEG_POINT_SIZE); - // gPipeline.enableLightsFullbright(); - gGL.setLineWidth(deg_edge_width()); - glPointSize(deg_point_size()); - // gPipeline.enableLightsFullbright(); // This may need to be restored when I fined the cause of the black rendering - // - //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); // restore proper functionality - const LLVector4a scale(0.5f); - - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*)mat.mMatrix); - - - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); - - LLModel::Decomposition& physics = model->mPhysics; - - if (physics.mHull.empty()) - { - if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) - { - genBuffers(LLModel::LOD_PHYSICS, false); - } - - U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); - for (U32 v = 0; v < num_models; ++v) - { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - - LLStrider pos_strider; - buffer->getVertexStrider(pos_strider, 0); - LLVector4a* pos = (LLVector4a*)pos_strider.get(); - - LLStrider idx; - buffer->getIndexStrider(idx, 0); - - for (U32 i = 0; i < buffer->getNumIndices(); i += 3) - { - LLVector4a v1; v1.setMul(pos[*idx++], scale); - LLVector4a v2; v2.setMul(pos[*idx++], scale); - LLVector4a v3; v3.setMul(pos[*idx++], scale); - - if (ll_is_degenerate(v1, v2, v3)) - { - // restore (configurable) coloured overlay - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - gGL.diffuseColor4fv(deg_fill_col().mV); - buffer->draw(LLRender::TRIANGLES, 3, i); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - gGL.diffuseColor3fv(deg_edge_col().mV); - gGL.color3fv(deg_edge_col().mV); - // - buffer->draw(LLRender::LINE_LOOP, 3, i); - buffer->draw(LLRender::POINTS, 3, i); - } - } - } - } - } - - gGL.popMatrix(); - } - gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt - glPointSize(1.f); - gPipeline.enableLightsPreview(); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - } - } - } - else - { - target_pos = getPreviewAvatar()->getPositionAgent(); - getPreviewAvatar()->clearAttachmentOverrides(); // removes pelvis fixup - LLUUID fake_mesh_id; - fake_mesh_id.generate(); - getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); - bool pelvis_recalc = false; - - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest - - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - - if (!model->mSkinWeights.empty()) - { - const LLMeshSkinInfo *skin = &model->mSkinInfo; - LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary - U32 joint_count = LLSkinningUtil::getMeshJointCount(skin); - U32 bind_count = skin->mAlternateBindMatrix.size(); - - if (joint_overrides - && bind_count > 0 - && joint_count == bind_count) - { - // mesh_id is used to determine which mesh gets to - // set the joint offset, in the event of a conflict. Since - // we don't know the mesh id yet, we can't guarantee that - // joint offsets will be applied with the same priority as - // in the uploaded model. If the file contains multiple - // meshes with conflicting joint offsets, preview may be - // incorrect. - LLUUID fake_mesh_id; - fake_mesh_id.generate(); - for (U32 j = 0; j < joint_count; ++j) - { - LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]); - if (joint) - { - const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation(); - if (joint->aboveJointPosThreshold(jointPos)) - { - bool override_changed; - joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed); - - if (override_changed) - { - //If joint is a pelvis then handle old/new pelvis to foot values - if (joint->getName() == "mPelvis")// or skin->mJointNames[j] - { - pelvis_recalc = true; - } - } - if (skin->mLockScaleIfJointPosition) - { - // Note that unlike positions, there's no threshold check here, - // just a lock at the default value. - joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model"); - } - } - } - } - } - - for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - const LLVolumeFace& face = model->getVolumeFace(i); - - LLStrider position; - buffer->getVertexStrider(position); - - // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis - //LLStrider weight; - LLStrider weight; - buffer->getWeight4Strider(weight); - - //quick 'n dirty software vertex skinning - - //build matrix palette - - LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT]; - // use Mat4a part of the caching changes, no point in using the cache itself in the preview though. - //LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, joint_count, - // skin, getPreviewAvatar()); - LLSkinningUtil::initSkinningMatrixPalette(mat, joint_count, - skin, getPreviewAvatar()); - // - - LLMatrix4a bind_shape_matrix; - bind_shape_matrix.loadu(skin->mBindShapeMatrix); - U32 max_joints = LLSkinningUtil::getMaxJointCount(); - for (U32 j = 0; j < buffer->getNumVerts(); ++j) - { - LLMatrix4a final_mat; - // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis - //F32 *wptr = weight[j].mV; - F32 *wptr = weight[j].getF32ptr(); - // - LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints); - - //VECTORIZE THIS - LLVector4a& v = face.mPositions[j]; - - LLVector4a t; - LLVector4a dst; - bind_shape_matrix.affineTransform(v, t); - final_mat.affineTransform(t, dst); - - position[j][0] = dst[0]; - position[j][1] = dst[1]; - position[j][2] = dst[2]; - } - - // FIRE-13465 Make sure there's a material set before dereferencing it - if( instance.mModel->mMaterialList.size() > i && - instance.mMaterial.end() != instance.mMaterial.find( instance.mModel->mMaterialList[ i ] ) ) - { - // - llassert(model->mMaterialList.size() > i); - const std::string& binding = instance.mModel->mMaterialList[i]; - const LLImportMaterial& material = instance.mMaterial[binding]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - gGL.diffuseColor4fv(material.mDiffuseColor.mV); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - // Find the tex for this material, bind it, and add it to our set - // - LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); - if (tex) - { - mTextureSet.insert(tex); - } - } else // FIRE-13465 Make sure there's a material set before dereferencing it, if none, set buffer type and unbind texture. - { - buffer->setBuffer(type_mask & buffer->getTypeMask()); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } // - - buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - - if (edges) - { - // restore behaviour removed by lab - // gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); - // gGL.setLineWidth(PREVIEW_EDGE_WIDTH); - gGL.diffuseColor4fv(edge_col().mV); - gGL.setLineWidth(edge_width()); - // - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt - } - } - } - } - } - - if (joint_positions) - { - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - if (shader) - { - gDebugProgram.bind(); - } - getPreviewAvatar()->renderCollisionVolumes(); - if (fmp->mTabContainer->getCurrentPanelIndex() == fmp->mAvatarTabIndex) - { - getPreviewAvatar()->renderBones(fmp->mSelectedJointName); - } - else - { - getPreviewAvatar()->renderBones(); - } - if (shader) - { - shader->bind(); - } - } - - if (pelvis_recalc) - { - // size/scale recalculation - getPreviewAvatar()->postPelvisSetRecalc(); - } - } - } - - if (use_shaders) - { - gObjectPreviewProgram.unbind(); - } - - gGL.popMatrix(); - - return TRUE; + assert_main_thread(); + + LLMutexLock lock(this); + mNeedsUpdate = FALSE; + + bool use_shaders = LLGLSLShader::sNoFixedFunction; + + bool edges = mViewOption["show_edges"]; + bool joint_overrides = mViewOption["show_joint_overrides"]; + bool joint_positions = mViewOption["show_joint_positions"]; + bool skin_weight = mViewOption["show_skin_weight"]; + bool textures = mViewOption["show_textures"]; + bool physics = mViewOption["show_physics"]; + bool uv_guide = mViewOption["show_uv_guide"]; // Add UV guide overlay in mesh preview + + // restore things lost by the lab during importer work + // Extra configurability, to be exposed later as controls? + static LLCachedControl canvas_col(gSavedSettings, "MeshPreviewCanvasColor"); + static LLCachedControl edge_col(gSavedSettings, "MeshPreviewEdgeColor"); + static LLCachedControl base_col(gSavedSettings, "MeshPreviewBaseColor"); + static LLCachedControl brightness(gSavedSettings, "MeshPreviewBrightnessColor"); + static LLCachedControl edge_width(gSavedSettings, "MeshPreviewEdgeWidth"); + static LLCachedControl phys_edge_col(gSavedSettings, "MeshPreviewPhysicsEdgeColor"); + static LLCachedControl phys_fill_col(gSavedSettings, "MeshPreviewPhysicsFillColor"); + static LLCachedControl phys_edge_width(gSavedSettings, "MeshPreviewPhysicsEdgeWidth"); + static LLCachedControl deg_edge_col(gSavedSettings, "MeshPreviewDegenerateEdgeColor"); + static LLCachedControl deg_fill_col(gSavedSettings, "MeshPreviewDegenerateFillColor"); + static LLCachedControl deg_edge_width(gSavedSettings, "MeshPreviewDegenerateEdgeWidth"); + static LLCachedControl deg_point_size(gSavedSettings, "MeshPreviewDegeneratePointSize"); + static LLCachedControl auto_enable_weight_upload(gSavedSettings, "FSMeshUploadAutoEnableWeights"); + static LLCachedControl auto_enable_show_weights(gSavedSettings, "FSMeshUploadAutoShowWeightsWhenEnabled"); + // + + S32 width = getWidth(); + S32 height = getHeight(); + + LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test + LLGLDisable no_blend(GL_BLEND); + LLGLEnable cull(GL_CULL_FACE); + LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color + LLGLDisable fog(GL_FOG); + + { + if (use_shaders) + { + gUIProgram.bind(); + } + //clear background to grey + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(0.0f, width, 0.0f, height, -1.0f, 1.0f); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.color4fv(canvas_col().mV); // restore changes removed by the lab + gl_rect_2d_simple(width, height); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + if (use_shaders) + { + gUIProgram.unbind(); + } + } + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + + bool has_skin_weights = false; + bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); + bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); + + if (upload_joints != mLastJointUpdate) + { + mLastJointUpdate = upload_joints; + if (fmp) + { + fmp->clearAvatarTab(); + } + } + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + model->mPelvisOffset = mPelvisZOffset; + if (!model->mSkinWeights.empty()) + { + has_skin_weights = true; + } + } + } + + if (has_skin_weights && lodsReady()) + { //model has skin weights, enable view options for skin weights and joint positions + U32 flags = getLegacyRigFlags(); + if (fmp) + { + if (flags == LEGACY_RIG_OK) + { + if (mFirstSkinUpdate) + { + // auto enable weight upload if weights are present + // (note: all these UI updates need to be somewhere that is not render) + // BUG-229632 auto enable weights slows manual workflow + // mViewOption["show_skin_weight"] = true; + // skin_weight = true; + // fmp->childSetValue("upload_skin", true); + LL_DEBUGS("MeshUpload") << "FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; + LL_DEBUGS("MeshUpload") << "FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; + upload_skin=auto_enable_weight_upload(); + fmp->childSetValue("upload_skin", upload_skin); + + skin_weight = upload_skin && auto_enable_show_weights(); + mViewOption["show_skin_weight"] = skin_weight; + mFMP->childSetValue("show_skin_weight", skin_weight); + fmp->setViewOptionEnabled("show_skin_weight", upload_skin); + fmp->setViewOptionEnabled("show_joint_overrides", upload_skin); + fmp->setViewOptionEnabled("show_joint_positions", upload_skin); + // + mFirstSkinUpdate = false; + } + // BUG-229632 auto enable weights slows manual workflow + // fmp->enableViewOption("show_skin_weight"); + // fmp->setViewOptionEnabled("show_joint_overrides", skin_weight); + // fmp->setViewOptionEnabled("show_joint_posi + else + { + LL_DEBUGS("MeshUpload") << "NOT FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; + LL_DEBUGS("MeshUpload") << "NOT FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; + fmp->setViewOptionEnabled("show_skin_weight", upload_skin); + fmp->setViewOptionEnabled("show_joint_overrides", upload_skin); + fmp->setViewOptionEnabled("show_joint_positions", upload_skin); + } + // + mFMP->childEnable("upload_skin"); + // mFMP->childSetValue("show_skin_weight", skin_weight); // BUG-229632 + + } + 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); + } + // defensive code to wanr for incorrect flags - no behavioural change + else + { + LL_WARNS("MeshUpload") << "Unexpected flags combination on weights check" << LL_ENDL; + } + // + } + } + else + { + mFMP->childDisable("upload_skin"); + if (fmp) + { + mViewOption["show_skin_weight"] = false; + fmp->disableViewOption("show_skin_weight"); + fmp->disableViewOption("show_joint_overrides"); + fmp->disableViewOption("show_joint_positions"); + + skin_weight = false; + mFMP->childSetValue("show_skin_weight", false); + fmp->setViewOptionEnabled("show_skin_weight", skin_weight); + } + } + + if (upload_skin && !has_skin_weights) + { //can't upload skin weights if model has no skin weights + mFMP->childSetValue("upload_skin", false); + upload_skin = false; + } + + if (!upload_skin && upload_joints) + { //can't upload joints if not uploading skin weights + mFMP->childSetValue("upload_joints", false); + upload_joints = false; + } + + if (fmp) + { + if (upload_skin) + { + // will populate list of joints + fmp->updateAvatarTab(upload_joints); + } + else + { + fmp->clearAvatarTab(); + } + } + + if (upload_skin && upload_joints) + { + mFMP->childEnable("lock_scale_if_joint_position"); + } + else + { + mFMP->childDisable("lock_scale_if_joint_position"); + mFMP->childSetValue("lock_scale_if_joint_position", false); + } + + //Only enable joint offsets if it passed the earlier critiquing + if (isRigValidForJointPositionUpload()) + { + mFMP->childSetEnabled("upload_joints", upload_skin); + } + + F32 explode = mFMP->childGetValue("physics_explode").asReal(); + + LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview + + LLRect preview_rect; + + preview_rect = mFMP->getChildView("preview_panel")->getRect(); + + F32 aspect = (F32)preview_rect.getWidth() / preview_rect.getHeight(); + + LLViewerCamera::getInstance()->setAspect(aspect); + + LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); + + LLVector3 offset = mCameraOffset; + LLVector3 target_pos = mPreviewTarget + offset; + + F32 z_near = 0.001f; + F32 z_far = mCameraDistance*10.0f + mPreviewScale.magVec() + mCameraOffset.magVec(); + + if (skin_weight) + { + target_pos = getPreviewAvatar()->getPositionAgent() + offset; + z_near = 0.01f; + z_far = 1024.f; + + //render avatar previews every frame + refresh(); + } + + if (use_shaders) + { + gObjectPreviewProgram.bind(); + } + + gGL.loadIdentity(); + gPipeline.enableLightsPreview(); + gObjectPreviewProgram.uniform4fv(LLShaderMgr::AMBIENT, 1, LLPipeline::PreviewAmbientColor.mV); // pass ambient setting to shader + + LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * + LLQuaternion(mCameraYaw, LLVector3::z_axis); + + LLQuaternion av_rot = camera_rot; + F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + + z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f); + + LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); + + stop_glerror(); + + gGL.pushMatrix(); + gGL.color4fv(edge_col().mV); // restore changes removed by the lab + + const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + LLGLEnable normalize(GL_NORMALIZE); + + if (!mBaseModel.empty() && mVertexBuffer[5].empty()) + { + genBuffers(-1, skin_weight); + //genBuffers(3); + //genLODs(); + } + + if (!mModel[mPreviewLOD].empty()) + { + mFMP->childEnable("reset_btn"); + + bool regen = mVertexBuffer[mPreviewLOD].empty(); + if (!regen) + { + const std::vector >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second; + if (!vb_vec.empty()) + { + const LLVertexBuffer* buff = vb_vec[0]; + regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; + } + else + { + LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL; + regen = TRUE; + } + } + + if (regen) + { + genBuffers(mPreviewLOD, skin_weight); + } + + if (!skin_weight) + { + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[mPreviewLOD]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + U32 num_models = mVertexBuffer[mPreviewLOD][model].size(); + for (U32 i = 0; i < num_models; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + + if (textures) + { + int materialCnt = instance.mModel->mMaterialList.size(); + if (i < materialCnt) + { + const std::string& binding = instance.mModel->mMaterialList[i]; + const LLImportMaterial& material = instance.mMaterial[binding]; + + gGL.diffuseColor4fv(material.mDiffuseColor.mV); + + // Find the tex for this material, bind it, and add it to our set + // + LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); + if (tex) + { + mTextureSet.insert(tex); + } + } + } + // improved mesh uploader + else if (uv_guide) + { + if(mUVGuideTexture) + { + if (mUVGuideTexture->getDiscardLevel() > -1) + { + gGL.getTexUnit(0)->bind(mUVGuideTexture, true); + } + } + gGL.diffuseColor4fv(base_col().mV); // restore changes removed by the lab + + } + // + else + { + gGL.diffuseColor4fv(base_col().mV); // restore changes removed by the lab + } + + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.diffuseColor4fv(edge_col().mV); // restore changes removed by the lab + if (edges) + { + gGL.setLineWidth(edge_width()); // restore changes removed by the lab + 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); + gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt + } + } + gGL.popMatrix(); + } + + if (physics) + { + glClear(GL_DEPTH_BUFFER_BIT); + + for (U32 pass = 0; pass < 2; pass++) + { + if (pass == 0) + { //depth only pass + gGL.setColorMask(false, false); + } + else + { + gGL.setColorMask(true, true); + } + + //enable alpha blending on second pass but not first pass + LLGLState blend(GL_BLEND, pass); + + gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA); + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + bool render_mesh = true; + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); + + LLModel::Decomposition& physics = model->mPhysics; + + if (!physics.mHull.empty()) + { + render_mesh = false; + + if (physics.mMesh.empty()) + { //build vertex buffer for physics mesh + gMeshRepo.buildPhysicsMesh(physics); + } + + if (!physics.mMesh.empty()) + { //render hull instead of mesh + for (U32 i = 0; i < physics.mMesh.size(); ++i) + { + if (explode > 0.f) + { + gGL.pushMatrix(); + + LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; + offset *= explode; + + gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); + } + + static std::vector hull_colors; + + if (i + 1 >= hull_colors.size()) + { + hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128)); + } + + gGL.diffuseColor4ubv(hull_colors[i].mV); + LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); + + if (explode > 0.f) + { + gGL.popMatrix(); + } + } + } + } + } + + if (render_mesh) + { + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + + U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); + if (pass > 0){ + for (U32 i = 0; i < num_models; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.diffuseColor4fv(phys_fill_col().mV); // restore changes removed by the lab + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + // restore changes removed by the lab + // gGL.diffuseColor4fv(PREVIEW_PSYH_EDGE_COL.mV); + // gGL.setLineWidth(PREVIEW_PSYH_EDGE_WIDTH); // Line width OGL core profile fix by Rye Mutt + gGL.diffuseColor4fv(phys_edge_col().mV); + gGL.setLineWidth(phys_edge_width()); + // + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt + } + } + } + gGL.popMatrix(); + } + + // only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks] + if (mHasDegenerate) + { + // restore older functionality lost in lab importer + // gGL.setLineWidth(PREVIEW_DEG_EDGE_WIDTH); // Line width OGL core profile fix by Rye Mutt + // glPointSize(PREVIEW_DEG_POINT_SIZE); + // gPipeline.enableLightsFullbright(); + gGL.setLineWidth(deg_edge_width()); + glPointSize(deg_point_size()); + // gPipeline.enableLightsFullbright(); // This may need to be restored when I fined the cause of the black rendering + // + //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); // restore proper functionality + const LLVector4a scale(0.5f); + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); + + LLModel::Decomposition& physics = model->mPhysics; + + if (physics.mHull.empty()) + { + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + + U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); + for (U32 v = 0; v < num_models; ++v) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + + LLStrider pos_strider; + buffer->getVertexStrider(pos_strider, 0); + LLVector4a* pos = (LLVector4a*)pos_strider.get(); + + LLStrider idx; + buffer->getIndexStrider(idx, 0); + + for (U32 i = 0; i < buffer->getNumIndices(); i += 3) + { + LLVector4a v1; v1.setMul(pos[*idx++], scale); + LLVector4a v2; v2.setMul(pos[*idx++], scale); + LLVector4a v3; v3.setMul(pos[*idx++], scale); + + if (ll_is_degenerate(v1, v2, v3)) + { + // restore (configurable) coloured overlay + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.diffuseColor4fv(deg_fill_col().mV); + buffer->draw(LLRender::TRIANGLES, 3, i); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + gGL.diffuseColor3fv(deg_edge_col().mV); + gGL.color3fv(deg_edge_col().mV); + // + buffer->draw(LLRender::LINE_LOOP, 3, i); + buffer->draw(LLRender::POINTS, 3, i); + } + } + } + } + } + + gGL.popMatrix(); + } + gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt + glPointSize(1.f); + gPipeline.enableLightsPreview(); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + } + } + } + else + { + target_pos = getPreviewAvatar()->getPositionAgent(); + getPreviewAvatar()->clearAttachmentOverrides(); // removes pelvis fixup + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); + bool pelvis_recalc = false; + + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + + if (!model->mSkinWeights.empty()) + { + const LLMeshSkinInfo *skin = &model->mSkinInfo; + LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary + U32 joint_count = LLSkinningUtil::getMeshJointCount(skin); + U32 bind_count = skin->mAlternateBindMatrix.size(); + + if (joint_overrides + && bind_count > 0 + && joint_count == bind_count) + { + // mesh_id is used to determine which mesh gets to + // set the joint offset, in the event of a conflict. Since + // we don't know the mesh id yet, we can't guarantee that + // joint offsets will be applied with the same priority as + // in the uploaded model. If the file contains multiple + // meshes with conflicting joint offsets, preview may be + // incorrect. + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + for (U32 j = 0; j < joint_count; ++j) + { + LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]); + if (joint) + { + const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation(); + if (joint->aboveJointPosThreshold(jointPos)) + { + bool override_changed; + joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed); + + if (override_changed) + { + //If joint is a pelvis then handle old/new pelvis to foot values + if (joint->getName() == "mPelvis")// or skin->mJointNames[j] + { + pelvis_recalc = true; + } + } + if (skin->mLockScaleIfJointPosition) + { + // Note that unlike positions, there's no threshold check here, + // just a lock at the default value. + joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model"); + } + } + } + } + } + + for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + const LLVolumeFace& face = model->getVolumeFace(i); + + LLStrider position; + buffer->getVertexStrider(position); + + // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis + //LLStrider weight; + LLStrider weight; + buffer->getWeight4Strider(weight); + + //quick 'n dirty software vertex skinning + + //build matrix palette + + LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT]; + // use Mat4a part of the caching changes, no point in using the cache itself in the preview though. + //LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, joint_count, + // skin, getPreviewAvatar()); + LLSkinningUtil::initSkinningMatrixPalette(mat, joint_count, + skin, getPreviewAvatar()); + // + + LLMatrix4a bind_shape_matrix; + bind_shape_matrix.loadu(skin->mBindShapeMatrix); + U32 max_joints = LLSkinningUtil::getMaxJointCount(); + for (U32 j = 0; j < buffer->getNumVerts(); ++j) + { + LLMatrix4a final_mat; + // Vectorized Weight4Strider and ClothWeightStrider by Drake Arconis + //F32 *wptr = weight[j].mV; + F32 *wptr = weight[j].getF32ptr(); + // + LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints); + + //VECTORIZE THIS + LLVector4a& v = face.mPositions[j]; + + LLVector4a t; + LLVector4a dst; + bind_shape_matrix.affineTransform(v, t); + final_mat.affineTransform(t, dst); + + position[j][0] = dst[0]; + position[j][1] = dst[1]; + position[j][2] = dst[2]; + } + + // FIRE-13465 Make sure there's a material set before dereferencing it + if( instance.mModel->mMaterialList.size() > i && + instance.mMaterial.end() != instance.mMaterial.find( instance.mModel->mMaterialList[ i ] ) ) + { + // + llassert(model->mMaterialList.size() > i); + const std::string& binding = instance.mModel->mMaterialList[i]; + const LLImportMaterial& material = instance.mMaterial[binding]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + gGL.diffuseColor4fv(material.mDiffuseColor.mV); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // Find the tex for this material, bind it, and add it to our set + // + LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); + if (tex) + { + mTextureSet.insert(tex); + } + } else // FIRE-13465 Make sure there's a material set before dereferencing it, if none, set buffer type and unbind texture. + { + buffer->setBuffer(type_mask & buffer->getTypeMask()); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } // + + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + + if (edges) + { + // restore behaviour removed by lab + // gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); + // gGL.setLineWidth(PREVIEW_EDGE_WIDTH); + gGL.diffuseColor4fv(edge_col().mV); + gGL.setLineWidth(edge_width()); + // + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.setLineWidth(1.f); // Line width OGL core profile fix by Rye Mutt + } + } + } + } + } + + if (joint_positions) + { + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + if (shader) + { + gDebugProgram.bind(); + } + getPreviewAvatar()->renderCollisionVolumes(); + if (fmp->mTabContainer->getCurrentPanelIndex() == fmp->mAvatarTabIndex) + { + getPreviewAvatar()->renderBones(fmp->mSelectedJointName); + } + else + { + getPreviewAvatar()->renderBones(); + } + if (shader) + { + shader->bind(); + } + } + + if (pelvis_recalc) + { + // size/scale recalculation + getPreviewAvatar()->postPelvisSetRecalc(); + } + } + } + + if (use_shaders) + { + gObjectPreviewProgram.unbind(); + } + + gGL.popMatrix(); + + return TRUE; } //----------------------------------------------------------------------------- @@ -3940,7 +3942,7 @@ BOOL LLModelPreview::render() //----------------------------------------------------------------------------- void LLModelPreview::refresh() { - mNeedsUpdate = TRUE; + mNeedsUpdate = TRUE; } //----------------------------------------------------------------------------- @@ -3948,9 +3950,9 @@ void LLModelPreview::refresh() //----------------------------------------------------------------------------- void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) { - mCameraYaw = mCameraYaw + yaw_radians; + mCameraYaw = mCameraYaw + yaw_radians; - mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); + mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); } //----------------------------------------------------------------------------- @@ -3958,115 +3960,115 @@ void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) //----------------------------------------------------------------------------- void LLModelPreview::zoom(F32 zoom_amt) { - F32 new_zoom = mCameraZoom + zoom_amt; - // TODO: stop clamping in render - // restore settings control - // mCameraZoom = llclamp(new_zoom, 1.f, PREVIEW_ZOOM_LIMIT); - static LLCachedControl zoom_limit(gSavedSettings, "MeshPreviewZoomLimit"); - mCameraZoom = llclamp(new_zoom, 1.f, zoom_limit()); - // + F32 new_zoom = mCameraZoom + zoom_amt; + // TODO: stop clamping in render + // restore settings control + // mCameraZoom = llclamp(new_zoom, 1.f, PREVIEW_ZOOM_LIMIT); + static LLCachedControl zoom_limit(gSavedSettings, "MeshPreviewZoomLimit"); + mCameraZoom = llclamp(new_zoom, 1.f, zoom_limit()); + // } void LLModelPreview::pan(F32 right, F32 up) { - bool skin_weight = mViewOption["show_skin_weight"]; - F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; - mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f); - mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f); + bool skin_weight = mViewOption["show_skin_weight"]; + F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; + mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f); + mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f); } void LLModelPreview::setPreviewLOD(S32 lod) { - lod = llclamp(lod, 0, (S32)LLModel::LOD_HIGH); + lod = llclamp(lod, 0, (S32)LLModel::LOD_HIGH); - if (lod != mPreviewLOD) - { - mPreviewLOD = lod; + if (lod != mPreviewLOD) + { + mPreviewLOD = lod; - LLComboBox* combo_box = mFMP->getChild("preview_lod_combo"); - combo_box->setCurrentByIndex((NUM_LOD - 1) - mPreviewLOD); // combo box list of lods is in reverse order - mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]); + LLComboBox* combo_box = mFMP->getChild("preview_lod_combo"); + combo_box->setCurrentByIndex((NUM_LOD - 1) - mPreviewLOD); // combo box list of lods is in reverse order + mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]); - LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); - LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); + LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); + LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); - for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i) - { - const LLColor4& color = (i == lod) ? highlight_color : normal_color; + 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); - } + mFMP->childSetColor(lod_status_name[i], color); + mFMP->childSetColor(lod_label_name[i], color); + mFMP->childSetColor(lod_triangles_name[i], color); + mFMP->childSetColor(lod_vertices_name[i], color); + } - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; - if (fmp) - { - // make preview repopulate tab - fmp->clearAvatarTab(); - } - } - refresh(); - updateStatusMessages(); + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + if (fmp) + { + // make preview repopulate tab + fmp->clearAvatarTab(); + } + } + refresh(); + updateStatusMessages(); } //static void LLModelPreview::textureLoadedCallback( - BOOL success, - LLViewerFetchedTexture *src_vi, - LLImageRaw* src, - LLImageRaw* src_aux, - S32 discard_level, - BOOL final, - void* userdata) + BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* src_aux, + S32 discard_level, + BOOL final, + void* userdata) { - LLModelPreview* preview = (LLModelPreview*)userdata; - preview->refresh(); + LLModelPreview* preview = (LLModelPreview*)userdata; + preview->refresh(); - if (final && preview->mModelLoader) - { - if (preview->mModelLoader->mNumOfFetchingTextures > 0) - { - preview->mModelLoader->mNumOfFetchingTextures--; - } - } + if (final && preview->mModelLoader) + { + if (preview->mModelLoader->mNumOfFetchingTextures > 0) + { + preview->mModelLoader->mNumOfFetchingTextures--; + } + } } // static bool LLModelPreview::lodQueryCallback() { - // not the best solution, but model preview belongs to floater - // so it is an easy way to check that preview still exists. - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp && fmp->mModelPreview) - { - LLModelPreview* preview = fmp->mModelPreview; - if (preview->mLodsQuery.size() > 0) - { - S32 lod = preview->mLodsQuery.back(); - preview->mLodsQuery.pop_back(); - preview->genLODs(lod); + // not the best solution, but model preview belongs to floater + // so it is an easy way to check that preview still exists. + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp && fmp->mModelPreview) + { + LLModelPreview* preview = fmp->mModelPreview; + if (preview->mLodsQuery.size() > 0) + { + S32 lod = preview->mLodsQuery.back(); + preview->mLodsQuery.pop_back(); + preview->genLODs(lod); - if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) - { - preview->lookupLODModelFiles(LLModel::LOD_HIGH); - } + if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) + { + preview->lookupLODModelFiles(LLModel::LOD_HIGH); + } - // return false to continue cycle - return false; - } - } - // nothing to process - return true; + // return false to continue cycle + return false; + } + } + // nothing to process + return true; } void LLModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) { - if (!mLODFrozen) - { - genLODs(lod, 3, enforce_tri_limit); - refresh(); - } + if (!mLODFrozen) + { + genLODs(lod, 3, enforce_tri_limit); + refresh(); + } } From ccc9196baac1d150447652f81067a8e2284241b4 Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Sat, 29 May 2021 03:47:20 +0200 Subject: [PATCH 07/14] Updated Polish translation --- indra/newview/skins/default/xui/pl/floater_model_preview.xml | 1 + .../newview/skins/default/xui/pl/panel_preferences_graphics1.xml | 1 + indra/newview/skins/default/xui/pl/strings.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/indra/newview/skins/default/xui/pl/floater_model_preview.xml b/indra/newview/skins/default/xui/pl/floater_model_preview.xml index ad5b5a73d4..d9b91fd3b7 100644 --- a/indra/newview/skins/default/xui/pl/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/pl/floater_model_preview.xml @@ -213,6 +213,7 @@ + Fizyka: diff --git a/indra/newview/skins/default/xui/pl/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/pl/panel_preferences_graphics1.xml index 67abe1959d..e8580b7d06 100644 --- a/indra/newview/skins/default/xui/pl/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/pl/panel_preferences_graphics1.xml @@ -121,6 +121,7 @@ + diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml index ee633a534a..e13a7e8796 100644 --- a/indra/newview/skins/default/xui/pl/strings.xml +++ b/indra/newview/skins/default/xui/pl/strings.xml @@ -42,6 +42,7 @@ Położenie: [REGION] Procesor (CPU): [CPU] Pamięć (Memory): [MEMORY_MB] MB +Wątki dekodowania (Concurrency): [CONCURRENCY] System operacyjny (OS Version): [OS_VERSION] Dostawca karty graficznej (Graphics Card Vendor): [GRAPHICS_CARD_VENDOR] Karta graficzna (Graphics Card): [GRAPHICS_CARD] From 9f3b23bd05bd9e11d3f707ba06f6726732546fd0 Mon Sep 17 00:00:00 2001 From: Liny Date: Fri, 28 May 2021 19:28:10 -0700 Subject: [PATCH 08/14] Fix mac and linux builds. --- indra/newview/llmodelpreview.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index b8fa419e3a..2cfee9e3cb 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -200,7 +200,7 @@ public: bool mHasDegenerate; protected: - bool LLModelPreview::matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); // FIRE-30965 Cleanup mesh material parsing + bool matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); // FIRE-30965 Cleanup mesh material parsing static void loadedCallback(LLModelLoader::scene& scene, LLModelLoader::model_list& model_list, S32 lod, void* opaque); static void stateChangedCallback(U32 state, void* opaque); From 72bd38a0ce10b37ce98ac16a88cf10e444bddebe Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 29 May 2021 22:40:02 +0200 Subject: [PATCH 09/14] Copy&paste error --- indra/llfilesystem/lldiskcache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index f2cc53d025..42a02a68e4 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -197,7 +197,7 @@ class LLDiskCache : class FSPurgeDiskCacheThread : public LLThread { public: - FSPurgeDiskCacheThread::FSPurgeDiskCacheThread(); + FSPurgeDiskCacheThread(); protected: void run() override; From dcdc510e698efded1ac9b0891bf42f2253f11163 Mon Sep 17 00:00:00 2001 From: Beq Date: Sat, 29 May 2021 21:45:09 +0100 Subject: [PATCH 10/14] FIRE-30947 - Auto-unmute Sounds/Ambient from patch by Rebecca Ashbourne --- indra/newview/app_settings/settings.xml | 22 +++++++++++++ indra/newview/llviewermessage.cpp | 7 ++++ .../skins/default/xui/en/floater_about.xml | 2 +- .../xui/en/panel_preferences_sound.xml | 32 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index f278f4dcf2..74d3110008 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -25683,5 +25683,27 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + FSAutoUnmuteSounds + + Comment + If Sound Effects are muted, unmute on TP. Default (false) + Persist + 1 + Type + Boolean + Value + 0 + + FSAutoUnmuteAmbient + + Comment + If Ambient sounds are muted, unmute on TP. Default (false) + Persist + 1 + Type + Boolean + Value + 0 + diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 270e342ec3..d37d84b188 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3956,6 +3956,13 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) gSavedPerAccountSettings.setBOOL("FSRenderFriendsOnly", FALSE); } // + // FIRE-30947: Auto-Unmute + if (gSavedSettings.getBOOL("MuteSounds") && gSavedSettings.getBOOL("FSAutoUnmuteSounds")) + gSavedSettings.setBOOL("MuteSounds", FALSE); + + if (gSavedSettings.getBOOL("MuteAmbient") && gSavedSettings.getBOOL("FSAutoUnmuteAmbient")) + gSavedSettings.setBOOL("MuteAmbient", FALSE); + // if (gAgent.getTeleportKeepsLookAt()) { // *NOTE: the LookAt data we get from the sim here doesn't diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index 13ae3186e7..8af051c4dd 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -165,7 +165,7 @@ Additional code generously contributed to Firestorm by: top_pad="4" width="450" wrap="true"> -Albatroz Hird, Alexie Birman, Andromeda Rage, Angeldark Raymaker, Animats, Armin Weatherwax, Beq Janus, Casper Warden, Chalice Yao, Chaser Zaks, Chorazin Allen, Cron Stardust, Damian Zhaoying, Dan Threebeards, Dawa Gurbux, Denver Maksim, Drake Arconis, Felyza Wishbringer, f0rbidden, Fractured Crystal, Geenz Spad, Gibson Firehawk, Hitomi Tiponi, Inusaito Sayori, Jean Severine, Katharine Berry, Kittin Ninetails, Kool Koolhoven, Lance Corrimal, Lassie, Latif Khalifa, Laurent Bechir, Magne Metaverse LLC, Magus Freston, Manami Hokkigai, MartinRJ Fayray, McCabe Maxstead, Melancholy Lemon, Melysmile, Mimika Oh, Mister Acacia, MorganMegan, mygoditsfullofstars, Mysty Saunders, Nagi Michinaga, Name Short, nhede Core, NiranV Dean, Nogardrevlis Lectar, Oren Hurvitz, Paladin Forzane, paperwork, Penny Patton, Peyton Menges, programmtest, Qwerty Venom, Revolution Smythe, Romka Swallowtail, Sahkolihaa Contepomi, sal Kaligawa, Samm Florian, Satomi Ahn, Sei Lisa, Sempervirens Oddfellow, Shin Wasp, Shyotl Kuhr, Sione Lomu, Skills Hak, StarlightShining, Sunset Faulkes, Testicular Slingshot, Thickbrick Sleaford, Ubit Umarov, Vaalith Jinn, Vincent Sylvester, Whirly Fizzle, Xenhat Liamano, Zwagoth Klaar and others. +Albatroz Hird, Alexie Birman, Andromeda Rage, Angeldark Raymaker, Animats, Armin Weatherwax, Beq Janus, Casper Warden, Chalice Yao, Chaser Zaks, Chorazin Allen, Cron Stardust, Damian Zhaoying, Dan Threebeards, Dawa Gurbux, Denver Maksim, Drake Arconis, Felyza Wishbringer, f0rbidden, Fractured Crystal, Geenz Spad, Gibson Firehawk, Hitomi Tiponi, Inusaito Sayori, Jean Severine, Katharine Berry, Kittin Ninetails, Kool Koolhoven, Lance Corrimal, Lassie, Latif Khalifa, Laurent Bechir, Magne Metaverse LLC, Magus Freston, Manami Hokkigai, MartinRJ Fayray, McCabe Maxstead, Melancholy Lemon, Melysmile, Mimika Oh, Mister Acacia, MorganMegan, mygoditsfullofstars, Mysty Saunders, Nagi Michinaga, Name Short, nhede Core, NiranV Dean, Nogardrevlis Lectar, Oren Hurvitz, Paladin Forzane, paperwork, Penny Patton, Peyton Menges, programmtest, Qwerty Venom, Rebecca Ashbourne, Revolution Smythe, Romka Swallowtail, Sahkolihaa Contepomi, sal Kaligawa, Samm Florian, Satomi Ahn, Sei Lisa, Sempervirens Oddfellow, Shin Wasp, Shyotl Kuhr, Sione Lomu, Skills Hak, StarlightShining, Sunset Faulkes, Testicular Slingshot, Thickbrick Sleaford, Ubit Umarov, Vaalith Jinn, Vincent Sylvester, Whirly Fizzle, Xenhat Liamano, Zwagoth Klaar and others. + Automatically unmute after teleport: + + + + + Date: Mon, 31 May 2021 01:33:15 +0100 Subject: [PATCH 11/14] Add thread names to Telemetry --- indra/llcommon/fstelemetry.h | 2 ++ indra/llcommon/llthread.cpp | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/fstelemetry.h b/indra/llcommon/fstelemetry.h index 87ec75b00a..9a0cdd5e18 100644 --- a/indra/llcommon/fstelemetry.h +++ b/indra/llcommon/fstelemetry.h @@ -45,6 +45,7 @@ #define FSZoneNC( name, color ) ZoneNamedNC( ___tracy_scoped_zone, name, color, FSTelemetry::active) #define FSPlot( name, value ) TracyPlot( name, value) #define FSFrameMark FrameMark +#define FSThreadName( name ) tracy::SetThreadName( name ) #define FSTelemetryIsConnected TracyIsConnected #else // (no telemetry) @@ -58,6 +59,7 @@ #define FSZoneNC( name, color ) #define FSPlot( name, value ) #define FSFrameMark +#define FSThreadName( name ) #define FSTelemetryIsConnected #endif // TRACY_ENABLE diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 8fbbc36162..2d6ebc0ad4 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -35,6 +35,7 @@ #include "lltrace.h" #include "lltracethreadrecorder.h" #include "llexception.h" +#include "fstelemetry.h" // allow thread naming #if LL_LINUX || LL_SOLARIS #include @@ -140,7 +141,10 @@ void LLThread::threadRun() // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder()); - + // - Add threadnames to telemetry + LL_INFOS("THREAD") << "Started thread " << mName << LL_ENDL; + FSThreadName( mName.c_str() ); + // // Run the user supplied function do { From 1ef2f20d129e0916f7eab884c850843e07b29722 Mon Sep 17 00:00:00 2001 From: Beq Date: Mon, 31 May 2021 01:38:27 +0100 Subject: [PATCH 12/14] de-couple the imageworkerthreads --- indra/llimage/llimageworker.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp index 53e02ee07f..468140e408 100644 --- a/indra/llimage/llimageworker.cpp +++ b/indra/llimage/llimageworker.cpp @@ -175,9 +175,34 @@ LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* U32 priority, S32 discard, BOOL needs_aux, Responder* responder) { FSZoneC(tracy::Color::Orange); // instrument the image decode pipeline - LLMutexLock lock(mCreationMutex); + // De-couple texture threading from mainloop + // LLMutexLock lock(mCreationMutex); + // handle_t handle = generateHandle(); + // mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder)); handle_t handle = generateHandle(); - mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder)); + // If we have a thread pool dispatch this directly. + // Note: addRequest could cause the handling to take place on the fetch thread, this is unlikely to be an issue. + // if this is an actual problem we move the fallback to here and place the unfulfilled request into the legacy queue + if (s_ChildThreads > 0) + { + FSZoneNC("DecodeDecoupled", tracy::Color::Orange); // instrument the image decode pipeline + ImageRequest* req = new ImageRequest(handle, image, + priority, discard, needs_aux, + responder, this); + bool res = addRequest(req); + if (!res) + { + LL_WARNS() << "Decode request not added because we are exiting." << LL_ENDL; + return 0; + } + } + else + { + FSZoneNC("DecodeQueued", tracy::Color::Orange); // instrument the image decode pipeline + LLMutexLock lock(mCreationMutex); + mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder)); + } + // return handle; } From 06d5f5339a6b22a029472a3ddbe2efeaa7953e7e Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 31 May 2021 09:07:34 +0200 Subject: [PATCH 13/14] Increase label width --- indra/newview/skins/default/xui/en/panel_preferences_sound.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml index 92b76ded74..66d63b7dc0 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml @@ -371,7 +371,7 @@ left="26" top_pad="15" name="auto_unmute_label" - width="230"> + width="430"> Automatically unmute after teleport: Date: Mon, 31 May 2021 09:07:42 +0200 Subject: [PATCH 14/14] Update German translation --- indra/newview/skins/default/xui/de/floater_model_preview.xml | 1 + .../newview/skins/default/xui/de/panel_preferences_sound.xml | 5 +++++ indra/newview/skins/default/xui/de/strings.xml | 1 + 3 files changed, 7 insertions(+) diff --git a/indra/newview/skins/default/xui/de/floater_model_preview.xml b/indra/newview/skins/default/xui/de/floater_model_preview.xml index 875a318f7d..088be49dbb 100644 --- a/indra/newview/skins/default/xui/de/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/de/floater_model_preview.xml @@ -393,6 +393,7 @@ + Physik: diff --git a/indra/newview/skins/default/xui/de/panel_preferences_sound.xml b/indra/newview/skins/default/xui/de/panel_preferences_sound.xml index a6a81583c0..8cdf0fa6dd 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_sound.xml @@ -25,6 +25,11 @@ + + Stummschaltung nach Teleport automatisch aufheben: + + + Klang abspielen wenn sich Freunde: diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index f5eba43d8e..6b7bfadb3a 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -53,6 +53,7 @@ SLURL: <nolink>[SLURL]</nolink> CPU: [CPU] Speicher: [MEMORY_MB] MB +Parallelität: [CONCURRENCY] Betriebssystemversion: [OS_VERSION] Grafikkartenhersteller: [GRAPHICS_CARD_VENDOR] Grafikkarte: [GRAPHICS_CARD]