Support exporting "material" "clickaction" "flags" and "ExtraPhysics" Support export of Light Texture feature, the expansion to Light feature. Initial work to support firestorm's format. Switched from exporting individual flags fields in favor of exporting one U32 field - We still support the old single flag fields though we no longer export them.
1333 lines
38 KiB
C++
1333 lines
38 KiB
C++
/**
|
|
* @file llviewerobjectbackup.cpp
|
|
*
|
|
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* 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"
|
|
|
|
// system library includes
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
// linden library includes
|
|
#include "indra_constants.h"
|
|
#include "llapr.h"
|
|
#include "llcallbacklist.h"
|
|
#include "lldir.h"
|
|
#include "lleconomy.h"
|
|
#include "llhttpclient.h"
|
|
#include "llimage.h"
|
|
#include "llimagej2c.h"
|
|
#include "lllfsthread.h"
|
|
#include "llsdserialize.h"
|
|
#include "llsdutil.h"
|
|
#include "llsdutil_math.h"
|
|
#include "lltransactiontypes.h"
|
|
#include "llinventorydefines.h"
|
|
|
|
// newview includes
|
|
#include "llagent.h"
|
|
#include "llappviewer.h"
|
|
#include "llassetuploadresponders.h"
|
|
#include "statemachine/aifilepicker.h"
|
|
#include "llfloaterbvhpreview.h"
|
|
#include "llfloaterbuycurrency.h"
|
|
#include "llfloaterimagepreview.h"
|
|
#include "llfloaternamedesc.h"
|
|
#include "llfloatersnapshot.h"
|
|
#include "llinventorymodel.h" // gInventory
|
|
#include "llinventoryfunctions.h"
|
|
#include "llnotificationsutil.h"
|
|
#include "llresourcedata.h"
|
|
#include "llselectmgr.h"
|
|
#include "llstatusbar.h"
|
|
#include "lltexturecache.h"
|
|
#include "lltoolplacer.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "lluploaddialog.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llviewertexturelist.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewermenu.h"
|
|
#include "llviewerregion.h"
|
|
#include "llviewerstats.h"
|
|
#include "llviewerwindow.h"
|
|
|
|
#include "hippogridmanager.h"
|
|
#include "lfsimfeaturehandler.h"
|
|
|
|
#include "llviewerobjectbackup.h"
|
|
|
|
LLObjectBackup* LLObjectBackup::sInstance = NULL;
|
|
|
|
// 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).
|
|
static LLUUID LL_TEXTURE_PLYWOOD = LLUUID("89556747-24cb-43ed-920b-47caed15465f");
|
|
static LLUUID LL_TEXTURE_BLANK = LLUUID("5748decc-f629-461c-9a36-a35a221fe21f");
|
|
static LLUUID LL_TEXTURE_INVISIBLE = LLUUID("38b86f85-2575-52a9-a531-23108d8da837");
|
|
static LLUUID LL_TEXTURE_TRANSPARENT = LLUUID("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903");
|
|
static LLUUID LL_TEXTURE_MEDIA = LLUUID("8b5fec65-8d8d-9dc5-cda8-8fdf2716e361");
|
|
|
|
void setDefaultTextures()
|
|
{
|
|
if (!gHippoGridManager->getConnectedGrid()->isSecondLife())
|
|
{
|
|
// When not in SL (no texture perm check needed), we can get these
|
|
// defaults from the user settings...
|
|
LL_TEXTURE_PLYWOOD = LLUUID(gSavedSettings.getString("DefaultObjectTexture"));
|
|
LL_TEXTURE_BLANK = LLUUID(gSavedSettings.getString("UIImgWhiteUUID"));
|
|
if (gSavedSettings.controlExists("UIImgInvisibleUUID"))
|
|
{
|
|
// This control only exists in the Cool VL Viewer (added by the
|
|
// AllowInvisibleTextureInPicker patch)
|
|
LL_TEXTURE_INVISIBLE = LLUUID(gSavedSettings.getString("UIImgInvisibleUUID"));
|
|
}
|
|
}
|
|
}
|
|
|
|
class importResponder : public LLNewAgentInventoryResponder
|
|
{
|
|
public:
|
|
|
|
importResponder(const LLSD& post_data, const LLUUID& vfile_id, LLAssetType::EType asset_type)
|
|
: LLNewAgentInventoryResponder(post_data, vfile_id, asset_type)
|
|
{
|
|
}
|
|
|
|
//virtual
|
|
virtual void uploadComplete(const LLSD& content)
|
|
{
|
|
LL_DEBUGS("ObjectBackup") << "LLNewAgentInventoryResponder::result from capabilities" << LL_ENDL;
|
|
|
|
LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString());
|
|
LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString());
|
|
|
|
// Update L$ and ownership credit information
|
|
// since it probably changed on the server
|
|
if (asset_type == LLAssetType::AT_TEXTURE ||
|
|
asset_type == LLAssetType::AT_SOUND ||
|
|
asset_type == LLAssetType::AT_ANIMATION)
|
|
{
|
|
gMessageSystem->newMessageFast(_PREHASH_MoneyBalanceRequest);
|
|
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
gMessageSystem->nextBlockFast(_PREHASH_MoneyData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_TransactionID, LLUUID::null );
|
|
gAgent.sendReliableMessage();
|
|
|
|
// LLStringUtil::format_map_t args;
|
|
// args["[AMOUNT]"] = llformat("%d",LLGlobalEconomy::Singleton::getInstance()->getPriceUpload());
|
|
// LLNotifyBox::showXml("UploadPayment", args);
|
|
}
|
|
|
|
// Actually add the upload to viewer inventory
|
|
LL_INFOS("ObjectBackup") << "Adding " << content["new_inventory_item"].asUUID() << " "
|
|
<< content["new_asset"].asUUID() << " to inventory." << LL_ENDL;
|
|
if (mPostData["folder_id"].asUUID().notNull())
|
|
{
|
|
LLPermissions perm;
|
|
U32 next_owner_perm;
|
|
perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
|
|
if (mPostData["inventory_type"].asString() == "snapshot")
|
|
{
|
|
next_owner_perm = PERM_ALL;
|
|
}
|
|
else
|
|
{
|
|
next_owner_perm = PERM_MOVE | PERM_TRANSFER;
|
|
}
|
|
perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, next_owner_perm);
|
|
S32 creation_date_now = time_corrected();
|
|
LLPointer<LLViewerInventoryItem> item
|
|
= new LLViewerInventoryItem(content["new_inventory_item"].asUUID(),
|
|
mPostData["folder_id"].asUUID(),
|
|
perm,
|
|
content["new_asset"].asUUID(),
|
|
asset_type,
|
|
inventory_type,
|
|
mPostData["name"].asString(),
|
|
mPostData["description"].asString(),
|
|
LLSaleInfo::DEFAULT,
|
|
LLInventoryItemFlags::II_FLAGS_NONE,
|
|
creation_date_now);
|
|
gInventory.updateItem(item);
|
|
gInventory.notifyObservers();
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ObjectBackup") << "Can't find a folder to put it into" << LL_ENDL;
|
|
}
|
|
|
|
// remove the "Uploading..." message
|
|
LLUploadDialog::modalUploadFinished();
|
|
|
|
LLObjectBackup::getInstance()->updateMap(content["new_asset"].asUUID());
|
|
LLObjectBackup::getInstance()->uploadNextAsset();
|
|
}
|
|
|
|
/*virtual*/ char const* getName(void) const { return "importResponder"; }
|
|
};
|
|
|
|
class CacheReadResponder : public LLTextureCache::ReadResponder
|
|
{
|
|
public:
|
|
CacheReadResponder(const LLUUID& id, LLImageFormatted* image)
|
|
: mFormattedImage(image), mID(id)
|
|
{
|
|
setImage(image);
|
|
}
|
|
void setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal)
|
|
{
|
|
if (imageformat == IMG_CODEC_TGA && mFormattedImage->getCodec() == IMG_CODEC_J2C)
|
|
{
|
|
LL_WARNS("ObjectBackup") << "FAILED: texture " << mID << " is formatted as TGA. Not saving." << LL_ENDL;
|
|
LLObjectBackup::getInstance()->mNonExportedTextures |= LLObjectBackup::TEXTURE_BAD_ENCODING;
|
|
mFormattedImage = NULL;
|
|
mImageSize = 0;
|
|
return;
|
|
}
|
|
|
|
if (mFormattedImage.notNull())
|
|
{
|
|
llassert_always(mFormattedImage->getCodec() == imageformat);
|
|
mFormattedImage->appendData(data, datasize);
|
|
}
|
|
else
|
|
{
|
|
mFormattedImage = LLImageFormatted::createFromType(imageformat);
|
|
mFormattedImage->setData(data, datasize);
|
|
}
|
|
mImageSize = imagesize;
|
|
mImageLocal = imagelocal;
|
|
}
|
|
|
|
virtual void completed(bool success)
|
|
{
|
|
if (success && mFormattedImage.notNull() && mImageSize > 0)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "SUCCESS getting texture " << mID << LL_ENDL;
|
|
std::string name;
|
|
mID.toString(name);
|
|
name = LLObjectBackup::getInstance()->getfolder() + "//" + name;
|
|
LL_INFOS("ObjectBackup") << "Saving to " << name << LL_ENDL;
|
|
if (!mFormattedImage->save(name))
|
|
{
|
|
LL_WARNS("ObjectBackup") << "FAILED to save texture " << mID << LL_ENDL;
|
|
LLObjectBackup::getInstance()->mNonExportedTextures |= LLObjectBackup::TEXTURE_SAVED_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!success)
|
|
{
|
|
LL_WARNS("ObjectBackup") << "FAILED to get texture " << mID << LL_ENDL;
|
|
LLObjectBackup::getInstance()->mNonExportedTextures |= LLObjectBackup::TEXTURE_MISSING;
|
|
}
|
|
if (mFormattedImage.isNull())
|
|
{
|
|
LL_WARNS("ObjectBackup") << "FAILED: NULL texture " << mID << LL_ENDL;
|
|
LLObjectBackup::getInstance()->mNonExportedTextures |= LLObjectBackup::TEXTURE_IS_NULL;
|
|
}
|
|
}
|
|
|
|
LLObjectBackup::getInstance()->mNextTextureReady = true;
|
|
//JUST SAY NO TO APR DEADLOCKING
|
|
//LLObjectBackup::getInstance()->exportNextTexture();
|
|
}
|
|
private:
|
|
LLPointer<LLImageFormatted> mFormattedImage;
|
|
LLUUID mID;
|
|
};
|
|
|
|
LLObjectBackup::LLObjectBackup()
|
|
: LLFloater(std::string("Object Backup Floater"), std::string("FloaterObjectBackuptRect"), LLStringUtil::null)
|
|
{
|
|
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_object_backup.xml");
|
|
mRunning = false;
|
|
mTexturesList.clear();
|
|
mAssetMap.clear();
|
|
mCurrentAsset = LLUUID::null;
|
|
mRetexture = false;
|
|
close();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
LLObjectBackup* LLObjectBackup::getInstance()
|
|
{
|
|
if (!sInstance)
|
|
sInstance = new LLObjectBackup();
|
|
return sInstance;
|
|
}
|
|
|
|
LLObjectBackup::~LLObjectBackup()
|
|
{
|
|
// save position of floater
|
|
gSavedSettings.setRect("FloaterObjectBackuptRect", getRect());
|
|
sInstance = NULL;
|
|
}
|
|
|
|
void LLObjectBackup::draw()
|
|
{
|
|
LLFloater::draw();
|
|
}
|
|
|
|
void LLObjectBackup::show(bool exporting)
|
|
{
|
|
// set the title
|
|
if (exporting)
|
|
{
|
|
setTitle("Object export");
|
|
}
|
|
else
|
|
{
|
|
setTitle("Object import");
|
|
}
|
|
mCurObject = 1;
|
|
mCurPrim = 0;
|
|
mObjects = 0;
|
|
mPrims = 0;
|
|
mRezCount = 0;
|
|
|
|
// make floater appear
|
|
setVisibleAndFrontmost();
|
|
}
|
|
|
|
void LLObjectBackup::onClose(bool app_quitting)
|
|
{
|
|
setVisible(false);
|
|
// HACK for fast XML iteration replace with:
|
|
// destroy();
|
|
}
|
|
|
|
void LLObjectBackup::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 LLObjectBackup::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());
|
|
}
|
|
|
|
void LLObjectBackup::exportObject()
|
|
{
|
|
mTexturesList.clear();
|
|
mLLSD.clear();
|
|
mThisGroup.clear();
|
|
|
|
setDefaultTextures();
|
|
LLSelectMgr::getInstance()->getSelection()->ref();
|
|
|
|
// Open the file save dialog
|
|
AIFilePicker* filepicker = AIFilePicker::create();
|
|
filepicker->open("", FFSAVE_XML);
|
|
filepicker->run(boost::bind(&LLObjectBackup::exportObject_continued, this, filepicker));
|
|
}
|
|
|
|
void LLObjectBackup::exportObject_continued(AIFilePicker* filepicker)
|
|
{
|
|
if (!filepicker->hasFilename())
|
|
{
|
|
// User canceled save.
|
|
LLSelectMgr::getInstance()->getSelection()->unref();
|
|
return;
|
|
}
|
|
|
|
mFileName = filepicker->getFilename();
|
|
mFolder = gDirUtilp->getDirName(mFileName);
|
|
|
|
mNonExportedTextures = TEXTURE_OK;
|
|
|
|
mExportState = EXPORT_INIT;
|
|
gIdleCallbacks.addFunction(exportWorker, NULL);
|
|
}
|
|
|
|
bool LLObjectBackup::validatePerms(const LLPermissions *item_permissions)
|
|
{
|
|
return item_permissions->allowExportBy(gAgent.getID(), LFSimFeatureHandler::instance().exportPolicy());
|
|
}
|
|
|
|
// So far, only Second Life forces TPVs to verify the creator for textures...
|
|
// which sucks, because there is no other way to check for the texture
|
|
// permissions or creator than to try and find the asset(s) corresponding to
|
|
// the texture in the inventory and check the permissions/creator on the said
|
|
// asset(s), 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 !
|
|
LLUUID LLObjectBackup::validateTextureID(LLUUID asset_id)
|
|
{
|
|
if (!gHippoGridManager->getConnectedGrid()->isSecondLife())
|
|
{
|
|
// If we are not in Second Life, don't bother.
|
|
return asset_id;
|
|
}
|
|
LLUUID texture = LL_TEXTURE_PLYWOOD;
|
|
if (asset_id == texture ||
|
|
asset_id == LL_TEXTURE_BLANK ||
|
|
asset_id == LL_TEXTURE_INVISIBLE ||
|
|
asset_id == LL_TEXTURE_TRANSPARENT ||
|
|
asset_id == LL_TEXTURE_MEDIA)
|
|
{
|
|
// Allow to export a few default SL textures.
|
|
return asset_id;
|
|
}
|
|
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);
|
|
|
|
if (items.count())
|
|
{
|
|
for (S32 i = 0; i < items.count(); i++)
|
|
{
|
|
const LLPermissions item_permissions = items[i]->getPermissions();
|
|
if (validatePerms(&item_permissions))
|
|
{
|
|
texture = asset_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (texture != asset_id)
|
|
{
|
|
mNonExportedTextures |= TEXTURE_BAD_PERM;
|
|
}
|
|
|
|
return texture;
|
|
}
|
|
|
|
void LLObjectBackup::exportWorker(void *userdata)
|
|
{
|
|
LLObjectBackup::getInstance()->updateExportNumbers();
|
|
|
|
switch (LLObjectBackup::getInstance()->mExportState)
|
|
{
|
|
case EXPORT_INIT:
|
|
{
|
|
LLObjectBackup::getInstance()->show(true);
|
|
struct ff : public LLSelectedNodeFunctor
|
|
{
|
|
virtual bool apply(LLSelectNode* node)
|
|
{
|
|
return LLObjectBackup::getInstance()->validatePerms(node->mPermissions);
|
|
}
|
|
} func;
|
|
|
|
if (LLSelectMgr::getInstance()->getSelection()->applyToNodes(&func, false))
|
|
{
|
|
LLObjectBackup::getInstance()->mExportState = EXPORT_STRUCTURE;
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ObjectBackup") << "Incorrect permission to export" << LL_ENDL;
|
|
LLObjectBackup::getInstance()->mExportState = EXPORT_FAILED;
|
|
LLSelectMgr::getInstance()->getSelection()->unref();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EXPORT_STRUCTURE:
|
|
{
|
|
struct ff : public LLSelectedObjectFunctor
|
|
{
|
|
virtual bool apply(LLViewerObject* object)
|
|
{
|
|
bool is_attachment = object->isAttachment();
|
|
object->boostTexturePriority(TRUE);
|
|
LLViewerObject::child_list_t children = object->getChildren();
|
|
children.push_front(object); //push root onto list
|
|
LLSD prim_llsd = LLObjectBackup::getInstance()->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;
|
|
LLObjectBackup::getInstance()->mLLSD["data"].append(stuff);
|
|
return true;
|
|
}
|
|
} func;
|
|
|
|
LLObjectBackup::getInstance()->mExportState = EXPORT_LLSD;
|
|
LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, false);
|
|
LLSelectMgr::getInstance()->getSelection()->unref();
|
|
}
|
|
break;
|
|
|
|
case EXPORT_TEXTURES:
|
|
if (LLObjectBackup::getInstance()->mNextTextureReady == false)
|
|
return;
|
|
|
|
// Ok we got work to do
|
|
LLObjectBackup::getInstance()->mNextTextureReady = false;
|
|
|
|
if (LLObjectBackup::getInstance()->mTexturesList.empty())
|
|
{
|
|
LLObjectBackup::getInstance()->mExportState = EXPORT_DONE;
|
|
return;
|
|
}
|
|
|
|
LLObjectBackup::getInstance()->exportNextTexture();
|
|
break;
|
|
|
|
case EXPORT_LLSD:
|
|
{
|
|
// Create a file stream and write to it
|
|
llofstream export_file(LLObjectBackup::getInstance()->mFileName);
|
|
LLSDSerialize::toPrettyXML(LLObjectBackup::getInstance()->mLLSD, export_file);
|
|
export_file.close();
|
|
LLObjectBackup::getInstance()->mNextTextureReady = true;
|
|
LLObjectBackup::getInstance()->mExportState = EXPORT_TEXTURES;
|
|
}
|
|
break;
|
|
|
|
case EXPORT_DONE:
|
|
gIdleCallbacks.deleteFunction(exportWorker);
|
|
if (LLObjectBackup::getInstance()->mNonExportedTextures == LLObjectBackup::TEXTURE_OK)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Export successful and complete." << LL_ENDL;
|
|
LLNotificationsUtil::add("ExportSuccessful");
|
|
}
|
|
else
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Export successful but incomplete: some texture(s) not saved." << LL_ENDL;
|
|
std::string reason;
|
|
if (LLObjectBackup::getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_BAD_PERM)
|
|
{
|
|
reason += "\nBad permissions/creator.";
|
|
}
|
|
if (LLObjectBackup::getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_MISSING)
|
|
{
|
|
reason += "\nMissing texture (retrying after full rezzing might work).";
|
|
}
|
|
if (LLObjectBackup::getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_BAD_ENCODING)
|
|
{
|
|
reason += "\nBad texture encoding.";
|
|
}
|
|
if (LLObjectBackup::getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_IS_NULL)
|
|
{
|
|
reason += "\nNull texture.";
|
|
}
|
|
if (LLObjectBackup::getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_SAVED_FAILED)
|
|
{
|
|
reason += "\nCould not write to disk.";
|
|
}
|
|
LLSD args;
|
|
args["REASON"] = reason;
|
|
LLNotificationsUtil::add("ExportPartial", args);
|
|
}
|
|
LLObjectBackup::getInstance()->close();
|
|
break;
|
|
|
|
case EXPORT_FAILED:
|
|
gIdleCallbacks.deleteFunction(exportWorker);
|
|
LL_WARNS("ObjectBackup") << "Export process aborted." << LL_ENDL;
|
|
LLNotificationsUtil::add("ExportFailed");
|
|
LLObjectBackup::getInstance()->close();
|
|
break;
|
|
}
|
|
}
|
|
|
|
LLSD LLObjectBackup::primsToLLSD(LLViewerObject::child_list_t child_list, bool is_attachment)
|
|
{
|
|
LLViewerObject* object;
|
|
LLSD llsd;
|
|
char localid[16];
|
|
|
|
for (LLViewerObject::child_list_t::iterator i = child_list.begin(); i != child_list.end(); ++i)
|
|
{
|
|
object = (*i);
|
|
LLUUID id = object->getID();
|
|
|
|
LL_INFOS("ObjectBackup") << "Exporting prim " << object->getID().asString() << LL_ENDL;
|
|
|
|
// Create an LLSD object that represents this prim. It will be injected in to the overall LLSD
|
|
// tree structure
|
|
LLSD prim_llsd;
|
|
|
|
if (!object->isRoot())
|
|
{
|
|
// Parent id
|
|
snprintf(localid, sizeof(localid), "%u", object->getSubParent()->getLocalID());
|
|
prim_llsd["parent"] = localid;
|
|
}
|
|
|
|
// Name and description
|
|
LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->findNode(object);
|
|
if (node)
|
|
{
|
|
prim_llsd["name"] = node->mName;
|
|
prim_llsd["description"] = node->mDescription;
|
|
}
|
|
|
|
// Transforms
|
|
if (is_attachment)
|
|
{
|
|
prim_llsd["position"] = object->getPositionEdit().getValue();
|
|
prim_llsd["rotation"] = ll_sd_from_quaternion(object->getRotationEdit());
|
|
}
|
|
else
|
|
{
|
|
prim_llsd["position"] = object->getPosition().getValue();
|
|
prim_llsd["rotation"] = ll_sd_from_quaternion(object->getRotation());
|
|
}
|
|
prim_llsd["scale"] = object->getScale().getValue();
|
|
|
|
// Flags
|
|
prim_llsd["flags"] = (S32)object->getFlags();
|
|
|
|
// Volume params
|
|
LLVolumeParams params = object->getVolume()->getParams();
|
|
prim_llsd["volume"] = params.asLLSD();
|
|
|
|
// Material
|
|
prim_llsd["material"] = object->getMaterial();
|
|
|
|
// Click Action
|
|
if (S32 action = object->getClickAction()) // Non-zero
|
|
prim_llsd["clickaction"] = action;
|
|
|
|
// Physics
|
|
if (!object->getPhysicsShapeUnknown())
|
|
{
|
|
LLSD& physics = prim_llsd["ExtraPhysics"];
|
|
physics["PhysicsShapeType"] = object->getPhysicsShapeType();
|
|
physics["Density"] = object->getPhysicsDensity();
|
|
physics["Friction"] = object->getPhysicsFriction();
|
|
physics["GravityMultiplier"] = object->getPhysicsGravity();
|
|
physics["Restitution"] = object->getPhysicsRestitution();
|
|
}
|
|
|
|
// Extra paramsb6fab961-af18-77f8-cf08-f021377a7244
|
|
if (object->isFlexible())
|
|
{
|
|
// Flexible
|
|
LLFlexibleObjectData* flex = (LLFlexibleObjectData*)object->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
|
|
prim_llsd["flexible"] = flex->asLLSD();
|
|
}
|
|
if (object->getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT))
|
|
{
|
|
// Light
|
|
LLLightParams* light = (LLLightParams*)object->getParameterEntry(LLNetworkData::PARAMS_LIGHT);
|
|
prim_llsd["light"] = light->asLLSD();
|
|
}
|
|
if (object->getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
|
|
{
|
|
// Light Texture
|
|
LLLightImageParams* light_texture = (LLLightImageParams*)object->getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
|
|
prim_llsd["light_texture"] = light_texture->asLLSD();
|
|
}
|
|
if (object->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT))
|
|
{
|
|
// Sculpt
|
|
LLSculptParams* sculpt = (LLSculptParams*)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
|
|
prim_llsd["sculpt"] = sculpt->asLLSD();
|
|
|
|
LLUUID sculpt_texture = sculpt->getSculptTexture();
|
|
if (sculpt_texture == validateTextureID(sculpt_texture))
|
|
{
|
|
bool alreadyseen = false;
|
|
std::list<LLUUID>::iterator iter;
|
|
for (iter = mTexturesList.begin(); iter != mTexturesList.end(); iter++)
|
|
{
|
|
if ((*iter) == sculpt_texture)
|
|
alreadyseen = true;
|
|
}
|
|
if (alreadyseen == false)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a sculpt texture, adding to list " << sculpt_texture << LL_ENDL;
|
|
mTexturesList.push_back(sculpt_texture);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ObjectBackup") << "Incorrect permission to export a sculpt texture." << LL_ENDL;
|
|
LLObjectBackup::getInstance()->mExportState = EXPORT_FAILED;
|
|
}
|
|
}
|
|
|
|
// Textures
|
|
LLSD te_llsd;
|
|
LLSD this_te_llsd;
|
|
LLUUID t_id;
|
|
U8 te_count = object->getNumTEs();
|
|
for (U8 i = 0; i < te_count; i++)
|
|
{
|
|
bool alreadyseen = false;
|
|
t_id = validateTextureID(object->getTE(i)->getID());
|
|
this_te_llsd = object->getTE(i)->asLLSD();
|
|
this_te_llsd["imageid"] = t_id;
|
|
te_llsd.append(this_te_llsd);
|
|
// Do not export Linden textures even though they don't taint creation.
|
|
if (t_id != LL_TEXTURE_PLYWOOD &&
|
|
t_id != LL_TEXTURE_BLANK &&
|
|
t_id != LL_TEXTURE_TRANSPARENT &&
|
|
t_id != LL_TEXTURE_INVISIBLE &&
|
|
t_id != LL_TEXTURE_MEDIA)
|
|
{
|
|
std::list<LLUUID>::iterator iter;
|
|
for (iter = mTexturesList.begin(); iter != mTexturesList.end(); iter++)
|
|
{
|
|
if ((*iter) == t_id)
|
|
{
|
|
alreadyseen = true;
|
|
break;
|
|
}
|
|
}
|
|
if (alreadyseen == false)
|
|
mTexturesList.push_back(t_id);
|
|
}
|
|
}
|
|
prim_llsd["textures"] = te_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", object->getLocalID());
|
|
llsd[(const char*)localid] = prim_llsd;
|
|
}
|
|
updateExportNumbers();
|
|
return llsd;
|
|
}
|
|
|
|
void LLObjectBackup::exportNextTexture()
|
|
{
|
|
if (mTexturesList.empty())
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Finished exporting textures." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
LLUUID id;
|
|
std::list<LLUUID>::iterator iter;
|
|
iter = mTexturesList.begin();
|
|
|
|
while (true)
|
|
{
|
|
if (iter == mTexturesList.end())
|
|
{
|
|
mNextTextureReady = true;
|
|
return;
|
|
}
|
|
|
|
id = (*iter);
|
|
if (id.isNull())
|
|
{
|
|
// NULL texture id: just remove and ignore.
|
|
mTexturesList.remove(id);
|
|
iter = mTexturesList.begin();
|
|
continue;
|
|
}
|
|
|
|
LLViewerTexture* imagep = LLViewerTextureManager::findTexture(id);
|
|
if (imagep != NULL)
|
|
{
|
|
S32 cur_discard = imagep->getDiscardLevel();
|
|
if (cur_discard > 0)
|
|
{
|
|
if (imagep->getBoostLevel() != LLGLTexture::BOOST_PREVIEW)
|
|
{
|
|
// we want to force discard 0: this one does this.
|
|
imagep->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ObjectBackup") << "We *DON'T* have the texture " << id.asString() << LL_ENDL;
|
|
mNonExportedTextures |= TEXTURE_MISSING;
|
|
mTexturesList.remove(id);
|
|
return;
|
|
}
|
|
iter++;
|
|
}
|
|
|
|
mTexturesList.remove(id);
|
|
|
|
LL_INFOS("ObjectBackup") << "Requesting texture " << id << LL_ENDL;
|
|
LLImageJ2C* mFormattedImage = new LLImageJ2C;
|
|
CacheReadResponder* responder = new CacheReadResponder(id, mFormattedImage);
|
|
LLAppViewer::getTextureCache()->readFromCache(id, LLWorkerThread::PRIORITY_HIGH, 0, 999999, responder);
|
|
}
|
|
|
|
void LLObjectBackup::importObject(bool upload)
|
|
{
|
|
mTexturesList.clear();
|
|
mAssetMap.clear();
|
|
mCurrentAsset = LLUUID::null;
|
|
|
|
setDefaultTextures();
|
|
|
|
mRetexture = upload;
|
|
|
|
// Open the file open dialog
|
|
AIFilePicker* filepicker = AIFilePicker::create();
|
|
filepicker->open(FFLOAD_XML, "", "import");
|
|
filepicker->run(boost::bind(&LLObjectBackup::importObject_continued, this, filepicker));
|
|
|
|
return;
|
|
}
|
|
|
|
void LLObjectBackup::importObject_continued(AIFilePicker* filepicker)
|
|
{
|
|
if (!filepicker->hasFilename())
|
|
{
|
|
// User canceled save.
|
|
return;
|
|
}
|
|
|
|
std::string file_name = filepicker->getFilename();
|
|
mFolder = gDirUtilp->getDirName(file_name);
|
|
llifstream import_file(file_name);
|
|
LLSDSerialize::fromXML(mLLSD, import_file);
|
|
import_file.close();
|
|
show(false);
|
|
|
|
mAgentPos = gAgent.getPositionAgent();
|
|
mAgentRot = LLQuaternion(gAgent.getAtAxis(), gAgent.getLeftAxis(), gAgent.getUpAxis());
|
|
|
|
// Get the texture map
|
|
|
|
LLSD::map_const_iterator prim_it;
|
|
LLSD::array_const_iterator prim_arr_it;
|
|
|
|
mCurObject = 1;
|
|
mCurPrim = 1;
|
|
mObjects = mLLSD["data"].size();
|
|
mPrims = 0;
|
|
mRezCount = 0;
|
|
updateImportNumbers();
|
|
|
|
for (prim_arr_it = mLLSD["data"].beginArray(); prim_arr_it != mLLSD["data"].endArray(); prim_arr_it++)
|
|
{
|
|
LLSD llsd2 = (*prim_arr_it)["group_body"];
|
|
|
|
for (prim_it = llsd2.beginMap(); prim_it != llsd2.endMap(); prim_it++)
|
|
{
|
|
LLSD prim_llsd = llsd2[prim_it->first];
|
|
LLSD::array_iterator text_it;
|
|
std::list<LLUUID>::iterator iter;
|
|
|
|
if (prim_llsd.has("sculpt"))
|
|
{
|
|
LLSculptParams sculpt;
|
|
sculpt.fromLLSD(prim_llsd["sculpt"]);
|
|
LLUUID orig = sculpt.getSculptTexture();
|
|
bool alreadyseen = false;
|
|
for (iter = mTexturesList.begin(); iter != mTexturesList.end(); iter++)
|
|
{
|
|
if ((*iter) == orig)
|
|
alreadyseen = true;
|
|
}
|
|
if (alreadyseen == false)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a new SCULPT texture to upload " << orig << LL_ENDL;
|
|
mTexturesList.push_back(orig);
|
|
}
|
|
}
|
|
|
|
LLSD& te_llsd = prim_llsd.has("textures") ? prim_llsd["textures"] : prim_llsd["texture"]; // Firestorm's format uses singular "texture"
|
|
|
|
for (text_it = te_llsd.beginArray(); text_it != te_llsd.endArray(); text_it++)
|
|
{
|
|
LLSD the_te = (*text_it);
|
|
LLTextureEntry te;
|
|
te.fromLLSD(the_te);
|
|
|
|
LLUUID id = te.getID();
|
|
if (id != LL_TEXTURE_PLYWOOD && id != LL_TEXTURE_BLANK && id != LL_TEXTURE_INVISIBLE) // Do not upload the default textures
|
|
{
|
|
bool alreadyseen = false;
|
|
|
|
for (iter = mTexturesList.begin(); iter != mTexturesList.end(); iter++)
|
|
{
|
|
if ((*iter) == te.getID())
|
|
alreadyseen = true;
|
|
}
|
|
if (alreadyseen == false)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a new texture to upload "<< te.getID() << LL_ENDL;
|
|
mTexturesList.push_back(te.getID());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mRetexture == TRUE)
|
|
uploadNextAsset();
|
|
else
|
|
importFirstObject();
|
|
}
|
|
|
|
LLVector3 LLObjectBackup::offsetAgent(LLVector3 offset)
|
|
{
|
|
return offset * mAgentRot + mAgentPos;
|
|
}
|
|
|
|
void LLObjectBackup::rezAgentOffset(LLVector3 offset)
|
|
{
|
|
// This will break for a sitting agent
|
|
LLToolPlacer mPlacer;
|
|
mPlacer.setObjectType(LL_PCODE_CUBE);
|
|
//LLVector3 pos = offsetAgent(offset);
|
|
mPlacer.placeObject((S32)offset.mV[0], (S32)offset.mV[1], MASK_NONE);
|
|
}
|
|
|
|
void LLObjectBackup::importFirstObject()
|
|
{
|
|
mRunning = true;
|
|
show(false);
|
|
mGroupPrimImportIter = mLLSD["data"].beginArray();
|
|
mRootRootPos = (*mGroupPrimImportIter)["root_position"];
|
|
mObjects = mLLSD["data"].size();
|
|
mCurObject = 1;
|
|
importNextObject();
|
|
}
|
|
|
|
void LLObjectBackup::importNextObject()
|
|
{
|
|
mToSelect.clear();
|
|
mRezCount = 0;
|
|
|
|
mThisGroup = (*mGroupPrimImportIter)["group_body"];
|
|
mPrimImportIter = mThisGroup.beginMap();
|
|
|
|
mCurPrim = 0;
|
|
mPrims = mThisGroup.size();
|
|
updateImportNumbers();
|
|
|
|
LLVector3 lgpos = (*mGroupPrimImportIter)["root_position"];
|
|
mGroupOffset = lgpos - mRootRootPos;
|
|
mRootPos = offsetAgent(LLVector3(2.0, 0.0, 0.0));
|
|
mRootRot = ll_quaternion_from_sd((*mGroupPrimImportIter)["root_rotation"]);
|
|
|
|
rezAgentOffset(LLVector3(0.0, 2.0, 0.0));
|
|
// Now we must wait for the callback when ViewerObjectList gets the new objects and we have the correct number selected
|
|
}
|
|
|
|
// This function takes a pointer to a viewerobject and applies the prim definition that prim_llsd has
|
|
void LLObjectBackup::xmlToPrim(LLSD prim_llsd, LLViewerObject* object)
|
|
{
|
|
LLUUID id = object->getID();
|
|
mExpectingUpdate = object->getID();
|
|
LLSelectMgr::getInstance()->selectObjectAndFamily(object);
|
|
|
|
if (prim_llsd.has("name"))
|
|
{
|
|
LLSelectMgr::getInstance()->selectionSetObjectName(prim_llsd["name"]);
|
|
}
|
|
|
|
if (prim_llsd.has("description"))
|
|
{
|
|
LLSelectMgr::getInstance()->selectionSetObjectDescription(prim_llsd["description"]);
|
|
}
|
|
|
|
if (prim_llsd.has("material"))
|
|
{
|
|
LLSelectMgr::getInstance()->selectionSetMaterial(prim_llsd["material"].asInteger());
|
|
}
|
|
|
|
if (prim_llsd.has("clickaction"))
|
|
{
|
|
LLSelectMgr::getInstance()->selectionSetClickAction(prim_llsd["clickaction"].asInteger());
|
|
}
|
|
|
|
if (prim_llsd.has("parent"))
|
|
{
|
|
//we are not the root node.
|
|
LLVector3 pos = prim_llsd["position"];
|
|
LLQuaternion rot = ll_quaternion_from_sd(prim_llsd["rotation"]);
|
|
object->setPositionRegion(pos * mRootRot + mRootPos + mGroupOffset);
|
|
object->setRotation(rot * mRootRot);
|
|
}
|
|
else
|
|
{
|
|
object->setPositionRegion(mRootPos + mGroupOffset);
|
|
LLQuaternion rot=ll_quaternion_from_sd(prim_llsd["rotation"]);
|
|
object->setRotation(rot);
|
|
}
|
|
|
|
object->setScale(prim_llsd["scale"]);
|
|
|
|
if (prim_llsd.has("flags"))
|
|
{
|
|
U32 flags(prim_llsd["flags"].asInteger());
|
|
object->setFlags(flags, true);
|
|
}
|
|
else // Legacy
|
|
{
|
|
/*if (prim_llsd.has("shadows"))
|
|
if (prim_llsd["shadows"].asInteger() == 1)
|
|
object->setFlags(FLAGS_CAST_SHADOWS, true);*/
|
|
|
|
if (prim_llsd.has("phantom"))
|
|
if (prim_llsd["phantom"].asInteger() == 1)
|
|
object->setFlags(FLAGS_PHANTOM, true);
|
|
|
|
if (prim_llsd.has("physical"))
|
|
if (prim_llsd["physical"].asInteger() == 1)
|
|
object->setFlags(FLAGS_USE_PHYSICS, true);
|
|
}
|
|
|
|
if (prim_llsd.has("ExtraPhysics"))
|
|
{
|
|
const LLSD& physics = prim_llsd["ExtraPhysics"];
|
|
object->setPhysicsShapeType(physics["PhysicsShapeType"].asInteger());
|
|
object->setPhysicsDensity(physics["Density"].asFloat());
|
|
object->setPhysicsFriction(physics["Friction"].asFloat());
|
|
object->setPhysicsGravity(physics["GravityMultiplier"].asFloat());
|
|
object->setPhysicsRestitution(physics["Restitution"].asFloat());
|
|
object->updateFlags(true);
|
|
}
|
|
|
|
// Volume params
|
|
LLVolumeParams volume_params = object->getVolume()->getParams();
|
|
volume_params.fromLLSD(prim_llsd["volume"]);
|
|
object->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
|
|
|
|
if (mAssetMap[sculpt.getSculptTexture()].notNull())
|
|
{
|
|
LLUUID replacment = mAssetMap[sculpt.getSculptTexture()];
|
|
sculpt.setSculptTexture(replacment);
|
|
}
|
|
|
|
object->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt, true);
|
|
}
|
|
|
|
if (prim_llsd.has("light"))
|
|
{
|
|
LLLightParams light;
|
|
light.fromLLSD(prim_llsd["light"]);
|
|
object->setParameterEntry(LLNetworkData::PARAMS_LIGHT, light, true);
|
|
}
|
|
|
|
if (prim_llsd.has("light_texture"))
|
|
{
|
|
// Light Texture
|
|
LLLightImageParams light_texture;
|
|
light_texture.fromLLSD(prim_llsd["light_texture"]);
|
|
object->setParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE, light_texture, true);
|
|
}
|
|
|
|
if (prim_llsd.has("flexible"))
|
|
{
|
|
LLFlexibleObjectData flex;
|
|
flex.fromLLSD(prim_llsd["flexible"]);
|
|
object->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, flex, true);
|
|
}
|
|
|
|
// Textures
|
|
LL_INFOS("ObjectBackup") << "Processing textures for prim" << LL_ENDL;
|
|
LLSD& te_llsd = prim_llsd.has("textures") ? prim_llsd["textures"] : prim_llsd["texture"]; // Firestorm's format uses singular "texture"
|
|
LLSD::array_iterator text_it;
|
|
U8 i = 0;
|
|
|
|
for (text_it = te_llsd.beginArray(); text_it != te_llsd.endArray(); text_it++)
|
|
{
|
|
LLSD the_te = (*text_it);
|
|
LLTextureEntry te;
|
|
te.fromLLSD(the_te);
|
|
|
|
if (mAssetMap[te.getID()].notNull())
|
|
{
|
|
LLUUID replacment = mAssetMap[te.getID()];
|
|
te.setID(replacment);
|
|
}
|
|
|
|
object->setTE(i++, te);
|
|
}
|
|
|
|
LL_INFOS("ObjectBackup") << "Textures done !" << LL_ENDL;
|
|
|
|
//bump the iterator now so the callbacks hook together nicely
|
|
//if (mPrimImportIter != mThisGroup.endMap())
|
|
// mPrimImportIter++;
|
|
|
|
object->sendRotationUpdate();
|
|
object->sendTEUpdate();
|
|
object->sendShapeUpdate();
|
|
LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_SCALE | UPD_POSITION);
|
|
|
|
LLSelectMgr::getInstance()->deselectAll();
|
|
}
|
|
|
|
// This is fired when the update packet is processed so we know the prim settings have stuck
|
|
void LLObjectBackup::primUpdate(LLViewerObject* object)
|
|
{
|
|
if (!mRunning)
|
|
return;
|
|
|
|
if (object != NULL)
|
|
if (object->mID != mExpectingUpdate)
|
|
return;
|
|
|
|
mCurPrim++;
|
|
updateImportNumbers();
|
|
mPrimImportIter++;
|
|
|
|
LLUUID x;
|
|
mExpectingUpdate = x.null;
|
|
|
|
if (mPrimImportIter == mThisGroup.endMap())
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Trying to link" << LL_ENDL;
|
|
|
|
if (mToSelect.size() > 1)
|
|
{
|
|
std::reverse(mToSelect.begin(), mToSelect.end());
|
|
// Now link
|
|
LLSelectMgr::getInstance()->deselectAll();
|
|
LLSelectMgr::getInstance()->selectObjectAndFamily(mToSelect, true);
|
|
LLSelectMgr::getInstance()->sendLink();
|
|
LLViewerObject* root = mToSelect.back();
|
|
root->setRotation(mRootRot);
|
|
}
|
|
|
|
mCurObject++;
|
|
mGroupPrimImportIter++;
|
|
if (mGroupPrimImportIter != mLLSD["data"].endArray())
|
|
{
|
|
importNextObject();
|
|
return;
|
|
}
|
|
|
|
mRunning = false;
|
|
close();
|
|
return;
|
|
}
|
|
|
|
LLSD prim_llsd = mThisGroup[mPrimImportIter->first];
|
|
|
|
if (mToSelect.empty())
|
|
{
|
|
LL_WARNS("ObjectBackup") << "error: ran out of objects to mod." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
if (mPrimImportIter != mThisGroup.endMap())
|
|
{
|
|
//rezAgentOffset(LLVector3(1.0, 0.0, 0.0));
|
|
LLSD prim_llsd = mThisGroup[mPrimImportIter->first];
|
|
mProcessIter++;
|
|
xmlToPrim(prim_llsd, *mProcessIter);
|
|
}
|
|
}
|
|
|
|
// Callback when we rez a new object when the importer is running.
|
|
bool LLObjectBackup::newPrim(LLViewerObject* pobject)
|
|
{
|
|
if (mRunning)
|
|
{
|
|
mRezCount++;
|
|
mToSelect.push_back(pobject);
|
|
updateImportNumbers();
|
|
mPrimImportIter++;
|
|
|
|
pobject->setPosition(offsetAgent(LLVector3(0.0, 1.0, 0.0)));
|
|
LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_POSITION);
|
|
|
|
if (mPrimImportIter != mThisGroup.endMap())
|
|
{
|
|
rezAgentOffset(LLVector3(1.0, 0.0 ,0.0));
|
|
}
|
|
else
|
|
{
|
|
LL_INFOS("ObjectBackup") << "All prims rezzed, moving to build stage" << LL_ENDL;
|
|
// 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.
|
|
LLSelectMgr::getInstance()->deselectAll();
|
|
mPrimImportIter = mThisGroup.beginMap();
|
|
LLSD prim_llsd = mThisGroup[mPrimImportIter->first];
|
|
mProcessIter = mToSelect.begin();
|
|
xmlToPrim(prim_llsd, *mProcessIter);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void LLObjectBackup::updateMap(LLUUID uploaded_asset)
|
|
{
|
|
if (mCurrentAsset.isNull())
|
|
return;
|
|
|
|
LL_INFOS("ObjectBackup") << "Mapping " << mCurrentAsset << " to " << uploaded_asset << LL_ENDL;
|
|
mAssetMap.insert(std::pair<LLUUID, LLUUID>(mCurrentAsset, uploaded_asset));
|
|
}
|
|
|
|
void myupload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_type,
|
|
std::string name, std::string desc, S32 compression_info,
|
|
LLFolderType::EType destination_folder_type,
|
|
LLInventoryType::EType inv_type, U32 next_owner_perm,
|
|
const std::string& display_name,
|
|
LLAssetStorage::LLStoreAssetCallback callback,
|
|
void *userdata)
|
|
{
|
|
if (gDisconnected)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID());
|
|
|
|
// At this point, we're ready for the upload.
|
|
std::string upload_message = "Uploading...\n\n";
|
|
upload_message.append(display_name);
|
|
LLUploadDialog::modalUploadDialog(upload_message);
|
|
|
|
std::string url = gAgent.getRegion()->getCapability("NewFileAgentInventory");
|
|
if (!url.empty())
|
|
{
|
|
LLSD body;
|
|
body["folder_id"] = gInventory.findCategoryUUIDForType((destination_folder_type == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(asset_type) : destination_folder_type);
|
|
body["asset_type"] = LLAssetType::lookup(asset_type);
|
|
body["inventory_type"] = LLInventoryType::lookup(inv_type);
|
|
body["name"] = name;
|
|
body["description"] = desc;
|
|
|
|
std::ostringstream llsdxml;
|
|
LLSDSerialize::toXML(body, llsdxml);
|
|
LL_DEBUGS("ObjectBackup") << "posting body to capability: " << llsdxml.str() << LL_ENDL;
|
|
//LLHTTPClient::post(url, body, new LLNewAgentInventoryResponder(body, uuid, asset_type));
|
|
LLHTTPClient::post(url, body, new importResponder(body, uuid, asset_type));
|
|
}
|
|
else
|
|
{
|
|
LL_INFOS("ObjectBackup") << "NewAgentInventory capability not found. Can't upload !" << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
void LLObjectBackup::uploadNextAsset()
|
|
{
|
|
if (mTexturesList.empty())
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Texture list is empty, moving to rez stage." << LL_ENDL;
|
|
mCurrentAsset = LLUUID::null;
|
|
importFirstObject();
|
|
return;
|
|
}
|
|
|
|
updateImportNumbers();
|
|
|
|
std::list<LLUUID>::iterator iter;
|
|
iter = mTexturesList.begin();
|
|
LLUUID id = *iter;
|
|
mTexturesList.pop_front();
|
|
|
|
LL_INFOS("ObjectBackup") << "Got texture ID " << id << ": trying to upload" << LL_ENDL;
|
|
|
|
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());
|
|
|
|
S32 file_size;
|
|
LLAPRFile outfile(filename, LL_APR_RB, &file_size);
|
|
if (outfile.getFileHandle())
|
|
{
|
|
const S32 buf_size = 65536;
|
|
U8 copy_buf[buf_size];
|
|
LLVFile file(gVFS, uuid, LLAssetType::AT_TEXTURE, LLVFile::WRITE);
|
|
file.setMaxSize(file_size);
|
|
while ((file_size = outfile.read(copy_buf, buf_size)))
|
|
{
|
|
file.write(copy_buf, file_size);
|
|
}
|
|
outfile.close();
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ObjectBackup") << "Unable to access output file " << filename << LL_ENDL;
|
|
uploadNextAsset();
|
|
return;
|
|
}
|
|
|
|
myupload_new_resource(tid, LLAssetType::AT_TEXTURE, struid, struid, 0,
|
|
LLFolderType::FT_TEXTURE, LLInventoryType::defaultForAssetType(LLAssetType::AT_TEXTURE),
|
|
0x0, "Uploaded texture", NULL, NULL);
|
|
}
|