/** * @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 /* image compression headers. */ #include "llimagebmp.h" #include "llimagetga.h" #include "llimagejpeg.h" #include "llimagepng.h" /* time headers */ #include #include /* 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 == "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 &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(utf8str_to_utf16str(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 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 texture = new LLViewerFetchedTexture ("file://"+mFilename, FTT_LOCAL_FILE, 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, 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_list_t::iterator end = mGLTFMaterialWithLocalTextures.end(); for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;) { if (it->get() == mat) { return; } if ((*it)->getNumRefs() == 1) { it = mGLTFMaterialWithLocalTextures.erase(it); end = mGLTFMaterialWithLocalTextures.end(); } else { it++; } } mat->addLocalTextureTracking(getTrackingID(), getWorldID()); mGLTFMaterialWithLocalTextures.push_back(mat); } bool LLLocalBitmap::decodeBitmap(LLPointer rawimg) { bool decode_successful = false; switch (mExtension) { case ET_IMG_BMP: { LLPointer 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 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 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 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 LLLocalBitmap::prepUpdateObjects(LLUUID old_id, U32 channel) { std::vector 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::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 objectlist = prepUpdateObjects(old_id, channel); for(std::vector::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 texture_list = wearable->getLocalTextureListSeq(); for(std::vector::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 mat_list_t::iterator end = mGLTFMaterialWithLocalTextures.end(); for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;) { if ((*it)->getNumRefs() == 1) { // render and override materials are often recreated, // clean up any remains it = mGLTFMaterialWithLocalTextures.erase(it); end = mGLTFMaterialWithLocalTextures.end(); } 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); end = mGLTFMaterialWithLocalTextures.end(); } } // Render material consists of base and override materials, make sure replaceLocalTexture // gets called for base and override before applyOverride end = mGLTFMaterialWithLocalTextures.end(); for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;) { LLFetchedGLTFMaterial* fetched_mat = dynamic_cast((*it).get()); if (fetched_mat) { for (LLTextureEntry* entry : fetched_mat->mTextureEntires) { // 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(); if (override_mat) { // do not create a new material, reuse existing pointer LLFetchedGLTFMaterial* render_mat = dynamic_cast(entry->getGLTFRenderMaterial()); if (render_mat) { *render_mat = *fetched_mat; render_mat->applyOverride(*override_mat); } else { LL_WARNS_ONCE() << "Failed to apply local material override, render material not found" << LL_ENDL; } } } } ++it; } } 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() { start(); } void LLLocalBitmapTimer::stopTimer() { stop(); } 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& filenames) { bool add_successful = false; std::vector::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 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::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; } }