storm-64: Local Textures

Vaalith Jinn 2012-04-02 09:34:14 -04:00
parent b187aeb8f1
commit f71ca07998
10 changed files with 1382 additions and 61 deletions

View File

@ -1171,6 +1171,8 @@ Unlikely Quintessa
UsikuFarasi Kanarik
Vadim Bigbear
VWR-2681
Vaalith Jinn
STORM-64
Vector Hastings
VWR-8726
Veritas Raymaker

View File

@ -303,6 +303,7 @@ set(viewer_SOURCE_FILES
lllistbrowser.cpp
lllistcontextmenu.cpp
lllistview.cpp
lllocalbitmaps.cpp
lllocaltextureobject.cpp
lllocationhistory.cpp
lllocationinputctrl.cpp
@ -859,6 +860,7 @@ set(viewer_HEADER_FILES
lllistbrowser.h
lllistcontextmenu.h
lllistview.h
lllocalbitmaps.h
lllocaltextureobject.h
lllocationhistory.h
lllocationinputctrl.h

View File

@ -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;
}
}

View File

@ -0,0 +1,130 @@
/**
* @file lllocalbitmaps.h
* @author Vaalith Jinn
* @brief Local Bitmaps header
*
* $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$
*/
#ifndef LL_LOCALBITMAPS_H
#define LL_LOCALBITMAPS_H
#include "lleventtimer.h"
#include "llwearabletype.h"
#include "llvoavatardefines.h"
class LLScrollListCtrl;
class LLLocalBitmap
{
public: /* main */
LLLocalBitmap(std::string filename);
~LLLocalBitmap();
bool updateSelf(bool first_update = false);
public: /* accessors */
std::string getFilename();
std::string getShortName();
LLUUID getTrackingID();
LLUUID getWorldID();
bool getValid();
private: /* maintenance */
bool decodeBitmap(LLPointer<LLImageRaw> raw);
void replaceIDs(LLUUID old_id, LLUUID new_id);
private: /* id replacement */
std::vector<LLViewerObject*> prepUpdateObjects(LLUUID old_id);
void updateUserPrims(LLUUID old_id, LLUUID new_id);
void updateUserSculpts(LLUUID old_id, LLUUID new_id);
void updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableType::EType type);
LLVOAvatarDefines::ETextureIndex getTexIndex(LLWearableType::EType type, LLVOAvatarDefines::EBakedTextureIndex baked_texind);
private: /* enums */
enum ELinkStatus
{
LS_ON,
LS_BROKEN,
};
enum EExtension
{
ET_IMG_BMP,
ET_IMG_TGA,
ET_IMG_JPG,
ET_IMG_PNG
};
private: /* members */
std::string mFilename;
std::string mShortName;
LLUUID mTrackingID;
LLUUID mWorldID;
bool mValid;
LLSD mLastModified;
EExtension mExtension;
ELinkStatus mLinkStatus;
S32 mUpdateRetries;
};
class LLLocalBitmapTimer : public LLEventTimer
{
public:
LLLocalBitmapTimer();
~LLLocalBitmapTimer();
public:
void startTimer();
void stopTimer();
bool isRunning();
BOOL tick();
};
class LLLocalBitmapMgr
{
public:
LLLocalBitmapMgr();
~LLLocalBitmapMgr();
public:
static bool addUnit();
static void delUnit(LLUUID tracking_id);
static LLUUID getWorldID(LLUUID tracking_id);
static std::string getFilename(LLUUID tracking_id);
static void feedScrollList(LLScrollListCtrl* ctrl);
static void doUpdates();
static void setNeedsRebake();
static void doRebake();
private:
static std::list<LLLocalBitmap*> sBitmapList;
static LLLocalBitmapTimer sTimer;
static bool sNeedsRebake;
typedef std::list<LLLocalBitmap*>::iterator local_list_iter;
};
#endif

View File

