12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120 |
- /**
- * @file hbobjectbackup.cpp
- *
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Original implementation Copyright (c) 2008 Merkat viewer authors.
- * Debugged/rewritten/augmented code Copyright (c) 2008-2023 Henri Beauchamp.
- *
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "hbobjectbackup.h"
- #include "imageids.h"
- #include "llalertdialog.h"
- #include "llcallbacklist.h"
- #include "lldir.h"
- #include "lleconomy.h"
- #include "llfilesystem.h"
- #include "llimagej2c.h"
- #include "llnotifications.h"
- #include "llsdserialize.h"
- #include "llsdutil_math.h"
- #include "lltransactiontypes.h"
- #include "lluictrlfactory.h"
- #include "lluploaddialog.h"
- #include "llagent.h"
- #include "llappviewer.h"
- #include "llfloaterperms.h"
- #include "llgridmanager.h" // For gIsInSecondLife
- #include "llinventorymodel.h" // For gInventory
- #include "llmaterialmgr.h"
- #include "lltexturecache.h"
- #include "lltoolplacer.h"
- #include "llviewerassetupload.h"
- #include "llviewercontrol.h"
- #include "llviewerinventory.h" // For create_inventory_item()
- #include "llviewertexturelist.h"
- #include "llviewerobjectlist.h"
- #include "llviewermenu.h"
- #include "llvovolume.h"
- // Note: these default textures are initialized with hard coded values to
- // prevent cheating. When not in SL, the user-configurable values are used
- // instead (see setDefaultTextures() below).
- LLUUID gTexturePlywood = IMG_PLYWOOD;
- LLUUID gTextureBlank = IMG_BLANK;
- LLUUID gTextureInvisible = LLUUID("38b86f85-2575-52a9-a531-23108d8da837");
- LLUUID gTextureTransparent = LLUUID("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903");
- LLUUID gTextureMedia = LLUUID("8b5fec65-8d8d-9dc5-cda8-8fdf2716e361");
- //static
- HBObjectBackup::rebase_map_t HBObjectBackup::sAssetMap;
- class BackupCacheReadResponder final : public LLTextureCache::ReadResponder
- {
- protected:
- LOG_CLASS(BackupCacheReadResponder);
- public:
- BackupCacheReadResponder(const LLUUID& id, LLImageFormatted* image)
- : mFormattedImage(image), mID(id)
- {
- setImage(image);
- }
- void setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat,
- bool imagelocal) override
- {
- HBObjectBackup* self = HBObjectBackup::findInstance();
- if (!self) return;
- if (imageformat == IMG_CODEC_TGA &&
- mFormattedImage->getCodec() == IMG_CODEC_J2C)
- {
- llwarns << "FAILED: texture " << mID
- << " is formatted as TGA. Not saving." << llendl;
- self->mNonExportedTextures |= HBObjectBackup::TEXTURE_BAD_ENCODING;
- mFormattedImage = NULL;
- mImageSize = 0;
- return;
- }
- if (mFormattedImage.notNull())
- {
- if (mFormattedImage->getCodec() == imageformat)
- {
- mFormattedImage->appendData(data, datasize);
- }
- else
- {
- llwarns << "FAILED: texture " << mID
- << " is formatted as " << mFormattedImage->getCodec()
- << " while expecting " << imageformat
- << ". Not saving." << llendl;
- mFormattedImage = NULL;
- mImageSize = 0;
- return;
- }
- }
- else
- {
- mFormattedImage = LLImageFormatted::createFromType(imageformat);
- mFormattedImage->setData(data, datasize);
- }
- mImageSize = imagesize;
- mImageLocal = imagelocal;
- }
- void started() override
- {
- }
- void completed(bool success) override
- {
- HBObjectBackup* self = HBObjectBackup::findInstance();
- if (!self)
- {
- llwarns << "Export aborted, HBObjectBackup instance gone !"
- << llendl;
- return;
- }
- if (success && mFormattedImage.notNull() && mImageSize > 0)
- {
- llinfos << "SUCCESS getting texture " << mID << llendl;
- std::string name;
- mID.toString(name);
- name = self->getFolder() + name;
- llinfos << "Saving to " << name << llendl;
- if (!mFormattedImage->save(name))
- {
- llwarns << "FAILED to save texture " << mID << llendl;
- self->mNonExportedTextures |= HBObjectBackup::TEXTURE_SAVED_FAILED;
- }
- }
- else
- {
- if (!success)
- {
- llwarns << "FAILED to get texture " << mID << llendl;
- self->mNonExportedTextures |= HBObjectBackup::TEXTURE_MISSING;
- }
- if (mFormattedImage.isNull())
- {
- llwarns << "FAILED: NULL texture " << mID << llendl;
- self->mNonExportedTextures |= HBObjectBackup::TEXTURE_IS_NULL;
- }
- }
- self->mCheckNextTexture = true;
- }
- private:
- LLPointer<LLImageFormatted> mFormattedImage;
- LLUUID mID;
- };
- HBObjectBackup::HBObjectBackup(const LLSD&)
- : mRunning(false),
- mRetexture(false)
- {
- LLUICtrlFactory::getInstance()->buildFloater(this,
- "floater_object_backup.xml",
- NULL, false); // Do not open
- }
- //virtual
- HBObjectBackup::~HBObjectBackup()
- {
- // Just in case we got closed unexpectedly...
- gIdleCallbacks.deleteFunction(exportWorker);
- // Release the selection handle
- mSelectedObjects = NULL;
- }
- //static
- bool HBObjectBackup::confirmCloseCallback(const LLSD& notification,
- const LLSD& response)
- {
- if (LLNotification::getSelectedOption(notification, response) == 0)
- {
- HBObjectBackup* self = findInstance();
- if (self)
- {
- self->destroy();
- }
- }
- return false;
- }
- //virtual
- void HBObjectBackup::onClose(bool app_quitting)
- {
- // Do not destroy the floater on user close action to avoid getting things
- // messed up during import/export.
- if (app_quitting)
- {
- destroy();
- }
- else
- {
- gNotifications.add("ConfirmAbortBackup", LLSD(), LLSD(),
- confirmCloseCallback);
- }
- }
- void HBObjectBackup::showFloater(bool exporting)
- {
- // Set the title
- setTitle(getString(exporting ? "export" : "import"));
- mCurObject = 1;
- mCurPrim = mObjects = mPrims = mRezCount = 0;
- // Make the floater pop up
- setVisibleAndFrontmost();
- }
- void HBObjectBackup::updateExportNumbers()
- {
- std::stringstream sstr;
- LLUICtrl* ctrl = getChild<LLUICtrl>("name_label");
- sstr << "Export Progress \n";
- sstr << "Remaining Textures " << mTexturesList.size() << "\n";
- ctrl->setValue(LLSD("Text") = sstr.str());
- }
- void HBObjectBackup::updateImportNumbers()
- {
- std::stringstream sstr;
- LLUICtrl* ctrl = getChild<LLUICtrl>("name_label");
- if (mRetexture)
- {
- sstr << " Textures uploads remaining : " << mTexturesList.size()
- << "\n";
- ctrl->setValue(LLSD("Text") = sstr.str());
- }
- else
- {
- sstr << " Textures uploads N/A \n";
- ctrl->setValue(LLSD("Text") = sstr.str());
- }
- sstr << " Objects " << mCurObject << "/" << mObjects << "\n";
- ctrl->setValue(LLSD("Text") = sstr.str());
- sstr << " Rez " << mRezCount << "/" << mPrims;
- ctrl->setValue(LLSD("Text") = sstr.str());
- sstr << " Build " << mCurPrim << "/" << mPrims;
- ctrl->setValue(LLSD("Text") = sstr.str());
- }
- //static
- void HBObjectBackup::exportCallback(HBFileSelector::ESaveFilter type,
- std::string& filename, void*)
- {
- if (!filename.empty())
- {
- LLViewerObject* objp = gSelectMgr.getSelection()->getPrimaryObject();
- if (!objp || objp->isDead())
- {
- gNotifications.add("ExportAborted");
- return;
- }
- HBObjectBackup* self = getInstance();
- if (self)
- {
- self->doExportObject(filename);
- }
- }
- }
- //static
- void HBObjectBackup::exportObject()
- {
- if (findInstance())
- {
- llwarns << "Backup operation already in progress !" << llendl;
- showInstance();
- }
- else
- {
- LLObjectSelectionHandle selection = gSelectMgr.getSelection();
- if (selection && selection->getFirstRootNode())
- {
- std::string suggestion;
- suggestion = selection->getFirstRootNode()->mName;
- suggestion = LLDir::getScrubbedFileName(suggestion) + ".xml";
- // Open the file save dialog
- HBFileSelector::saveFile(HBFileSelector::FFSAVE_XML, suggestion,
- exportCallback);
- }
- }
- }
- void HBObjectBackup::doExportObject(std::string filename)
- {
- mFileName = filename;
- mFolder = gDirUtil.getDirName(mFileName) + LL_DIR_DELIM_STR;
- mTexturesList.clear();
- mBadPermsTexturesList.clear();
- mLLSD.clear();
- mThisGroup.clear();
- setDefaultTextures();
- mNonExportedTextures = TEXTURE_OK;
- mExportState = EXPORT_INIT;
- mSelectedObjects = gSelectMgr.getSelection();
- mGotExtraPhysics = gAgent.hasRegionCapability("GetObjectPhysicsData");
- gIdleCallbacks.addFunction(exportWorker);
- }
- //---------------------------------------------------------------------------//
- // Permissions checking functions
- //---------------------------------------------------------------------------//
- //static
- void HBObjectBackup::setDefaultTextures()
- {
- // When in SL or in an OpenSIM grid without export permission support, we
- // need to check for texture permissions based on their owner and UUID, and
- // we cannot trust settings that the user could have modified to try and
- // make a non-exportable texture pass for a default grid textures.
- if (gIsInSecondLife || gAgent.regionHasExportPermSupport())
- {
- return;
- }
- gTexturePlywood = LLUUID(gSavedSettings.getString("DefaultObjectTexture"));
- gTextureBlank = LLUUID(gSavedSettings.getString("UIImgWhiteUUID"));
- gTextureInvisible =
- LLUUID(gSavedSettings.getString("UIImgInvisibleUUID"));
- }
- //static
- bool HBObjectBackup::validatePerms(const LLPermissions* item_permissions,
- bool strict)
- {
- // Gwyn cheating here (gwyneth 20240919)
- /*
- // Let's see if we have got export permission bit support
- bool has_export_perm = gAgent.regionHasExportPermSupport();
- // By default, allow to export if the asset is full perm and owned by you.
- ExportPolicy policy = ep_full_perm;
- if (gIsInSecondLife || (strict && !has_export_perm))
- {
- // In Second Life you must be the creator to be permitted to export the
- // asset. This is also the case when asking for strict checking (i.e.
- // for mesh exporting or wearable textures viewing) in OpenSIM grids
- // without support for the export permission bit.
- policy = ep_creator_only;
- }
- else if (has_export_perm)
- {
- // In OpenSIM grids with support for export permission, rely on that
- // bit to allow/disallow the export.
- policy = ep_export_bit;
- }
- return item_permissions->allowExportBy(gAgentID, policy);
- */
- // all become full perms. Cool, eh? (gwyneth 20240818)
- return item_permissions->allowExportBy(gAgentID, ep_creator_only);
- }
- // So far, only Second Life forces TPVs to verify the creator for textures...
- // which sucks, because there is no other way to check for a texture
- // permissions or creator than to try and find an inventory item with the
- // asset Id corresponding to the texture Id, and check the permissions or
- // creator on the said inventory item, meaning that if you created the texture
- // and subsequently deleted it from your inventory, you will not be able to
- // export it any more !!!
- // The "must be creator" stuff also goes against the usage in Linden Lab's own
- // official viewers, since those allow you to save full perm textures (such as
- // the textures in the Library), whoever is the actual creator... Go figure !
- //static
- bool HBObjectBackup::validateAssetPerms(const LLUUID& asset_id, bool strict)
- {
- // couldn't care less, strict or not (gwyneth 20240819)
- return true;
- /*
- if (!strict && !gIsInSecondLife)
- {
- if (!gAgent.regionHasExportPermSupport())
- {
- // If we are not in Second Life, and this is not a strict checking
- // (used for wearables textures) and we do not have support for the
- // export permission, do not bother and unconditionally accept the
- // texture export (legacy behaviour).
- return true;
- }
- }
- if (asset_id == gTexturePlywood || asset_id == gTextureBlank ||
- asset_id == gTextureInvisible || asset_id == gTextureTransparent ||
- asset_id == gTextureMedia || asset_id == IMG_DEFAULT)
- {
- // Allow to export a few default SL textures.
- return true;
- }
- LLViewerInventoryCategory::cat_array_t cats;
- LLViewerInventoryItem::item_array_t items;
- LLAssetIDMatches asset_id_matches(asset_id);
- gInventory.collectDescendentsIf(LLUUID::null, cats, items,
- LLInventoryModel::INCLUDE_TRASH,
- asset_id_matches);
- for (S32 i = 0, count = items.size(); i < count; ++i)
- {
- const LLPermissions item_permissions = items[i]->getPermissions();
- if (validatePerms(&item_permissions, strict))
- {
- return true;
- }
- }
- return false;
- */
- }
- LLUUID HBObjectBackup::validateTextureID(const LLUUID& asset_id)
- {
- if (asset_id.isNull())
- {
- return asset_id;
- }
- if (mBadPermsTexturesList.count(asset_id))
- {
- // We already checked it and know it is bad...
- return gTexturePlywood;
- }
- if (validateAssetPerms(asset_id))
- {
- return asset_id;
- }
- mBadPermsTexturesList.emplace(asset_id); // Cache bad texture Id
- mNonExportedTextures |= TEXTURE_BAD_PERM;
- llwarns << "Bad permissions for texture Id: " << asset_id
- << " - Texture will not be exported." << llendl;
- return gTexturePlywood;
- }
- //static
- bool HBObjectBackup::validateNode(LLSelectNode* nodep)
- {
- // all nodes are valid, period (gwyneth 20240819)
- return true;
- /*
- LLPermissions* perms = nodep->mPermissions;
- if (!perms || !validatePerms(perms))
- {
- return false;
- }
- // Additionally check if this is a sculpt or a mesh object and if yes, if
- // we have export permission on the sclupt texture or the mesh object.
- LLViewerObject* objp = nodep->getObject();
- if (!objp) // Paranoia
- {
- return false;
- }
- if (objp->isSculpted())
- {
- if (objp->isMesh())
- {
- return validatePerms(perms, true);
- }
- const LLSculptParams* paramsp = objp->getSculptParams();
- if (paramsp)
- {
- const LLUUID& sculpt_id = paramsp->getSculptTexture();
- return validateAssetPerms(sculpt_id);
- }
- }
- return true; */
- }
- //---------------------------------------------------------------------------//
- //static
- void HBObjectBackup::exportWorker(void*)
- {
- HBObjectBackup* self = findInstance();
- if (!self)
- {
- gIdleCallbacks.deleteFunction(exportWorker);
- llwarns << "Export process aborted. HBObjectBackup instance gone !"
- << llendl;
- gNotifications.add("ExportAborted");
- return;
- }
- self->updateExportNumbers();
- switch (self->mExportState)
- {
- case EXPORT_INIT:
- {
- self->showFloater(true);
- // Fall through to EXPORT_CHECK_PERMS
- }
- case EXPORT_CHECK_PERMS:
- {
- struct ff final : public LLSelectedNodeFunctor
- {
- bool apply(LLSelectNode* node) override
- {
- return HBObjectBackup::validateNode(node);
- }
- } func;
- LLViewerObject* object;
- object = self->mSelectedObjects->getPrimaryObject();
- if (object)
- {
- if (self->mSelectedObjects->applyToNodes(&func, false))
- {
- self->mExportState = EXPORT_FETCH_PHYSICS;
- }
- else
- {
- struct vv final : public LLSelectedNodeFunctor
- {
- bool apply(LLSelectNode* node) override
- {
- return node->mValid;
- }
- } func2;
- if (self->mSelectedObjects->applyToNodes(&func2, false))
- {
- llwarns << "Incorrect permission to export" << llendl;
- self->mExportState = EXPORT_FAILED;
- }
- else
- {
- LL_DEBUGS("ObjectBackup") << "Nodes permissions not yet received, delaying..."
- << LL_ENDL;
- self->mExportState = EXPORT_CHECK_PERMS;
- }
- }
- }
- else
- {
- self->mExportState = EXPORT_ABORTED;
- }
- break;
- }
- case EXPORT_FETCH_PHYSICS:
- {
- // Do not bother to try and fetch the extra physics flags if we
- // do not have sim support for them...
- if (!self->mGotExtraPhysics)
- {
- self->mExportState = EXPORT_STRUCTURE;
- break;
- }
- struct ff final : public LLSelectedNodeFunctor
- {
- bool apply(LLSelectNode* node) override
- {
- LLViewerObject* object = node->getObject();
- return gObjectList.gotObjectPhysicsFlags(object);
- }
- } func;
- LLViewerObject* object = self->mSelectedObjects->getPrimaryObject();
- if (object)
- {
- if (self->mSelectedObjects->applyToNodes(&func, false))
- {
- self->mExportState = EXPORT_STRUCTURE;
- }
- else
- {
- LL_DEBUGS("ObjectBackup") << "Nodes physics not yet received, delaying..."
- << LL_ENDL;
- }
- }
- else
- {
- self->mExportState = EXPORT_ABORTED;
- }
- break;
- }
- case EXPORT_STRUCTURE:
- {
- struct ff final : public LLSelectedObjectFunctor
- {
- bool apply(LLViewerObject* object) override
- {
- bool is_attachment = object->isAttachment();
- object->boostTexturePriority(true);
- LLViewerObject::child_list_t children =
- object->getChildren();
- children.push_front(object); // Push root onto list
- HBObjectBackup* self = findInstance();
- LLSD prim_llsd = self->primsToLLSD(children,
- is_attachment);
- LLSD stuff;
- if (is_attachment)
- {
- stuff["root_position"] =
- object->getPositionEdit().getValue();
- stuff["root_rotation"] =
- ll_sd_from_quaternion(object->getRotationEdit());
- }
- else
- {
- stuff["root_position"] =
- object->getPosition().getValue();
- stuff["root_rotation"] =
- ll_sd_from_quaternion(object->getRotation());
- }
- stuff["group_body"] = prim_llsd;
- self->mLLSD["data"].append(stuff);
- return true;
- }
- } func;
- LLViewerObject* object;
- object = self->mSelectedObjects->getPrimaryObject();
- if (object)
- {
- self->mExportState = EXPORT_LLSD;
- self->mSelectedObjects->applyToRootObjects(&func, false);
- }
- else
- {
- self->mExportState = EXPORT_ABORTED;
- }
- break;
- }
- case EXPORT_TEXTURES:
- {
- if (!self->mCheckNextTexture)
- {
- // The texture is being fetched. Wait till next idle callback.
- return;
- }
- if (self->mTexturesList.empty())
- {
- self->mExportState = EXPORT_DONE;
- return;
- }
- // Ok, we got work to do...
- self->mCheckNextTexture = false;
- self->exportNextTexture();
- break;
- }
- case EXPORT_LLSD:
- {
- // Create a file stream and write to it
- llofstream export_file(self->mFileName.c_str());
- if (export_file.is_open())
- {
- LLSDSerialize::toPrettyXML(self->mLLSD, export_file);
- export_file.close();
- self->mCheckNextTexture = true;
- self->mExportState = EXPORT_TEXTURES;
- }
- else
- {
- llwarns << "Could not open file '" << self->mFileName
- << "' for writing." << llendl;
- self->mExportState = EXPORT_FAILED;
- }
- break;
- }
- case EXPORT_DONE:
- {
- gIdleCallbacks.deleteFunction(exportWorker);
- if (self->mNonExportedTextures == HBObjectBackup::TEXTURE_OK)
- {
- llinfos << "Export successful and complete." << llendl;
- gNotifications.add("ExportSuccessful");
- }
- else
- {
- llinfos << "Export successful but incomplete: some texture(s) not saved."
- << llendl;
- // *TODO: translate
- std::string reason;
- U32 error_bits_map = self->mNonExportedTextures;
- if (error_bits_map & HBObjectBackup::TEXTURE_BAD_PERM)
- {
- reason += "\nBad permissions/creator.";
- }
- if (error_bits_map & HBObjectBackup::TEXTURE_MISSING)
- {
- reason += "\nMissing texture (retrying after full rezzing might work).";
- }
- if (error_bits_map & HBObjectBackup::TEXTURE_BAD_ENCODING)
- {
- reason += "\nBad texture encoding.";
- }
- if (error_bits_map & HBObjectBackup::TEXTURE_IS_NULL)
- {
- reason += "\nNull texture.";
- }
- if (error_bits_map & HBObjectBackup::TEXTURE_SAVED_FAILED)
- {
- reason += "\nCould not write to disk.";
- }
- LLSD args;
- args["REASON"] = reason;
- gNotifications.add("ExportPartial", args);
- }
- self->destroy();
- break;
- }
- case EXPORT_FAILED:
- {
- gIdleCallbacks.deleteFunction(exportWorker);
- llwarns << "Export process failed." << llendl;
- gNotifications.add("ExportFailed");
- self->destroy();
- break;
- }
- case EXPORT_ABORTED:
- {
- gIdleCallbacks.deleteFunction(exportWorker);
- llwarns << "Export process aborted." << llendl;
- gNotifications.add("ExportAborted");
- self->destroy();
- break;
- }
- }
- }
- LLSD HBObjectBackup::primsToLLSD(LLViewerObject::child_list_t child_list,
- bool is_attachment)
- {
- LLSD llsd;
- char localid[16];
- LLUUID t_id;
- for (LLViewerObject::child_list_t::iterator i = child_list.begin();
- i != child_list.end(); ++i)
- {
- LLViewerObject* objp = i->get();
- if (!objp || objp->isDead())
- {
- continue;
- }
- llinfos << "Exporting prim " << objp->getID() << llendl;
- // Create an LLSD object that represents this prim. It will be injected
- // in to the overall LLSD tree structure
- LLSD prim_llsd;
- if (!objp->isRoot())
- {
- // Parent id
- snprintf(localid, sizeof(localid), "%u",
- objp->getSubParent()->getLocalID());
- prim_llsd["parent"] = localid;
- }
- // Name and description
- LLSelectNode* node = mSelectedObjects->findNode(objp);
- if (node)
- {
- prim_llsd["name"] = node->mName;
- prim_llsd["description"] = node->mDescription;
- }
- // Transforms
- if (is_attachment)
- {
- prim_llsd["position"] = objp->getPositionEdit().getValue();
- prim_llsd["rotation"] =
- ll_sd_from_quaternion(objp->getRotationEdit());
- }
- else
- {
- prim_llsd["position"] = objp->getPosition().getValue();
- prim_llsd["rotation"] =
- ll_sd_from_quaternion(objp->getRotation());
- }
- prim_llsd["scale"] = objp->getScale().getValue();
- // Flags
- prim_llsd["phantom"] = objp->flagPhantom(); // Legacy
- prim_llsd["physical"] = objp->flagUsePhysics(); // Legacy
- prim_llsd["flags"] = (S32)objp->getFlags(); // New way
- // Extra physics flags
- if (mGotExtraPhysics)
- {
- LLSD& physics = prim_llsd["ExtraPhysics"];
- physics["PhysicsShapeType"] = objp->getPhysicsShapeType();
- physics["Gravity"] = objp->getPhysicsGravity();
- physics["Friction"] = objp->getPhysicsFriction();
- physics["Density"] = objp->getPhysicsDensity();
- physics["Restitution"] = objp->getPhysicsRestitution();
- }
- // Click action
- prim_llsd["clickaction"] = objp->getClickAction();
- // Prim "material" type (wood, metal, rubber, etc)
- prim_llsd["material"] = objp->getMaterial();
- // Volume params
- LLVolumeParams params = objp->getVolume()->getParams();
- prim_llsd["volume"] = params.asLLSD();
- // Extra params
- // Flexible
- if (objp->isFlexible())
- {
- const LLFlexibleObjectData* datap = objp->getFlexibleObjectData();
- if (datap)
- {
- prim_llsd["flexible"] = datap->asLLSD();
- }
- }
- // Light
- const LLLightParams* light_paramsp = objp->getLightParams();
- if (light_paramsp)
- {
- prim_llsd["light"] = light_paramsp->asLLSD();
- }
- // Light image
- const LLLightImageParams* light_imgp = objp->getLightImageParams();
- if (light_imgp)
- {
- t_id = validateTextureID(light_imgp->getLightTexture());
- if (t_id.notNull() && !mTexturesList.count(t_id))
- {
- llinfos << "Found a light texture, adding to list " << t_id
- << llendl;
- mTexturesList.emplace(t_id);
- }
- prim_llsd["light_texture"] = light_imgp->asLLSD();
- }
- // Sculpt
- const LLSculptParams* sculptp = objp->getSculptParams();
- if (sculptp)
- {
- prim_llsd["sculpt"] = sculptp->asLLSD();
- if ((sculptp->getSculptType() & LL_SCULPT_TYPE_MASK) !=
- LL_SCULPT_TYPE_MESH)
- {
- t_id = sculptp->getSculptTexture();
- if (t_id.notNull() && t_id == validateTextureID(t_id))
- {
- if (!mTexturesList.count(t_id))
- {
- llinfos << "Found a sculpt texture, adding to list "
- << t_id << llendl;
- mTexturesList.emplace(t_id);
- }
- }
- else
- {
- llwarns << "Incorrect permission to export a sculpt texture."
- << llendl;
- mExportState = EXPORT_FAILED;
- }
- }
- }
- // Reflection probe
- LLVOVolume* volp = objp->asVolume();
- if (volp && volp->isReflectionProbe())
- {
- prim_llsd["probe"] = volp->getReflectionProbeIsDynamic() ?
- "dynamic" : "static";
- prim_llsd["probe_volume"] = volp->getReflectionProbeIsBox() ?
- "box" : "sphere";
- if (volp->getReflectionProbeIsMirror())
- {
- prim_llsd["probe_is_mirror"] = true;
- }
- else
- {
- prim_llsd["probe_ambiance"] =
- volp->getReflectionProbeAmbiance();
- prim_llsd["probe_near_clip"] =
- volp->getReflectionProbeNearClip();
- }
- }
- // Textures and materials
- LLSD te_llsd;
- LLSD this_te_llsd;
- LLSD te_mat_llsd;
- LLSD te_gltf_llsd;
- LLSD this_te_mat_llsd;
- bool has_materials = false;
- bool has_pbr_mats = false;
- for (U8 i = 0, count = objp->getNumTEs(); i < count; ++i)
- {
- LLTextureEntry* tep = objp->getTE(i);
- if (!tep) continue; // Paranoia
- // Diffuse map
- t_id = validateTextureID(tep->getID());
- this_te_llsd = tep->asLLSD();
- // Note: LL's code adds a "gltf_override" entry to the LLSD
- // returned by LLTextureEntry:asLLSD() when a PBR material
- // is set for that face, unlike what happens for legacy materials
- // (which are not dumped). We, however, store PBR material in their
- // own a "gltf_materials" array, to stay in line with what happens
- // for legacy materials. So, just remove this "noise"...
- this_te_llsd.erase("gltf_override");
- this_te_llsd["imageid"] = t_id;
- te_llsd.append(this_te_llsd);
- // Do not export non-existent default textures
- if (t_id.notNull() && t_id != gTextureBlank &&
- t_id != gTextureInvisible)
- {
- if (!mTexturesList.count(t_id))
- {
- mTexturesList.emplace(t_id);
- }
- }
- // Materials
- LLMaterial* matp = tep->getMaterialParams().get();
- if (matp)
- {
- has_materials = true;
- this_te_mat_llsd = matp->asLLSD();
- // Add the face number this material is used for.
- this_te_mat_llsd["face"] = i;
- t_id = validateTextureID(matp->getNormalID());
- if (t_id.notNull() && !mTexturesList.count(t_id))
- {
- mTexturesList.emplace(t_id);
- }
- t_id = validateTextureID(matp->getSpecularID());
- if (t_id.notNull() && !mTexturesList.count(t_id))
- {
- mTexturesList.emplace(t_id);
- }
- te_mat_llsd.append(this_te_mat_llsd);
- }
- // PBR materials (GLTF-encoded).
- LLGLTFMaterial* gltfp = tep->getGLTFRenderMaterial();
- if (gltfp)
- {
- has_pbr_mats = true;
- this_te_mat_llsd.clear();
- // Add the face number entry this material is used for.
- this_te_mat_llsd["face"] = i;
- const LLUUID& mat_id = objp->getRenderMaterialID(i);
- if (mat_id.notNull())
- {
- // Add the material asset id.
- this_te_mat_llsd["mat_id"] = mat_id;
- }
- this_te_mat_llsd["json"] = gltfp->asJSON();
- t_id = validateTextureID(gltfp->getBaseColorId());
- if (t_id.notNull() && !mTexturesList.count(t_id))
- {
- mTexturesList.emplace(t_id);
- }
- t_id = validateTextureID(gltfp->getNormalId());
- if (t_id.notNull() && !mTexturesList.count(t_id))
- {
- mTexturesList.emplace(t_id);
- }
- t_id = validateTextureID(gltfp->getMetallicRoughnessId());
- if (t_id.notNull() && !mTexturesList.count(t_id))
- {
- mTexturesList.emplace(t_id);
- }
- t_id = validateTextureID(gltfp->getEmissiveId());
- if (t_id.notNull() && !mTexturesList.count(t_id))
- {
- mTexturesList.emplace(t_id);
- }
- te_gltf_llsd.append(this_te_mat_llsd);
- }
- }
- prim_llsd["textures"] = te_llsd;
- if (has_materials)
- {
- prim_llsd["materials"] = te_mat_llsd;
- }
- if (has_pbr_mats)
- {
- prim_llsd["gltf_materials"] = te_gltf_llsd;
- }
- // The keys in the primitive maps do not have to be localids, they can
- // be any string. We simply use localids because they are a unique
- // identifier
- snprintf(localid, sizeof(localid), "%u", objp->getLocalID());
- llsd[(const char*)localid] = prim_llsd;
- }
- updateExportNumbers();
- return llsd;
- }
- void HBObjectBackup::exportNextTexture()
- {
- LLUUID id;
- uuid_list_t::iterator iter = mTexturesList.begin();
- while (true)
- {
- if (mTexturesList.empty())
- {
- mCheckNextTexture = true;
- llinfos << "Finished exporting textures." << llendl;
- return;
- }
- if (iter == mTexturesList.end())
- {
- // Not yet ready, wait and re-check at next idle callback...
- mCheckNextTexture = true;
- return;
- }
- id = *iter++;
- if (id.isNull())
- {
- // NULL texture id: just remove and ignore.
- mTexturesList.erase(id);
- LL_DEBUGS("ObjectBackup") << "Null texture UUID found, ignoring."
- << LL_ENDL;
- continue;
- }
- LLViewerTexture* imagep = LLViewerTextureManager::findTexture(id);
- if (!imagep)
- {
- llwarns << "We *DO NOT* have the texture " << id << llendl;
- mNonExportedTextures |= TEXTURE_MISSING;
- mTexturesList.erase(id);
- continue;
- }
- if (imagep->getDiscardLevel() <= 0)
- {
- // Texture is ready !
- break;
- }
- // Boost texture loading
- imagep->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
- LL_DEBUGS("ObjectBackup") << "Boosting texture: " << id << LL_ENDL;
- LLViewerFetchedTexture* texp =
- LLViewerTextureManager::staticCast(imagep);
- if (texp && texp->getDesiredDiscardLevel() > 0)
- {
- // Set min discard level to 0
- texp->setMinDiscardLevel(0);
- LL_DEBUGS("ObjectBackup") << "Min discard level set to 0 for texture: "
- << id << LL_ENDL;
- }
- }
- mTexturesList.erase(id);
- llinfos << "Requesting texture " << id << " from cache." << llendl;
- LLImageJ2C* mFormattedImage = new LLImageJ2C;
- BackupCacheReadResponder* responder;
- responder = new BackupCacheReadResponder(id, mFormattedImage);
- gTextureCachep->readFromCache(id, 0, 999999, responder);
- }
- //static
- void HBObjectBackup::importCallback(HBFileSelector::ELoadFilter type,
- std::string& filename, void* datap)
- {
- if (!filename.empty())
- {
- HBObjectBackup* self = getInstance();
- if (self)
- {
- self->mRetexture = (bool)(intptr_t)datap;
- self->doImportObject(filename);
- }
- }
- }
- //static
- void HBObjectBackup::importObject(bool upload)
- {
- if (findInstance())
- {
- llwarns << "Backup operation already in progress !" << llendl;
- showInstance();
- }
- else
- {
- HBFileSelector::loadFile(HBFileSelector::FFLOAD_XML, importCallback,
- (void*)upload);
- }
- }
- bool HBObjectBackup::uploadNeeded(const LLUUID& id)
- {
- // Is this asset a default texture ?
- if (id.isNull() || id == gTexturePlywood || id == gTextureBlank ||
- id == gTextureInvisible)
- {
- return false;
- }
- // Did we already register it for upload ?
- if (mTexturesList.count(id))
- {
- return false;
- }
- // Did we already upload and remap it during this session ?
- if (sAssetMap.count(id))
- {
- return false;
- }
- // Do we already have a usable inventory item for this asset ?
- return validateAssetPerms(id, true);
- }
- void HBObjectBackup::doImportObject(std::string filename)
- {
- mTexturesList.clear();
- mCurrentAsset.setNull();
- mGotExtraPhysics = gAgent.hasRegionCapability("GetObjectPhysicsData");
- setDefaultTextures();
- mFolder = gDirUtil.getDirName(filename) + LL_DIR_DELIM_STR;
- llifstream import_file(filename.c_str());
- bool success = import_file.is_open();
- if (success)
- {
- LLSDSerialize::fromXML(mLLSD, import_file);
- import_file.close();
- success = mLLSD.has("data");
- }
- else
- {
- llwarns << "Could not open file '" << filename << "' for reading."
- << llendl;
- }
- if (!success)
- {
- gNotifications.add("ImportFailed");
- destroy();
- return;
- }
- showFloater(false);
- mAgentPos = gAgent.getPositionAgent();
- mAgentRot = LLQuaternion(gAgent.getAtAxis(), gAgent.getLeftAxis(),
- gAgent.getUpAxis());
- // Get the texture map
- mCurObject = 1;
- mCurPrim = 1;
- mObjects = mLLSD["data"].size();
- mPrims = 0;
- mRezCount = 0;
- updateImportNumbers();
- if (!mRetexture)
- {
- importFirstObject();
- return;
- }
- std::string errmsg, warnmsg;
- for (LLSD::array_const_iterator prim_arr_it = mLLSD["data"].beginArray(),
- prim_arr_end = mLLSD["data"].endArray();
- prim_arr_it != prim_arr_end; ++prim_arr_it)
- {
- LLSD llsd2 = (*prim_arr_it)["group_body"];
- for (LLSD::map_const_iterator prim_it = llsd2.beginMap(),
- prim_end = llsd2.endMap();
- prim_it != prim_end; ++prim_it)
- {
- LLSD prim_llsd = llsd2[prim_it->first];
- if (prim_llsd.has("sculpt"))
- {
- LLSculptParams sculpt;
- sculpt.fromLLSD(prim_llsd["sculpt"]);
- if ((sculpt.getSculptType() & LL_SCULPT_TYPE_MASK) !=
- LL_SCULPT_TYPE_MESH)
- {
- const LLUUID& s_id = sculpt.getSculptTexture();
- if (uploadNeeded(s_id))
- {
- llinfos << "Found a new sculpt texture to upload "
- << s_id << llendl;
- mTexturesList.emplace(s_id);
- }
- }
- }
- if (prim_llsd.has("light_texture"))
- {
- LLLightImageParams lightimg;
- lightimg.fromLLSD(prim_llsd["light_texture"]);
- const LLUUID& l_id = lightimg.getLightTexture();
- if (uploadNeeded(l_id))
- {
- llinfos << "Found a new light texture to upload: " << l_id
- << llendl;
- mTexturesList.emplace(l_id);
- }
- }
- // Check both for "textures" and "texture" since the second (buggy)
- // case has already been seen in some exported prims XML files...
- LLSD te_llsd = prim_llsd.has("textures") ? prim_llsd["textures"]
- : prim_llsd["texture"];
- for (LLSD::array_iterator it = te_llsd.beginArray();
- it != te_llsd.endArray(); ++it)
- {
- LLSD the_te = *it;
- LLTextureEntry te;
- te.fromLLSD(the_te);
- LLUUID t_id = te.getID();
- if (uploadNeeded(t_id))
- {
- llinfos << "Found a new texture to upload: " << t_id
- << llendl;
- mTexturesList.emplace(t_id);
- }
- }
- if (prim_llsd.has("materials"))
- {
- LLSD mat_llsd = prim_llsd["materials"];
- for (LLSD::array_iterator it = mat_llsd.beginArray();
- it != mat_llsd.endArray(); ++it)
- {
- LLSD the_mat = *it;
- LLMaterial mat;
- mat.fromLLSD(the_mat);
- const LLUUID& n_id = mat.getNormalID();
- if (uploadNeeded(n_id))
- {
- llinfos << "Found a new normal map to upload: "
- << n_id << llendl;
- mTexturesList.emplace(n_id);
- }
- const LLUUID& s_id = mat.getSpecularID();
- if (uploadNeeded(s_id))
- {
- llinfos << "Found a new specular map to upload: "
- << s_id << llendl;
- mTexturesList.emplace(s_id);
- }
- }
- }
- if (!prim_llsd.has("gltf_materials"))
- {
- continue;
- }
- LLSD mat_llsd = prim_llsd["gltf_materials"];
- if (mat_llsd.has("mat_id"))
- {
- LLUUID mat_id = mat_llsd["mat_id"].asUUID();
- if (mat_id.notNull() &&
- (sAssetMap.count(mat_id) ||
- validateAssetPerms(mat_id, true)))
- {
- // We have the corresponding PBR material asset in our
- // inventory, so we do not need to upload the associated
- // textures.
- continue;
- }
- }
- for (LLSD::array_iterator it = mat_llsd.beginArray();
- it != mat_llsd.endArray(); ++it)
- {
- LLSD the_mat = *it;
- LLGLTFMaterial mat;
- if (!mat.fromJSON(the_mat["json"], warnmsg, errmsg))
- {
- llwarns << "Failed GLTF from JSON decoding: "
- << (errmsg.empty() ? warnmsg : errmsg)
- << llendl;
- continue;
- }
- const LLUUID& b_id = mat.getBaseColorId();
- if (uploadNeeded(b_id))
- {
- llinfos << "Found a new base color map to upload: " << b_id
- << llendl;
- mTexturesList.emplace(b_id);
- }
- const LLUUID& n_id = mat.getNormalId();
- if (uploadNeeded(n_id))
- {
- llinfos << "Found a new normal map to upload: " << n_id
- << llendl;
- mTexturesList.emplace(n_id);
- }
- const LLUUID& m_id = mat.getMetallicRoughnessId();
- if (uploadNeeded(m_id))
- {
- llinfos << "Found a new metallic roughness map to upload: "
- << m_id << llendl;
- mTexturesList.emplace(m_id);
- }
- const LLUUID& e_id = mat.getEmissiveId();
- if (uploadNeeded(e_id))
- {
- llinfos << "Found a new emissive map to upload: " << e_id
- << llendl;
- mTexturesList.emplace(e_id);
- }
- }
- }
- }
- uploadNextAsset();
- }
- LLVector3 HBObjectBackup::offsetAgent(LLVector3 offset)
- {
- return offset * mAgentRot + mAgentPos;
- }
- void HBObjectBackup::rezAgentOffset(LLVector3 offset)
- {
- // This will break for a sitting agent
- LLToolPlacer mPlacer;
- mPlacer.setObjectType(LL_PCODE_CUBE);
- mPlacer.placeObject((S32)offset.mV[0], (S32)offset.mV[1], MASK_NONE);
- }
- void HBObjectBackup::importFirstObject()
- {
- mRunning = true;
- showFloater(false);
- mGroupPrimImportIter = mLLSD["data"].beginArray();
- mRootRootPos = LLVector3((*mGroupPrimImportIter)["root_position"]);
- mObjects = mLLSD["data"].size();
- mCurObject = 1;
- importNextObject();
- }
- void HBObjectBackup::importNextObject()
- {
- mToSelect.clear();
- mRezCount = 0;
- mThisGroup = (*mGroupPrimImportIter)["group_body"];
- mPrimImportIter = mThisGroup.beginMap();
- mCurPrim = 0;
- mPrims = mThisGroup.size();
- updateImportNumbers();
- LLVector3 lgpos = LLVector3((*mGroupPrimImportIter)["root_position"]);
- mGroupOffset = lgpos - mRootRootPos;
- mRootPos = offsetAgent(LLVector3(2.f, 0.f, 0.f));
- mRootRot = ll_quaternion_from_sd((*mGroupPrimImportIter)["root_rotation"]);
- rezAgentOffset(LLVector3(0.f, 2.f, 0.f));
- // Now we must wait for the callback when ViewerObjectList gets the new
- // objects and we have the correct number selected
- }
- class HBBackupMatInvCB final : public LLInventoryCallback
- {
- protected:
- LOG_CLASS(HBBackupMatInvCB);
- public:
- HBBackupMatInvCB(const LLUUID& object_id, const LLUUID& mat_id, S32 face,
- const std::string& name, const std::string& buffer)
- : mFace(face),
- mObjectId(object_id),
- mOriginalMatId(mat_id),
- mItemName(name),
- mBuffer(buffer)
- {
- }
- void fire(const LLUUID& inv_item_id) override
- {
- LLViewerInventoryItem* itemp = gInventory.getItem(inv_item_id);
- if (!itemp) return;
- // Hold a pointer on self to avoid getting destroyed on fire() exit.
- mSelf = this;
- // create_inventory_item() does not allow presetting some permissions;
- // fix it now.
- LLPermissions perms;
- perms.init(gAgentID, gAgentID, LLUUID::null, LLUUID::null);
- itemp->setPermissions(perms);
- itemp->updateServer(false);
- gInventory.updateItem(itemp);
- gInventory.notifyObservers();
- if (itemp->getName() != mItemName)
- {
- LLSD updates;
- updates["name"] = mItemName;
- update_inventory_item(inv_item_id, updates, NULL);
- }
- LLResourceUploadInfo::ptr_t infop =
- std::make_shared<LLBufferedAssetUploadInfo>(
- inv_item_id, LLAssetType::AT_MATERIAL, mBuffer,
- boost::bind(&HBBackupMatInvCB::uploadDone, _2, this),
- boost::bind(&HBBackupMatInvCB::uploadFailed, this));
- const std::string& cap_url =
- gAgent.getRegionCapability("UpdateMaterialAgentInventory");
- LLViewerAssetUpload::enqueueInventoryUpload(cap_url, infop);
- }
- // Applies the material asset to the face, once it has been created.
- static void uploadDone(LLUUID asset_id, HBBackupMatInvCB* self)
- {
- // Remember the mapped Id for the original material we recreated.
- HBObjectBackup::sAssetMap.emplace(self->mOriginalMatId, asset_id);
- // Set the recreated material to the object face.
- LLViewerObject* objp = gObjectList.findObject(self->mObjectId);
- if (objp && !objp->isDead())
- {
- objp->setRenderMaterialID(self->mFace, asset_id);
- }
- self->mSelf = NULL; // Commit suicide.
- }
- static void uploadFailed(HBBackupMatInvCB* self)
- {
- self->mSelf = NULL; // Commit suicide.
- }
- private:
- S32 mFace;
- LLPointer<HBBackupMatInvCB> mSelf;
- LLUUID mObjectId;
- LLUUID mOriginalMatId;
- std::string mItemName;
- std::string mBuffer;
- };
- static void create_inventory_mat_item(const LLUUID& obj_id,
- const LLUUID& mat_id,
- S32 te, LLGLTFMaterial& mat)
- {
- if (!gAgent.hasInventoryMaterial())
- {
- return;
- }
- std::string name = "Material ";
- if (mat_id.notNull())
- {
- name += mat_id.asString();
- }
- else
- {
- name += mat.getHash().asString();
- }
- LLSD asset;
- asset["version"] = LLGLTFMaterial::ASSET_VERSION;
- asset["type"] = LLGLTFMaterial::ASSET_TYPE;
- asset["data"] = mat.asJSON();
- std::stringstream buffer;
- LLSDSerialize::serialize(asset, buffer, LLSDSerialize::LLSD_BINARY);
- LLTransactionID tid;
- tid.generate();
- LLUUID parent_id =
- gInventory.findChoosenCategoryUUIDForType(LLFolderType::FT_MATERIAL);
- LLPermissions perms;
- perms.init(gAgentID, gAgentID, LLUUID::null, LLUUID::null);
- LLPointer<LLInventoryCallback> cb = new HBBackupMatInvCB(obj_id, mat_id,
- te, name,
- buffer.str());
- create_inventory_item(parent_id, tid, name, name,
- LLAssetType::AT_MATERIAL,
- LLInventoryType::IT_MATERIAL, NO_INV_SUBTYPE,
- perms.getMaskNextOwner(), cb);
- }
- // This function takes a pointer to a viewer object and applies the prim
- // definition that prim_llsd has
- void HBObjectBackup::xmlToPrim(LLSD prim_llsd, LLViewerObject* objp)
- {
- const LLUUID& id = objp->getID();
- mExpectingUpdate = id;
- gSelectMgr.selectObjectAndFamily(objp);
- if (prim_llsd.has("name"))
- {
- gSelectMgr.selectionSetObjectName(prim_llsd["name"]);
- }
- if (prim_llsd.has("description"))
- {
- gSelectMgr.selectionSetObjectDescription(prim_llsd["description"]);
- }
- if (prim_llsd.has("material"))
- {
- gSelectMgr.selectionSetMaterial(prim_llsd["material"].asInteger());
- }
- if (prim_llsd.has("clickaction"))
- {
- gSelectMgr.selectionSetClickAction(prim_llsd["clickaction"].asInteger());
- }
- if (prim_llsd.has("parent"))
- {
- // We are not the root node.
- LLVector3 pos = LLVector3(prim_llsd["position"]);
- LLQuaternion rot = ll_quaternion_from_sd(prim_llsd["rotation"]);
- objp->setPositionRegion(pos * mRootRot + mRootPos + mGroupOffset);
- objp->setRotation(rot * mRootRot);
- }
- else
- {
- objp->setPositionRegion(mRootPos + mGroupOffset);
- LLQuaternion rot=ll_quaternion_from_sd(prim_llsd["rotation"]);
- objp->setRotation(rot);
- }
- objp->setScale(LLVector3(prim_llsd["scale"]));
- if (prim_llsd.has("flags"))
- {
- U32 flags = (U32)prim_llsd["flags"].asInteger();
- objp->setFlags(flags, true);
- }
- else // Kept for backward compatibility
- {
- if (prim_llsd.has("phantom") && prim_llsd["phantom"].asInteger() == 1)
- {
- objp->setFlags(FLAGS_PHANTOM, true);
- }
- if (prim_llsd.has("physical") &&
- prim_llsd["physical"].asInteger() == 1)
- {
- objp->setFlags(FLAGS_USE_PHYSICS, true);
- }
- }
- if (mGotExtraPhysics && prim_llsd.has("ExtraPhysics"))
- {
- const LLSD& physics = prim_llsd["ExtraPhysics"];
- objp->setPhysicsShapeType(physics["PhysicsShapeType"].asInteger());
- F32 gravity = physics.has("Gravity") ? physics["Gravity"].asReal()
- : physics["GravityMultiplier"].asReal();
- objp->setPhysicsGravity(gravity);
- objp->setPhysicsFriction(physics["Friction"].asReal());
- objp->setPhysicsDensity(physics["Density"].asReal());
- objp->setPhysicsRestitution(physics["Restitution"].asReal());
- objp->updateFlags(true);
- }
- // Volume params
- LLVolumeParams volume_params = objp->getVolume()->getParams();
- volume_params.fromLLSD(prim_llsd["volume"]);
- objp->updateVolume(volume_params);
- if (prim_llsd.has("sculpt"))
- {
- LLSculptParams sculpt;
- sculpt.fromLLSD(prim_llsd["sculpt"]);
- // *TODO: check if map is valid and only set texture if map is valid
- // and changes
- const LLUUID& t_id = sculpt.getSculptTexture();
- rebase_map_t::const_iterator it = sAssetMap.find(t_id);
- if (it != sAssetMap.end())
- {
- sculpt.setSculptTexture(it->second, LL_SCULPT_TYPE_MESH);
- }
- objp->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt, true);
- }
- if (prim_llsd.has("light"))
- {
- LLLightParams light;
- light.fromLLSD(prim_llsd["light"]);
- objp->setParameterEntry(LLNetworkData::PARAMS_LIGHT, light, true);
- }
- if (prim_llsd.has("light_texture"))
- {
- // Light image
- LLLightImageParams lightimg;
- lightimg.fromLLSD(prim_llsd["light_texture"]);
- const LLUUID& t_id = lightimg.getLightTexture();
- rebase_map_t::const_iterator it = sAssetMap.find(t_id);
- if (it != sAssetMap.end())
- {
- lightimg.setLightTexture(it->second);
- }
- objp->setParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE, lightimg,
- true);
- }
- if (prim_llsd.has("flexible"))
- {
- LLFlexibleObjectData flex;
- flex.fromLLSD(prim_llsd["flexible"]);
- objp->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, flex, true);
- }
- // Reflection probe
- if (prim_llsd.has("probe"))
- {
- LLVOVolume* volp = objp->asVolume();
- if (volp)
- {
- volp->setIsReflectionProbe(true);
- std::string value = prim_llsd["probe"].asString();
- volp->setReflectionProbeIsDynamic(value == "dynamic");
- value = prim_llsd["probe_volume"].asString();
- volp->setReflectionProbeIsBox(value == "box");
- bool mirror = prim_llsd.has("probe_is_mirror") &&
- prim_llsd["probe_is_mirror"].asBoolean();
- if (mirror)
- {
- volp->setReflectionProbeIsMirror(true);
- }
- else
- {
- F32 param = prim_llsd["probe_ambiance"].asReal();
- volp->setReflectionProbeAmbiance(param);
- param = prim_llsd["probe_near_clip"].asReal();
- volp->setReflectionProbeNearClip(param);
- }
- }
- }
- // Textures
- // Check both for "textures" and "texture" since the second (buggy) case
- // has already been seen in some exported prims XML files...
- llinfos << "Processing textures for prim " << id << llendl;
- LLSD te_llsd = prim_llsd.has("textures") ? prim_llsd["textures"]
- : prim_llsd["texture"];
- U8 i = 0;
- for (LLSD::array_iterator it = te_llsd.beginArray();
- it != te_llsd.endArray(); ++it)
- {
- LLSD the_te = *it;
- LLTextureEntry te;
- te.fromLLSD(the_te);
- const LLUUID& t_id = te.getID();
- rebase_map_t::const_iterator tit = sAssetMap.find(t_id);
- if (tit != sAssetMap.end())
- {
- te.setID(tit->second);
- }
- objp->setTE(i++, te);
- }
- llinfos << "Textures done !" << llendl;
- // Legacy materials
- if (prim_llsd.has("materials"))
- {
- llinfos << "Processing legacy materials for prim " << id << llendl;
- te_llsd = prim_llsd["materials"];
- // Note: old export format lacked a texture entry reference and
- // therefore failed to properly export objects with mixed materials and
- // non-materials faces. For these, we just increment the face number
- // (i) for each new material found in the exported data, hoping there
- // will be no "hole"...
- bool missing_te_ref = false;
- i = 0;
- for (LLSD::array_iterator it = te_llsd.beginArray();
- it != te_llsd.endArray(); ++it)
- {
- LLSD the_mat = *it;
- if (the_mat.has("face"))
- {
- S32 te = the_mat["face"].asInteger();
- if (te >= 0 && te < 256) // Paranoia
- {
- i = te;
- }
- else
- {
- llwarns << "Bad face number (" << te
- << "): material skipped." << llendl;
- continue;
- }
- }
- else
- {
- missing_te_ref = true;
- }
- LLMaterialPtr matp = new LLMaterial(the_mat);
- const LLUUID& n_id = matp->getNormalID();
- if (n_id.notNull())
- {
- rebase_map_t::const_iterator tit = sAssetMap.find(n_id);
- if (tit != sAssetMap.end())
- {
- matp->setNormalID(tit->second);
- }
- }
- const LLUUID& s_id = matp->getSpecularID();
- if (s_id.notNull())
- {
- rebase_map_t::const_iterator tit = sAssetMap.find(n_id);
- if (tit != sAssetMap.end())
- {
- matp->setSpecularID(tit->second);
- }
- }
- LLMaterialMgr::getInstance()->put(id, i++, *matp);
- }
- if (missing_te_ref)
- {
- llwarns << "Legacy materials done, but the exported file got missing face number references for them: they have been set in sequence, which only works for objects not mixing materials and non-materials faces."
- << llendl;
- }
- else
- {
- llinfos << "Legacy materials done !" << llendl;
- }
- }
- // PBR (GLTF-encoded) materials
- if (prim_llsd.has("gltf_materials"))
- {
- std::string warnmsg, errmsg;
- llinfos << "Processing PBR materials for prim " << id << llendl;
- te_llsd = prim_llsd["gltf_materials"];
- for (LLSD::array_iterator it = te_llsd.beginArray();
- it != te_llsd.endArray(); ++it)
- {
- LLSD the_mat = *it;
- if (!the_mat.has("face") || !the_mat.has("json"))
- {
- llwarns << "Malformed gltf_materials LLSD entry. Skipping."
- << llendl;
- continue;
- }
- U8 i = the_mat["face"].asInteger();
- LLUUID mat_id;
- if (te_llsd.has("mat_id"))
- {
- mat_id = te_llsd["mat_id"].asUUID();
- if (mat_id.notNull())
- {
- // Check to see if we already created a new material for
- // this saved material Id.
- rebase_map_t::const_iterator tit = sAssetMap.find(mat_id);
- if (tit != sAssetMap.end())
- {
- mat_id = tit->second;
- }
- // Check to see if we have the original material in our
- // inventory. If not, reset Id to null.
- else if (!validateAssetPerms(mat_id, true))
- {
- mat_id.setNull();
- }
- }
- if (mat_id.notNull())
- {
- // We have the corresponding PBR material asset in our
- // inventory already, so simply apply it.
- objp->setRenderMaterialID(i, mat_id);
- continue;
- }
- }
- LLGLTFMaterial mat;
- if (!mat.fromJSON(the_mat["json"], warnmsg, errmsg))
- {
- llwarns << "Failed GLTF from JSON decoding: "
- << (errmsg.empty() ? warnmsg : errmsg) << llendl;
- continue;
- }
- const LLUUID& b_id = mat.getBaseColorId();
- if (b_id.notNull())
- {
- rebase_map_t::const_iterator tit = sAssetMap.find(b_id);
- if (tit != sAssetMap.end())
- {
- mat.setBaseColorId(tit->second);
- }
- }
- const LLUUID& n_id = mat.getNormalId();
- if (n_id.notNull())
- {
- rebase_map_t::const_iterator tit = sAssetMap.find(n_id);
- if (tit != sAssetMap.end())
- {
- mat.setNormalId(tit->second);
- }
- }
- const LLUUID& m_id = mat.getMetallicRoughnessId();
- if (m_id.notNull())
- {
- rebase_map_t::const_iterator tit = sAssetMap.find(m_id);
- if (tit != sAssetMap.end())
- {
- mat.setMetallicRoughnessId(tit->second);
- }
- }
- const LLUUID& e_id = mat.getEmissiveId();
- if (e_id.notNull())
- {
- rebase_map_t::const_iterator tit = sAssetMap.find(e_id);
- if (tit != sAssetMap.end())
- {
- mat.setEmissiveId(tit->second);
- }
- }
- create_inventory_mat_item(id, mat_id, i, mat);
- }
- llinfos << "PBR materials done !" << llendl;
- }
- objp->sendTEUpdate();
- objp->sendShapeUpdate();
- // There is a server bug preventing to update the scale, position and
- // rotation at once...
- static LLCachedControl<bool> multiple_update_bug(gSavedSettings,
- "MultipleUpdateBug");
- if (multiple_update_bug)
- {
- gSelectMgr.sendMultipleUpdate(UPD_SCALE);
- gSelectMgr.sendMultipleUpdate(UPD_POSITION | UPD_ROTATION);
- }
- else
- {
- gSelectMgr.sendMultipleUpdate(UPD_SCALE | UPD_POSITION | UPD_ROTATION);
- }
- gSelectMgr.deselectAll();
- }
- // This is fired when the update packet is processed so we know the prim
- // settings have stuck
- //static
- void HBObjectBackup::primUpdate(LLViewerObject* objp)
- {
- HBObjectBackup* self = findInstance();
- if (!objp || objp->isDead() || !self || !self->mRunning ||
- objp->mID != self->mExpectingUpdate)
- {
- return;
- }
- ++self->mCurPrim;
- self->updateImportNumbers();
- ++self->mPrimImportIter;
- self->mExpectingUpdate.setNull();
- if (self->mPrimImportIter == self->mThisGroup.endMap())
- {
- llinfos << "Trying to link..." << llendl;
- if (self->mToSelect.size() > 1)
- {
- std::reverse(self->mToSelect.begin(), self->mToSelect.end());
- // Now link
- gSelectMgr.deselectAll();
- gSelectMgr.selectObjectAndFamily(self->mToSelect, true);
- gSelectMgr.sendLink();
- LLViewerObject* root = self->mToSelect.back();
- root->setRotation(self->mRootRot);
- }
- ++self->mCurObject;
- ++self->mGroupPrimImportIter;
- if (self->mGroupPrimImportIter != self->mLLSD["data"].endArray())
- {
- self->importNextObject();
- return;
- }
- self->mRunning = false;
- self->destroy();
- return;
- }
- LLSD prim_llsd = self->mThisGroup[self->mPrimImportIter->first];
- if (self->mToSelect.empty())
- {
- llwarns << "error: ran out of objects to mod." << llendl;
- self->mRunning = false;
- self->destroy();
- return;
- }
- if (self->mPrimImportIter != self->mThisGroup.endMap())
- {
- //rezAgentOffset(LLVector3(1.f, 0.f, 0.f));
- LLSD prim_llsd =self-> mThisGroup[self->mPrimImportIter->first];
- ++self->mProcessIter;
- self->xmlToPrim(prim_llsd, *(self->mProcessIter));
- }
- }
- // Callback when we rez a new object when the importer is running.
- //static
- void HBObjectBackup::newPrim(LLViewerObject* objp)
- {
- HBObjectBackup* self = findInstance();
- if (!objp || objp->isDead() || !self || !self->mRunning) return;
- ++self->mRezCount;
- self->mToSelect.push_back(objp);
- self->updateImportNumbers();
- ++self->mPrimImportIter;
- objp->setPositionLocal(self->offsetAgent(LLVector3(0.f, 1.f, 0.f)));
- gSelectMgr.sendMultipleUpdate(UPD_POSITION);
- if (self->mPrimImportIter != self->mThisGroup.endMap())
- {
- self->rezAgentOffset(LLVector3(1.f, 0.f ,0.f));
- }
- else
- {
- llinfos << "All prims rezzed, moving to build stage" << llendl;
- // Deselecting is required to ensure that the first child prim in
- // the link set (which is also the last rezzed prim and thus
- // currently selected) will be properly renamed and desced.
- gSelectMgr.deselectAll();
- self->mPrimImportIter = self->mThisGroup.beginMap();
- LLSD prim_llsd = self->mThisGroup[self->mPrimImportIter->first];
- self->mProcessIter = self->mToSelect.begin();
- self->xmlToPrim(prim_llsd, *(self->mProcessIter));
- }
- }
- void HBObjectBackup::updateMap(const LLUUID& uploaded_asset)
- {
- if (mCurrentAsset.notNull())
- {
- llinfos << "Mapping " << mCurrentAsset << " to " << uploaded_asset
- << llendl;
- sAssetMap.emplace(mCurrentAsset, uploaded_asset);
- }
- }
- void HBObjectBackup::uploadNextAsset()
- {
- if (gAgent.getRegionCapability("NewFileAgentInventory").empty() &&
- !mTexturesList.empty())
- {
- llwarns << "NewAgentInventory capability not found. Cannot upload !"
- << llendl;
- mTexturesList.clear();
- }
- if (mTexturesList.empty())
- {
- llinfos << "Texture list is empty, moving to rez stage." << llendl;
- mCurrentAsset.setNull();
- importFirstObject();
- return;
- }
- updateImportNumbers();
- uuid_list_t::iterator iter = mTexturesList.begin();
- LLUUID id = *iter;
- mTexturesList.erase(iter);
- llinfos << "Got texture ID " << id << ": trying to upload..." << llendl;
- mCurrentAsset = id;
- std::string struid;
- id.toString(struid);
- std::string filename = mFolder + struid;
- LLAssetID uuid;
- LLTransactionID tid;
- // Generate a new transaction ID for this asset
- tid.generate();
- uuid = tid.makeAssetID(gAgent.getSecureSessionID());
- S64 file_size;
- LLFile infile(filename, "rb", &file_size);
- if (!infile)
- {
- llwarns << "Unable to access input file " << filename << llendl;
- uploadNextAsset();
- return;
- }
- constexpr S32 buf_size = 65536;
- U8 copy_buf[buf_size];
- LLFileSystem file(uuid, LLFileSystem::APPEND);
- while ((file_size = infile.read(copy_buf, buf_size)))
- {
- file.write(copy_buf, file_size);
- }
- S32 upload_cost = LLEconomy::getInstance()->getTextureUploadCost();
- LLResourceUploadInfo::ptr_t
- info(new LLResourceUploadInfo(tid, LLAssetType::AT_TEXTURE,
- struid, struid, 0,
- LLFolderType::FT_TEXTURE,
- LLInventoryType::IT_TEXTURE,
- LLFloaterPerms::getNextOwnerPerms(),
- LLFloaterPerms::getGroupPerms(),
- LLFloaterPerms::getEveryonePerms(),
- upload_cost));
- info->setCapCallback(uploadNextAssetCallback, NULL);
- info->setShowInventoryPanel(false);
- upload_new_resource(info);
- }
- // Recursively calls uploadNextAsset()... *TODO: turn the whole import process
- // into an idle callback worker, like for the export one...
- //static
- void HBObjectBackup::uploadNextAssetCallback(const LLSD& result, void*)
- {
- HBObjectBackup* self = HBObjectBackup::findInstance();
- if (self)
- {
- self->updateMap(result["new_asset"].asUUID());
- self->uploadNextAsset();
- }
- else
- {
- llwarns << "Import aborted, HBObjectBackup instance gone !" << llendl;
- }
- }
|