Adds separated default permissions for Objects, Uploads, Scripts, Notecards, Gestures and Wearables. Compatible with Export Permission Backwards compatible with default permissions for objects on regions where AgentPreferences capability is not available (opensim users rejoice!) These changes to llfloaterperms are made available under lgpl, if there's a v3 that wants to adopt default export permission for opensim.
1530 lines
43 KiB
C++
1530 lines
43 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 "llnotificationsutil.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 "llresourcedata.h"
|
|
#include "llmaterialmgr.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");
|
|
|
|
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, gAgentID);
|
|
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
|
|
gMessageSystem->nextBlockFast(_PREHASH_MoneyData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_TransactionID, LLUUID::null );
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
// 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(gAgentID, gAgentID, 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()->mCheckNextTexture = true;
|
|
}
|
|
|
|
private:
|
|
LLPointer<LLImageFormatted> mFormattedImage;
|
|
LLUUID mID;
|
|
};
|
|
|
|
LLObjectBackup::LLObjectBackup()
|
|
: LLFloater(std::string("Object Backup Floater"), std::string("FloaterObjectBackuptRect"), LLStringUtil::null)
|
|
{
|
|
//buildFromFile("floater_object.xml");
|
|
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_object_backup.xml", NULL, false);
|
|
mRunning = false;
|
|
mTexturesList.clear();
|
|
mAssetMap.clear();
|
|
mCurrentAsset.setNull();
|
|
mRetexture = false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
LLObjectBackup* LLObjectBackup::getInstance()
|
|
{
|
|
if (!sInstance)
|
|
{
|
|
sInstance = new LLObjectBackup();
|
|
}
|
|
return sInstance;
|
|
}
|
|
|
|
LLObjectBackup::~LLObjectBackup()
|
|
{
|
|
// save position of floater
|
|
gSavedSettings.setRect("FloaterObjectBackuptRect", getRect());
|
|
sInstance = NULL;
|
|
}
|
|
|
|
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();
|
|
mBadPermsTexturesList.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;
|
|
mGotExtraPhysics = !gAgent.getRegion()->getCapability("GetObjectPhysicsData").empty();
|
|
gIdleCallbacks.addFunction(exportWorker, NULL);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------//
|
|
// Permissions checking functions
|
|
//---------------------------------------------------------------------------//
|
|
|
|
//static
|
|
void LLObjectBackup::setDefaultTextures()
|
|
{
|
|
if (LFSimFeatureHandler::instance().exportPolicy() == ep_full_perm)
|
|
{
|
|
// When not in SL and not in an OpenSIM grid with export permission
|
|
// support (i.e. when no texture permission check is needed), we can
|
|
// get these defaults from the user settings...
|
|
LL_TEXTURE_PLYWOOD = LLUUID(gSavedSettings.getString("DefaultObjectTexture"));
|
|
LL_TEXTURE_BLANK = LLUUID(gSavedSettings.getString("UIImgWhiteUUID"));
|
|
LL_TEXTURE_INVISIBLE = LLUUID(gSavedSettings.getString("UIImgInvisibleUUID"));
|
|
}
|
|
}
|
|
|
|
bool LLObjectBackup::validatePerms(const LLPermissions* item_permissions)
|
|
{
|
|
return item_permissions->allowExportBy(gAgentID, 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(const LLUUID& asset_id)
|
|
{
|
|
if (!gHippoGridManager->getConnectedGrid()->isSecondLife())
|
|
{
|
|
// If we are not in Second Life, don't bother.
|
|
return asset_id;
|
|
}
|
|
|
|
if (mBadPermsTexturesList.count(asset_id))
|
|
{
|
|
// We already checked it and know it's bad...
|
|
return LL_TEXTURE_PLYWOOD;
|
|
}
|
|
|
|
if (asset_id == LL_TEXTURE_PLYWOOD ||
|
|
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);
|
|
S32 count = items.size();
|
|
if (count > 0)
|
|
{
|
|
for (S32 i = 0; i < count; ++i)
|
|
{
|
|
const LLPermissions item_permissions = items[i]->getPermissions();
|
|
if (validatePerms(&item_permissions))
|
|
{
|
|
return asset_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
mBadPermsTexturesList.insert(asset_id); // Cache bad texture ID
|
|
mNonExportedTextures |= TEXTURE_BAD_PERM;
|
|
LL_WARNS("ObjectBackup") << "Bad permissions for texture ID: " << asset_id
|
|
<< " - Texture will not be exported." << LL_ENDL;
|
|
return LL_TEXTURE_PLYWOOD;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
|
|
void LLObjectBackup::exportWorker(void *userdata)
|
|
{
|
|
getInstance()->updateExportNumbers();
|
|
|
|
switch (getInstance()->mExportState)
|
|
{
|
|
case EXPORT_INIT:
|
|
{
|
|
getInstance()->show(true);
|
|
// Fall through to EXPORT_CHECK_PERMS
|
|
}
|
|
case EXPORT_CHECK_PERMS:
|
|
{
|
|
struct ff : public LLSelectedNodeFunctor
|
|
{
|
|
virtual bool apply(LLSelectNode* node)
|
|
{
|
|
return getInstance()->validatePerms(node->mPermissions);
|
|
}
|
|
} func;
|
|
|
|
LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
|
|
if (object)
|
|
{
|
|
if (LLSelectMgr::getInstance()->getSelection()->applyToNodes(&func, false))
|
|
{
|
|
getInstance()->mExportState = EXPORT_FETCH_PHYSICS;
|
|
}
|
|
else
|
|
{
|
|
struct vv : public LLSelectedNodeFunctor
|
|
{
|
|
virtual bool apply(LLSelectNode* node)
|
|
{
|
|
return node->mValid;
|
|
}
|
|
} func2;
|
|
|
|
if (LLSelectMgr::getInstance()->getSelection()->applyToNodes(&func2, false))
|
|
{
|
|
LL_WARNS("ObjectBackup") << "Incorrect permission to export" << LL_ENDL;
|
|
getInstance()->mExportState = EXPORT_FAILED;
|
|
LLSelectMgr::getInstance()->getSelection()->unref();
|
|
}
|
|
else
|
|
{
|
|
LL_DEBUGS("ObjectBackup") << "Nodes permissions not yet received, delaying..."
|
|
<< LL_ENDL;
|
|
getInstance()->mExportState = EXPORT_CHECK_PERMS;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLObjectBackup::getInstance()->mExportState = EXPORT_FAILED;
|
|
LLSelectMgr::getInstance()->getSelection()->unref();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EXPORT_FETCH_PHYSICS:
|
|
{
|
|
// Don't bother to try and fetch the extra physics flags if we
|
|
// don't have sim support for them...
|
|
if (!getInstance()->mGotExtraPhysics)
|
|
{
|
|
getInstance()->mExportState = EXPORT_STRUCTURE;
|
|
break;
|
|
}
|
|
|
|
struct ff : public LLSelectedNodeFunctor
|
|
{
|
|
virtual bool apply(LLSelectNode* node)
|
|
{
|
|
LLViewerObject* object = node->getObject();
|
|
return object->getPhysicsShapeUnknown();
|
|
}
|
|
} func;
|
|
|
|
LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
|
|
if (object)
|
|
{
|
|
if (LLSelectMgr::getInstance()->getSelection()->applyToNodes(&func, false))
|
|
{
|
|
getInstance()->mExportState = EXPORT_STRUCTURE;
|
|
}
|
|
else
|
|
{
|
|
LL_DEBUGS("ObjectBackup") << "Nodes physics not yet received, delaying..."
|
|
<< LL_ENDL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
getInstance()->mLLSD["data"].append(stuff);
|
|
return true;
|
|
}
|
|
} func;
|
|
|
|
getInstance()->mExportState = EXPORT_LLSD;
|
|
LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, false);
|
|
LLSelectMgr::getInstance()->getSelection()->unref();
|
|
break;
|
|
}
|
|
|
|
case EXPORT_TEXTURES:
|
|
{
|
|
if (!getInstance()->mCheckNextTexture)
|
|
{
|
|
// The texture is being fetched. Wait till next idle callback.
|
|
return;
|
|
}
|
|
|
|
if (getInstance()->mTexturesList.empty())
|
|
{
|
|
getInstance()->mExportState = EXPORT_DONE;
|
|
return;
|
|
}
|
|
|
|
// Ok, we got work to do...
|
|
getInstance()->mCheckNextTexture= false;
|
|
getInstance()->exportNextTexture();
|
|
break;
|
|
}
|
|
|
|
case EXPORT_LLSD:
|
|
{
|
|
// Create a file stream and write to it
|
|
llofstream export_file(getInstance()->mFileName);
|
|
LLSDSerialize::toPrettyXML(getInstance()->mLLSD, export_file);
|
|
export_file.close();
|
|
getInstance()->mCheckNextTexture = true;
|
|
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 (getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_BAD_PERM)
|
|
{
|
|
reason += "\nBad permissions/creator.";
|
|
}
|
|
if (getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_MISSING)
|
|
{
|
|
reason += "\nMissing texture (retrying after full rezzing might work).";
|
|
}
|
|
if (getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_BAD_ENCODING)
|
|
{
|
|
reason += "\nBad texture encoding.";
|
|
}
|
|
if (getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_IS_NULL)
|
|
{
|
|
reason += "\nNull texture.";
|
|
}
|
|
if (getInstance()->mNonExportedTextures & LLObjectBackup::TEXTURE_SAVED_FAILED)
|
|
{
|
|
reason += "\nCould not write to disk.";
|
|
}
|
|
LLSD args;
|
|
args["REASON"] = reason;
|
|
LLNotificationsUtil::add("ExportPartial", args);
|
|
}
|
|
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];
|
|
LLUUID t_id;
|
|
|
|
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["phantom"] = object->flagPhantom(); // legacy
|
|
prim_llsd["physical"] = object->flagUsePhysics(); // legacy
|
|
prim_llsd["flags"] = (S32)object->getFlags(); // new way
|
|
|
|
// Extra physics flags
|
|
if (mGotExtraPhysics)
|
|
{
|
|
LLSD& physics = prim_llsd["ExtraPhysics"];
|
|
physics["PhysicsShapeType"] = object->getPhysicsShapeType();
|
|
physics["Gravity"] = object->getPhysicsGravity();
|
|
physics["Friction"] = object->getPhysicsFriction();
|
|
physics["Density"] = object->getPhysicsDensity();
|
|
physics["Restitution"] = object->getPhysicsRestitution();
|
|
}
|
|
|
|
// Click action
|
|
if (S32 action = object->getClickAction()) // Non-zero
|
|
prim_llsd["clickaction"] = action;
|
|
|
|
// Prim material
|
|
prim_llsd["material"] = object->getMaterial();
|
|
|
|
// Volume params
|
|
LLVolumeParams params = object->getVolume()->getParams();
|
|
prim_llsd["volume"] = params.asLLSD();
|
|
|
|
// 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 image
|
|
LLLightImageParams* light_param = (LLLightImageParams*)object->getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
|
|
t_id = validateTextureID(light_param->getLightTexture());
|
|
if (mTexturesList.count(t_id) == 0)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a light texture, adding to list " << t_id << LL_ENDL;
|
|
mTexturesList.insert(t_id);
|
|
}
|
|
prim_llsd["light_texture"] = light_param->asLLSD();
|
|
}
|
|
|
|
if (object->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT))
|
|
{
|
|
// Sculpt
|
|
LLSculptParams* sculpt = (LLSculptParams*)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
|
|
prim_llsd["sculpt"] = sculpt->asLLSD();
|
|
if ((sculpt->getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
|
|
{
|
|
LLUUID sculpt_texture = sculpt->getSculptTexture();
|
|
if (sculpt_texture == validateTextureID(sculpt_texture))
|
|
{
|
|
if (mTexturesList.count(sculpt_texture) == 0)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a sculpt texture, adding to list " << sculpt_texture << LL_ENDL;
|
|
mTexturesList.insert(sculpt_texture);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ObjectBackup") << "Incorrect permission to export a sculpt texture." << LL_ENDL;
|
|
getInstance()->mExportState = EXPORT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Textures and materials
|
|
LLSD te_llsd;
|
|
LLSD this_te_llsd;
|
|
LLSD te_mat_llsd;
|
|
LLSD this_te_mat_llsd;
|
|
bool has_materials = false;
|
|
for (U8 i = 0, count = object->getNumTEs(); i < count; ++i)
|
|
{
|
|
LLTextureEntry* te = object->getTE(i);
|
|
if (!te) continue; // Paranoia
|
|
|
|
// Normal texture/diffuse map
|
|
t_id = validateTextureID(te->getID());
|
|
this_te_llsd = te->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)
|
|
{
|
|
if (mTexturesList.count(t_id) == 0)
|
|
{
|
|
mTexturesList.insert(t_id);
|
|
}
|
|
}
|
|
|
|
// Materials
|
|
LLMaterial* mat = te->getMaterialParams().get();
|
|
if (mat)
|
|
{
|
|
has_materials = true;
|
|
this_te_mat_llsd = mat->asLLSD();
|
|
|
|
t_id = validateTextureID(mat->getNormalID());
|
|
this_te_mat_llsd["NormMap"] = t_id;
|
|
if (mTexturesList.count(t_id) == 0)
|
|
{
|
|
mTexturesList.insert(t_id);
|
|
}
|
|
|
|
t_id = validateTextureID(mat->getSpecularID());
|
|
this_te_mat_llsd["SpecMap"] = t_id;
|
|
if (mTexturesList.count(t_id) == 0)
|
|
{
|
|
mTexturesList.insert(t_id);
|
|
}
|
|
|
|
te_mat_llsd.append(this_te_mat_llsd);
|
|
}
|
|
}
|
|
prim_llsd["textures"] = te_llsd;
|
|
if (has_materials)
|
|
{
|
|
prim_llsd["materials"] = te_mat_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()
|
|
{
|
|
LLUUID id;
|
|
textures_set_t::iterator iter = mTexturesList.begin();
|
|
while (true)
|
|
{
|
|
if (mTexturesList.empty())
|
|
{
|
|
mCheckNextTexture = true;
|
|
LL_INFOS("ObjectBackup") << "Finished exporting textures." << LL_ENDL;
|
|
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)
|
|
{
|
|
if (imagep->getDiscardLevel() > 0)
|
|
{
|
|
// Boost texture loading
|
|
imagep->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
|
|
LL_DEBUGS("ObjectBackup") << "Boosting texture: " << id
|
|
<< LL_ENDL;
|
|
LLViewerFetchedTexture* tex;
|
|
tex = LLViewerTextureManager::staticCastToFetchedTexture(imagep);
|
|
if (tex && tex->getDesiredDiscardLevel() > 0)
|
|
{
|
|
// Set min discard level to 0
|
|
tex->setMinDiscardLevel(0);
|
|
LL_DEBUGS("ObjectBackup") << "Min discard level set to 0 for texture: "
|
|
<< id << LL_ENDL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Texture is ready !
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ObjectBackup") << "We *DON'T* have the texture " << id.asString() << LL_ENDL;
|
|
mNonExportedTextures |= TEXTURE_MISSING;
|
|
mTexturesList.erase(id);
|
|
}
|
|
}
|
|
|
|
mTexturesList.erase(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)
|
|
{
|
|
mRetexture = upload;
|
|
mTexturesList.clear();
|
|
mAssetMap.clear();
|
|
mCurrentAsset.setNull();
|
|
|
|
mGotExtraPhysics = !gAgent.getRegion()->getCapability("GetObjectPhysicsData").empty();
|
|
|
|
setDefaultTextures();
|
|
|
|
// Open the file open dialog
|
|
AIFilePicker* filepicker = AIFilePicker::create();
|
|
filepicker->open(FFLOAD_XML, "", "import");
|
|
filepicker->run(boost::bind(&LLObjectBackup::importObject_continued, this, filepicker));
|
|
}
|
|
|
|
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();
|
|
if (!mLLSD.has("data"))
|
|
{
|
|
LLNotificationsUtil::add("InvalidObjectParams");
|
|
close();
|
|
return;
|
|
}
|
|
|
|
show(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();
|
|
|
|
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)
|
|
{
|
|
LLUUID orig = sculpt.getSculptTexture();
|
|
if (mTexturesList.count(orig) == 0)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a new SCULPT texture to upload " << orig << LL_ENDL;
|
|
mTexturesList.insert(orig);
|
|
}
|
|
}
|
|
}
|
|
|
|
LLSD& te_llsd = prim_llsd.has("textures") ? prim_llsd["textures"] : prim_llsd["texture"]; // Firestorm's format uses singular "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();
|
|
// Do not upload the default textures
|
|
if (t_id != LL_TEXTURE_PLYWOOD && t_id != LL_TEXTURE_BLANK && t_id != LL_TEXTURE_INVISIBLE) // Do not upload the default textures
|
|
{
|
|
if (mTexturesList.count(t_id) == 0)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a new texture to upload: "<< t_id << LL_ENDL;
|
|
mTexturesList.insert(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);
|
|
|
|
LLUUID t_id = mat.getNormalID();
|
|
if (t_id.notNull() && t_id != LL_TEXTURE_PLYWOOD &&
|
|
t_id != LL_TEXTURE_BLANK &&
|
|
t_id != LL_TEXTURE_INVISIBLE)
|
|
{
|
|
if (mTexturesList.count(t_id) == 0)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a new normal map to upload: "
|
|
<< t_id << LL_ENDL;
|
|
mTexturesList.insert(t_id);
|
|
}
|
|
}
|
|
|
|
t_id = mat.getSpecularID();
|
|
if (t_id.notNull() && t_id != LL_TEXTURE_PLYWOOD &&
|
|
t_id != LL_TEXTURE_BLANK &&
|
|
t_id != LL_TEXTURE_INVISIBLE)
|
|
{
|
|
if (mTexturesList.count(t_id) == 0)
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Found a new specular map to upload: "
|
|
<< t_id << LL_ENDL;
|
|
mTexturesList.insert(t_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mRetexture)
|
|
{
|
|
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);
|
|
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 // Kept for backward compatibility
|
|
{
|
|
/*if (prim_llsd.has("shadows"))
|
|
if (prim_llsd["shadows"].asInteger() == 1)
|
|
object->setFlags(FLAGS_CAST_SHADOWS, true);*/
|
|
|
|
if (prim_llsd.has("phantom") && prim_llsd["phantom"].asInteger() == 1)
|
|
{
|
|
object->setFlags(FLAGS_PHANTOM, true);
|
|
}
|
|
|
|
if (prim_llsd.has("physical") &&
|
|
prim_llsd["physical"].asInteger() == 1)
|
|
{
|
|
object->setFlags(FLAGS_USE_PHYSICS, true);
|
|
}
|
|
}
|
|
|
|
if (mGotExtraPhysics && prim_llsd.has("ExtraPhysics"))
|
|
{
|
|
const LLSD& physics = prim_llsd["ExtraPhysics"];
|
|
object->setPhysicsShapeType(physics["PhysicsShapeType"].asInteger());
|
|
F32 gravity = physics.has("Gravity") ? physics["Gravity"].asReal()
|
|
: physics["GravityMultiplier"].asReal();
|
|
object->setPhysicsGravity(gravity);
|
|
object->setPhysicsFriction(physics["Friction"].asReal());
|
|
object->setPhysicsDensity(physics["Density"].asReal());
|
|
object->setPhysicsRestitution(physics["Restitution"].asReal());
|
|
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.count(sculpt.getSculptTexture()))
|
|
{
|
|
LLUUID replacement = mAssetMap[sculpt.getSculptTexture()];
|
|
sculpt.setSculptTexture(replacement);
|
|
}
|
|
|
|
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 image
|
|
LLLightImageParams lightimg;
|
|
lightimg.fromLLSD(prim_llsd["light_texture"]);
|
|
object->setParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE, lightimg, 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" << id << LL_ENDL;
|
|
LLSD& te_llsd = prim_llsd.has("textures") ? prim_llsd["textures"] : prim_llsd["texture"]; // Firestorm's format uses singular "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);
|
|
LLUUID t_id = te.getID();
|
|
if (mAssetMap.count(t_id))
|
|
{
|
|
LLUUID replacement = mAssetMap[t_id];
|
|
te.setID(replacement);
|
|
}
|
|
|
|
object->setTE(i++, te);
|
|
}
|
|
LL_INFOS("ObjectBackup") << "Textures done !" << LL_ENDL;
|
|
|
|
// Materials
|
|
if (prim_llsd.has("materials"))
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Processing materials for prim " << id << LL_ENDL;
|
|
te_llsd = prim_llsd["materials"];
|
|
i = 0;
|
|
for (LLSD::array_iterator it = te_llsd.beginArray();
|
|
it != te_llsd.endArray(); ++it)
|
|
{
|
|
LLSD the_mat = *it;
|
|
LLMaterialPtr mat = new LLMaterial(the_mat);
|
|
|
|
LLUUID t_id = mat->getNormalID();
|
|
if (id.notNull() && mAssetMap.count(t_id))
|
|
{
|
|
LLUUID replacement = mAssetMap[t_id];
|
|
mat->setNormalID(replacement);
|
|
}
|
|
|
|
t_id = mat->getSpecularID();
|
|
if (id.notNull() && mAssetMap.count(t_id))
|
|
{
|
|
LLUUID replacement = mAssetMap[t_id];
|
|
mat->setSpecularID(replacement);
|
|
}
|
|
|
|
LLMaterialMgr::getInstance()->put(id, i++, *mat);
|
|
}
|
|
LL_INFOS("ObjectBackup") << "Materials done !" << LL_ENDL;
|
|
}
|
|
|
|
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 || (object != NULL && 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;
|
|
mRunning = false;
|
|
close();
|
|
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.notNull())
|
|
{
|
|
LL_INFOS("ObjectBackup") << "Mapping " << mCurrentAsset << " to " << uploaded_asset << LL_ENDL;
|
|
|
|
mAssetMap[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 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.setNull();
|
|
importFirstObject();
|
|
return;
|
|
}
|
|
|
|
updateImportNumbers();
|
|
|
|
textures_set_t::iterator iter = mTexturesList.begin();
|
|
LLUUID id = *iter;
|
|
mTexturesList.erase(iter);
|
|
|
|
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);
|
|
}
|