summaryrefslogtreecommitdiff
path: root/indra/newview/lllocalbitmaps.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/lllocalbitmaps.cpp')
-rw-r--r--indra/newview/lllocalbitmaps.cpp925
1 files changed, 925 insertions, 0 deletions
diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp
new file mode 100644
index 0000000000..b96fc06226
--- /dev/null
+++ b/indra/newview/lllocalbitmaps.cpp
@@ -0,0 +1,925 @@
+/**
+ * @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. */
+#ifdef equivalent
+#undef equivalent
+#endif
+#include <boost/filesystem.hpp>
+
+/* image compression headers. */
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagejpeg.h"
+#include "llimagepng.h"
+
+/* time headers */
+#include <time.h>
+#include <ctime>
+
+/* misc headers */
+#include "llscrolllistctrl.h"
+#include "llfilepicker.h"
+#include "llviewertexturelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerobject.h"
+#include "llface.h"
+#include "llvoavatarself.h"
+#include "llwearable.h"
+#include "llagentwearables.h"
+#include "lltexlayerparams.h"
+#include "llvovolume.h"
+
+/*=======================================*/
+/* Formal declarations, constants, etc. */
+/*=======================================*/
+std::list<LLLocalBitmap*> LLLocalBitmapMgr::sBitmapList;
+LLLocalBitmapTimer LLLocalBitmapMgr::sTimer;
+bool LLLocalBitmapMgr::sNeedsRebake;
+
+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 U32 LL_LOCAL_TEXLAYER_FOR_IDX = 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 == "png")
+ {
+ mExtension = ET_IMG_PNG;
+ }
+ else
+ {
+ llwarns << "File of no valid extension given, local bitmap creation aborted." << "\n"
+ << "Filename: " << mFilename << llendl;
+ return; // no valid extension.
+ }
+
+ /* next phase of unit creation is nearly the same as an update cycle.
+ true means the unit's update is running for the first time so it will not check
+ for current usage nor will it attempt to replace the old, non existent image */
+ mValid = updateSelf(true);
+}
+
+LLLocalBitmap::~LLLocalBitmap()
+{
+ // replace IDs with defaults, if set to do so.
+ if(LL_LOCAL_REPLACE_ON_DEL)
+ {
+ replaceIDs(mWorldID, IMG_DEFAULT);
+ LLLocalBitmapMgr::doRebake();
+ }
+
+ // delete self from gimagelist
+ LLViewerFetchedTexture* image = gTextureList.findImage(mWorldID);
+ gTextureList.deleteImage(image);
+
+ if (image)
+ {
+ image->unref();
+ }
+}
+
+/* accessors */
+std::string LLLocalBitmap::getFilename()
+{
+ return mFilename;
+}
+
+std::string LLLocalBitmap::getShortName()
+{
+ return mShortName;
+}
+
+LLUUID LLLocalBitmap::getTrackingID()
+{
+ return mTrackingID;
+}
+
+LLUUID LLLocalBitmap::getWorldID()
+{
+ return mWorldID;
+}
+
+bool LLLocalBitmap::getValid()
+{
+ return mValid;
+}
+
+/* update functions */
+bool LLLocalBitmap::updateSelf(bool first_update)
+{
+ bool updated = false;
+
+ if (mLinkStatus == LS_ON)
+ {
+ // verifying that the file exists
+ if (gDirUtilp->fileExists(mFilename))
+ {
+ // verifying that the file has indeed been modified
+ const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(mFilename));
+ 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 (!first_update && !mWorldID.isNull())
+ {
+ old_id = mWorldID;
+ }
+ mWorldID.generate();
+ mLastModified = new_last_modified;
+
+ LLPointer<LLViewerFetchedTexture> texture = new LLViewerFetchedTexture
+ ("file://"+mFilename, mWorldID, LL_LOCAL_USE_MIPMAPS);
+
+ texture->createGLTexture(LL_LOCAL_DISCARD_LEVEL, raw_image);
+ texture->setCachedRawImage(LL_LOCAL_DISCARD_LEVEL, raw_image);
+ texture->ref();
+
+ gTextureList.addImage(texture);
+
+ if (!first_update)
+ {
+ // 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);
+ 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
+ {
+ llwarns << "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." << llendl;
+
+ mLinkStatus = LS_BROKEN;
+ }
+ }
+ }
+
+ } // end if file exists
+
+ else
+ {
+ llwarns << "During the update process, the following file was not found." << "\n"
+ << "Filename: " << mFilename << "\n"
+ << "Disabling further update attempts for this file." << llendl;
+
+ mLinkStatus = LS_BROKEN;
+ }
+ }
+
+ return updated;
+}
+
+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_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- llwarns 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.
+
+ llwarns << "During a decode attempt, the following local bitmap had no properly assigned extension." << llendl;
+ llwarns << "Filename: " << mFilename << llendl;
+ llwarns << "Disabling further update attempts for this file." << llendl;
+ mLinkStatus = LS_BROKEN;
+ }
+ }
+
+ return decode_successful;
+}
+
+void LLLocalBitmap::replaceIDs(LLUUID old_id, LLUUID new_id)
+{
+ // checking for misuse.
+ if (old_id == new_id)
+ {
+ llinfos << "An attempt was made to replace a texture with itself. (matching UUIDs)" << "\n"
+ << "Texture UUID: " << old_id.asString() << llendl;
+ return;
+ }
+
+ updateUserPrims(old_id, new_id);
+ updateUserSculpts(old_id, new_id); // 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_UNDERPANTS);
+ updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERSHIRT);
+}
+
+// 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)
+{
+ std::vector<LLViewerObject*> obj_list;
+ LLViewerFetchedTexture* old_texture = gTextureList.findImage(old_id);
+
+ for(U32 face_iterator = 0; face_iterator < old_texture->getNumFaces(); face_iterator++)
+ {
+ // getting an object from a face
+ LLFace* face_to_object = (*old_texture->getFaceList())[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)
+{
+ std::vector<LLViewerObject*> objectlist = prepUpdateObjects(old_id);
+
+ for(std::vector<LLViewerObject*>::iterator object_iterator = objectlist.begin();
+ object_iterator != objectlist.end(); object_iterator++)
+ {
+ LLViewerObject* object = *object_iterator;
+
+ if(object)
+ {
+ bool update_obj = 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() && face->getTexture()->getID() == old_id)
+ {
+ object->setTEImage(face_iter, LLViewerTextureManager::getFetchedTexture
+ (new_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE));
+
+ update_obj = true;
+ }
+ }
+ }
+
+ if (update_obj)
+ {
+ object->sendTEUpdate();
+ }
+ }
+ }
+
+}
+
+void LLLocalBitmap::updateUserSculpts(LLUUID old_id, LLUUID new_id)
+{
+ LLViewerFetchedTexture* old_texture = gTextureList.findImage(old_id);
+ for(U32 volume_iter = 0; volume_iter < old_texture->getNumVolumes(); volume_iter++)
+ {
+ LLVOVolume* volume_to_object = (*old_texture->getVolumeList())[volume_iter];
+ LLViewerObject* object = (LLViewerObject*)volume_to_object;
+
+ 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);
+ 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++)
+ {
+ LLWearable* wearable = gAgentWearables.getWearable(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 */
+ LLVOAvatarDefines::EBakedTextureIndex baked_texind =
+ lto->getTexLayer(local_texlayer_index)->getTexLayerSet()->getBakedTexIndex();
+
+ LLVOAvatarDefines::ETextureIndex reg_texind = getTexIndex(type, baked_texind);
+ if (reg_texind != LLVOAvatarDefines::TEX_NUM_INDICES)
+ {
+ U32 index = gAgentWearables.getWearableIndex(wearable);
+ gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), FALSE, index);
+ gAgentAvatarp->wearableUpdated(type, FALSE);
+
+ /* telling the manager to rebake once update cycle is fully done */
+ LLLocalBitmapMgr::setNeedsRebake();
+ }
+
+ }
+ }
+ }
+ }
+}
+
+LLVOAvatarDefines::ETextureIndex LLLocalBitmap::getTexIndex(
+ LLWearableType::EType type, LLVOAvatarDefines::EBakedTextureIndex baked_texind)
+{
+ LLVOAvatarDefines::ETextureIndex result = LLVOAvatarDefines::TEX_NUM_INDICES; // using as a default/fail return.
+
+ switch(type)
+ {
+ case LLWearableType::WT_ALPHA:
+ {
+ switch(baked_texind)
+ {
+ case LLVOAvatarDefines::BAKED_EYES:
+ {
+ result = LLVOAvatarDefines::TEX_EYES_ALPHA;
+ break;
+ }
+
+ case LLVOAvatarDefines::BAKED_HAIR:
+ {
+ result = LLVOAvatarDefines::TEX_HAIR_ALPHA;
+ break;
+ }
+
+ case LLVOAvatarDefines::BAKED_HEAD:
+ {
+ result = LLVOAvatarDefines::TEX_HEAD_ALPHA;
+ break;
+ }
+
+ case LLVOAvatarDefines::BAKED_LOWER:
+ {
+ result = LLVOAvatarDefines::TEX_LOWER_ALPHA;
+ break;
+ }
+ case LLVOAvatarDefines::BAKED_UPPER:
+ {
+ result = LLVOAvatarDefines::TEX_UPPER_ALPHA;
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+
+ }
+ break;
+
+ }
+
+ case LLWearableType::WT_EYES:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_EYES)
+ {
+ result = LLVOAvatarDefines::TEX_EYES_IRIS;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_GLOVES:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
+ {
+ result = LLVOAvatarDefines::TEX_UPPER_GLOVES;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_JACKET:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+ {
+ result = LLVOAvatarDefines::TEX_LOWER_JACKET;
+ }
+ else if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
+ {
+ result = LLVOAvatarDefines::TEX_UPPER_JACKET;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_PANTS:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+ {
+ result = LLVOAvatarDefines::TEX_LOWER_PANTS;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_SHIRT:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
+ {
+ result = LLVOAvatarDefines::TEX_UPPER_SHIRT;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_SHOES:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+ {
+ result = LLVOAvatarDefines::TEX_LOWER_SHOES;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_SKIN:
+ {
+ switch(baked_texind)
+ {
+ case LLVOAvatarDefines::BAKED_HEAD:
+ {
+ result = LLVOAvatarDefines::TEX_HEAD_BODYPAINT;
+ break;
+ }
+
+ case LLVOAvatarDefines::BAKED_LOWER:
+ {
+ result = LLVOAvatarDefines::TEX_LOWER_BODYPAINT;
+ break;
+ }
+ case LLVOAvatarDefines::BAKED_UPPER:
+ {
+ result = LLVOAvatarDefines::TEX_UPPER_BODYPAINT;
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+
+ }
+ break;
+ }
+
+ case LLWearableType::WT_SKIRT:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_SKIRT)
+ {
+ result = LLVOAvatarDefines::TEX_SKIRT;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_SOCKS:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+ {
+ result = LLVOAvatarDefines::TEX_LOWER_SOCKS;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_TATTOO:
+ {
+ switch(baked_texind)
+ {
+ case LLVOAvatarDefines::BAKED_HEAD:
+ {
+ result = LLVOAvatarDefines::TEX_HEAD_TATTOO;
+ break;
+ }
+
+ case LLVOAvatarDefines::BAKED_LOWER:
+ {
+ result = LLVOAvatarDefines::TEX_LOWER_TATTOO;
+ break;
+ }
+ case LLVOAvatarDefines::BAKED_UPPER:
+ {
+ result = LLVOAvatarDefines::TEX_UPPER_TATTOO;
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+
+ }
+ break;
+ }
+
+ case LLWearableType::WT_UNDERPANTS:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+ {
+ result = LLVOAvatarDefines::TEX_LOWER_UNDERPANTS;
+ }
+
+ break;
+ }
+
+ case LLWearableType::WT_UNDERSHIRT:
+ {
+ if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
+ {
+ result = LLVOAvatarDefines::TEX_UPPER_UNDERSHIRT;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ llwarns << "Unknown wearable type: " << (int)type << "\n"
+ << "Baked Texture Index: " << (int)baked_texind << "\n"
+ << "Filename: " << mFilename << "\n"
+ << "TrackingID: " << mTrackingID << "\n"
+ << "InworldID: " << mWorldID << llendl;
+ }
+
+ }
+ 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::doUpdates();
+ return FALSE;
+}
+
+/*=======================================*/
+/* LLLocalBitmapMgr: manager class */
+/*=======================================*/
+LLLocalBitmapMgr::LLLocalBitmapMgr()
+{
+ // The class is all made of static members, should i even bother instantiating?
+}
+
+LLLocalBitmapMgr::~LLLocalBitmapMgr()
+{
+}
+
+bool LLLocalBitmapMgr::addUnit()
+{
+ bool add_successful = false;
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getMultipleOpenFiles(LLFilePicker::FFLOAD_IMAGE))
+ {
+ sTimer.stopTimer();
+
+ std::string filename = picker.getFirstFile();
+ while(!filename.empty())
+ {
+ LLLocalBitmap* unit = new LLLocalBitmap(filename);
+
+ if (unit->getValid())
+ {
+ sBitmapList.push_back(unit);
+ add_successful = true;
+ }
+ else
+ {
+ delete unit;
+ unit = NULL;
+ }
+
+ filename = picker.getNextFile();
+ }
+
+ sTimer.startTimer();
+ }
+
+ return add_successful;
+}
+
+void LLLocalBitmapMgr::delUnit(LLUUID tracking_id)
+{
+ if (!sBitmapList.empty())
+ {
+ std::vector<LLLocalBitmap*> to_delete;
+ for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.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;
+ sBitmapList.remove(unit);
+ delete unit;
+ unit = NULL;
+ }
+ }
+}
+
+LLUUID LLLocalBitmapMgr::getWorldID(LLUUID tracking_id)
+{
+ LLUUID world_id = LLUUID::null;
+
+ for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
+ {
+ LLLocalBitmap* unit = *iter;
+ if (unit->getTrackingID() == tracking_id)
+ {
+ world_id = unit->getWorldID();
+ }
+ }
+
+ return world_id;
+}
+
+std::string LLLocalBitmapMgr::getFilename(LLUUID tracking_id)
+{
+ std::string filename = "";
+
+ for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
+ {
+ LLLocalBitmap* unit = *iter;
+ if (unit->getTrackingID() == tracking_id)
+ {
+ filename = unit->getFilename();
+ }
+ }
+
+ return filename;
+}
+
+void LLLocalBitmapMgr::feedScrollList(LLScrollListCtrl* ctrl)
+{
+ if (ctrl)
+ {
+ ctrl->clearRows();
+
+ if (!sBitmapList.empty())
+ {
+ for (local_list_iter iter = sBitmapList.begin();
+ iter != sBitmapList.end(); iter++)
+ {
+ LLSD element;
+ element["columns"][0]["column"] = "unit_name";
+ element["columns"][0]["type"] = "text";
+ element["columns"][0]["value"] = (*iter)->getShortName();
+
+ element["columns"][1]["column"] = "unit_id_HIDDEN";
+ element["columns"][1]["type"] = "text";
+ element["columns"][1]["value"] = (*iter)->getTrackingID();
+
+ ctrl->addElement(element);
+ }
+ }
+ }
+
+}
+
+void LLLocalBitmapMgr::doUpdates()
+{
+ // preventing theoretical overlap in cases with huge number of loaded images.
+ sTimer.stopTimer();
+ sNeedsRebake = false;
+
+ for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
+ {
+ (*iter)->updateSelf();
+ }
+
+ doRebake();
+ sTimer.startTimer();
+}
+
+void LLLocalBitmapMgr::setNeedsRebake()
+{
+ sNeedsRebake = true;
+}
+
+void LLLocalBitmapMgr::doRebake()
+{ /* separated that from doUpdates to insure a rebake can be called separately during deletion */
+ if (sNeedsRebake)
+ {
+ gAgentAvatarp->forceBakeAllTextures(LL_LOCAL_SLAM_FOR_DEBUG);
+ sNeedsRebake = false;
+ }
+}
+