1316 lines
42 KiB
C++
1316 lines
42 KiB
C++
/**
|
|
* @file lllocalbitmaps.cpp
|
|
* @author Vaalith Jinn
|
|
* @brief Local Bitmaps source
|
|
*
|
|
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2011, Linden Research, Inc.
|
|
*
|
|
* 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
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
/* precompiled headers */
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
/* own header */
|
|
#include "lllocalbitmaps.h"
|
|
|
|
/* boost: will not compile unless equivalent is undef'd, beware. */
|
|
#include "fix_macros.h"
|
|
#include <boost/filesystem.hpp>
|
|
|
|
/* image compression headers. */
|
|
#include "llimagebmp.h"
|
|
#include "llimagetga.h"
|
|
#include "llimagej2c.h"
|
|
#include "llimagejpeg.h"
|
|
#include "llimagepng.h"
|
|
|
|
/* time headers */
|
|
#include <time.h>
|
|
#include <ctime>
|
|
|
|
/* misc headers */
|
|
#include "llgltfmaterial.h"
|
|
#include "llscrolllistctrl.h"
|
|
#include "lllocaltextureobject.h"
|
|
#include "llviewertexturelist.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerobject.h"
|
|
#include "llface.h"
|
|
#include "llvoavatarself.h"
|
|
#include "llviewerwearable.h"
|
|
#include "llagentwearables.h"
|
|
#include "lltexlayerparams.h"
|
|
#include "llvovolume.h"
|
|
#include "llnotificationsutil.h"
|
|
#include "pipeline.h"
|
|
#include "llmaterialmgr.h"
|
|
#include "llimagedimensionsinfo.h"
|
|
#include "llinventoryicon.h"
|
|
#include "llviewercontrol.h"
|
|
#include "lltrans.h"
|
|
#include "llviewerdisplay.h"
|
|
|
|
/*=======================================*/
|
|
/* Formal declarations, constants, etc. */
|
|
/*=======================================*/
|
|
|
|
static const F32 LL_LOCAL_TIMER_HEARTBEAT = 3.0;
|
|
static const bool LL_LOCAL_USE_MIPMAPS = true;
|
|
static const S32 LL_LOCAL_DISCARD_LEVEL = 0;
|
|
static const bool LL_LOCAL_SLAM_FOR_DEBUG = true;
|
|
static const bool LL_LOCAL_REPLACE_ON_DEL = true;
|
|
static const S32 LL_LOCAL_UPDATE_RETRIES = 5;
|
|
|
|
/*=======================================*/
|
|
/* LLLocalBitmap: unit class */
|
|
/*=======================================*/
|
|
LLLocalBitmap::LLLocalBitmap(std::string filename)
|
|
: mFilename(filename)
|
|
, mShortName(gDirUtilp->getBaseFileName(filename, true))
|
|
, mValid(false)
|
|
, mLastModified()
|
|
, mLinkStatus(LS_ON)
|
|
, mUpdateRetries(LL_LOCAL_UPDATE_RETRIES)
|
|
{
|
|
mTrackingID.generate();
|
|
|
|
/* extension */
|
|
std::string temp_exten = gDirUtilp->getExtension(mFilename);
|
|
|
|
if (temp_exten == "bmp")
|
|
{
|
|
mExtension = ET_IMG_BMP;
|
|
}
|
|
else if (temp_exten == "tga")
|
|
{
|
|
mExtension = ET_IMG_TGA;
|
|
}
|
|
else if (temp_exten == "jpg" || temp_exten == "jpeg")
|
|
{
|
|
mExtension = ET_IMG_JPG;
|
|
}
|
|
else if (temp_exten == "j2c" || temp_exten == "jp2")
|
|
{
|
|
mExtension = ET_IMG_J2C;
|
|
}
|
|
else if (temp_exten == "png")
|
|
{
|
|
mExtension = ET_IMG_PNG;
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "File of no valid extension given, local bitmap creation aborted." << "\n"
|
|
<< "Filename: " << mFilename << LL_ENDL;
|
|
return; // no valid extension.
|
|
}
|
|
|
|
/* next phase of unit creation is nearly the same as an update cycle.
|
|
we're running updateSelf as a special case with the optional UT_FIRSTUSE
|
|
which omits the parts associated with removing the outdated texture */
|
|
mValid = updateSelf(UT_FIRSTUSE);
|
|
}
|
|
|
|
LLLocalBitmap::~LLLocalBitmap()
|
|
{
|
|
// replace IDs with defaults, if set to do so.
|
|
if(LL_LOCAL_REPLACE_ON_DEL && mValid && gAgentAvatarp) // fix for STORM-1837
|
|
{
|
|
replaceIDs(mWorldID, IMG_DEFAULT);
|
|
LLLocalBitmapMgr::getInstance()->doRebake();
|
|
}
|
|
|
|
for (LLPointer<LLGLTFMaterial> &mat : mGLTFMaterialWithLocalTextures)
|
|
{
|
|
mat->removeLocalTextureTracking(getTrackingID());
|
|
}
|
|
|
|
mChangedSignal(getTrackingID(), getWorldID(), LLUUID());
|
|
mChangedSignal.disconnect_all_slots();
|
|
|
|
// delete self from gimagelist
|
|
LLViewerFetchedTexture* image = gTextureList.findImage(mWorldID, TEX_LIST_STANDARD);
|
|
gTextureList.deleteImage(image);
|
|
|
|
if (image)
|
|
{
|
|
image->unref();
|
|
}
|
|
}
|
|
|
|
/* accessors */
|
|
std::string LLLocalBitmap::getFilename() const
|
|
{
|
|
return mFilename;
|
|
}
|
|
|
|
std::string LLLocalBitmap::getShortName() const
|
|
{
|
|
return mShortName;
|
|
}
|
|
|
|
LLUUID LLLocalBitmap::getTrackingID() const
|
|
{
|
|
return mTrackingID;
|
|
}
|
|
|
|
LLUUID LLLocalBitmap::getWorldID() const
|
|
{
|
|
return mWorldID;
|
|
}
|
|
|
|
bool LLLocalBitmap::getValid() const
|
|
{
|
|
return mValid;
|
|
}
|
|
|
|
/* update functions */
|
|
bool LLLocalBitmap::updateSelf(EUpdateType optional_firstupdate)
|
|
{
|
|
bool updated = false;
|
|
|
|
if (mLinkStatus == LS_ON)
|
|
{
|
|
// verifying that the file exists
|
|
if (gDirUtilp->fileExists(mFilename))
|
|
{
|
|
// verifying that the file has indeed been modified
|
|
|
|
#ifndef LL_WINDOWS
|
|
const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(mFilename));
|
|
#else
|
|
const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(ll_convert<std::wstring>(mFilename)));
|
|
#endif
|
|
LLSD new_last_modified = asctime(localtime(&temp_time));
|
|
|
|
if (mLastModified.asString() != new_last_modified.asString())
|
|
{
|
|
/* loading the image file and decoding it, here is a critical point which,
|
|
if fails, invalidates the whole update (or unit creation) process. */
|
|
LLPointer<LLImageRaw> raw_image = new LLImageRaw();
|
|
if (decodeBitmap(raw_image))
|
|
{
|
|
// decode is successful, we can safely proceed.
|
|
LLUUID old_id = LLUUID::null;
|
|
if ((optional_firstupdate != UT_FIRSTUSE) && !mWorldID.isNull())
|
|
{
|
|
old_id = mWorldID;
|
|
}
|
|
mWorldID.generate();
|
|
mLastModified = new_last_modified;
|
|
|
|
LLPointer<LLViewerFetchedTexture> texture = new LLViewerFetchedTexture
|
|
("file://"+mFilename, FTT_LOCAL_FILE, mWorldID, LL_LOCAL_USE_MIPMAPS);
|
|
|
|
texture->createGLTexture(LL_LOCAL_DISCARD_LEVEL, raw_image);
|
|
texture->ref();
|
|
|
|
gTextureList.addImage(texture, TEX_LIST_STANDARD);
|
|
|
|
if (optional_firstupdate != UT_FIRSTUSE)
|
|
{
|
|
// seek out everything old_id uses and replace it with mWorldID
|
|
replaceIDs(old_id, mWorldID);
|
|
|
|
// remove old_id from gimagelist
|
|
LLViewerFetchedTexture* image = gTextureList.findImage(old_id, TEX_LIST_STANDARD);
|
|
if (image != NULL)
|
|
{
|
|
gTextureList.deleteImage(image);
|
|
image->unref();
|
|
}
|
|
}
|
|
|
|
mUpdateRetries = LL_LOCAL_UPDATE_RETRIES;
|
|
updated = true;
|
|
}
|
|
|
|
// if decoding failed, we get here and it will attempt to decode it in the next cycles
|
|
// until mUpdateRetries runs out. this is done because some software lock the bitmap while writing to it
|
|
else
|
|
{
|
|
if (mUpdateRetries)
|
|
{
|
|
mUpdateRetries--;
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "During the update process the following file was found" << "\n"
|
|
<< "but could not be opened or decoded for " << LL_LOCAL_UPDATE_RETRIES << " attempts." << "\n"
|
|
<< "Filename: " << mFilename << "\n"
|
|
<< "Disabling further update attempts for this file." << LL_ENDL;
|
|
|
|
LLSD notif_args;
|
|
notif_args["FNAME"] = mFilename;
|
|
notif_args["NRETRIES"] = LL_LOCAL_UPDATE_RETRIES;
|
|
LLNotificationsUtil::add("LocalBitmapsUpdateFailedFinal", notif_args);
|
|
|
|
mLinkStatus = LS_BROKEN;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // end if file exists
|
|
|
|
else
|
|
{
|
|
LL_WARNS() << "During the update process, the following file was not found." << "\n"
|
|
<< "Filename: " << mFilename << "\n"
|
|
<< "Disabling further update attempts for this file." << LL_ENDL;
|
|
|
|
LLSD notif_args;
|
|
notif_args["FNAME"] = mFilename;
|
|
LLNotificationsUtil::add("LocalBitmapsUpdateFileNotFound", notif_args);
|
|
|
|
mLinkStatus = LS_BROKEN;
|
|
}
|
|
}
|
|
|
|
return updated;
|
|
}
|
|
|
|
boost::signals2::connection LLLocalBitmap::setChangedCallback(const LLLocalTextureCallback& cb)
|
|
{
|
|
return mChangedSignal.connect(cb);
|
|
}
|
|
|
|
void LLLocalBitmap::addGLTFMaterial(LLGLTFMaterial* mat)
|
|
{
|
|
if (!mat)
|
|
{
|
|
return;
|
|
}
|
|
|
|
mat->addLocalTextureTracking(getTrackingID(), getWorldID());
|
|
|
|
for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != mGLTFMaterialWithLocalTextures.end();)
|
|
{
|
|
if (it->get() == mat)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((*it)->getNumRefs() == 1)
|
|
{
|
|
it = mGLTFMaterialWithLocalTextures.erase(it);
|
|
}
|
|
else
|
|
{
|
|
it++;
|
|
}
|
|
}
|
|
mGLTFMaterialWithLocalTextures.push_back(mat);
|
|
}
|
|
|
|
bool LLLocalBitmap::decodeBitmap(LLPointer<LLImageRaw> rawimg)
|
|
{
|
|
bool decode_successful = false;
|
|
|
|
switch (mExtension)
|
|
{
|
|
case ET_IMG_BMP:
|
|
{
|
|
LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
|
|
if (bmp_image->load(mFilename) && bmp_image->decode(rawimg, 0.0f))
|
|
{
|
|
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
|
|
decode_successful = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ET_IMG_TGA:
|
|
{
|
|
LLPointer<LLImageTGA> tga_image = new LLImageTGA;
|
|
if ((tga_image->load(mFilename) && tga_image->decode(rawimg))
|
|
&& ((tga_image->getComponents() == 3) || (tga_image->getComponents() == 4)))
|
|
{
|
|
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
|
|
decode_successful = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ET_IMG_JPG:
|
|
{
|
|
LLPointer<LLImageJPEG> jpeg_image = new LLImageJPEG;
|
|
if (jpeg_image->load(mFilename) && jpeg_image->decode(rawimg, 0.0f))
|
|
{
|
|
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
|
|
decode_successful = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ET_IMG_J2C:
|
|
{
|
|
LLPointer<LLImageJ2C> jpeg_image = new LLImageJ2C;
|
|
if (jpeg_image->load(mFilename))
|
|
{
|
|
jpeg_image->setDiscardLevel(0);
|
|
if (jpeg_image->decode(rawimg, 0.0f))
|
|
{
|
|
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
|
|
decode_successful = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ET_IMG_PNG:
|
|
{
|
|
LLPointer<LLImagePNG> png_image = new LLImagePNG;
|
|
if (png_image->load(mFilename) && png_image->decode(rawimg, 0.0f))
|
|
{
|
|
rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
|
|
decode_successful = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// separating this into -several- LL_WARNS() calls because in the extremely unlikely case that this happens
|
|
// accessing mFilename and any other object properties might very well crash the viewer.
|
|
// getting here should be impossible, or there's been a pretty serious bug.
|
|
|
|
LL_WARNS() << "During a decode attempt, the following local bitmap had no properly assigned extension." << LL_ENDL;
|
|
LL_WARNS() << "Filename: " << mFilename << LL_ENDL;
|
|
LL_WARNS() << "Disabling further update attempts for this file." << LL_ENDL;
|
|
mLinkStatus = LS_BROKEN;
|
|
}
|
|
}
|
|
|
|
return decode_successful;
|
|
}
|
|
|
|
void LLLocalBitmap::replaceIDs(const LLUUID& old_id, LLUUID new_id)
|
|
{
|
|
// checking for misuse.
|
|
if (old_id == new_id)
|
|
{
|
|
LL_INFOS() << "An attempt was made to replace a texture with itself. (matching UUIDs)" << "\n"
|
|
<< "Texture UUID: " << old_id.asString() << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
mChangedSignal(getTrackingID(), old_id, new_id);
|
|
|
|
// processing updates per channel; makes the process scalable.
|
|
// the only actual difference is in SetTE* call i.e. SetTETexture, SetTENormal, etc.
|
|
updateUserPrims(old_id, new_id, LLRender::DIFFUSE_MAP);
|
|
updateUserPrims(old_id, new_id, LLRender::NORMAL_MAP);
|
|
updateUserPrims(old_id, new_id, LLRender::SPECULAR_MAP);
|
|
|
|
updateUserVolumes(old_id, new_id, LLRender::LIGHT_TEX);
|
|
updateUserVolumes(old_id, new_id, LLRender::SCULPT_TEX); // isn't there supposed to be an IMG_DEFAULT_SCULPT or something?
|
|
|
|
// default safeguard image for layers
|
|
if( new_id == IMG_DEFAULT )
|
|
{
|
|
new_id = IMG_DEFAULT_AVATAR;
|
|
}
|
|
|
|
/* It doesn't actually update all of those, it merely checks if any of them
|
|
contain the referenced ID and if so, updates. */
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_ALPHA);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_EYES);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_GLOVES);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_JACKET);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_PANTS);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_SHIRT);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_SHOES);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_SKIN);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_SKIRT);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_SOCKS);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_TATTOO);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_UNIVERSAL);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERPANTS);
|
|
updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERSHIRT);
|
|
|
|
updateGLTFMaterials(old_id, new_id);
|
|
}
|
|
|
|
// this function sorts the faces from a getFaceList[getNumFaces] into a list of objects
|
|
// in order to prevent multiple sendTEUpdate calls per object during updateUserPrims
|
|
std::vector<LLViewerObject*> LLLocalBitmap::prepUpdateObjects(LLUUID old_id, U32 channel)
|
|
{
|
|
std::vector<LLViewerObject*> obj_list;
|
|
LLViewerFetchedTexture* old_texture = gTextureList.findImage(old_id, TEX_LIST_STANDARD);
|
|
|
|
for (S32 face_iterator = 0; face_iterator < old_texture->getNumFaces(channel); face_iterator++)
|
|
{
|
|
// getting an object from a face
|
|
LLFace* face_to_object = (*old_texture->getFaceList(channel))[face_iterator];
|
|
|
|
if(face_to_object)
|
|
{
|
|
LLViewerObject* affected_object = face_to_object->getViewerObject();
|
|
|
|
if(affected_object)
|
|
{
|
|
|
|
// we have an object, we'll take it's UUID and compare it to
|
|
// whatever we already have in the returnable object list.
|
|
// if there is a match - we do not add (to prevent duplicates)
|
|
LLUUID mainlist_obj_id = affected_object->getID();
|
|
bool add_object = true;
|
|
|
|
// begin looking for duplicates
|
|
std::vector<LLViewerObject*>::iterator objlist_iter = obj_list.begin();
|
|
for(; (objlist_iter != obj_list.end()) && add_object; objlist_iter++)
|
|
{
|
|
LLViewerObject* obj = *objlist_iter;
|
|
if (obj->getID() == mainlist_obj_id)
|
|
{
|
|
add_object = false; // duplicate found.
|
|
}
|
|
}
|
|
// end looking for duplicates
|
|
|
|
if(add_object)
|
|
{
|
|
obj_list.push_back(affected_object);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // end of face-iterating for()
|
|
|
|
return obj_list;
|
|
}
|
|
|
|
void LLLocalBitmap::updateUserPrims(LLUUID old_id, LLUUID new_id, U32 channel)
|
|
{
|
|
std::vector<LLViewerObject*> objectlist = prepUpdateObjects(old_id, channel);
|
|
|
|
for(std::vector<LLViewerObject*>::iterator object_iterator = objectlist.begin();
|
|
object_iterator != objectlist.end(); object_iterator++)
|
|
{
|
|
LLViewerObject* object = *object_iterator;
|
|
|
|
if(object)
|
|
{
|
|
bool update_tex = false;
|
|
bool update_mat = false;
|
|
S32 num_faces = object->getNumFaces();
|
|
|
|
for (U8 face_iter = 0; face_iter < num_faces; face_iter++)
|
|
{
|
|
if (object->mDrawable)
|
|
{
|
|
LLFace* face = object->mDrawable->getFace(face_iter);
|
|
if (face && face->getTexture(channel) && face->getTexture(channel)->getID() == old_id)
|
|
{
|
|
// these things differ per channel, unless there already is a universal
|
|
// texture setting function to setTE that takes channel as a param?
|
|
// p.s.: switch for now, might become if - if an extra test is needed to verify before touching normalmap/specmap
|
|
switch(channel)
|
|
{
|
|
case LLRender::DIFFUSE_MAP:
|
|
{
|
|
object->setTETexture(face_iter, new_id);
|
|
update_tex = true;
|
|
break;
|
|
}
|
|
|
|
case LLRender::NORMAL_MAP:
|
|
{
|
|
object->setTENormalMap(face_iter, new_id);
|
|
update_mat = true;
|
|
update_tex = true;
|
|
break;
|
|
}
|
|
|
|
case LLRender::SPECULAR_MAP:
|
|
{
|
|
object->setTESpecularMap(face_iter, new_id);
|
|
update_mat = true;
|
|
update_tex = true;
|
|
break;
|
|
}
|
|
}
|
|
// end switch
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (update_tex)
|
|
{
|
|
object->sendTEUpdate();
|
|
}
|
|
|
|
if (update_mat)
|
|
{
|
|
object->mDrawable->getVOVolume()->faceMappingChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLLocalBitmap::updateUserVolumes(LLUUID old_id, LLUUID new_id, U32 channel)
|
|
{
|
|
LLViewerFetchedTexture* old_texture = gTextureList.findImage(old_id, TEX_LIST_STANDARD);
|
|
for (S32 volume_iter = 0; volume_iter < old_texture->getNumVolumes(channel); volume_iter++)
|
|
{
|
|
LLVOVolume* volobjp = (*old_texture->getVolumeList(channel))[volume_iter];
|
|
switch (channel)
|
|
{
|
|
case LLRender::LIGHT_TEX:
|
|
{
|
|
if (volobjp->getLightTextureID() == old_id)
|
|
{
|
|
volobjp->setLightTextureID(new_id);
|
|
}
|
|
break;
|
|
}
|
|
case LLRender::SCULPT_TEX:
|
|
{
|
|
LLViewerObject* object = (LLViewerObject*)volobjp;
|
|
|
|
if (object)
|
|
{
|
|
if (object->isSculpted() && object->getVolume() &&
|
|
object->getVolume()->getParams().getSculptID() == old_id)
|
|
{
|
|
LLSculptParams* old_params = (LLSculptParams*)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
|
|
LLSculptParams new_params(*old_params);
|
|
new_params.setSculptTexture(new_id, (*old_params).getSculptType());
|
|
object->setParameterEntry(LLNetworkData::PARAMS_SCULPT, new_params, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLLocalBitmap::updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableType::EType type)
|
|
{
|
|
U32 count = gAgentWearables.getWearableCount(type);
|
|
for(U32 wearable_iter = 0; wearable_iter < count; wearable_iter++)
|
|
{
|
|
LLViewerWearable* wearable = gAgentWearables.getViewerWearable(type, wearable_iter);
|
|
if (wearable)
|
|
{
|
|
std::vector<LLLocalTextureObject*> texture_list = wearable->getLocalTextureListSeq();
|
|
for(std::vector<LLLocalTextureObject*>::iterator texture_iter = texture_list.begin();
|
|
texture_iter != texture_list.end(); texture_iter++)
|
|
{
|
|
LLLocalTextureObject* lto = *texture_iter;
|
|
|
|
if (lto && lto->getID() == old_id)
|
|
{
|
|
U32 local_texlayer_index = 0; /* can't keep that as static const, gives errors, so i'm leaving this var here */
|
|
LLAvatarAppearanceDefines::EBakedTextureIndex baked_texind =
|
|
lto->getTexLayer(local_texlayer_index)->getTexLayerSet()->getBakedTexIndex();
|
|
|
|
LLAvatarAppearanceDefines::ETextureIndex reg_texind = getTexIndex(type, baked_texind);
|
|
if (reg_texind != LLAvatarAppearanceDefines::TEX_NUM_INDICES)
|
|
{
|
|
U32 index;
|
|
if (gAgentWearables.getWearableIndex(wearable,index))
|
|
{
|
|
gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), false, index);
|
|
gAgentAvatarp->wearableUpdated(type);
|
|
/* telling the manager to rebake once update cycle is fully done */
|
|
LLLocalBitmapMgr::getInstance()->setNeedsRebake();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLLocalBitmap::updateGLTFMaterials(LLUUID old_id, LLUUID new_id)
|
|
{
|
|
// Might be a better idea to hold this in LLGLTFMaterialList
|
|
for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != mGLTFMaterialWithLocalTextures.end();)
|
|
{
|
|
if ((*it)->getNumRefs() == 1)
|
|
{
|
|
// render and override materials are often recreated,
|
|
// clean up any remains
|
|
it = mGLTFMaterialWithLocalTextures.erase(it);
|
|
}
|
|
// Render material consists of base and override materials, make sure replaceLocalTexture
|
|
// gets called for base and override before applyOverride
|
|
else if ((*it)->replaceLocalTexture(mTrackingID, old_id, new_id))
|
|
{
|
|
it++;
|
|
}
|
|
else
|
|
{
|
|
// Matching id not found, no longer in use
|
|
// material would clean itself, remove from the list
|
|
it = mGLTFMaterialWithLocalTextures.erase(it);
|
|
}
|
|
}
|
|
|
|
// Updating render materials calls updateTextureTracking which can modify
|
|
// mGLTFMaterialWithLocalTextures, so precollect all entries that need to be updated
|
|
std::set<LLTextureEntry*> update_entries;
|
|
for (LLGLTFMaterial* mat : mGLTFMaterialWithLocalTextures)
|
|
{
|
|
// mGLTFMaterialWithLocalTextures includes overrides that are not 'fetched'
|
|
// and don't have texture entries (they don't need to since render material does).
|
|
LLFetchedGLTFMaterial* fetched_mat = dynamic_cast<LLFetchedGLTFMaterial*>(mat);
|
|
if (fetched_mat)
|
|
{
|
|
for (LLTextureEntry* entry : fetched_mat->mTextureEntires)
|
|
{
|
|
update_entries.insert(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for (LLTextureEntry* entry : update_entries)
|
|
{
|
|
// Normally a change in applied material id is supposed to
|
|
// drop overrides thus reset material, but local materials
|
|
// currently reuse their existing asset id, and purpose is
|
|
// to preview how material will work in-world, overrides
|
|
// included, so do an override to render update instead.
|
|
LLGLTFMaterial* override_mat = entry->getGLTFMaterialOverride();
|
|
LLGLTFMaterial* mat = entry->getGLTFMaterial();
|
|
if (override_mat && mat)
|
|
{
|
|
// do not create a new material, reuse existing pointer
|
|
// so that mTextureEntires remains untouched
|
|
LLGLTFMaterial* render_mat = entry->getGLTFRenderMaterial();
|
|
if (render_mat && render_mat != mat)
|
|
{
|
|
*render_mat = *mat;
|
|
render_mat->applyOverride(*override_mat); // can update mGLTFMaterialWithLocalTextures
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "A TE had an override, but no render material" << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LLAvatarAppearanceDefines::ETextureIndex LLLocalBitmap::getTexIndex(
|
|
LLWearableType::EType type, LLAvatarAppearanceDefines::EBakedTextureIndex baked_texind)
|
|
{
|
|
LLAvatarAppearanceDefines::ETextureIndex result = LLAvatarAppearanceDefines::TEX_NUM_INDICES; // using as a default/fail return.
|
|
|
|
switch(type)
|
|
{
|
|
case LLWearableType::WT_ALPHA:
|
|
{
|
|
switch(baked_texind)
|
|
{
|
|
case LLAvatarAppearanceDefines::BAKED_EYES:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_EYES_ALPHA;
|
|
break;
|
|
}
|
|
|
|
case LLAvatarAppearanceDefines::BAKED_HAIR:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_HAIR_ALPHA;
|
|
break;
|
|
}
|
|
|
|
case LLAvatarAppearanceDefines::BAKED_HEAD:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_HEAD_ALPHA;
|
|
break;
|
|
}
|
|
|
|
case LLAvatarAppearanceDefines::BAKED_LOWER:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_ALPHA;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_UPPER:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_UPPER_ALPHA;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
case LLWearableType::WT_EYES:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_EYES)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_EYES_IRIS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_GLOVES:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_UPPER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_UPPER_GLOVES;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_JACKET:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_LOWER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_JACKET;
|
|
}
|
|
else if (baked_texind == LLAvatarAppearanceDefines::BAKED_UPPER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_UPPER_JACKET;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_PANTS:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_LOWER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_PANTS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_SHIRT:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_UPPER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_UPPER_SHIRT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_SHOES:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_LOWER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_SHOES;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_SKIN:
|
|
{
|
|
switch(baked_texind)
|
|
{
|
|
case LLAvatarAppearanceDefines::BAKED_HEAD:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_HEAD_BODYPAINT;
|
|
break;
|
|
}
|
|
|
|
case LLAvatarAppearanceDefines::BAKED_LOWER:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_BODYPAINT;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_UPPER:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_UPPER_BODYPAINT;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_SKIRT:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_SKIRT)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_SKIRT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_SOCKS:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_LOWER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_SOCKS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_TATTOO:
|
|
{
|
|
switch (baked_texind)
|
|
{
|
|
case LLAvatarAppearanceDefines::BAKED_HEAD:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_HEAD_TATTOO;
|
|
break;
|
|
}
|
|
|
|
case LLAvatarAppearanceDefines::BAKED_LOWER:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_UPPER:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_UPPER_TATTOO;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
case LLWearableType::WT_UNIVERSAL:
|
|
{
|
|
switch (baked_texind)
|
|
{
|
|
|
|
case LLAvatarAppearanceDefines::BAKED_SKIRT:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_SKIRT_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_EYES:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_EYES_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_HAIR:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_HAIR_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_LEFT_ARM:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LEFT_ARM_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_LEFT_LEG:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LEFT_LEG_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_AUX1:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_AUX1_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_AUX2:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_AUX2_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_AUX3:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_AUX3_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_UPPER:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_UPPER_UNIVERSAL_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_LOWER:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_UNIVERSAL_TATTOO;
|
|
break;
|
|
}
|
|
case LLAvatarAppearanceDefines::BAKED_HEAD:
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_HEAD_UNIVERSAL_TATTOO;
|
|
break;
|
|
}
|
|
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_UNDERPANTS:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_LOWER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_LOWER_UNDERPANTS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LLWearableType::WT_UNDERSHIRT:
|
|
{
|
|
if (baked_texind == LLAvatarAppearanceDefines::BAKED_UPPER)
|
|
{
|
|
result = LLAvatarAppearanceDefines::TEX_UPPER_UNDERSHIRT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
LL_WARNS() << "Unknown wearable type: " << (int)type << "\n"
|
|
<< "Baked Texture Index: " << (int)baked_texind << "\n"
|
|
<< "Filename: " << mFilename << "\n"
|
|
<< "TrackingID: " << mTrackingID << "\n"
|
|
<< "InworldID: " << mWorldID << LL_ENDL;
|
|
}
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*=======================================*/
|
|
/* LLLocalBitmapTimer: timer class */
|
|
/*=======================================*/
|
|
LLLocalBitmapTimer::LLLocalBitmapTimer() : LLEventTimer(LL_LOCAL_TIMER_HEARTBEAT)
|
|
{
|
|
}
|
|
|
|
LLLocalBitmapTimer::~LLLocalBitmapTimer()
|
|
{
|
|
}
|
|
|
|
void LLLocalBitmapTimer::startTimer()
|
|
{
|
|
mEventTimer.start();
|
|
}
|
|
|
|
void LLLocalBitmapTimer::stopTimer()
|
|
{
|
|
mEventTimer.stop();
|
|
}
|
|
|
|
bool LLLocalBitmapTimer::isRunning()
|
|
{
|
|
return mEventTimer.getStarted();
|
|
}
|
|
|
|
bool LLLocalBitmapTimer::tick()
|
|
{
|
|
LLLocalBitmapMgr::getInstance()->doUpdates();
|
|
return false;
|
|
}
|
|
|
|
/*=======================================*/
|
|
/* LLLocalBitmapMgr: manager class */
|
|
/*=======================================*/
|
|
LLLocalBitmapMgr::LLLocalBitmapMgr()
|
|
{
|
|
}
|
|
|
|
LLLocalBitmapMgr::~LLLocalBitmapMgr()
|
|
{
|
|
std::for_each(mBitmapList.begin(), mBitmapList.end(), DeletePointer());
|
|
mBitmapList.clear();
|
|
}
|
|
|
|
bool LLLocalBitmapMgr::addUnit(const std::vector<std::string>& filenames)
|
|
{
|
|
bool add_successful = false;
|
|
std::vector<std::string>::const_iterator iter = filenames.begin();
|
|
while (iter != filenames.end())
|
|
{
|
|
if (!iter->empty() && addUnit(*iter).notNull())
|
|
{
|
|
add_successful = true;
|
|
}
|
|
iter++;
|
|
}
|
|
return add_successful;
|
|
}
|
|
|
|
LLUUID LLLocalBitmapMgr::addUnit(const std::string& filename)
|
|
{
|
|
if (!checkTextureDimensions(filename))
|
|
{
|
|
return LLUUID::null;
|
|
}
|
|
|
|
LLLocalBitmap* unit = new LLLocalBitmap(filename);
|
|
|
|
if (unit->getValid())
|
|
{
|
|
mBitmapList.push_back(unit);
|
|
return unit->getTrackingID();
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Attempted to add invalid or unreadable image file, attempt cancelled.\n"
|
|
<< "Filename: " << filename << LL_ENDL;
|
|
|
|
LLSD notif_args;
|
|
notif_args["FNAME"] = filename;
|
|
LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args);
|
|
|
|
delete unit;
|
|
unit = NULL;
|
|
}
|
|
|
|
return LLUUID::null;
|
|
}
|
|
|
|
bool LLLocalBitmapMgr::checkTextureDimensions(std::string filename)
|
|
{
|
|
std::string exten = gDirUtilp->getExtension(filename);
|
|
U32 codec = LLImageBase::getCodecFromExtension(exten);
|
|
std::string mImageLoadError;
|
|
LLImageDimensionsInfo image_info;
|
|
if (!image_info.load(filename,codec))
|
|
{
|
|
LLSD args;
|
|
args["NAME"] = gDirUtilp->getBaseFileName(filename);
|
|
if (!image_info.getWarningName().empty())
|
|
{
|
|
args["REASON"] = LLTrans::getString(image_info.getWarningName());
|
|
}
|
|
LLNotificationsUtil::add("CannotUploadTexture", args);
|
|
return false;
|
|
}
|
|
|
|
// allow loading up to 4x max rez but implicitly downrez to max rez before upload
|
|
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X")*4;
|
|
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y")*4;
|
|
|
|
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
|
|
{
|
|
LLStringUtil::format_map_t args;
|
|
args["WIDTH"] = llformat("%d", max_width);
|
|
args["HEIGHT"] = llformat("%d", max_height);
|
|
mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args);
|
|
|
|
LLSD notif_args;
|
|
notif_args["REASON"] = mImageLoadError;
|
|
notif_args["NAME"] = gDirUtilp->getBaseFileName(filename);
|
|
LLNotificationsUtil::add("CannotUploadTexture", notif_args);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void LLLocalBitmapMgr::delUnit(LLUUID tracking_id)
|
|
{
|
|
if (!mBitmapList.empty())
|
|
{
|
|
std::vector<LLLocalBitmap*> to_delete;
|
|
for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
|
|
{ /* finding which ones we want deleted and making a separate list */
|
|
LLLocalBitmap* unit = *iter;
|
|
if (unit->getTrackingID() == tracking_id)
|
|
{
|
|
to_delete.push_back(unit);
|
|
}
|
|
}
|
|
|
|
for(std::vector<LLLocalBitmap*>::iterator del_iter = to_delete.begin();
|
|
del_iter != to_delete.end(); del_iter++)
|
|
{ /* iterating over a temporary list, hence preserving the iterator validity while deleting. */
|
|
LLLocalBitmap* unit = *del_iter;
|
|
mBitmapList.remove(unit);
|
|
delete unit;
|
|
unit = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
LLUUID LLLocalBitmapMgr::getTrackingID(const LLUUID& world_id) const
|
|
{
|
|
for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
|
|
{
|
|
LLLocalBitmap* unit = *iter;
|
|
if (unit->getWorldID() == world_id)
|
|
{
|
|
return unit->getTrackingID();
|
|
}
|
|
}
|
|
|
|
return LLUUID::null;
|
|
}
|
|
|
|
LLUUID LLLocalBitmapMgr::getWorldID(const LLUUID &tracking_id) const
|
|
{
|
|
LLUUID world_id = LLUUID::null;
|
|
|
|
for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
|
|
{
|
|
LLLocalBitmap* unit = *iter;
|
|
if (unit->getTrackingID() == tracking_id)
|
|
{
|
|
world_id = unit->getWorldID();
|
|
}
|
|
}
|
|
|
|
return world_id;
|
|
}
|
|
|
|
bool LLLocalBitmapMgr::isLocal(const LLUUID &world_id) const
|
|
{
|
|
for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
|
|
{
|
|
LLLocalBitmap* unit = *iter;
|
|
if (unit->getWorldID() == world_id)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string LLLocalBitmapMgr::getFilename(const LLUUID &tracking_id) const
|
|
{
|
|
std::string filename = "";
|
|
|
|
for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
|
|
{
|
|
LLLocalBitmap* unit = *iter;
|
|
if (unit->getTrackingID() == tracking_id)
|
|
{
|
|
filename = unit->getFilename();
|
|
}
|
|
}
|
|
|
|
return filename;
|
|
}
|
|
|
|
boost::signals2::connection LLLocalBitmapMgr::setOnChangedCallback(const LLUUID tracking_id, const LLLocalBitmap::LLLocalTextureCallback &cb)
|
|
{
|
|
for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
|
|
{
|
|
LLLocalBitmap* unit = *iter;
|
|
if (unit->getTrackingID() == tracking_id)
|
|
{
|
|
return unit->setChangedCallback(cb);
|
|
}
|
|
}
|
|
|
|
return boost::signals2::connection();
|
|
}
|
|
|
|
void LLLocalBitmapMgr::associateGLTFMaterial(const LLUUID tracking_id, LLGLTFMaterial* mat)
|
|
{
|
|
for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
|
|
{
|
|
LLLocalBitmap* unit = *iter;
|
|
if (unit->getTrackingID() == tracking_id)
|
|
{
|
|
unit->addGLTFMaterial(mat);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLLocalBitmapMgr::feedScrollList(LLScrollListCtrl* ctrl)
|
|
{
|
|
if (ctrl)
|
|
{
|
|
std::string icon_name = LLInventoryIcon::getIconName(
|
|
LLAssetType::AT_TEXTURE,
|
|
LLInventoryType::IT_NONE);
|
|
|
|
if (!mBitmapList.empty())
|
|
{
|
|
for (local_list_iter iter = mBitmapList.begin();
|
|
iter != mBitmapList.end(); iter++)
|
|
{
|
|
LLSD element;
|
|
|
|
element["columns"][0]["column"] = "icon";
|
|
element["columns"][0]["type"] = "icon";
|
|
element["columns"][0]["value"] = icon_name;
|
|
|
|
element["columns"][1]["column"] = "unit_name";
|
|
element["columns"][1]["type"] = "text";
|
|
element["columns"][1]["value"] = (*iter)->getShortName();
|
|
|
|
LLSD data;
|
|
data["id"] = (*iter)->getTrackingID();
|
|
data["type"] = (S32)LLAssetType::AT_TEXTURE;
|
|
element["value"] = data;
|
|
|
|
ctrl->addElement(element);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void LLLocalBitmapMgr::doUpdates()
|
|
{
|
|
// preventing theoretical overlap in cases with huge number of loaded images.
|
|
mTimer.stopTimer();
|
|
mNeedsRebake = false;
|
|
|
|
for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
|
|
{
|
|
(*iter)->updateSelf();
|
|
}
|
|
|
|
doRebake();
|
|
mTimer.startTimer();
|
|
}
|
|
|
|
void LLLocalBitmapMgr::setNeedsRebake()
|
|
{
|
|
mNeedsRebake = true;
|
|
}
|
|
|
|
void LLLocalBitmapMgr::doRebake()
|
|
{ /* separated that from doUpdates to insure a rebake can be called separately during deletion */
|
|
if (mNeedsRebake)
|
|
{
|
|
gAgentAvatarp->forceBakeAllTextures(LL_LOCAL_SLAM_FOR_DEBUG);
|
|
mNeedsRebake = false;
|
|
}
|
|
}
|
|
|