@ -67,6 +67,9 @@
#include "lluictrlfactory.h"
#include "lltrans.h"
#include "llradiogroup.h"
#include "llfloaterreg.h"
#include "lllocalbitmaps.h"
static const S32 HPAD = 4;
static const S32 VPAD = 4;
@ -78,6 +81,8 @@ static const F32 CONTEXT_CONE_IN_ALPHA = 0.0f;
static const F32 CONTEXT_CONE_OUT_ALPHA = 1.f;
static const F32 CONTEXT_FADE_TIME = 0.08f;
static const S32 LOCAL_TRACKING_ID_COLUMN = 1;
//static const char CURRENT_IMAGE_NAME[] = "Current Texture";
//static const char WHITE_IMAGE_NAME[] = "Blank Texture";
//static const char NO_IMAGE_NAME[] = "None";
@ -142,6 +147,12 @@ public:
static void onApplyImmediateCheck(LLUICtrl* ctrl, void* userdata);
void onTextureSelect( const LLTextureEntry& te );
static void onModeSelect(LLUICtrl* ctrl, void *userdata);
static void onBtnAdd(void* userdata);
static void onBtnRemove(void* userdata);
static void onBtnUpload(void* userdata);
static void onLocalScrollCommit(LLUICtrl* ctrl, void* userdata);
protected:
LLPointer<LLViewerTexture> mTexturep;
LLTextureCtrl* mOwner;
@ -169,8 +180,10 @@ protected:
BOOL mNoCopyTextureSelected;
F32 mContextConeOpacity;
LLSaveFolderState mSavedFolderState;
BOOL mSelectedItemPinned;
LLRadioGroup* mModeSelector;
LLScrollListCtrl* mLocalScrollCtrl;
};
LLFloaterTexturePicker::LLFloaterTexturePicker(
@ -437,6 +450,17 @@ BOOL LLFloaterTexturePicker::postBuild()
mInventoryPanel->setSelection(findItemID(mImageAssetID, FALSE), TAKE_FOCUS_NO);
}
mModeSelector = getChild<LLRadioGroup>("mode_selection");
mModeSelector->setCommitCallback(onModeSelect, this);
mModeSelector->setSelectedIndex(0, 0);
childSetAction("l_add_btn", LLFloaterTexturePicker::onBtnAdd, this);
childSetAction("l_rem_btn", LLFloaterTexturePicker::onBtnRemove, this);
childSetAction("l_upl_btn", LLFloaterTexturePicker::onBtnUpload, this);
mLocalScrollCtrl = getChild<LLScrollListCtrl>("l_name_list");
mLocalScrollCtrl->setCommitCallback(onLocalScrollCommit, this);
LLLocalBitmapMgr::feedScrollList(mLocalScrollCtrl);
mNoCopyTextureSelected = FALSE;
@ -464,7 +488,6 @@ BOOL LLFloaterTexturePicker::postBuild()
// virtual
void LLFloaterTexturePicker::draw()
{
S32 floater_header_size = getHeaderHeight();
if (mOwner)
{
// draw cone of context pointing back to texture swatch
@ -554,10 +577,7 @@ void LLFloaterTexturePicker::draw()
}
// Border
LLRect border( BORDER_PAD,
getRect().getHeight() - floater_header_size - BORDER_PAD,
((getMinWidth() / 2) - TEXTURE_INVENTORY_PADDING - HPAD) - BORDER_PAD,
BORDER_PAD + FOOTER_HEIGHT + (getRect().getHeight() - getMinHeight()));
LLRect border = getChildView("preview_widget")->getRect();
gl_rect_2d( border, LLColor4::black, FALSE );
@ -748,7 +768,15 @@ void LLFloaterTexturePicker::onBtnSelect(void* userdata)
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
if (self->mOwner)
{
self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT);
LLUUID local_id = LLUUID::null;
if (self->mLocalScrollCtrl->getVisible() && !self->mLocalScrollCtrl->getAllSelected().empty())
{
LLUUID temp_id = self->mLocalScrollCtrl->getFirstSelected()->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID();
local_id = LLLocalBitmapMgr::getWorldID(temp_id);
}
self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT, local_id);
}
self->closeFloater();
}
@ -791,6 +819,112 @@ void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem
}
}
// static
void LLFloaterTexturePicker::onModeSelect(LLUICtrl* ctrl, void *userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
bool mode = (self->mModeSelector->getSelectedIndex() == 0);
self->getChild<LLButton>("Default")->setVisible(mode);
self->getChild<LLButton>("Blank")->setVisible(mode);
self->getChild<LLButton>("None")->setVisible(mode);
self->getChild<LLButton>("Pipette")->setVisible(mode);
self->getChild<LLFilterEditor>("inventory search editor")->setVisible(mode);
self->getChild<LLInventoryPanel>("inventory panel")->setVisible(mode);
/*self->getChild<LLCheckBox>("show_folders_check")->setVisible(mode);
no idea under which conditions the above is even shown, needs testing. */
self->getChild<LLButton>("l_add_btn")->setVisible(!mode);
self->getChild<LLButton>("l_rem_btn")->setVisible(!mode);
self->getChild<LLButton>("l_upl_btn")->setVisible(!mode);
self->getChild<LLScrollListCtrl>("l_name_list")->setVisible(!mode);
}
// static
void LLFloaterTexturePicker::onBtnAdd(void* userdata)
{
if (LLLocalBitmapMgr::addUnit() == true)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
LLLocalBitmapMgr::feedScrollList(self->mLocalScrollCtrl);
}
}
// static
void LLFloaterTexturePicker::onBtnRemove(void* userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
if (!selected_items.empty())
{
for(std::vector<LLScrollListItem*>::iterator iter = selected_items.begin();
iter != selected_items.end(); iter++)
{
LLScrollListItem* list_item = *iter;
if (list_item)
{
LLUUID tracking_id = list_item->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID();
LLLocalBitmapMgr::delUnit(tracking_id);
}
}
self->getChild<LLButton>("l_rem_btn")->setEnabled(false);
self->getChild<LLButton>("l_upl_btn")->setEnabled(false);
LLLocalBitmapMgr::feedScrollList(self->mLocalScrollCtrl);
}
}
// static
void LLFloaterTexturePicker::onBtnUpload(void* userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
if (selected_items.empty())
{
return;
}
/* currently only allows uploading one by one, picks the first item from the selection list. (not the vector!)
in the future, it might be a good idea to check the vector size and if more than one units is selected - opt for multi-image upload. */
LLUUID tracking_id = (LLUUID)self->mLocalScrollCtrl->getSelectedItemLabel(LOCAL_TRACKING_ID_COLUMN);
std::string filename = LLLocalBitmapMgr::getFilename(tracking_id);
if (!filename.empty())
{
LLFloaterReg::showInstance("upload_image", LLSD(filename));
}
}
//static
void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
bool has_selection = !selected_items.empty();
self->getChild<LLButton>("l_rem_btn")->setEnabled(has_selection);
self->getChild<LLButton>("l_upl_btn")->setEnabled(has_selection && (selected_items.size() < 2));
/* since multiple-localbitmap upload is not implemented, upl button gets disabled if more than one is selected. */
if (has_selection)
{
LLUUID tracking_id = (LLUUID)self->mLocalScrollCtrl->getSelectedItemLabel(LOCAL_TRACKING_ID_COLUMN);
LLUUID inworld_id = LLLocalBitmapMgr::getWorldID(tracking_id);
self->mOwner->setImageAssetID(inworld_id);
if (self->childGetValue("apply_immediate_check").asBoolean())
{
self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE, inworld_id);
}
}
}
// static
void LLFloaterTexturePicker::onShowFolders(LLUICtrl* ctrl, void *user_data)
{
@ -1126,7 +1260,7 @@ void LLTextureCtrl::onFloaterClose()
mFloaterHandle.markDead();
}
void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id)
{
LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
@ -1139,14 +1273,24 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
// (i.e. op == TEXTURE_SELECT) or texture changes via DnD.
else if (mCommitOnSelection || op == TEXTURE_SELECT)
mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here?
if( floaterp->isDirty() )
if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work.
{
setTentative( FALSE );
mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
lldebugs << "mImageItemID: " << mImageItemID << llendl;
mImageAssetID = floaterp->getAssetID();
lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
if (id.notNull())
{
mImageItemID = id;
mImageAssetID = id;
}
else
{
mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
lldebugs << "mImageItemID: " << mImageItemID << llendl;
mImageAssetID = floaterp->getAssetID();
lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
}
if (op == TEXTURE_SELECT && mOnSelectCallback)
{
mOnSelectCallback( this, LLSD() );

View File

@ -157,7 +157,7 @@ public:
void closeDependentFloater();
void onFloaterClose();
void onFloaterCommit(ETexturePickOp op);
void onFloaterCommit(ETexturePickOp op, LLUUID id = LLUUID::null);
// This call is returned when a drag is detected. Your callback
// should return TRUE if the drag is acceptable.

View File

@ -65,6 +65,7 @@ class LLViewerTextureList
friend class LLTextureView;
friend class LLViewerTextureManager;
friend class LLLocalBitmap;
public:
static BOOL createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec);

View File

@ -810,6 +810,20 @@ const LLLocalTextureObject* LLWearable::getLocalTextureObject(S32 index) const
return NULL;
}
std::vector<LLLocalTextureObject*> LLWearable::getLocalTextureListSeq()
{
std::vector<LLLocalTextureObject*> result;
for(te_map_t::const_iterator iter = mTEMap.begin();
iter != mTEMap.end(); iter++)
{
LLLocalTextureObject* lto = iter->second;
result.push_back(lto);
}
return result;
}
void LLWearable::setLocalTextureObject(S32 index, LLLocalTextureObject &lto)
{
if( mTEMap.find(index) != mTEMap.end() )

View File

@ -106,6 +106,7 @@ public:
LLLocalTextureObject* getLocalTextureObject(S32 index);
const LLLocalTextureObject* getLocalTextureObject(S32 index) const;
std::vector<LLLocalTextureObject*> getLocalTextureListSeq();
void setLocalTextureObject(S32 index, LLLocalTextureObject &lto);
void addVisualParam(LLVisualParam *param);

View File

@ -3,15 +3,17 @@
legacy_header_height="18"
can_minimize="false"
can_resize="true"
height="290"
height="330"
layout="topleft"
min_height="290"
min_height="330"
min_width="410"
name="texture picker"
help_topic="texture_picker"
title="PICK: TEXTURE"
width="410">
<floater.string
<!-- top static -->
<floater.string
name="choose_picture">
Click to choose a picture
</floater.string>
@ -19,6 +21,16 @@
name="pick title">
Pick:
</floater.string>
<view
left="4"
top="20"
name="preview_widget"
height="165"
width="165"
follows="left|top"
/>
<text
type="string"
length="1"
@ -34,70 +46,94 @@
width="163">
Multiple textures
</text>
<!-- mode selector -->
<radio_group
control_name="mode_selection"
height="20"
layout="topleft"
left="18"
top_pad="80"
name="mode_selection"
follows="left|top">
<radio_item
label="Inventory"
name="inventory"
top_delta="20"
layout="topleft"
height="16"
left="0"
value="0"
width="80" />
<radio_item
label="Local"
left_pad="0"
layout="topleft"
top_delta="0"
height="16"
name="local"
value="1"
width="75" />
</radio_group>
<!-- -->
<text
type="string"
length="1"
follows="left|top"
height="14"
layout="topleft"
left_delta="0"
left_delta="-12"
name="unknown"
top_pad="80"
width="163">
top_pad="4"
width="">
Size: [DIMENSIONS]
</text>
<!-- middle: inventory mode -->
<button
enabled="false"
follows="left|bottom"
height="20"
follows="left|top"
height="18"
label="Default"
label_selected="Default"
layout="topleft"
left_delta="0"
name="Default"
top_pad="4"
width="80" />
width="73"
left="94"
top="215"/>
<button
enabled="false"
follows="left|bottom"
height="20"
label="None"
label_selected="None"
layout="topleft"
left_pad="4"
name="None"
top_delta="0"
width="80" />
<button
follows="left|bottom"
follows="left|top"
height="20"
label="Blank"
label_selected="Blank"
layout="topleft"
left="4"
left_delta="0"
name="Blank"
top_pad="5"
width="80" />
<button
follows="left|bottom"
width="73" />
<button
enabled="false"
follows="left|top"
height="20"
label="None"
label_selected="None"
layout="topleft"
left_delta="0"
name="None"
top_pad="5"
width="73" />
<button
follows="left|top"
height="28"
image_selected="eye_button_active.tga"
image_unselected="eye_button_inactive.tga"
layout="topleft"
left_pad="50"
top_delta="3"
left_delta="-80"
top_delta="-25"
name="Pipette"
width="28" />
<check_box
follows="left|bottom"
height="20"
initial_value="true"
label="Apply now"
layout="topleft"
left="4"
name="apply_immediate_check"
top="262"
width="120" />
<filter_editor
follows="left|top|right"
height="23"
@ -113,7 +149,7 @@
bg_alpha_color="DkGray2"
border="false"
follows="all"
height="200"
height="233"
layout="topleft"
left_delta="0"
name="inventory panel"
@ -128,23 +164,89 @@
top_pad="0"
left_delta="-3"
width="200" />
<button
follows="right|bottom"
<!-- middle: local mode -->
<button
follows="left|top"
height="18"
label="Add"
label_selected="Add"
layout="topleft"
left="94"
top="215"
name="l_add_btn"
width="73"
visible="false"/>
<button
enabled="false"
follows="left|top"
height="20"
label="Remove"
label_selected="Remove"
layout="topleft"
left_delta="0"
name="l_rem_btn"
top_pad="5"
width="73"
visible="false"/>
<button
enabled="false"
follows="left|top"
height="20"
label="Upload"
label_selected="Upload"
layout="topleft"
left_delta="0"
name="l_upl_btn"
top_pad="5"
width="73"
visible="false"/>
<scroll_list
name="l_name_list"
left="170"
top="22"
width="235"
height="260"
follows="left|top|right|bottom"
column_padding="0"
can_resize="false"
draw_heading="true"
multi_select="true"
search_column="1"
visible="false">
<column name="unit_name" label="Name" dynamicwidth="true" />
<column name="unit_id_HIDDEN" label="ID" width="0" />
</scroll_list>
<!-- bottom static -->
<button
follows="bottom"
height="20"
label="OK"
label_selected="OK"
layout="topleft"
right="-120"
left="95"
top="-30"
name="Select"
width="100" />
<button
follows="right|bottom"
follows="bottom"
height="20"
label="Cancel"
label_selected="Cancel"
layout="topleft"
right="-10"
left_pad="5"
left_delta="120"
top_delta="0"
name="Cancel"
width="100" />
<check_box
follows="left|bottom"
height="20"
initial_value="true"
label="Apply now"
layout="topleft"
left="6"
name="apply_immediate_check"
top_delta="0"
width="120" />
</floater>