1297 lines
45 KiB
C++
1297 lines
45 KiB
C++
/**
|
|
* @file fsfloaterexport.cpp
|
|
* @brief export selected objects to an file using LLSD.
|
|
*
|
|
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
|
|
* Phoenix Firestorm Viewer Source Code
|
|
* Copyright (c) 2013 Techwolf Lupindo
|
|
* Copyright (c) 2013 Cinder Roxley <cinder.roxley@phoenixviewer.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
|
|
* http://www.firestormviewer.org
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "fsfloaterexport.h"
|
|
|
|
#include "lfsimfeaturehandler.h"
|
|
#include "llagent.h"
|
|
#include "llagentdata.h"
|
|
#include "llavatarnamecache.h"
|
|
#include "llbufferstream.h"
|
|
#include "llcallbacklist.h"
|
|
#include "lldatapacker.h"
|
|
#include "lldir.h"
|
|
#include "llfilepicker.h"
|
|
#include "llimagej2c.h"
|
|
#include "llinventoryfunctions.h"
|
|
#include "llmultigesture.h"
|
|
#include "llnotecard.h"
|
|
#include "llnotificationsutil.h"
|
|
#include "llscrollcontainer.h"
|
|
#include "llsdserialize.h"
|
|
#include "llsdutil_math.h"
|
|
#include "llsdutil.h"
|
|
#include "lltexturectrl.h"
|
|
#include "lltrans.h"
|
|
#include "llversioninfo.h"
|
|
#include "llfilesystem.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llviewerinventory.h"
|
|
#include "llviewermenufile.h"
|
|
#include "llviewernetwork.h"
|
|
#include "llviewerpartsource.h"
|
|
#include "llviewerregion.h"
|
|
#include "llviewertexturelist.h"
|
|
#include "llvovolume.h"
|
|
#include "fsexportperms.h"
|
|
|
|
#include "llfloaterreg.h"
|
|
#include "llappviewer.h"
|
|
#include "fscommon.h"
|
|
|
|
#include <boost/algorithm/string_regex.hpp>
|
|
|
|
constexpr F32 MAX_TEXTURE_WAIT_TIME = 30.0f;
|
|
constexpr F32 MAX_INVENTORY_WAIT_TIME = 30.0f;
|
|
constexpr F32 MAX_ASSET_WAIT_TIME = 60.0f;
|
|
|
|
// static
|
|
void FSFloaterObjectExport::onIdle(void* user_data)
|
|
{
|
|
FSFloaterObjectExport* self = (FSFloaterObjectExport*)user_data;
|
|
self->onIdle();
|
|
}
|
|
|
|
void FSFloaterObjectExport::onIdle()
|
|
{
|
|
switch(mExportState)
|
|
{
|
|
case IDLE:
|
|
break;
|
|
case INVENTORY_DOWNLOAD:
|
|
if (gDisconnected)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mInventoryRequests.empty())
|
|
{
|
|
mLastRequest = mAssetRequests.size();
|
|
mWaitTimer.start();
|
|
mExportState = ASSET_DOWNLOAD;
|
|
}
|
|
else if (mLastRequest != mInventoryRequests.size())
|
|
{
|
|
mWaitTimer.start();
|
|
mLastRequest = mInventoryRequests.size();
|
|
updateTitleProgress(INVENTORY_DOWNLOAD);
|
|
}
|
|
else if (mWaitTimer.getElapsedTimeF32() > MAX_INVENTORY_WAIT_TIME)
|
|
{
|
|
mWaitTimer.start();
|
|
for (uuid_vec_t::const_iterator iter = mInventoryRequests.begin(); iter != mInventoryRequests.end(); ++iter)
|
|
{
|
|
LLViewerObject* object = gObjectList.findObject((*iter));
|
|
object->dirtyInventory();
|
|
object->requestInventory();
|
|
|
|
LL_DEBUGS("export") << "re-requested inventory of " << (*iter).asString() << LL_ENDL;
|
|
}
|
|
}
|
|
break;
|
|
case ASSET_DOWNLOAD:
|
|
if (gDisconnected)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mAssetRequests.empty())
|
|
{
|
|
mLastRequest = mRequestedTexture.size();
|
|
mWaitTimer.start();
|
|
mExportState = TEXTURE_DOWNLOAD;
|
|
}
|
|
else if (mLastRequest != mAssetRequests.size())
|
|
{
|
|
mWaitTimer.start();
|
|
mLastRequest = mAssetRequests.size();
|
|
updateTitleProgress(ASSET_DOWNLOAD);
|
|
}
|
|
else if (mWaitTimer.getElapsedTimeF32() > MAX_ASSET_WAIT_TIME)
|
|
{
|
|
//abort for now
|
|
LL_DEBUGS("export") << "Asset timeout with " << (S32)mAssetRequests.size() << " requests left." << LL_ENDL;
|
|
for (uuid_vec_t::iterator iter = mAssetRequests.begin(); iter != mAssetRequests.end(); ++iter)
|
|
{
|
|
LL_DEBUGS("export") << "Asset: " << (*iter).asString() << LL_ENDL;
|
|
}
|
|
mAssetRequests.clear();
|
|
}
|
|
break;
|
|
case TEXTURE_DOWNLOAD:
|
|
if (gDisconnected)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(mRequestedTexture.empty())
|
|
{
|
|
mExportState = IDLE;
|
|
if (!gIdleCallbacks.deleteFunction(onIdle, this))
|
|
{
|
|
LL_WARNS("export") << "Failed to delete idle callback" << LL_ENDL;
|
|
}
|
|
mWaitTimer.stop();
|
|
|
|
llofstream file;
|
|
file.open(mFilename.c_str(), std::ios_base::out | std::ios_base::binary);
|
|
std::string zip_data = zip_llsd(mManifest);
|
|
file.write(zip_data.data(), zip_data.size());
|
|
file.close();
|
|
LL_DEBUGS("export") << "Export finished and written to " << mFilename << LL_ENDL;
|
|
|
|
LLSD args;
|
|
args["FILENAME"] = mFilename;
|
|
LLNotificationsUtil::add("ExportFinished", args);
|
|
closeFloater();
|
|
}
|
|
else if (mLastRequest != mRequestedTexture.size())
|
|
{
|
|
mWaitTimer.start();
|
|
mLastRequest = mRequestedTexture.size();
|
|
updateTitleProgress(TEXTURE_DOWNLOAD);
|
|
}
|
|
else if (mWaitTimer.getElapsedTimeF32() > MAX_TEXTURE_WAIT_TIME)
|
|
{
|
|
mWaitTimer.start();
|
|
for (std::map<LLUUID, FSAssetResourceData>::iterator iter = mRequestedTexture.begin(); iter != mRequestedTexture.end(); ++iter)
|
|
{
|
|
LLUUID texture_id = iter->first;
|
|
LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(texture_id, FTT_DEFAULT, MIPMAP_TRUE);
|
|
image->setBoostLevel(LLViewerTexture::BOOST_MAX_LEVEL);
|
|
image->forceToSaveRawImage(0);
|
|
image->setLoadedCallback(FSFloaterObjectExport::onImageLoaded, 0, true, false, this, &mCallbackTextureList);
|
|
|
|
LL_DEBUGS("export") << "re-requested texture " << texture_id.asString() << LL_ENDL;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
FSFloaterObjectExport::FSFloaterObjectExport(const LLSD& key)
|
|
: LLFloater(key),
|
|
mCurrentObjectID(NULL),
|
|
mDirty(true)
|
|
{
|
|
}
|
|
|
|
FSFloaterObjectExport::~FSFloaterObjectExport()
|
|
{
|
|
if (gIdleCallbacks.containsFunction(FSFloaterObjectExport::onIdle, this))
|
|
{
|
|
gIdleCallbacks.deleteFunction(FSFloaterObjectExport::onIdle, this);
|
|
}
|
|
|
|
for (const auto& object_id : mInventoryRequests)
|
|
{
|
|
LLViewerObject* object = gObjectList.findObject(object_id);
|
|
if (object)
|
|
{
|
|
object->removeInventoryListener(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FSFloaterObjectExport::postBuild()
|
|
{
|
|
mObjectList = getChild<LLScrollListCtrl>("selected_objects");
|
|
mTexturePanel = getChild<LLPanel>("textures_panel");
|
|
childSetAction("export_btn", boost::bind(&FSFloaterObjectExport::onClickExport, this));
|
|
|
|
LLSelectMgr::getInstance()->mUpdateSignal.connect(boost::bind(&FSFloaterObjectExport::updateSelection, this));
|
|
|
|
return true;
|
|
}
|
|
|
|
void FSFloaterObjectExport::draw()
|
|
{
|
|
if (mDirty)
|
|
{
|
|
refresh();
|
|
mDirty = false;
|
|
}
|
|
LLFloater::draw();
|
|
}
|
|
|
|
void FSFloaterObjectExport::refresh()
|
|
{
|
|
addSelectedObjects();
|
|
addTexturePreview();
|
|
populateObjectList();
|
|
updateUI();
|
|
}
|
|
|
|
void FSFloaterObjectExport::dirty()
|
|
{
|
|
mDirty = true;
|
|
}
|
|
|
|
void FSFloaterObjectExport::onOpen(const LLSD& key)
|
|
{
|
|
LLObjectSelectionHandle object_selection = LLSelectMgr::getInstance()->getSelection();
|
|
if(!(object_selection->getPrimaryObject()))
|
|
{
|
|
closeFloater();
|
|
return;
|
|
}
|
|
mObjectSelection = LLSelectMgr::getInstance()->getEditSelection();
|
|
refresh();
|
|
}
|
|
|
|
void FSFloaterObjectExport::updateSelection()
|
|
{
|
|
LLObjectSelectionHandle object_selection = LLSelectMgr::getInstance()->getSelection();
|
|
LLSelectNode* node = object_selection->getFirstRootNode();
|
|
|
|
if (node && !node->mValid && node->getObject()->getID() == mCurrentObjectID)
|
|
{
|
|
return;
|
|
}
|
|
|
|
mObjectSelection = object_selection;
|
|
dirty();
|
|
refresh();
|
|
}
|
|
|
|
bool FSFloaterObjectExport::exportSelection()
|
|
{
|
|
if (!mObjectSelection)
|
|
{
|
|
LL_WARNS("export") << "Nothing selected; Bailing!" << LL_ENDL;
|
|
return false;
|
|
}
|
|
LLObjectSelection::valid_root_iterator iter = mObjectSelection->valid_root_begin();
|
|
LLSelectNode* node = *iter;
|
|
if (!node)
|
|
{
|
|
LL_WARNS("export") << "No node selected; Bailing!" << LL_ENDL;
|
|
return false;
|
|
}
|
|
mFilePath = gDirUtilp->getDirName(mFilename);
|
|
|
|
mManifest.clear();
|
|
mRequestedTexture.clear();
|
|
|
|
mExported = false;
|
|
mAborted = false;
|
|
mInventoryRequests.clear();
|
|
mAssetRequests.clear();
|
|
mTextureChecked.clear();
|
|
|
|
std::string author = "Unknown";
|
|
if (!gAgentUsername.empty())
|
|
author = gAgentUsername;
|
|
|
|
time_t rawtime;
|
|
time(&rawtime);
|
|
struct tm* utc_time = gmtime(&rawtime);
|
|
std::string date = llformat("%04d-%02d-%02d", utc_time->tm_year + 1900, utc_time->tm_mon + 1, utc_time->tm_mday);
|
|
mManifest["format_version"] = OXP_FORMAT_VERSION;
|
|
mManifest["client"] = LLVersionInfo::getInstance()->getChannelAndVersion();
|
|
mManifest["creation_date"] = date;
|
|
mManifest["author"] = author;
|
|
mManifest["grid"] = LLGridManager::getInstance()->getGridLabel();
|
|
|
|
for ( ; iter != mObjectSelection->valid_root_end(); ++iter)
|
|
{
|
|
mManifest["linkset"].append(getLinkSet((*iter)));
|
|
}
|
|
|
|
if (mExported && !mAborted)
|
|
{
|
|
mWaitTimer.start();
|
|
mLastRequest = mInventoryRequests.size();
|
|
mExportState = INVENTORY_DOWNLOAD;
|
|
gIdleCallbacks.addFunction(onIdle, this);
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("export") << "Nothing was exported. File not created." << LL_ENDL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
LLSD FSFloaterObjectExport::getLinkSet(LLSelectNode* node)
|
|
{
|
|
LLSD linkset;
|
|
LLViewerObject* object = node->getObject();
|
|
LLUUID object_id = object->getID();
|
|
|
|
// root prim
|
|
linkset.append(object_id);
|
|
addPrim(object, true);
|
|
|
|
// child prims
|
|
LLViewerObject::const_child_list_t& child_list = object->getChildren();
|
|
for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
|
|
iter != child_list.end(); ++iter)
|
|
{
|
|
LLViewerObject* child = *iter;
|
|
linkset.append(child->getID());
|
|
addPrim(child, false);
|
|
}
|
|
|
|
return linkset;
|
|
}
|
|
|
|
void FSFloaterObjectExport::addPrim(LLViewerObject* object, bool root)
|
|
{
|
|
LLSD prim;
|
|
LLUUID object_id = object->getID();
|
|
bool default_prim = true;
|
|
|
|
struct f : public LLSelectedNodeFunctor
|
|
{
|
|
LLUUID mID;
|
|
f(const LLUUID& id) : mID(id) {}
|
|
virtual bool apply(LLSelectNode* node)
|
|
{
|
|
return (node->getObject() && node->getObject()->mID == mID);
|
|
}
|
|
} func(object_id);
|
|
|
|
LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(&func);
|
|
default_prim = (!FSExportPermsCheck::canExportNode(node, false));
|
|
|
|
if (root)
|
|
{
|
|
if (object->isAttachment())
|
|
{
|
|
prim["attachment_point"] = ATTACHMENT_ID_FROM_STATE(object->getAttachmentState());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLViewerObject* parent_object = (LLViewerObject*)object->getParent();
|
|
prim["parent"] = parent_object->getID();
|
|
}
|
|
prim["position"] = object->getPosition().getValue();
|
|
prim["scale"] = object->getScale().getValue();
|
|
prim["rotation"] = ll_sd_from_quaternion(object->getRotation());
|
|
|
|
if (default_prim)
|
|
{
|
|
LL_DEBUGS("export") << object_id.asString() << " failed export check. Using default prim" << LL_ENDL;
|
|
prim["flags"] = ll_sd_from_U32((U32)0);
|
|
prim["volume"]["path"] = LLPathParams().asLLSD();
|
|
prim["volume"]["profile"] = LLProfileParams().asLLSD();
|
|
prim["material"] = (S32)LL_MCODE_WOOD;
|
|
}
|
|
else
|
|
{
|
|
mExported = true;
|
|
prim["flags"] = ll_sd_from_U32(object->getFlags());
|
|
prim["volume"]["path"] = object->getVolume()->getParams().getPathParams().asLLSD();
|
|
prim["volume"]["profile"] = object->getVolume()->getParams().getProfileParams().asLLSD();
|
|
prim["material"] = (S32)object->getMaterial();
|
|
if (object->getClickAction() != 0)
|
|
{
|
|
prim["clickaction"] = (S32)object->getClickAction();
|
|
}
|
|
|
|
LLVOVolume *volobjp = NULL;
|
|
if (object->getPCode() == LL_PCODE_VOLUME)
|
|
{
|
|
volobjp = (LLVOVolume *)object;
|
|
}
|
|
if(volobjp)
|
|
{
|
|
if(volobjp->isSculpted())
|
|
{
|
|
const LLSculptParams *sculpt_params = (const LLSculptParams *)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
|
|
if (sculpt_params)
|
|
{
|
|
if(volobjp->isMesh())
|
|
{
|
|
if (!mAborted)
|
|
{
|
|
mAborted = true;
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (exportTexture(sculpt_params->getSculptTexture()))
|
|
{
|
|
prim["sculpt"] = sculpt_params->asLLSD();
|
|
}
|
|
else
|
|
{
|
|
LLSculptParams default_sculpt;
|
|
prim["sculpt"] = default_sculpt.asLLSD();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(volobjp->isFlexible())
|
|
{
|
|
const LLFlexibleObjectData *flexible_param_block = (const LLFlexibleObjectData *)object->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
|
|
if (flexible_param_block)
|
|
{
|
|
prim["flexible"] = flexible_param_block->asLLSD();
|
|
}
|
|
}
|
|
|
|
if (volobjp->getIsLight())
|
|
{
|
|
const LLLightParams *light_param_block = (const LLLightParams *)object->getParameterEntry(LLNetworkData::PARAMS_LIGHT);
|
|
if (light_param_block)
|
|
{
|
|
prim["light"] = light_param_block->asLLSD();
|
|
}
|
|
}
|
|
|
|
if (volobjp->hasLightTexture())
|
|
{
|
|
const LLLightImageParams* light_image_param_block = (const LLLightImageParams*)object->getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
|
|
if (light_image_param_block)
|
|
{
|
|
prim["light_texture"] = light_image_param_block->asLLSD();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if(object->isParticleSource())
|
|
{
|
|
LLViewerPartSourceScript* partSourceScript = object->getPartSourceScript();
|
|
prim["particle"] = partSourceScript->mPartSysData.asLLSD();
|
|
if (!exportTexture(partSourceScript->mPartSysData.mPartImageID))
|
|
{
|
|
prim["particle"]["PartImageID"] = LLUUID::null.asString();
|
|
}
|
|
}
|
|
|
|
U8 texture_count = object->getNumTEs();
|
|
for(U8 i = 0; i < texture_count; ++i)
|
|
{
|
|
LLTextureEntry *checkTE = object->getTE(i);
|
|
LL_DEBUGS("export") << "Checking texture number " << (S32)i
|
|
<< ", ID " << checkTE->getID() << LL_ENDL;
|
|
if (FSCommon::isDefaultTexture(checkTE->getID())) // <FS:CR> Check against default textures
|
|
{
|
|
LL_DEBUGS("export") << "...is a default texture." << LL_ENDL;
|
|
prim["texture"].append(checkTE->asLLSD());
|
|
}
|
|
else if (exportTexture(checkTE->getID()))
|
|
{
|
|
LL_DEBUGS("export") << "...export check passed." << LL_ENDL;
|
|
prim["texture"].append(checkTE->asLLSD());
|
|
}
|
|
else
|
|
{
|
|
LL_DEBUGS("export") << "...export check failed." << LL_ENDL;
|
|
checkTE->setID(LL_DEFAULT_WOOD_UUID); // TODO: use user option of default texture.
|
|
prim["texture"].append(checkTE->asLLSD());
|
|
}
|
|
|
|
// [FS:CR] Materials support
|
|
if (checkTE->getMaterialParams().notNull())
|
|
{
|
|
LL_DEBUGS("export") << "found materials. Checking permissions..." << LL_ENDL;
|
|
LLSD params = checkTE->getMaterialParams().get()->asLLSD();
|
|
/// *TODO: Feeling lazy so I made it check both. This is incorrect and needs to be expanded
|
|
/// to retain exportable textures not just failing both when one is non-exportable (or unset).
|
|
if (exportTexture(params["NormMap"].asUUID()) &&
|
|
exportTexture(params["SpecMap"].asUUID()))
|
|
{
|
|
LL_DEBUGS("export") << "...passed check." << LL_ENDL;
|
|
prim["materials"].append(params);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!object->getPhysicsShapeUnknown())
|
|
{
|
|
prim["ExtraPhysics"]["PhysicsShapeType"] = (S32)object->getPhysicsShapeType();
|
|
prim["ExtraPhysics"]["Density"] = (F64)object->getPhysicsDensity();
|
|
prim["ExtraPhysics"]["Friction"] = (F64)object->getPhysicsFriction();
|
|
prim["ExtraPhysics"]["Restitution"] = (F64)object->getPhysicsRestitution();
|
|
prim["ExtraPhysics"]["GravityMultiplier"] = (F64)object->getPhysicsGravity();
|
|
}
|
|
|
|
prim["name"] = node->mName;
|
|
prim["description"] = node->mDescription;
|
|
prim["creation_date"] = ll_sd_from_U64(node->mCreationDate);
|
|
|
|
LLAvatarName avatar_name;
|
|
LLUUID creator_id = node->mPermissions->getCreator();
|
|
if (creator_id.notNull())
|
|
{
|
|
prim["creator_id"] = creator_id;
|
|
if (LLAvatarNameCache::get(creator_id, &avatar_name))
|
|
{
|
|
prim["creator_name"] = avatar_name.asLLSD();
|
|
}
|
|
}
|
|
LLUUID owner_id = node->mPermissions->getOwner();
|
|
if (owner_id.notNull())
|
|
{
|
|
prim["owner_id"] = owner_id;
|
|
if (LLAvatarNameCache::get(owner_id, &avatar_name))
|
|
{
|
|
prim["owner_name"] = avatar_name.asLLSD();
|
|
}
|
|
}
|
|
LLUUID group_id = node->mPermissions->getGroup();
|
|
if (group_id.notNull())
|
|
{
|
|
prim["group_id"] = group_id;
|
|
if (LLAvatarNameCache::get(group_id, &avatar_name))
|
|
{
|
|
prim["group_name"] = avatar_name.asLLSD();
|
|
}
|
|
}
|
|
LLUUID last_owner_id = node->mPermissions->getLastOwner();
|
|
if (last_owner_id.notNull())
|
|
{
|
|
prim["last_owner_id"] = last_owner_id;
|
|
if (LLAvatarNameCache::get(last_owner_id, &avatar_name))
|
|
{
|
|
prim["last_owner_name"] = avatar_name.asLLSD();
|
|
}
|
|
}
|
|
prim["base_mask"] = ll_sd_from_U32(node->mPermissions->getMaskBase());
|
|
prim["owner_mask"] = ll_sd_from_U32(node->mPermissions->getMaskOwner());
|
|
prim["group_mask"] = ll_sd_from_U32(node->mPermissions->getMaskGroup());
|
|
prim["everyone_mask"] = ll_sd_from_U32(node->mPermissions->getMaskEveryone());
|
|
prim["next_owner_mask"] = ll_sd_from_U32(node->mPermissions->getMaskNextOwner());
|
|
|
|
prim["sale_info"] = node->mSaleInfo.asLLSD();
|
|
prim["touch_name"] = node->mTouchName;
|
|
prim["sit_name"] = node->mSitName;
|
|
|
|
static LLCachedControl<bool> sExportContents(gSavedSettings, "FSExportContents");
|
|
if (sExportContents)
|
|
{
|
|
mInventoryRequests.push_back(object_id);
|
|
object->registerInventoryListener(this, NULL);
|
|
object->dirtyInventory();
|
|
object->requestInventory();
|
|
}
|
|
}
|
|
|
|
mManifest["prim"][object_id.asString()] = prim;
|
|
}
|
|
|
|
bool FSFloaterObjectExport::exportTexture(const LLUUID& texture_id)
|
|
{
|
|
if(texture_id.isNull())
|
|
{
|
|
LL_WARNS("export") << "Attempted to export NULL texture." << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
if (mTextureChecked.count(texture_id) != 0)
|
|
{
|
|
return mTextureChecked[texture_id];
|
|
}
|
|
|
|
if (gAssetStorage->hasLocalAsset(texture_id, LLAssetType::AT_TEXTURE))
|
|
{
|
|
LL_DEBUGS("export") << "Texture " << texture_id.asString() << " is local static." << LL_ENDL;
|
|
// no need to save the texture data as the viewer already has it in a local file.
|
|
mTextureChecked[texture_id] = true;
|
|
return true;
|
|
}
|
|
|
|
//TODO: check for local file static texture. The above will only get the static texture in the static db, not individual textures.
|
|
|
|
LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(texture_id);
|
|
bool texture_export = false;
|
|
std::string name;
|
|
std::string description;
|
|
|
|
if (LLGridManager::getInstance()->isInSecondLife())
|
|
{
|
|
if (imagep->mComment.find("a") != imagep->mComment.end())
|
|
{
|
|
if (LLUUID(imagep->mComment["a"]) == gAgentID)
|
|
{
|
|
texture_export = true;
|
|
LL_DEBUGS("export") << texture_id << " pass texture export comment check." << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (texture_export)
|
|
{
|
|
FSExportPermsCheck::canExportAsset(texture_id, &name, &description);
|
|
}
|
|
else
|
|
{
|
|
texture_export = FSExportPermsCheck::canExportAsset(texture_id, &name, &description);
|
|
}
|
|
|
|
mTextureChecked[texture_id] = texture_export;
|
|
|
|
if (!texture_export)
|
|
{
|
|
LL_DEBUGS("export") << "Texture " << texture_id << " failed export check." << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
LL_DEBUGS("export") << "Loading image texture " << texture_id << LL_ENDL;
|
|
mRequestedTexture[texture_id].name = name;
|
|
mRequestedTexture[texture_id].description = description;
|
|
LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(texture_id, FTT_DEFAULT, MIPMAP_TRUE);
|
|
image->setBoostLevel(LLViewerTexture::BOOST_MAX_LEVEL);
|
|
image->forceToSaveRawImage(0);
|
|
image->setLoadedCallback(FSFloaterObjectExport::onImageLoaded, 0, true, false, this, &mCallbackTextureList);
|
|
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
void FSFloaterObjectExport::onImageLoaded(bool success, LLViewerFetchedTexture* src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata)
|
|
{
|
|
if(final && success)
|
|
{
|
|
// *HACK ALERT: I'm lazy so I moved this to a non-static member function. <FS:CR>
|
|
FSFloaterObjectExport* parent = (FSFloaterObjectExport *)userdata;
|
|
parent->fetchTextureFromCache(src_vi);
|
|
}
|
|
}
|
|
|
|
void FSFloaterObjectExport::fetchTextureFromCache(LLViewerFetchedTexture* src_vi)
|
|
{
|
|
const LLUUID& texture_id = src_vi->getID();
|
|
LLImageJ2C* mFormattedImage = new LLImageJ2C;
|
|
FSFloaterObjectExport::FSExportCacheReadResponder* responder = new FSFloaterObjectExport::FSExportCacheReadResponder(texture_id, mFormattedImage, this);
|
|
// <FS:minerjr> [FIRE-35292] Fix for textures getting downscaled and compressed
|
|
//LLAppViewer::getTextureCache()->readFromCache(id, 0, 999999, responder);
|
|
// The above line hard coded the size of data to read from the cached version of the texture as 999999,
|
|
// where now we will calcuate the correct value based upon the texture's full width, height and # of components (3=RGB, 4=RGBA) and
|
|
// the discard level (0)). There is a choice to change the rate, but we seem to use the value of 1/8 compression level
|
|
S32 texture_size = LLImageJ2C::calcDataSizeJ2C(src_vi->getFullWidth(), src_vi->getFullHeight(), src_vi->getComponents(), 0);// , F32 rate) rate = const F32 DEFAULT_COMPRESSION_RATE = 1.f/8.f;
|
|
// Use calculated texture_size (from LLTextureFetch::createRequest see "else if (w*h*c > 0)" statement for more info)
|
|
LLAppViewer::getTextureCache()->readFromCache(texture_id, 0, texture_size, responder);
|
|
LL_DEBUGS("export") << "Fetching " << texture_id << " from the TextureCache" << LL_ENDL;
|
|
}
|
|
|
|
void FSFloaterObjectExport::removeRequestedTexture(LLUUID texture_id)
|
|
{
|
|
if (mRequestedTexture.count(texture_id) != 0)
|
|
{
|
|
mRequestedTexture.erase(texture_id);
|
|
}
|
|
}
|
|
|
|
void FSFloaterObjectExport::saveFormattedImage(LLPointer<LLImageFormatted> mFormattedImage, LLUUID id)
|
|
{
|
|
std::stringstream texture_str;
|
|
texture_str.write((const char*) mFormattedImage->getData(), mFormattedImage->getDataSize());
|
|
std::string str = texture_str.str();
|
|
|
|
mManifest["asset"][id.asString()]["name"] = mRequestedTexture[id].name;
|
|
mManifest["asset"][id.asString()]["description"] = mRequestedTexture[id].description;
|
|
mManifest["asset"][id.asString()]["type"] = LLAssetType::lookup(LLAssetType::AT_TEXTURE);
|
|
mManifest["asset"][id.asString()]["data"] = LLSD::Binary(str.begin(),str.end());
|
|
|
|
removeRequestedTexture(id);
|
|
}
|
|
|
|
void FSFloaterObjectExport::inventoryChanged(LLViewerObject* object, LLInventoryObject::object_list_t* inventory, S32 serial_num, void* user_data)
|
|
{
|
|
uuid_vec_t::iterator v_iter = std::find(mInventoryRequests.begin(), mInventoryRequests.end(), object->getID());
|
|
if (v_iter != mInventoryRequests.end())
|
|
{
|
|
LL_DEBUGS("export") << "Erasing inventory request " << object->getID() << LL_ENDL;
|
|
mInventoryRequests.erase(v_iter);
|
|
}
|
|
|
|
LLSD& prim = mManifest["prim"][object->getID().asString()];
|
|
for (LLInventoryObject::object_list_t::const_iterator iter = inventory->begin(); iter != inventory->end(); ++iter)
|
|
{
|
|
LLInventoryItem* item = dynamic_cast<LLInventoryItem*>(iter->get());
|
|
if (!item)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool exportable = false;
|
|
LLPermissions perms = item->getPermissions();
|
|
#ifdef OPENSIM
|
|
if (LLGridManager::getInstance()->isInOpenSim())
|
|
{
|
|
switch (LFSimFeatureHandler::instance().exportPolicy())
|
|
{
|
|
case EXPORT_ALLOWED:
|
|
exportable = (perms.getMaskOwner() & PERM_EXPORT) == PERM_EXPORT;
|
|
break;
|
|
/// TODO: Once enough grids adopt a version supporting exports, get consensus
|
|
/// on whether we should allow full perm exports anymore.
|
|
case EXPORT_UNDEFINED:
|
|
exportable = (perms.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED;
|
|
break;
|
|
case EXPORT_DENIED:
|
|
default:
|
|
exportable = perms.getCreator() == gAgentID;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
if (LLGridManager::getInstance()->isInSecondLife() && (perms.getCreator() == gAgentID))
|
|
{
|
|
exportable = true;
|
|
}
|
|
|
|
if (!exportable)
|
|
{
|
|
//<FS:TS> Only complain if we're trying to export a non-NULL item and fail
|
|
if (!item->getUUID().isNull())
|
|
{
|
|
LL_DEBUGS("export") << "Item " << item->getName() << ", UUID " << item->getUUID() << " failed export check." << LL_ENDL;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (item->getType() == LLAssetType::AT_NONE || item->getType() == LLAssetType::AT_OBJECT)
|
|
{
|
|
// currentelly not exportable
|
|
LL_DEBUGS("export") << "Skipping " << LLAssetType::lookup(item->getType()) << " item " << item->getName() << LL_ENDL;
|
|
continue;
|
|
}
|
|
|
|
prim["content"].append(item->getUUID());
|
|
mManifest["inventory"][item->getUUID().asString()] = ll_create_sd_from_inventory_item(item);
|
|
|
|
if (item->getAssetUUID().isNull() && item->getType() == LLAssetType::AT_NOTECARD)
|
|
{
|
|
// FIRE-9655
|
|
// Blank Notecard item can have NULL asset ID.
|
|
// Generate a new UUID and save as an empty asset.
|
|
LLUUID asset_uuid = LLUUID::generateNewID();
|
|
mManifest["inventory"][item->getUUID().asString()]["asset_id"] = asset_uuid;
|
|
|
|
mManifest["asset"][asset_uuid.asString()]["name"] = item->getName();
|
|
mManifest["asset"][asset_uuid.asString()]["description"] = item->getDescription();
|
|
mManifest["asset"][asset_uuid.asString()]["type"] = LLAssetType::lookup(item->getType());
|
|
|
|
LLNotecard nc(255); //don't need to allocate default size of 65536
|
|
std::stringstream out_stream;
|
|
nc.exportStream(out_stream);
|
|
std::string out_string = out_stream.str();
|
|
std::vector<U8> buffer(out_string.begin(), out_string.end());
|
|
mManifest["asset"][asset_uuid.asString()]["data"] = buffer;
|
|
}
|
|
else
|
|
{
|
|
LL_DEBUGS("export") << "Requesting asset " << item->getAssetUUID() << " for item " << item->getUUID() << LL_ENDL;
|
|
mAssetRequests.push_back(item->getUUID());
|
|
FSAssetResourceData* data = new FSAssetResourceData;
|
|
data->name = item->getName();
|
|
data->description = item->getDescription();
|
|
data->user_data = this;
|
|
data->uuid = item->getUUID();
|
|
|
|
if (item->getAssetUUID().isNull() || item->getType() == LLAssetType::AT_NOTECARD || item->getType() == LLAssetType::AT_LSL_TEXT)
|
|
{
|
|
gAssetStorage->getInvItemAsset(object->getRegion()->getHost(),
|
|
gAgent.getID(),
|
|
gAgent.getSessionID(),
|
|
item->getPermissions().getOwner(),
|
|
object->getID(),
|
|
item->getUUID(),
|
|
item->getAssetUUID(),
|
|
item->getType(),
|
|
onLoadComplete,
|
|
data,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
gAssetStorage->getAssetData(item->getAssetUUID(),
|
|
item->getType(),
|
|
onLoadComplete,
|
|
data,
|
|
true);
|
|
}
|
|
}
|
|
}
|
|
object->removeInventoryListener(this);
|
|
}
|
|
|
|
// static
|
|
void FSFloaterObjectExport::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status)
|
|
{
|
|
FSAssetResourceData* data = (FSAssetResourceData*)user_data;
|
|
FSFloaterObjectExport* self = (FSFloaterObjectExport*)data->user_data;
|
|
LLUUID item_uuid = data->uuid;
|
|
self->removeRequestedAsset(item_uuid);
|
|
|
|
if (status != 0)
|
|
{
|
|
LL_WARNS("export") << "Problem fetching asset: " << asset_uuid << " " << status << " " << (U32)ext_status << LL_ENDL;
|
|
delete data;
|
|
return;
|
|
}
|
|
|
|
LL_DEBUGS("export") << "Saving asset " << asset_uuid.asString() << " of item " << item_uuid.asString() << LL_ENDL;
|
|
LLFileSystem file(asset_uuid, type);
|
|
S32 file_length = file.getSize();
|
|
std::vector<U8> buffer(file_length);
|
|
file.read(&buffer[0], file_length);
|
|
self->mManifest["asset"][asset_uuid.asString()]["name"] = data->name;
|
|
self->mManifest["asset"][asset_uuid.asString()]["description"] = data->description;
|
|
self->mManifest["asset"][asset_uuid.asString()]["type"] = LLAssetType::lookup(type);
|
|
self->mManifest["asset"][asset_uuid.asString()]["data"] = buffer;
|
|
|
|
if (self->mManifest["inventory"].has(item_uuid.asString()))
|
|
{
|
|
if (self->mManifest["inventory"][item_uuid.asString()]["asset_id"].asUUID().isNull())
|
|
{
|
|
self->mManifest["inventory"][item_uuid.asString()]["asset_id"] = asset_uuid;
|
|
}
|
|
}
|
|
|
|
switch(type)
|
|
{
|
|
case LLAssetType::AT_CLOTHING:
|
|
case LLAssetType::AT_BODYPART:
|
|
{
|
|
std::string asset(buffer.begin(), buffer.end());
|
|
auto position = asset.rfind("textures");
|
|
boost::regex pattern("[[:xdigit:]]{8}(-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12}");
|
|
boost::sregex_iterator m1(asset.begin() + position, asset.end(), pattern);
|
|
boost::sregex_iterator m2;
|
|
for( ; m1 != m2; ++m1)
|
|
{
|
|
LL_DEBUGS("export") << "Found wearable texture " << m1->str() << LL_ENDL;
|
|
if(LLUUID::validate(m1->str()))
|
|
{
|
|
self->exportTexture(LLUUID(m1->str()));
|
|
}
|
|
else
|
|
{
|
|
LL_DEBUGS("export") << "Invalid uuid: " << m1->str() << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case LLAssetType::AT_GESTURE:
|
|
{
|
|
buffer.push_back('\0');
|
|
LLMultiGesture* gesture = new LLMultiGesture();
|
|
LLDataPackerAsciiBuffer dp((char*)&buffer[0], file_length+1);
|
|
if (!gesture->deserialize(dp))
|
|
{
|
|
LL_WARNS("export") << "Unable to load gesture " << asset_uuid << LL_ENDL;
|
|
delete gesture;
|
|
gesture = NULL;
|
|
break;
|
|
}
|
|
std::string name;
|
|
std::string description;
|
|
auto count = gesture->mSteps.size();
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
LLGestureStep* step = gesture->mSteps[i];
|
|
|
|
switch(step->getType())
|
|
{
|
|
case STEP_ANIMATION:
|
|
{
|
|
LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
|
|
if (!FSExportPermsCheck::canExportAsset(anim_step->mAnimAssetID, &name, &description))
|
|
{
|
|
LL_DEBUGS("export") << "Asset in gesture " << data->name << " failed export check." << LL_ENDL;
|
|
break;
|
|
}
|
|
|
|
FSAssetResourceData* anim_data = new FSAssetResourceData;
|
|
anim_data->name = anim_step->mAnimName;
|
|
anim_data->user_data = self;
|
|
anim_data->uuid = anim_step->mAnimAssetID;
|
|
|
|
LL_DEBUGS("export") << "Requesting animation asset " << anim_step->mAnimAssetID.asString() << LL_ENDL;
|
|
self->mAssetRequests.push_back(anim_step->mAnimAssetID);
|
|
gAssetStorage->getAssetData(anim_step->mAnimAssetID,
|
|
LLAssetType::AT_ANIMATION,
|
|
onLoadComplete,
|
|
anim_data,
|
|
true);
|
|
}
|
|
break;
|
|
case STEP_SOUND:
|
|
{
|
|
LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
|
|
if (!FSExportPermsCheck::canExportAsset(sound_step->mSoundAssetID, &name, &description))
|
|
{
|
|
LL_DEBUGS("export") << "Asset in gesture " << data->name << " failed export check." << LL_ENDL;
|
|
break;
|
|
}
|
|
|
|
FSAssetResourceData* sound_data = new FSAssetResourceData;
|
|
sound_data->name = sound_step->mSoundName;
|
|
sound_data->user_data = self;
|
|
sound_data->uuid = sound_step->mSoundAssetID;
|
|
|
|
LL_DEBUGS("export") << "Requesting sound asset " << sound_step->mSoundAssetID.asString() << LL_ENDL;
|
|
self->mAssetRequests.push_back(sound_step->mSoundAssetID);
|
|
gAssetStorage->getAssetData(sound_step->mSoundAssetID,
|
|
LLAssetType::AT_SOUND,
|
|
onLoadComplete,
|
|
sound_data,
|
|
true);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
delete gesture;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
delete data;
|
|
}
|
|
|
|
void FSFloaterObjectExport::removeRequestedAsset(LLUUID asset_uuid)
|
|
{
|
|
uuid_vec_t::iterator iter = std::find(mAssetRequests.begin(), mAssetRequests.end(), asset_uuid);
|
|
if (iter != mAssetRequests.end())
|
|
{
|
|
LL_DEBUGS("export") << "Erasing asset request " << asset_uuid.asString() << LL_ENDL;
|
|
mAssetRequests.erase(iter);
|
|
}
|
|
}
|
|
|
|
void FSFloaterObjectExport::addObject(const LLViewerObject* prim, const std::string name)
|
|
{
|
|
mObjects.push_back(std::pair<LLViewerObject*,std::string>((LLViewerObject*)prim, name));
|
|
}
|
|
|
|
// <FS:CR> *TODO: I know it's really lame to tack this in here, maybe someday it can be integrated properly.
|
|
void FSFloaterObjectExport::updateTextureInfo()
|
|
{
|
|
mTextures.clear();
|
|
//mTextureNames.clear();
|
|
|
|
for (obj_info_t::iterator obj_iter = mObjects.begin(); obj_iter != mObjects.end(); ++obj_iter)
|
|
{
|
|
LLViewerObject* obj = obj_iter->first;
|
|
S32 num_faces = obj->getVolume()->getNumVolumeFaces();
|
|
for (S32 face_num = 0; face_num < num_faces; ++face_num)
|
|
{
|
|
LLTextureEntry* te = obj->getTE(face_num);
|
|
const LLUUID id = te->getID();
|
|
|
|
if (std::find(mTextures.begin(), mTextures.end(), id) != mTextures.end()) continue;
|
|
|
|
mTextures.push_back(id);
|
|
bool exportable = false;
|
|
LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(id);
|
|
std::string name;
|
|
std::string description;
|
|
if (LLGridManager::getInstance()->isInSecondLife())
|
|
{
|
|
if (imagep->mComment.find("a") != imagep->mComment.end())
|
|
{
|
|
if (LLUUID(imagep->mComment["a"]) == gAgentID)
|
|
{
|
|
exportable = true;
|
|
LL_DEBUGS("export") << id << " passed texture export comment check." << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
if (exportable)
|
|
FSExportPermsCheck::canExportAsset(id, &name, &description);
|
|
else
|
|
exportable = FSExportPermsCheck::canExportAsset(id, &name, &description);
|
|
|
|
if (exportable)
|
|
{
|
|
//std::string safe_name = gDirUtilp->getScrubbedFileName(name);
|
|
//std::replace(safe_name.begin(), safe_name.end(), ' ', '_');
|
|
mTextureNames.push_back(name);
|
|
}
|
|
else
|
|
{
|
|
mTextureNames.push_back(std::string());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FSFloaterObjectExport::updateTitleProgress(FSExportState state)
|
|
{
|
|
LLUIString title;
|
|
switch (state)
|
|
{
|
|
case INVENTORY_DOWNLOAD:
|
|
{
|
|
title = getString("title_inventory");
|
|
break;
|
|
}
|
|
case ASSET_DOWNLOAD:
|
|
{
|
|
title = getString("title_assets");
|
|
break;
|
|
}
|
|
case TEXTURE_DOWNLOAD:
|
|
{
|
|
title = getString("title_textures");
|
|
break;
|
|
}
|
|
case IDLE:
|
|
default:
|
|
LL_WARNS("export") << "Unhandled case: " << state << LL_ENDL;
|
|
return;
|
|
}
|
|
LLSD args;
|
|
args["OBJECT"] = mObjectName;
|
|
title.setArgs(args);
|
|
setTitle(title);
|
|
}
|
|
|
|
void FSFloaterObjectExport::updateUI()
|
|
{
|
|
childSetTextArg("NameText", "[NAME]", mObjectName);
|
|
childSetTextArg("exportable_prims", "[COUNT]", llformat("%d", mIncluded));
|
|
childSetTextArg("exportable_prims", "[TOTAL]", llformat("%d", mTotal));
|
|
childSetTextArg("exportable_textures", "[COUNT]", llformat("%d", mNumExportableTextures));
|
|
childSetTextArg("exportable_textures", "[TOTAL]", llformat("%d", mNumTextures));
|
|
|
|
LLUIString title = getString("title_floater");
|
|
title.setArg("[OBJECT]", mObjectName);
|
|
setTitle(title);
|
|
childSetEnabled("export_textures_check", mNumExportableTextures);
|
|
childSetEnabled("export_btn", mIncluded);
|
|
}
|
|
|
|
void FSFloaterObjectExport::onClickExport()
|
|
{
|
|
LLFilePickerReplyThread::startPicker(boost::bind(&FSFloaterObjectExport::onExportFileSelected, this, _1),
|
|
LLFilePicker::FFSAVE_EXPORT, LLDir::getScrubbedFileName(mObjectName + ".oxp"));
|
|
}
|
|
|
|
void FSFloaterObjectExport::onExportFileSelected(const std::vector<std::string>& filenames)
|
|
{
|
|
mFilename = filenames[0];
|
|
|
|
LLUIString title = getString("title_working");
|
|
title.setArg("[OBJECT]", mObjectName);
|
|
setTitle(title);
|
|
|
|
if (!exportSelection())
|
|
{
|
|
LLNotificationsUtil::add("ExportFailed");
|
|
closeFloater();
|
|
}
|
|
}
|
|
|
|
void FSFloaterObjectExport::populateObjectList()
|
|
{
|
|
|
|
if (mObjectList)
|
|
{
|
|
mObjectList->deleteAllItems();
|
|
mObjectList->setCommentText(LLTrans::getString("LoadingData"));
|
|
for (obj_info_t::iterator obj_iter = mObjects.begin(); obj_iter != mObjects.end(); ++obj_iter)
|
|
{
|
|
LLSD element;
|
|
element["columns"][0]["column"] = "icon";
|
|
element["columns"][0]["type"] = "icon";
|
|
element["columns"][0]["value"] = "Inv_Object";
|
|
element["columns"][1]["column"] = "name";
|
|
element["columns"][1]["value"] = obj_iter->second;
|
|
mObjectList->addElement(element, ADD_BOTTOM);
|
|
|
|
}
|
|
if (mObjectList && !mTextureNames.empty())
|
|
{
|
|
for (string_list_t::iterator iter = mTextureNames.begin(); iter != mTextureNames.end(); ++iter)
|
|
{
|
|
LLSD element;
|
|
element["columns"][0]["column"] = "icon";
|
|
element["columns"][0]["type"] = "icon";
|
|
element["columns"][0]["value"] = "Inv_Texture";
|
|
element["columns"][1]["column"] = "name";
|
|
element["columns"][1]["value"] = (*iter);
|
|
mObjectList->addElement(element, ADD_BOTTOM);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copypasta from DAE Export. :o
|
|
void FSFloaterObjectExport::addSelectedObjects()
|
|
{
|
|
mTotal = 0;
|
|
mIncluded = 0;
|
|
mNumTextures = 0;
|
|
mNumExportableTextures = 0;
|
|
mObjects.clear();
|
|
mTextures.clear();
|
|
mTextureNames.clear();
|
|
|
|
if (mObjectSelection)
|
|
{
|
|
LLSelectNode* node = mObjectSelection->getFirstRootNode();
|
|
if (node)
|
|
{
|
|
mCurrentObjectID = node->getObject()->getID();
|
|
mObjectName = node->mName;
|
|
for (LLObjectSelection::iterator iter = mObjectSelection->begin(); iter != mObjectSelection->end(); ++iter)
|
|
{
|
|
node = *iter;
|
|
mTotal++;
|
|
if (!node->getObject()->getVolume() || !FSExportPermsCheck::canExportNode(node, false)) continue;
|
|
mIncluded++;
|
|
addObject(node->getObject(), node->mName);
|
|
}
|
|
|
|
if (mObjects.empty())
|
|
{
|
|
LL_WARNS("export") << "Nothing selected passed permissions checks!" << LL_ENDL;
|
|
//LLNotificationsUtil::add("ExportFailed");
|
|
return;
|
|
}
|
|
|
|
updateTextureInfo();
|
|
mNumTextures = static_cast<S32>(mTextures.size());
|
|
mNumExportableTextures = getNumExportableTextures();
|
|
}
|
|
else
|
|
{
|
|
mObjectName = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
S32 FSFloaterObjectExport::getNumExportableTextures()
|
|
{
|
|
S32 res = 0;
|
|
for (string_list_t::const_iterator t = mTextureNames.begin(); t != mTextureNames.end(); ++t)
|
|
{
|
|
std::string name = *t;
|
|
if (!name.empty())
|
|
{
|
|
++res;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void FSFloaterObjectExport::addTexturePreview()
|
|
{
|
|
S32 num_text = mNumExportableTextures;
|
|
if (num_text == 0) return;
|
|
S32 img_width = 100;
|
|
S32 img_height = img_width + 15;
|
|
S32 panel_height = (num_text / 2 + 1) * (img_height) + 10;
|
|
// *TODO: It would be better to check against a list of controls
|
|
mTexturePanel->deleteAllChildren();
|
|
mTexturePanel->reshape(230, panel_height);
|
|
S32 img_nr = 0;
|
|
for (S32 i=0; i < mTextures.size(); i++)
|
|
{
|
|
if (mTextureNames[i].empty()) continue;
|
|
S32 left = 8 + (img_nr % 2) * (img_width + 13);
|
|
S32 bottom = panel_height - (10 + (img_nr / 2 + 1) * (img_height));
|
|
LLRect r(left, bottom + img_height, left + img_width, bottom);
|
|
LLTextureCtrl::Params p;
|
|
p.rect(r);
|
|
p.layout("topleft");
|
|
p.name(mTextureNames[i]);
|
|
p.image_id(mTextures[i]);
|
|
p.tool_tip(mTextureNames[i]);
|
|
LLTextureCtrl* texture_block = LLUICtrlFactory::create<LLTextureCtrl>(p);
|
|
mTexturePanel->addChild(texture_block);
|
|
img_nr++;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////
|
|
// FSExportCacheReadResponder
|
|
|
|
FSFloaterObjectExport::FSExportCacheReadResponder::FSExportCacheReadResponder(const LLUUID& id, LLImageFormatted* image, FSFloaterObjectExport* parent)
|
|
: mFormattedImage(image),
|
|
mID(id),
|
|
mParent(parent)
|
|
{
|
|
setImage(image);
|
|
}
|
|
|
|
void FSFloaterObjectExport::FSExportCacheReadResponder::setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, bool imagelocal)
|
|
{
|
|
if (imageformat != IMG_CODEC_J2C)
|
|
{
|
|
LL_WARNS("export") << "Texture " << mID << " is not formatted as J2C." << LL_ENDL;
|
|
}
|
|
|
|
if (mFormattedImage.notNull())
|
|
{
|
|
mFormattedImage->appendData(data, datasize);
|
|
}
|
|
else
|
|
{
|
|
mFormattedImage = LLImageFormatted::createFromType(imageformat);
|
|
mFormattedImage->setData(data, datasize);
|
|
}
|
|
mImageSize = imagesize;
|
|
mImageLocal = imagelocal;
|
|
}
|
|
|
|
void FSFloaterObjectExport::FSExportCacheReadResponder::completed(bool success)
|
|
{
|
|
if (success && mFormattedImage.notNull() && mImageSize > 0)
|
|
{
|
|
LL_DEBUGS("export") << "SUCCESS getting texture " << mID << LL_ENDL;
|
|
if (mParent)
|
|
mParent->saveFormattedImage(mFormattedImage, mID);
|
|
}
|
|
else
|
|
{
|
|
/// NOTE: we can get here due to trying to fetch a static local texture
|
|
/// do nothing special as importing static local texture just needs an UUID only.
|
|
if (!success)
|
|
{
|
|
LL_WARNS("export") << "FAILED to get texture " << mID << LL_ENDL;
|
|
}
|
|
if (mFormattedImage.isNull())
|
|
{
|
|
LL_WARNS("export") << "FAILED: NULL texture " << mID << LL_ENDL;
|
|
}
|
|
mParent->removeRequestedTexture(mID);
|
|
}
|
|
}
|