diff options
Diffstat (limited to 'indra/newview/lltexlayer.cpp')
-rw-r--r-- | indra/newview/lltexlayer.cpp | 1554 |
1 files changed, 990 insertions, 564 deletions
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp index 716ab8eef4..500c2a7b86 100644 --- a/indra/newview/lltexlayer.cpp +++ b/indra/newview/lltexlayer.cpp @@ -2,37 +2,38 @@ * @file lltexlayer.cpp * @brief A texture layer. Used for avatars. * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" -#include "llagent.h" + #include "lltexlayer.h" + +#include "llagent.h" +#include "llimagej2c.h" +#include "llimagetga.h" +#include "llnotificationsutil.h" +#include "llvfile.h" +#include "llvfs.h" #include "llviewerstats.h" #include "llviewerregion.h" #include "llvoavatar.h" @@ -41,11 +42,52 @@ #include "llassetuploadresponders.h" #include "lltexlayerparams.h" #include "llui.h" +#include "llagentwearables.h" +#include "llwearable.h" +#include "llviewercontrol.h" +#include "llviewervisualparam.h" //#include "../tools/imdebug/imdebug.h" using namespace LLVOAvatarDefines; +class LLTexLayerInfo +{ + friend class LLTexLayer; + friend class LLTexLayerTemplate; + friend class LLTexLayerInterface; +public: + LLTexLayerInfo(); + ~LLTexLayerInfo(); + + BOOL parseXml(LLXmlTreeNode* node); + BOOL createVisualParams(LLVOAvatar *avatar); + BOOL isUserSettable() { return mLocalTexture != -1; } + S32 getLocalTexture() const { return mLocalTexture; } + BOOL getOnlyAlpha() const { return mUseLocalTextureAlphaOnly; } + std::string getName() const { return mName; } + +private: + std::string mName; + + BOOL mWriteAllChannels; // Don't use masking. Just write RGBA into buffer, + LLTexLayerInterface::ERenderPass mRenderPass; + + std::string mGlobalColor; + LLColor4 mFixedColor; + + S32 mLocalTexture; + std::string mStaticImageFileName; + BOOL mStaticImageIsMask; + BOOL mUseLocalTextureAlphaOnly; // Ignore RGB channels from the input texture. Use alpha as a mask + BOOL mIsVisibilityMask; + + typedef std::vector< std::pair< std::string,BOOL > > morph_name_list_t; + morph_name_list_t mMorphNameList; + param_color_info_list_t mParamColorInfoList; + param_alpha_info_list_t mParamAlphaInfoList; +}; + //----------------------------------------------------------------------------- // LLBakedUploadData() //----------------------------------------------------------------------------- @@ -66,96 +108,62 @@ LLBakedUploadData::LLBakedUploadData(const LLVOAvatarSelf* avatar, // static S32 LLTexLayerSetBuffer::sGLByteCount = 0; -S32 LLTexLayerSetBuffer::sGLBumpByteCount = 0; LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner, - S32 width, S32 height, - BOOL has_bump) : + S32 width, S32 height) : // ORDER_LAST => must render these after the hints are created. - LLDynamicTexture( width, height, 4, LLDynamicTexture::ORDER_LAST, TRUE ), - mNeedsUpdate( TRUE ), - mNeedsUpload( FALSE ), - mUploadPending( FALSE ), // Not used for any logic here, just to sync sending of updates - mTexLayerSet(owner), - mHasBump(has_bump), - mBumpTex(NULL) + LLViewerDynamicTexture( width, height, 4, LLViewerDynamicTexture::ORDER_LAST, TRUE ), + mUploadPending(FALSE), // Not used for any logic here, just to sync sending of updates + mNeedsUpload(FALSE), + mNumLowresUploads(0), + mNeedsUpdate(TRUE), + mNumLowresUpdates(0), + mTexLayerSet(owner) { LLTexLayerSetBuffer::sGLByteCount += getSize(); - createBumpTexture() ; + mNeedsUploadTimer.start(); + mNeedsUpdateTimer.start(); } LLTexLayerSetBuffer::~LLTexLayerSetBuffer() { LLTexLayerSetBuffer::sGLByteCount -= getSize(); - - if( mBumpTex.notNull()) + destroyGLTexture(); + for( S32 order = 0; order < ORDER_COUNT; order++ ) { - mBumpTex = NULL ; - LLImageGL::sGlobalTextureMemoryInBytes -= mWidth * mHeight * 4; - LLTexLayerSetBuffer::sGLBumpByteCount -= mWidth * mHeight * 4; + LLViewerDynamicTexture::sInstances[order].erase(this); // will fail in all but one case. } } //virtual +S8 LLTexLayerSetBuffer::getType() const +{ + return LLViewerDynamicTexture::LL_TEX_LAYER_SET_BUFFER ; +} + +//virtual void LLTexLayerSetBuffer::restoreGLTexture() { - createBumpTexture() ; - LLDynamicTexture::restoreGLTexture() ; + LLViewerDynamicTexture::restoreGLTexture() ; } //virtual void LLTexLayerSetBuffer::destroyGLTexture() { - if( mBumpTex.notNull() ) - { - mBumpTex = NULL ; - LLImageGL::sGlobalTextureMemoryInBytes -= mWidth * mHeight * 4; - LLTexLayerSetBuffer::sGLBumpByteCount -= mWidth * mHeight * 4; - } - - LLDynamicTexture::destroyGLTexture() ; -} - -void LLTexLayerSetBuffer::createBumpTexture() -{ - if( mHasBump ) - { - LLGLSUIDefault gls_ui; - mBumpTex = new LLImageGL(FALSE) ; - if(!mBumpTex->createGLTexture()) - { - mBumpTex = NULL ; - return ; - } - - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mBumpTex->getTexName()); - stop_glerror(); - - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - - LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_RGBA8, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - stop_glerror(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - LLImageGL::sGlobalTextureMemoryInBytes += mWidth * mHeight * 4; - LLTexLayerSetBuffer::sGLBumpByteCount += mWidth * mHeight * 4; - } + LLViewerDynamicTexture::destroyGLTexture() ; } // static void LLTexLayerSetBuffer::dumpTotalByteCount() { llinfos << "Composite System GL Buffers: " << (LLTexLayerSetBuffer::sGLByteCount/1024) << "KB" << llendl; - llinfos << "Composite System GL Bump Buffers: " << (LLTexLayerSetBuffer::sGLBumpByteCount/1024) << "KB" << llendl; } void LLTexLayerSetBuffer::requestUpdate() { + restartUpdateTimer(); mNeedsUpdate = TRUE; - + mNumLowresUpdates = 0; // If we're in the middle of uploading a baked texture, we don't care about it any more. // When it's downloaded, ignore it. mUploadID.setNull(); @@ -163,20 +171,39 @@ void LLTexLayerSetBuffer::requestUpdate() void LLTexLayerSetBuffer::requestUpload() { - if (!mNeedsUpload) + conditionalRestartUploadTimer(); + mNeedsUpload = TRUE; + mNumLowresUploads = 0; + mUploadPending = TRUE; +} + +void LLTexLayerSetBuffer::conditionalRestartUploadTimer() +{ + // If we requested a new upload but haven't even uploaded + // a low res version of our last upload request, then + // keep the timer ticking instead of resetting it. + if (mNeedsUpload && (mNumLowresUploads == 0)) + { + mNeedsUploadTimer.unpause(); + } + else { - mNeedsUpload = TRUE; - mUploadPending = TRUE; + mNeedsUploadTimer.reset(); + mNeedsUploadTimer.start(); } } +void LLTexLayerSetBuffer::restartUpdateTimer() +{ + mNeedsUpdateTimer.reset(); + mNeedsUpdateTimer.start(); +} + void LLTexLayerSetBuffer::cancelUpload() { - if (mNeedsUpload) - { - mNeedsUpload = FALSE; - } + mNeedsUpload = FALSE; mUploadPending = FALSE; + mNeedsUploadTimer.pause(); } void LLTexLayerSetBuffer::pushProjection() const @@ -184,7 +211,7 @@ void LLTexLayerSetBuffer::pushProjection() const glMatrixMode(GL_PROJECTION); gGL.pushMatrix(); glLoadIdentity(); - glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f); + glOrtho(0.0f, mFullWidth, 0.0f, mFullHeight, -1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); gGL.pushMatrix(); @@ -202,26 +229,34 @@ void LLTexLayerSetBuffer::popProjection() const BOOL LLTexLayerSetBuffer::needsRender() { - const LLVOAvatarSelf* avatar = mTexLayerSet->getAvatar(); - BOOL upload_now = mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal(); - BOOL needs_update = gAgentQueryManager.hasNoPendingQueries() && (mNeedsUpdate || upload_now) && !avatar->mAppearanceAnimating; - if (needs_update) + llassert(mTexLayerSet->getAvatar() == gAgentAvatarp); + if (!isAgentAvatarValid()) return FALSE; + + const BOOL upload_now = mNeedsUpload && isReadyToUpload(); + const BOOL update_now = mNeedsUpdate && isReadyToUpdate(); + + // Don't render if we don't want to (or aren't ready to) upload or update. + if (!(update_now || upload_now)) { - BOOL invalid_skirt = avatar->getBakedTE(mTexLayerSet) == LLVOAvatarDefines::TEX_SKIRT_BAKED && !avatar->isWearingWearableType(WT_SKIRT); - if (invalid_skirt) - { - // we were trying to create a skirt texture - // but we're no longer wearing a skirt... - needs_update = FALSE; - cancelUpload(); - } - else - { - needs_update &= (avatar->isSelf() || (avatar->isVisible() && !avatar->isCulled())); - needs_update &= mTexLayerSet->isLocalTextureDataAvailable(); - } + return FALSE; + } + + // Don't render if we're animating our appearance. + if (gAgentAvatarp->getIsAppearanceAnimating()) + { + return FALSE; } - return needs_update; + + // Don't render if we are trying to create a shirt texture but aren't wearing a skirt. + if (gAgentAvatarp->getBakedTE(mTexLayerSet) == LLVOAvatarDefines::TEX_SKIRT_BAKED && + !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT)) + { + cancelUpload(); + return FALSE; + } + + // Render if we have at least minimal level of detail for each local texture. + return mTexLayerSet->isLocalTextureDataAvailable(); } void LLTexLayerSetBuffer::preRender(BOOL clear_depth) @@ -230,92 +265,141 @@ void LLTexLayerSetBuffer::preRender(BOOL clear_depth) pushProjection(); // keep depth buffer, we don't need to clear it - LLDynamicTexture::preRender(FALSE); + LLViewerDynamicTexture::preRender(FALSE); } void LLTexLayerSetBuffer::postRender(BOOL success) { popProjection(); - LLDynamicTexture::postRender(success); + LLViewerDynamicTexture::postRender(success); } BOOL LLTexLayerSetBuffer::render() { - U8* baked_bump_data = NULL; - // Default color mask for tex layer render gGL.setColorMask(true, true); // do we need to upload, and do we have sufficient data to create an uploadable composite? - // When do we upload the texture if gAgent.mNumPendingQueries is non-zero? - BOOL upload_now = (gAgentQueryManager.hasNoPendingQueries() && mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal()); + // TODO: When do we upload the texture if gAgent.mNumPendingQueries is non-zero? + const BOOL upload_now = mNeedsUpload && isReadyToUpload(); + const BOOL update_now = mNeedsUpdate && isReadyToUpdate(); + BOOL success = TRUE; - // Composite bump - if( mBumpTex.notNull() ) - { - // Composite the bump data - success &= mTexLayerSet->renderBump( mOrigin.mX, mOrigin.mY, mWidth, mHeight ); - stop_glerror(); - - if (success) - { - LLGLSUIDefault gls_ui; - - // read back into texture (this is done externally for the color data) - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mBumpTex->getTexName()); - stop_glerror(); - - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mOrigin.mX, mOrigin.mY, mWidth, mHeight); - stop_glerror(); - - // if we need to upload the data, read it back into a buffer - if( upload_now ) - { - baked_bump_data = new U8[ mWidth * mHeight * 4 ]; - glReadPixels(mOrigin.mX, mOrigin.mY, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_bump_data ); - stop_glerror(); - } - } - } - // Composite the color data LLGLSUIDefault gls_ui; - success &= mTexLayerSet->render( mOrigin.mX, mOrigin.mY, mWidth, mHeight ); + success &= mTexLayerSet->render( mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight ); gGL.flush(); - if( upload_now ) + if(upload_now) { if (!success) { - llinfos << "Failed attempt to bake " << mTexLayerSet->getBodyRegion() << llendl; + llinfos << "Failed attempt to bake " << mTexLayerSet->getBodyRegionName() << llendl; mUploadPending = FALSE; } else { - readBackAndUpload(baked_bump_data); + if (mTexLayerSet->isVisible()) + { + mTexLayerSet->getAvatar()->debugBakedTextureUpload(mTexLayerSet->getBakedTexIndex(), FALSE); // FALSE for start of upload, TRUE for finish. + doUpload(); + } + else + { + mUploadPending = FALSE; + mNeedsUpload = FALSE; + mNeedsUploadTimer.pause(); + mTexLayerSet->getAvatar()->setNewBakedTexture(mTexLayerSet->getBakedTexIndex(),IMG_INVISIBLE); + } } } + + if (update_now) + { + doUpdate(); + } // reset GL state gGL.setColorMask(true, true); gGL.setSceneBlendType(LLRender::BT_ALPHA); // we have valid texture data now - mTexture->setGLTextureCreated(true); - mNeedsUpdate = FALSE; + mGLTexturep->setGLTextureCreated(true); - delete [] baked_bump_data; return success; } -bool LLTexLayerSetBuffer::isInitialized(void) const +BOOL LLTexLayerSetBuffer::isInitialized(void) const +{ + return mGLTexturep.notNull() && mGLTexturep->isGLTextureCreated(); +} + +BOOL LLTexLayerSetBuffer::uploadPending() const +{ + return mUploadPending; +} + +BOOL LLTexLayerSetBuffer::uploadNeeded() const +{ + return mNeedsUpload; +} + +BOOL LLTexLayerSetBuffer::uploadInProgress() const { - return mTexture.notNull() && mTexture->isGLTextureCreated(); + return !mUploadID.isNull(); +} + +BOOL LLTexLayerSetBuffer::isReadyToUpload() const +{ + if (!gAgentQueryManager.hasNoPendingQueries()) return FALSE; // Can't upload if there are pending queries. + if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) return FALSE; // Don't upload if avatar is using composites. + + // If we requested an upload and have the final LOD ready, then upload. + if (mTexLayerSet->isLocalTextureDataFinal()) return TRUE; + + // Upload if we've hit a timeout. Upload is a pretty expensive process so we need to make sure + // we aren't doing uploads too frequently. + const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout"); + if (texture_timeout != 0) + { + // The timeout period increases exponentially between every lowres upload in order to prevent + // spamming the server with frequent uploads. + const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads); + + // If we hit our timeout and have textures available at even lower resolution, then upload. + const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold; + const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable(); + if (has_lower_lod && is_upload_textures_timeout) return TRUE; + } + + return FALSE; +} + +BOOL LLTexLayerSetBuffer::isReadyToUpdate() const +{ + // If we requested an update and have the final LOD ready, then update. + if (mTexLayerSet->isLocalTextureDataFinal()) return TRUE; + + // If we haven't done an update yet, then just do one now regardless of state of textures. + if (mNumLowresUpdates == 0) return TRUE; + + // Update if we've hit a timeout. Unlike for uploads, we can make this timeout fairly small + // since render unnecessarily doesn't cost much. + const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedLocalTextureUpdateTimeout"); + if (texture_timeout != 0) + { + // If we hit our timeout and have textures available at even lower resolution, then update. + const BOOL is_update_textures_timeout = mNeedsUpdateTimer.getElapsedTimeF32() >= texture_timeout; + const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable(); + if (has_lower_lod && is_update_textures_timeout) return TRUE; + } + + return FALSE; } -BOOL LLTexLayerSetBuffer::updateImmediate() +BOOL LLTexLayerSetBuffer::requestUpdateImmediate() { mNeedsUpdate = TRUE; BOOL result = FALSE; @@ -330,90 +414,62 @@ BOOL LLTexLayerSetBuffer::updateImmediate() return result; } -void LLTexLayerSetBuffer::readBackAndUpload(const U8* baked_bump_data) +// Create the baked texture, send it out to the server, then wait for it to come +// back so we can switch to using it. +void LLTexLayerSetBuffer::doUpload() { - // pointers for storing data to upload - U8* baked_color_data = new U8[ mWidth * mHeight * 4 ]; - - glReadPixels(mOrigin.mX, mOrigin.mY, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_color_data ); - stop_glerror(); - - llinfos << "Baked " << mTexLayerSet->getBodyRegion() << llendl; + llinfos << "Uploading baked " << mTexLayerSet->getBodyRegionName() << llendl; LLViewerStats::getInstance()->incStat(LLViewerStats::ST_TEX_BAKES); - llassert( gAgent.getAvatarObject() == mTexLayerSet->getAvatar() ); - - // We won't need our caches since we're baked now. (Techically, we won't - // really be baked until this image is sent to the server and the Avatar - // Appearance message is received.) + // Don't need caches since we're baked now. (note: we won't *really* be baked + // until this image is sent to the server and the Avatar Appearance message is received.) mTexLayerSet->deleteCaches(); - LLGLSUIDefault gls_ui; + // Get the COLOR information from our texture + U8* baked_color_data = new U8[ mFullWidth * mFullHeight * 4 ]; + glReadPixels(mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_color_data ); + stop_glerror(); - LLPointer<LLImageRaw> baked_mask_image = new LLImageRaw(mWidth, mHeight, 1 ); + // Get the MASK information from our texture + LLGLSUIDefault gls_ui; + LLPointer<LLImageRaw> baked_mask_image = new LLImageRaw(mFullWidth, mFullHeight, 1 ); U8* baked_mask_data = baked_mask_image->getData(); - - mTexLayerSet->gatherMorphMaskAlpha(baked_mask_data, mWidth, mHeight); + mTexLayerSet->gatherMorphMaskAlpha(baked_mask_data, mFullWidth, mFullHeight); - // writes into baked_color_data - const char* comment_text = NULL; - S32 baked_image_components = mBumpTex.notNull() ? 5 : 4; // red green blue [bump] clothing - LLPointer<LLImageRaw> baked_image = new LLImageRaw( mWidth, mHeight, baked_image_components ); + // Create the baked image from our color and mask information + const S32 baked_image_components = 5; // red green blue [bump] clothing + LLPointer<LLImageRaw> baked_image = new LLImageRaw( mFullWidth, mFullHeight, baked_image_components ); U8* baked_image_data = baked_image->getData(); - - - if( mBumpTex.notNull() ) + S32 i = 0; + for (S32 u=0; u < mFullWidth; u++) { - comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // 5 channels: rgb, heightfield/alpha, mask - - S32 i = 0; - for( S32 u = 0; u < mWidth; u++ ) - { - for( S32 v = 0; v < mHeight; v++ ) - { - baked_image_data[5*i + 0] = baked_color_data[4*i + 0]; - baked_image_data[5*i + 1] = baked_color_data[4*i + 1]; - baked_image_data[5*i + 2] = baked_color_data[4*i + 2]; - baked_image_data[5*i + 3] = baked_color_data[4*i + 3]; // alpha should be correct for eyelashes. - baked_image_data[5*i + 4] = baked_mask_data[i]; - i++; - } - } - } - else + for (S32 v=0; v < mFullHeight; v++) { - S32 i = 0; - for( S32 u = 0; u < mWidth; u++ ) - { - for( S32 v = 0; v < mHeight; v++ ) - { - baked_image_data[4*i + 0] = baked_color_data[4*i + 0]; - baked_image_data[4*i + 1] = baked_color_data[4*i + 1]; - baked_image_data[4*i + 2] = baked_color_data[4*i + 2]; - baked_image_data[4*i + 3] = baked_color_data[4*i + 3]; // Use alpha, not bump - i++; - } - } + baked_image_data[5*i + 0] = baked_color_data[4*i + 0]; + baked_image_data[5*i + 1] = baked_color_data[4*i + 1]; + baked_image_data[5*i + 2] = baked_color_data[4*i + 2]; + baked_image_data[5*i + 3] = baked_color_data[4*i + 3]; // alpha should be correct for eyelashes. + baked_image_data[5*i + 4] = baked_mask_data[i]; + i++; } + } LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C; compressedImage->setRate(0.f); - LLTransactionID tid; - LLAssetID asset_id; - tid.generate(); - asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - - BOOL res = false; - if( compressedImage->encode(baked_image, comment_text)) - { - res = LLVFile::writeFile(compressedImage->getData(), compressedImage->getDataSize(), - gVFS, asset_id, LLAssetType::AT_TEXTURE); - if (res) + const char* comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // writes into baked_color_data. 5 channels (rgb, heightfield/alpha, mask) + if (compressedImage->encode(baked_image, comment_text)) + { + LLTransactionID tid; + tid.generate(); + const LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + if (LLVFile::writeFile(compressedImage->getData(), compressedImage->getDataSize(), + gVFS, asset_id, LLAssetType::AT_TEXTURE)) { - LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C; + // Read back the file and validate. BOOL valid = FALSE; - S32 file_size; + LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C; + S32 file_size = 0; U8* data = LLVFile::readFile(gVFS, asset_id, LLAssetType::AT_TEXTURE, &file_size); if (data) { @@ -424,29 +480,27 @@ void LLTexLayerSetBuffer::readBackAndUpload(const U8* baked_bump_data) integrity_test->setLastError("Unable to read entire file"); } - if( valid ) + if (valid) { - // baked_upload_data is owned by the responder and deleted after the request completes - LLBakedUploadData* baked_upload_data = - new LLBakedUploadData(gAgent.getAvatarObject(), this->mTexLayerSet, asset_id); + // Baked_upload_data is owned by the responder and deleted after the request completes. + LLBakedUploadData* baked_upload_data = new LLBakedUploadData(gAgentAvatarp, + this->mTexLayerSet, + asset_id); + // upload ID is used to avoid overlaps, e.g. when the user rapidly makes two changes outside of Face Edit. mUploadID = asset_id; - - // upload the image - std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture"); + // Upload the image + const std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture"); if(!url.empty() - && !LLPipeline::sForceOldBakedUpload) // Toggle the debug setting UploadBakedTexOld to change between the new caps method and old method + && !LLPipeline::sForceOldBakedUpload) // toggle debug setting UploadBakedTexOld to change between the new caps method and old method { - llinfos << "Baked texture upload via capability of " << mUploadID << " to " << url << llendl; - LLSD body = LLSD::emptyMap(); + // The responder will call LLTexLayerSetBuffer::onTextureUploadComplete() LLHTTPClient::post(url, body, new LLSendTexLayerResponder(body, mUploadID, LLAssetType::AT_TEXTURE, baked_upload_data)); - // Responder will call LLTexLayerSetBuffer::onTextureUploadComplete() + llinfos << "Baked texture upload via capability of " << mUploadID << " to " << url << llendl; } else { - llinfos << "Baked texture upload via Asset Store." << llendl; - // gAssetStorage->storeAssetData(mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)this, FALSE); gAssetStorage->storeAssetData(tid, LLAssetType::AT_TEXTURE, LLTexLayerSetBuffer::onTextureUploadComplete, @@ -454,28 +508,92 @@ void LLTexLayerSetBuffer::readBackAndUpload(const U8* baked_bump_data) TRUE, // temp_file TRUE, // is_priority TRUE); // store_local + llinfos << "Baked texture upload via Asset Store." << llendl; + } + + const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal(); + if (highest_lod) + { + // Sending the final LOD for the baked texture. All done, pause + // the upload timer so we know how long it took. + mNeedsUpload = FALSE; + mNeedsUploadTimer.pause(); + } + else + { + // Sending a lower level LOD for the baked texture. Restart the upload timer. + mNumLowresUploads++; + mNeedsUploadTimer.unpause(); + mNeedsUploadTimer.reset(); + } + + // Print out notification that we uploaded this texture. + if (gSavedSettings.getBOOL("DebugAvatarRezTime")) + { + const std::string lod_str = highest_lod ? "HighRes" : "LowRes"; + LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mTexLayerSet->getAvatar()->debugGetExistenceTimeElapsedF32()); + args["TIME"] = llformat("%d",(U32)mNeedsUploadTimer.getElapsedTimeF32()); + args["BODYREGION"] = mTexLayerSet->getBodyRegionName(); + args["RESOLUTION"] = lod_str; + LLNotificationsUtil::add("AvatarRezSelfBakedTextureUploadNotification",args); + llinfos << "Uploading [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUploadTimer.getElapsedTimeF32() << " ]" << llendl; } - - mNeedsUpload = FALSE; } else { + // The read back and validate operation failed. Remove the uploaded file. mUploadPending = FALSE; - llinfos << "unable to create baked upload file: corrupted" << llendl; LLVFile file(gVFS, asset_id, LLAssetType::AT_TEXTURE, LLVFile::WRITE); file.remove(); + llinfos << "Unable to create baked upload file (reason: corrupted)." << llendl; } } } - if (!res) + else { + // The VFS write file operation failed. mUploadPending = FALSE; - llinfos << "unable to create baked upload file" << llendl; + llinfos << "Unable to create baked upload file (reason: failed to write file)" << llendl; } delete [] baked_color_data; } +// Mostly bookkeeping; don't need to actually "do" anything since +// render() will actually do the update. +void LLTexLayerSetBuffer::doUpdate() +{ + const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal(); + if (highest_lod) + { + mNeedsUpdate = FALSE; + } + else + { + mNumLowresUpdates++; + } + + restartUpdateTimer(); + + // need to swtich to using this layerset if this is the first update + // after getting the lowest LOD + mTexLayerSet->getAvatar()->updateMeshTextures(); + + // Print out notification that we uploaded this texture. + if (gSavedSettings.getBOOL("DebugAvatarRezTime")) + { + const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal(); + const std::string lod_str = highest_lod ? "HighRes" : "LowRes"; + LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mTexLayerSet->getAvatar()->debugGetExistenceTimeElapsedF32()); + args["TIME"] = llformat("%d",(U32)mNeedsUpdateTimer.getElapsedTimeF32()); + args["BODYREGION"] = mTexLayerSet->getBodyRegionName(); + args["RESOLUTION"] = lod_str; + LLNotificationsUtil::add("AvatarRezSelfBakedTextureUpdateNotification",args); + llinfos << "Locally updating [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUpdateTimer.getElapsedTimeF32() << " ]" << llendl; + } +} // static void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, @@ -485,14 +603,11 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, { LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)userdata; - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - - if (0 == result && - avatar && - !avatar->isDead() && - baked_upload_data->mAvatar == avatar && // Sanity check: only the user's avatar should be uploading textures. - baked_upload_data->mTexLayerSet->hasComposite() - ) + if ((result == 0) && + isAgentAvatarValid() && + !gAgentAvatarp->isDead() && + (baked_upload_data->mAvatar == gAgentAvatarp) && // Sanity check: only the user's avatar should be uploading textures. + (baked_upload_data->mTexLayerSet->hasComposite())) { LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mTexLayerSet->getComposite(); @@ -511,15 +626,14 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, { // This is the upload we're currently waiting for. layerset_buffer->mUploadID.setNull(); - layerset_buffer->mUploadPending = FALSE; - if (result >= 0) { - LLVOAvatarDefines::ETextureIndex baked_te = avatar->getBakedTE(layerset_buffer->mTexLayerSet); + layerset_buffer->mUploadPending = FALSE; + LLVOAvatarDefines::ETextureIndex baked_te = gAgentAvatarp->getBakedTE(layerset_buffer->mTexLayerSet); // Update baked texture info with the new UUID U64 now = LLFrameTimer::getTotalTime(); // Record starting time llinfos << "Baked texture upload took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl; - avatar->setNewBakedTexture(baked_te, uuid); + gAgentAvatarp->setNewBakedTexture(baked_te, uuid); } else { @@ -533,7 +647,7 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, llinfos << "Received baked texture out of date, ignored." << llendl; } - avatar->dirtyMesh(); + gAgentAvatarp->dirtyMesh(); } else { @@ -548,27 +662,6 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, delete baked_upload_data; } -void LLTexLayerSetBuffer::bindBumpTexture( U32 stage ) -{ - if( mBumpTex.notNull() ) - { - gGL.getTexUnit(stage)->bindManual(LLTexUnit::TT_TEXTURE, mBumpTex->getTexName()); - gGL.getTexUnit(0)->activate(); - - if( mLastBindTime != LLImageGL::sLastFrameTime ) - { - mLastBindTime = LLImageGL::sLastFrameTime; - LLImageGL::updateBoundTexMem(mWidth * mHeight * 4); - } - } - else - { - gGL.getTexUnit(stage)->unbind(LLTexUnit::TT_TEXTURE); - gGL.getTexUnit(0)->activate(); - } -} - - //----------------------------------------------------------------------------- // LLTexLayerSet // An ordered set of texture layers that get composited into a single texture. @@ -663,7 +756,8 @@ LLTexLayerSet::LLTexLayerSet(LLVOAvatarSelf* const avatar) : mComposite( NULL ), mAvatar( avatar ), mUpdatesEnabled( FALSE ), - mHasBump( FALSE ), + mIsVisible( TRUE ), + mBakedTexIndex(LLVOAvatarDefines::BAKED_HEAD), mInfo( NULL ) { } @@ -673,7 +767,6 @@ LLTexLayerSet::~LLTexLayerSet() deleteCaches(); std::for_each(mLayerList.begin(), mLayerList.end(), DeletePointer()); std::for_each(mMaskLayerList.begin(), mMaskLayerList.end(), DeletePointer()); - delete mComposite; } //----------------------------------------------------------------------------- @@ -691,16 +784,25 @@ BOOL LLTexLayerSet::setInfo(const LLTexLayerSetInfo *info) iter != info->mLayerInfoList.end(); iter++) { - LLTexLayer* layer = new LLTexLayer( this ); - if (!layer->setInfo(*iter)) + LLTexLayerInterface *layer = NULL; + if ( (*iter)->isUserSettable() ) + { + layer = new LLTexLayerTemplate( this ); + } + else + { + layer = new LLTexLayer(this); + } + // this is the first time this layer (of either type) is being created - make sure you add the parameters to the avatar + if (!layer->setInfo(*iter, NULL)) { mInfo = NULL; return FALSE; } if (!layer->isVisibilityMask()) { - mLayerList.push_back( layer ); - } + mLayerList.push_back( layer ); + } else { mMaskLayerList.push_back(layer); @@ -741,12 +843,12 @@ void LLTexLayerSet::deleteCaches() { for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) { - LLTexLayer* layer = *iter; + LLTexLayerInterface* layer = *iter; layer->deleteCaches(); } for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++) { - LLTexLayer* layer = *iter; + LLTexLayerInterface* layer = *iter; layer->deleteCaches(); } } @@ -770,74 +872,80 @@ BOOL LLTexLayerSet::isLocalTextureDataFinal() const BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height ) { BOOL success = TRUE; + mIsVisible = TRUE; - LLGLSUIDefault gls_ui; - LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE); - gGL.setColorMask(true, true); - - BOOL render_morph = mAvatar->morphMaskNeedsUpdate(mBakedTexIndex); - - // composite color layers - for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) + if (mMaskLayerList.size() > 0) { - LLTexLayer* layer = *iter; - if (layer->getRenderPass() == LLTexLayer::RP_COLOR) + for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++) { - gGL.flush(); - success &= layer->render(x, y, width, height, render_morph); - gGL.flush(); - if (layer->isMorphValid()) + LLTexLayerInterface* layer = *iter; + if (layer->isInvisibleAlphaMask()) { - mAvatar->setMorphMasksValid(TRUE, mBakedTexIndex); + mIsVisible = FALSE; } } } - - renderAlphaMaskTextures(width, height, false); - - stop_glerror(); - - return success; -} - -BOOL LLTexLayerSet::renderBump( S32 x, S32 y, S32 width, S32 height ) -{ - BOOL success = TRUE; LLGLSUIDefault gls_ui; LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE); + gGL.setColorMask(true, true); - //static S32 bump_layer_count = 1; + // clear buffer area to ensure we don't pick up UI elements + { + gGL.flush(); + LLGLDisable no_alpha(GL_ALPHA_TEST); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4f( 0.f, 0.f, 0.f, 1.f ); - for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) + gl_rect_2d_simple( width, height ); + + gGL.flush(); + } + + if (mIsVisible) { - LLTexLayer* layer = *iter; - if (layer->getRenderPass() == LLTexLayer::RP_BUMP) + // composite color layers + for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) { -// success &= layer->render(x, y, width, height); + LLTexLayerInterface* layer = *iter; + if (layer->getRenderPass() == LLTexLayer::RP_COLOR) + { + gGL.flush(); + success &= layer->render(x, y, width, height); + gGL.flush(); + } } + + renderAlphaMaskTextures(x, y, width, height, false); + + stop_glerror(); } + else + { + gGL.flush(); - // Set the alpha channel to one (clean up after previous blending) - LLGLDisable no_alpha(GL_ALPHA_TEST); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4f( 0.f, 0.f, 0.f, 1.f ); - gGL.setColorMask(false, true); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + LLGLDisable no_alpha(GL_ALPHA_TEST); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4f( 0.f, 0.f, 0.f, 0.f ); - gl_rect_2d_simple( width, height ); - - gGL.setColorMask(true, true); - stop_glerror(); + gl_rect_2d_simple( width, height ); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + gGL.flush(); + + } return success; } + BOOL LLTexLayerSet::isBodyRegion(const std::string& region) const { return mInfo->mBodyRegion == region; } -const std::string LLTexLayerSet::getBodyRegion() const +const std::string LLTexLayerSet::getBodyRegionName() const { return mInfo->mBodyRegion; } @@ -867,18 +975,16 @@ void LLTexLayerSet::cancelUpload() void LLTexLayerSet::createComposite() { - if( !mComposite ) + if(!mComposite) { S32 width = mInfo->mWidth; S32 height = mInfo->mHeight; // Composite other avatars at reduced resolution if( !mAvatar->isSelf() ) { - // TODO: replace with sanity check to ensure not called for non-self avatars -// width /= 2; -// height /= 2; + llerrs << "composites should not be created for non-self avatars!" << llendl; } - mComposite = new LLTexLayerSetBuffer( this, width, height, mHasBump ); + mComposite = new LLTexLayerSetBuffer( this, width, height ); } } @@ -886,7 +992,6 @@ void LLTexLayerSet::destroyComposite() { if( mComposite ) { - delete mComposite; mComposite = NULL; } } @@ -900,71 +1005,55 @@ void LLTexLayerSet::setUpdatesEnabled( BOOL b ) void LLTexLayerSet::updateComposite() { createComposite(); - mComposite->updateImmediate(); + mComposite->requestUpdateImmediate(); } LLTexLayerSetBuffer* LLTexLayerSet::getComposite() { - createComposite(); + if (!mComposite) + { + createComposite(); + } return mComposite; } -void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 width, S32 height) +const LLTexLayerSetBuffer* LLTexLayerSet::getComposite() const { - S32 size = width * height; + return mComposite; +} +void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 width, S32 height) +{ memset(data, 255, width * height); - BOOL render_morph = mAvatar->morphMaskNeedsUpdate(mBakedTexIndex); - for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) { - LLTexLayer* layer = *iter; - U8* alphaData = layer->getAlphaData(); - if (!alphaData && layer->hasAlphaParams()) - { - LLColor4 net_color; - layer->findNetColor( &net_color ); - // TODO: eliminate need for layer morph mask valid flag - layer->invalidateMorphMasks(); - mAvatar->invalidateMorphMasks(mBakedTexIndex); - layer->renderMorphMasks(mComposite->getOriginX(), mComposite->getOriginY(), width, height, net_color, render_morph); - alphaData = layer->getAlphaData(); - } - if (alphaData) - { - for( S32 i = 0; i < size; i++ ) - { - U8 curAlpha = data[i]; - U16 resultAlpha = curAlpha; - resultAlpha *= (alphaData[i] + 1); - resultAlpha = resultAlpha >> 8; - data[i] = (U8)resultAlpha; - } - } + LLTexLayerInterface* layer = *iter; + layer->gatherAlphaMasks(data, mComposite->getOriginX(),mComposite->getOriginY(), width, height); } // Set alpha back to that of our alpha masks. - renderAlphaMaskTextures(width, height, true); + renderAlphaMaskTextures(mComposite->getOriginX(), mComposite->getOriginY(), width, height, true); } -void LLTexLayerSet::renderAlphaMaskTextures(S32 width, S32 height, bool forceClear) +void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, bool forceClear) { const LLTexLayerSetInfo *info = getInfo(); gGL.setColorMask(false, true); gGL.setSceneBlendType(LLRender::BT_REPLACE); + // (Optionally) replace alpha with a single component image from a tga file. - if (!info->mStaticAlphaFileName.empty() && mMaskLayerList.empty()) + if (!info->mStaticAlphaFileName.empty()) { LLGLSNoAlphaTest gls_no_alpha_test; gGL.flush(); { - LLImageGL* image_gl = LLTexLayerStaticImageList::getInstance()->getImageGL(info->mStaticAlphaFileName, TRUE); - if( image_gl ) + LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(info->mStaticAlphaFileName, TRUE); + if( tex ) { LLGLSUIDefault gls_ui; - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(tex); gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE ); gl_rect_2d_simple_tex( width, height ); } @@ -992,9 +1081,9 @@ void LLTexLayerSet::renderAlphaMaskTextures(S32 width, S32 height, bool forceCle gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE ); for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++) { - LLTexLayer* layer = *iter; + LLTexLayerInterface* layer = *iter; gGL.flush(); - layer->blendAlphaTexture(width, height); + layer->blendAlphaTexture(x,y,width, height); gGL.flush(); } @@ -1012,21 +1101,29 @@ void LLTexLayerSet::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_ mAvatar->applyMorphMask(tex_data, width, height, num_components, mBakedTexIndex); } -//----------------------------------------------------------------------------- -// finds a specific layer based on a passed in name -//----------------------------------------------------------------------------- -LLTexLayer* LLTexLayerSet::findLayerByName(std::string name) +BOOL LLTexLayerSet::isMorphValid() const { - for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) + for(layer_list_t::const_iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) { - LLTexLayer* layer = *iter; + const LLTexLayerInterface* layer = *iter; + if (layer && !layer->isMorphValid()) + { + return FALSE; + } + } + return TRUE; +} - if (layer->getName().compare(name) == 0) +void LLTexLayerSet::invalidateMorphMasks() +{ + for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) + { + LLTexLayerInterface* layer = *iter; + if (layer) { - return layer; + layer->invalidateMorphMasks(); } } - return NULL; } @@ -1129,7 +1226,7 @@ BOOL LLTexLayerInfo::parseXml(LLXmlTreeNode* node) } if (mLocalTexture == TEX_NUM_INDICES) { - llwarns << "<texture> element has invalid local_texure attribute: " << mName << " " << local_texture_name << llendl; + llwarns << "<texture> element has invalid local_texture attribute: " << mName << " " << local_texture_name << llendl; return FALSE; } } @@ -1196,7 +1293,7 @@ BOOL LLTexLayerInfo::createVisualParams(LLVOAvatar *avatar) { LLTexLayerParamColorInfo * color_info = *color_info_iter; LLTexLayerParamColor* param_color = new LLTexLayerParamColor(avatar); - if (!param_color->setInfo(color_info)) + if (!param_color->setInfo(color_info, TRUE)) { llwarns << "NULL TexLayer Color Param could not be added to visual param list. Deleting." << llendl; delete param_color; @@ -1210,7 +1307,7 @@ BOOL LLTexLayerInfo::createVisualParams(LLVOAvatar *avatar) { LLTexLayerParamAlphaInfo * alpha_info = *alpha_info_iter; LLTexLayerParamAlpha* param_alpha = new LLTexLayerParamAlpha(avatar); - if (!param_alpha->setInfo(alpha_info)) + if (!param_alpha->setInfo(alpha_info, TRUE)) { llwarns << "NULL TexLayer Alpha Param could not be added to visual param list. Deleting." << llendl; delete param_alpha; @@ -1221,6 +1318,143 @@ BOOL LLTexLayerInfo::createVisualParams(LLVOAvatar *avatar) return success; } +LLTexLayerInterface::LLTexLayerInterface(LLTexLayerSet* const layer_set): + mTexLayerSet( layer_set ), + mMorphMasksValid( FALSE ), + mInfo(NULL), + mHasMorph(FALSE) +{ +} + +LLTexLayerInterface::LLTexLayerInterface(const LLTexLayerInterface &layer, LLWearable *wearable): + mTexLayerSet( layer.mTexLayerSet ), + mInfo(NULL) +{ + // don't add visual params for cloned layers + setInfo(layer.getInfo(), wearable); + + mHasMorph = layer.mHasMorph; +} + +BOOL LLTexLayerInterface::setInfo(const LLTexLayerInfo *info, LLWearable* wearable ) // This sets mInfo and calls initialization functions +{ + // setInfo should only be called once. Code is not robust enough to handle redefinition of a texlayer. + // Not a critical warning, but could be useful for debugging later issues. -Nyx + if (mInfo != NULL) + { + llwarns << "mInfo != NULL" << llendl; + } + mInfo = info; + //mID = info->mID; // No ID + + mParamColorList.reserve(mInfo->mParamColorInfoList.size()); + for (param_color_info_list_t::const_iterator iter = mInfo->mParamColorInfoList.begin(); + iter != mInfo->mParamColorInfoList.end(); + iter++) + { + LLTexLayerParamColor* param_color; + if (!wearable) + { + param_color = new LLTexLayerParamColor(this); + if (!param_color->setInfo(*iter, TRUE)) + { + mInfo = NULL; + return FALSE; + } + } + else + { + param_color = (LLTexLayerParamColor*)wearable->getVisualParam((*iter)->getID()); + if (!param_color) + { + mInfo = NULL; + return FALSE; + } + } + mParamColorList.push_back( param_color ); + } + + mParamAlphaList.reserve(mInfo->mParamAlphaInfoList.size()); + for (param_alpha_info_list_t::const_iterator iter = mInfo->mParamAlphaInfoList.begin(); + iter != mInfo->mParamAlphaInfoList.end(); + iter++) + { + LLTexLayerParamAlpha* param_alpha; + if (!wearable) + { + param_alpha = new LLTexLayerParamAlpha( this ); + if (!param_alpha->setInfo(*iter, TRUE)) + { + mInfo = NULL; + return FALSE; + } + } + else + { + param_alpha = (LLTexLayerParamAlpha*) wearable->getVisualParam((*iter)->getID()); + if (!param_alpha) + { + mInfo = NULL; + return FALSE; + } + } + mParamAlphaList.push_back( param_alpha ); + } + + return TRUE; +} + +/*virtual*/ void LLTexLayerInterface::requestUpdate() +{ + mTexLayerSet->requestUpdate(); +} + +const std::string& LLTexLayerInterface::getName() const +{ + return mInfo->mName; +} + +LLTexLayerInterface::ERenderPass LLTexLayerInterface::getRenderPass() const +{ + return mInfo->mRenderPass; +} + +const std::string& LLTexLayerInterface::getGlobalColor() const +{ + return mInfo->mGlobalColor; +} + +BOOL LLTexLayerInterface::isVisibilityMask() const +{ + return mInfo->mIsVisibilityMask; +} + +void LLTexLayerInterface::invalidateMorphMasks() +{ + mMorphMasksValid = FALSE; +} + +LLViewerVisualParam* LLTexLayerInterface::getVisualParamPtr(S32 index) const +{ + LLViewerVisualParam *result = NULL; + for (param_color_list_t::const_iterator color_iter = mParamColorList.begin(); color_iter != mParamColorList.end() && !result; ++color_iter) + { + if ((*color_iter)->getID() == index) + { + result = *color_iter; + } + } + for (param_alpha_list_t::const_iterator alpha_iter = mParamAlphaList.begin(); alpha_iter != mParamAlphaList.end() && !result; ++alpha_iter) + { + if ((*alpha_iter)->getID() == index) + { + result = *alpha_iter; + } + } + + return result; +} + //----------------------------------------------------------------------------- // LLTexLayer // A single texture layer, consisting of: @@ -1234,12 +1468,21 @@ BOOL LLTexLayerInfo::createVisualParams(LLVOAvatar *avatar) // * a texture entry index (TE) // * (optional) one or more alpha parameters (weighted alpha textures) //----------------------------------------------------------------------------- -LLTexLayer::LLTexLayer(LLTexLayerSet* layer_set) : - mTexLayerSet( layer_set ), - mMorphMasksValid( FALSE ), - mStaticImageInvalid( FALSE ), - mInfo(NULL), - mHasMorph(FALSE) +LLTexLayer::LLTexLayer(LLTexLayerSet* const layer_set) : + LLTexLayerInterface( layer_set ), + mLocalTextureObject(NULL) +{ +} + +LLTexLayer::LLTexLayer(const LLTexLayer &layer, LLWearable *wearable) : + LLTexLayerInterface( layer, wearable ), + mLocalTextureObject(NULL) +{ +} + +LLTexLayer::LLTexLayer(const LLTexLayerTemplate &layer_template, LLLocalTextureObject *lto, LLWearable *wearable) : + LLTexLayerInterface( layer_template, wearable ), + mLocalTextureObject(lto) { } @@ -1263,44 +1506,9 @@ LLTexLayer::~LLTexLayer() // setInfo //----------------------------------------------------------------------------- -BOOL LLTexLayer::setInfo(const LLTexLayerInfo* info) +BOOL LLTexLayer::setInfo(const LLTexLayerInfo* info, LLWearable* wearable ) { - llassert(mInfo == NULL); - mInfo = info; - //mID = info->mID; // No ID - - if (info->mRenderPass == LLTexLayer::RP_BUMP) - mTexLayerSet->setBump(TRUE); - - mParamColorList.reserve(mInfo->mParamColorInfoList.size()); - for (param_color_info_list_t::const_iterator iter = mInfo->mParamColorInfoList.begin(); - iter != mInfo->mParamColorInfoList.end(); - iter++) - { - LLTexLayerParamColor* param_color = new LLTexLayerParamColor(this); - if (!param_color->setInfo(*iter)) - { - mInfo = NULL; - return FALSE; - } - mParamColorList.push_back( param_color ); - } - - mParamAlphaList.reserve(mInfo->mParamAlphaInfoList.size()); - for (param_alpha_info_list_t::const_iterator iter = mInfo->mParamAlphaInfoList.begin(); - iter != mInfo->mParamAlphaInfoList.end(); - iter++) - { - LLTexLayerParamAlpha* param_alpha = new LLTexLayerParamAlpha( this ); - if (!param_alpha->setInfo(*iter)) - { - mInfo = NULL; - return FALSE; - } - mParamAlphaList.push_back( param_alpha ); - } - - return TRUE; + return LLTexLayerInterface::setInfo(info, wearable); } //static @@ -1308,12 +1516,12 @@ void LLTexLayer::calculateTexLayerColor(const param_color_list_t ¶m_list, LL { for (param_color_list_t::const_iterator iter = param_list.begin(); iter != param_list.end(); iter++) -{ + { const LLTexLayerParamColor* param = *iter; LLColor4 param_net = param->getNetColor(); const LLTexLayerParamColorInfo *info = (LLTexLayerParamColorInfo *)param->getInfo(); switch(info->getOperation()) - { + { case LLTexLayerParamColor::OP_ADD: net_color += param_net; break; @@ -1326,13 +1534,14 @@ void LLTexLayer::calculateTexLayerColor(const param_color_list_t ¶m_list, LL default: llassert(0); break; - } + } } net_color.clamp(); } -void LLTexLayer::deleteCaches() +/*virtual*/ void LLTexLayer::deleteCaches() { + // Only need to delete caches for alpha params. Color params don't hold extra memory for (param_alpha_list_t::iterator iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++ ) { @@ -1341,7 +1550,7 @@ void LLTexLayer::deleteCaches() } } -BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) +BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height) { LLGLEnable color_mat(GL_COLOR_MATERIAL); gPipeline.disableLights(); @@ -1369,7 +1578,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) { // If we have alpha masks, but we're skipping all of them, skip the whole layer. // However, we can't do this optimization if we have morph masks that need updating. - if (!mHasMorph) +/* if (!mHasMorph) { BOOL skip_layer = TRUE; @@ -1390,9 +1599,9 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) { return success; } - } + }//*/ - renderMorphMasks(x, y, width, height, net_color, render_morph); + renderMorphMasks(x, y, width, height, net_color); alpha_mask_specified = TRUE; gGL.flush(); gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ONE_MINUS_DEST_ALPHA); @@ -1409,16 +1618,28 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) if( (getInfo()->mLocalTexture != -1) && !getInfo()->mUseLocalTextureAlphaOnly ) { { - LLImageGL* image_gl = NULL; - if( mTexLayerSet->getAvatar()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl ) ) + LLViewerTexture* tex = NULL; + if (mLocalTextureObject && mLocalTextureObject->getImage()) + { + tex = mLocalTextureObject->getImage(); + if (mLocalTextureObject->getID() == IMG_DEFAULT_AVATAR) + { + tex = NULL; + } + } + else { - if( image_gl ) + llinfos << "lto not defined or image not defined: " << getInfo()->getLocalTexture() << " lto: " << mLocalTextureObject << llendl; + } +// if( mTexLayerSet->getAvatar()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl ) ) + { + if( tex ) { LLGLDisable alpha_test(getInfo()->mWriteAllChannels ? GL_ALPHA_TEST : 0); - LLTexUnit::eTextureAddressMode old_mode = image_gl->getAddressMode(); + LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode(); - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(tex, TRUE); gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); gl_rect_2d_simple_tex( width, height ); @@ -1427,20 +1648,20 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } } - else - { - success = FALSE; - } +// else +// { +// success = FALSE; +// } } } if( !getInfo()->mStaticImageFileName.empty() ) { { - LLImageGL* image_gl = LLTexLayerStaticImageList::getInstance()->getImageGL(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); - if( image_gl ) + LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); + if( tex ) { - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(tex, TRUE); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } @@ -1477,22 +1698,23 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) return success; } -U8* LLTexLayer::getAlphaData() +const U8* LLTexLayer::getAlphaData() const { LLCRC alpha_mask_crc; - const LLUUID& uuid = mTexLayerSet->getAvatar()->getLocalTextureID((ETextureIndex)getInfo()->mLocalTexture); + const LLUUID& uuid = getUUID(); alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES); for (param_alpha_list_t::const_iterator iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++) { const LLTexLayerParamAlpha* param = *iter; + // MULTI-WEARABLE: verify visual parameters used here F32 param_weight = param->getWeight(); alpha_mask_crc.update((U8*)¶m_weight, sizeof(F32)); } U32 cache_index = alpha_mask_crc.getCRC(); - alpha_cache_t::iterator iter2 = mAlphaCache.find(cache_index); + alpha_cache_t::const_iterator iter2 = mAlphaCache.find(cache_index); return (iter2 == mAlphaCache.end()) ? 0 : iter2->second; } @@ -1540,7 +1762,7 @@ BOOL LLTexLayer::findNetColor(LLColor4* net_color) const return FALSE; // No need to draw a separate colored polygon } -BOOL LLTexLayer::blendAlphaTexture(S32 width, S32 height) +BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height) { BOOL success = TRUE; @@ -1548,11 +1770,11 @@ BOOL LLTexLayer::blendAlphaTexture(S32 width, S32 height) if( !getInfo()->mStaticImageFileName.empty() ) { - LLImageGL* image_gl = LLTexLayerStaticImageList::getInstance()->getImageGL( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask ); - if( image_gl ) + LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask ); + if( tex ) { LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(tex, TRUE); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } @@ -1565,17 +1787,14 @@ BOOL LLTexLayer::blendAlphaTexture(S32 width, S32 height) { if (getInfo()->mLocalTexture >=0 && getInfo()->mLocalTexture < TEX_NUM_INDICES) { - LLImageGL* image_gl = NULL; - if (mTexLayerSet->getAvatar()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl)) + LLViewerTexture* tex = mLocalTextureObject->getImage(); + if (tex) { - if (image_gl) - { - LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(image_gl); - gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - success = TRUE; - } + LLGLSNoAlphaTest gls_no_alpha_test; + gGL.getTexUnit(0)->bind(tex); + gl_rect_2d_simple_tex( width, height ); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + success = TRUE; } } } @@ -1583,8 +1802,12 @@ BOOL LLTexLayer::blendAlphaTexture(S32 width, S32 height) return success; } +/*virtual*/ void LLTexLayer::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height) +{ + addAlphaMask(data, originX, originY, width, height); +} -BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, BOOL render_morph) +BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color) { BOOL success = TRUE; @@ -1623,49 +1846,38 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC // Accumulate the alpha component of the texture if( getInfo()->mLocalTexture != -1 ) { - LLImageGL* image_gl = NULL; - if( mTexLayerSet->getAvatar()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl ) ) - { - if( image_gl && (image_gl->getComponents() == 4) ) - { - LLGLSNoAlphaTest gls_no_alpha_test; + LLViewerTexture* tex = mLocalTextureObject->getImage(); + if( tex && (tex->getComponents() == 4) ) + { + LLGLSNoAlphaTest gls_no_alpha_test; - LLTexUnit::eTextureAddressMode old_mode = image_gl->getAddressMode(); - - gGL.getTexUnit(0)->bind(image_gl); - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode(); + + gGL.getTexUnit(0)->bind(tex, TRUE); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - gl_rect_2d_simple_tex( width, height ); + gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->setTextureAddressMode(old_mode); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - } - else - { - success = FALSE; - } + gGL.getTexUnit(0)->setTextureAddressMode(old_mode); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } + } if( !getInfo()->mStaticImageFileName.empty() ) { - LLImageGL* image_gl = LLTexLayerStaticImageList::getInstance()->getImageGL(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); - if( image_gl ) - { - if( (image_gl->getComponents() == 4) || - ( (image_gl->getComponents() == 1) && getInfo()->mStaticImageIsMask ) ) - { - LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(image_gl); - gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - } - else + LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); + if( tex ) + { + if( (tex->getComponents() == 4) || + ( (tex->getComponents() == 1) && getInfo()->mStaticImageIsMask ) ) { - success = FALSE; + LLGLSNoAlphaTest gls_no_alpha_test; + gGL.getTexUnit(0)->bind(tex, TRUE); + gl_rect_2d_simple_tex( width, height ); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } } + } // Draw a rectangle with the layer color to multiply the alpha by that color's alpha. // Note: we're still using gGL.blendFunc( GL_DST_ALPHA, GL_ZERO ); @@ -1682,10 +1894,10 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC gGL.setColorMask(true, true); - if (render_morph && mHasMorph) + if (hasMorph() && success) { LLCRC alpha_mask_crc; - const LLUUID& uuid = mTexLayerSet->getAvatar()->getLocalTextureID((ETextureIndex)getInfo()->mLocalTexture); + const LLUUID& uuid = getUUID(); alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES); for (param_alpha_list_t::const_iterator iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++) @@ -1722,106 +1934,301 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC return success; } -// Returns TRUE on success. -BOOL LLTexLayer::renderImageRaw( U8* in_data, S32 in_width, S32 in_height, S32 in_components, S32 width, S32 height, BOOL is_mask ) +void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height) { - if (!in_data) + S32 size = width * height; + const U8* alphaData = getAlphaData(); + if (!alphaData && hasAlphaParams()) { - return FALSE; + LLColor4 net_color; + findNetColor( &net_color ); + // TODO: eliminate need for layer morph mask valid flag + invalidateMorphMasks(); + renderMorphMasks(originX, originY, width, height, net_color); + alphaData = getAlphaData(); } - GLenum format_options[4] = { GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA }; - GLenum format = format_options[in_components-1]; - if( is_mask ) + if (alphaData) { - llassert( 1 == in_components ); - format = GL_ALPHA; + for( S32 i = 0; i < size; i++ ) + { + U8 curAlpha = data[i]; + U16 resultAlpha = curAlpha; + resultAlpha *= (alphaData[i] + 1); + resultAlpha = resultAlpha >> 8; + data[i] = (U8)resultAlpha; + } } +} - if( (in_width != SCRATCH_TEX_WIDTH) || (in_height != SCRATCH_TEX_HEIGHT) ) +/*virtual*/ BOOL LLTexLayer::isInvisibleAlphaMask() const +{ + if (mLocalTextureObject) { - LLGLSNoAlphaTest gls_no_alpha_test; - - GLenum internal_format_options[4] = { GL_LUMINANCE8, GL_LUMINANCE8_ALPHA8, GL_RGB8, GL_RGBA8 }; - GLenum internal_format = internal_format_options[in_components-1]; - if( is_mask ) + if (mLocalTextureObject->getID() == IMG_INVISIBLE) { - llassert( 1 == in_components ); - internal_format = GL_ALPHA8; + return TRUE; } - - U32 name = 0; - LLImageGL::generateTextures(1, &name ); - stop_glerror(); + } - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, name); - stop_glerror(); + return FALSE; +} - LLImageGL::setManualImage( - GL_TEXTURE_2D, 0, internal_format, - in_width, in_height, - format, GL_UNSIGNED_BYTE, in_data ); - stop_glerror(); +LLUUID LLTexLayer::getUUID() const +{ + LLUUID uuid; + if( getInfo()->mLocalTexture != -1 ) + { + LLViewerTexture* tex = mLocalTextureObject->getImage(); + if (tex) + { + uuid = mLocalTextureObject->getID(); + } + } + if( !getInfo()->mStaticImageFileName.empty() ) + { + LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); + if( tex ) + { + uuid = tex->getID(); + } + } + return uuid; +} - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - gl_rect_2d_simple_tex( width, height ); +//----------------------------------------------------------------------------- +// LLTexLayerTemplate +// A single texture layer, consisting of: +// * color, consisting of either +// * one or more color parameters (weighted colors) +// * a reference to a global color +// * a fixed color with non-zero alpha +// * opaque white (the default) +// * (optional) a texture defined by either +// * a GUID +// * a texture entry index (TE) +// * (optional) one or more alpha parameters (weighted alpha textures) +//----------------------------------------------------------------------------- +LLTexLayerTemplate::LLTexLayerTemplate(LLTexLayerSet* layer_set) : + LLTexLayerInterface(layer_set) +{ +} - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); +LLTexLayerTemplate::LLTexLayerTemplate(const LLTexLayerTemplate &layer) : + LLTexLayerInterface(layer) +{ +} - LLImageGL::deleteTextures(1, &name ); - stop_glerror(); +LLTexLayerTemplate::~LLTexLayerTemplate() +{ +} + +//----------------------------------------------------------------------------- +// setInfo +//----------------------------------------------------------------------------- + +/*virtual*/ BOOL LLTexLayerTemplate::setInfo(const LLTexLayerInfo* info, LLWearable* wearable ) +{ + return LLTexLayerInterface::setInfo(info, wearable); +} + +U32 LLTexLayerTemplate::updateWearableCache() const +{ + mWearableCache.clear(); + + S32 te = mInfo->mLocalTexture; + if (te == -1) + { + //this isn't a cloneable layer + return 0; } - else + LLWearableType::EType wearable_type = LLVOAvatarDictionary::getTEWearableType((ETextureIndex)te); + U32 num_wearables = gAgentWearables.getWearableCount(wearable_type); + U32 added = 0; + for (U32 i = 0; i < num_wearables; i++) { - LLGLSNoAlphaTest gls_no_alpha_test; - - if( !mTexLayerSet->getAvatar()->bindScratchTexture(format) ) + LLWearable* wearable = gAgentWearables.getWearable(wearable_type, i); + if (!wearable) { - return FALSE; + continue; } + mWearableCache.push_back(wearable); + added++; + } + return added; +} +LLTexLayer* LLTexLayerTemplate::getLayer(U32 i) const +{ + if (mWearableCache.size() <= i) + { + return NULL; + } + LLWearable *wearable = mWearableCache[i]; + LLLocalTextureObject *lto = NULL; + LLTexLayer *layer = NULL; + if (wearable) + { + lto = wearable->getLocalTextureObject(mInfo->mLocalTexture); + } + if (lto) + { + layer = lto->getTexLayer(getName()); + } + return layer; +} - glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, in_width, in_height, format, GL_UNSIGNED_BYTE, in_data ); - stop_glerror(); - - gl_rect_2d_simple_tex( width, height ); +/*virtual*/ BOOL LLTexLayerTemplate::render(S32 x, S32 y, S32 width, S32 height) +{ + if(!mInfo) + { + return FALSE ; + } - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + BOOL success = TRUE; + updateWearableCache(); + for (wearable_cache_t::const_iterator iter = mWearableCache.begin(); iter!= mWearableCache.end(); iter++) + { + LLWearable* wearable = NULL; + LLLocalTextureObject *lto = NULL; + LLTexLayer *layer = NULL; + wearable = *iter; + if (wearable) + { + lto = wearable->getLocalTextureObject(mInfo->mLocalTexture); + } + if (lto) + { + layer = lto->getTexLayer(getName()); + } + if (layer) + { + wearable->writeToAvatar(); + layer->setLTO(lto); + success &= layer->render(x,y,width,height); + } } - return TRUE; + return success; } -void LLTexLayer::requestUpdate() +/*virtual*/ BOOL LLTexLayerTemplate::blendAlphaTexture( S32 x, S32 y, S32 width, S32 height) // Multiplies a single alpha texture against the frame buffer { - mTexLayerSet->requestUpdate(); + BOOL success = TRUE; + U32 num_wearables = updateWearableCache(); + for (U32 i = 0; i < num_wearables; i++) + { + LLTexLayer *layer = getLayer(i); + if (layer) + { + success &= layer->blendAlphaTexture(x,y,width,height); + } + } + return success; } -const std::string& LLTexLayer::getName() const +/*virtual*/ void LLTexLayerTemplate::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height) +{ + U32 num_wearables = updateWearableCache(); + for (U32 i = 0; i < num_wearables; i++) + { + LLTexLayer *layer = getLayer(i); + if (layer) + { + layer->addAlphaMask(data, originX, originY, width, height); + } + } +} + +/*virtual*/ void LLTexLayerTemplate::setHasMorph(BOOL newval) { - return mInfo->mName; + mHasMorph = newval; + U32 num_wearables = updateWearableCache(); + for (U32 i = 0; i < num_wearables; i++) + { + LLTexLayer *layer = getLayer(i); + if (layer) + { + layer->setHasMorph(newval); + } + } } -LLTexLayer::ERenderPass LLTexLayer::getRenderPass() const +/*virtual*/ void LLTexLayerTemplate::deleteCaches() { - return mInfo->mRenderPass; + U32 num_wearables = updateWearableCache(); + for (U32 i = 0; i < num_wearables; i++) + { + LLTexLayer *layer = getLayer(i); + if (layer) + { + layer->deleteCaches(); + } + } } -const std::string& LLTexLayer::getGlobalColor() const + +/*virtual*/ BOOL LLTexLayerTemplate::isInvisibleAlphaMask() const { - return mInfo->mGlobalColor; + U32 num_wearables = updateWearableCache(); + for (U32 i = 0; i < num_wearables; i++) + { + LLTexLayer *layer = getLayer(i); + if (layer) + { + if (layer->isInvisibleAlphaMask()) + { + return TRUE; + } + } + } + + return FALSE; } -void LLTexLayer::invalidateMorphMasks() + +//----------------------------------------------------------------------------- +// finds a specific layer based on a passed in name +//----------------------------------------------------------------------------- +LLTexLayerInterface* LLTexLayerSet::findLayerByName(const std::string& name) { - mMorphMasksValid = FALSE; + for (layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) + { + LLTexLayerInterface* layer = *iter; + if (layer->getName() == name) + { + return layer; + } } - -BOOL LLTexLayer::isVisibilityMask() const + for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++ ) { - return mInfo->mIsVisibilityMask; + LLTexLayerInterface* layer = *iter; + if (layer->getName() == name) + { + return layer; + } + } + return NULL; } +void LLTexLayerSet::cloneTemplates(LLLocalTextureObject *lto, LLVOAvatarDefines::ETextureIndex tex_index, LLWearable *wearable) +{ + // initialize all texlayers with this texture type for this LTO + for( LLTexLayerSet::layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) + { + LLTexLayerTemplate* layer = (LLTexLayerTemplate*)*iter; + if (layer->getInfo()->getLocalTexture() == (S32) tex_index) + { + lto->addTexLayer(layer, wearable); + } + } + for( LLTexLayerSet::layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++ ) + { + LLTexLayerTemplate* layer = (LLTexLayerTemplate*)*iter; + if (layer->getInfo()->getLocalTexture() == (S32) tex_index) + { + lto->addTexLayer(layer, wearable); + } + } +} //----------------------------------------------------------------------------- // LLTexLayerStaticImageList //----------------------------------------------------------------------------- @@ -1838,7 +2245,7 @@ LLTexLayerStaticImageList::~LLTexLayerStaticImageList() deleteCachedImages(); } -void LLTexLayerStaticImageList::dumpByteCount() +void LLTexLayerStaticImageList::dumpByteCount() const { llinfos << "Avatar Static Textures " << "KB GL:" << (mGLBytes / 1024) << @@ -1856,16 +2263,16 @@ void LLTexLayerStaticImageList::deleteCachedImages() //mStaticImageLists uses LLPointers, clear() will cause deletion mStaticImageListTGA.clear(); - mStaticImageListGL.clear(); + mStaticImageList.clear(); mGLBytes = 0; mTGABytes = 0; } } -// Note: in general, for a given image image we'll call either getImageTga() or getImageGL(). +// Note: in general, for a given image image we'll call either getImageTga() or getTexture(). // We call getImageTga() if the image is used as an alpha gradient. -// Otherwise, we call getImageGL() +// Otherwise, we call getTexture() // Returns an LLImageTGA that contains the encoded data from a tga file named file_name. // Caches the result to speed identical subsequent requests. @@ -1897,19 +2304,19 @@ LLImageTGA* LLTexLayerStaticImageList::getImageTGA(const std::string& file_name) // Returns a GL Image (without a backing ImageRaw) that contains the decoded data from a tga file named file_name. // Caches the result to speed identical subsequent requests. -LLImageGL* LLTexLayerStaticImageList::getImageGL(const std::string& file_name, BOOL is_mask) +LLViewerTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name, BOOL is_mask) { - LLPointer<LLImageGL> image_gl; + LLPointer<LLViewerTexture> tex; const char *namekey = mImageNames.addString(file_name); - image_gl_map_t::const_iterator iter = mStaticImageListGL.find(namekey); - if( iter != mStaticImageListGL.end() ) + texture_map_t::const_iterator iter = mStaticImageList.find(namekey); + if( iter != mStaticImageList.end() ) { - image_gl = iter->second; + tex = iter->second; } else { - image_gl = new LLImageGL( FALSE ); + tex = LLViewerTextureManager::getLocalTexture( FALSE ); LLPointer<LLImageRaw> image_raw = new LLImageRaw; if( loadImageRaw( file_name, image_raw ) ) { @@ -1917,23 +2324,23 @@ LLImageGL* LLTexLayerStaticImageList::getImageGL(const std::string& file_name, B { // Note: these are static, unchanging images so it's ok to assume // that once an image is a mask it's always a mask. - image_gl->setExplicitFormat( GL_ALPHA8, GL_ALPHA ); + tex->setExplicitFormat( GL_ALPHA8, GL_ALPHA ); } - image_gl->createGLTexture(0, image_raw); + tex->createGLTexture(0, image_raw, 0, TRUE, LLViewerTexture::LOCAL); - gGL.getTexUnit(0)->bind(image_gl); - image_gl->setAddressMode(LLTexUnit::TAM_CLAMP); + gGL.getTexUnit(0)->bind(tex); + tex->setAddressMode(LLTexUnit::TAM_CLAMP); - mStaticImageListGL [ namekey ] = image_gl; - mGLBytes += (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents(); + mStaticImageList [ namekey ] = tex; + mGLBytes += (S32)tex->getWidth() * tex->getHeight() * tex->getComponents(); } else { - image_gl = NULL; + tex = NULL; } } - return image_gl; + return tex; } // Reads a .tga file, decodes it, and puts the decoded data in image_raw. @@ -1953,4 +2360,23 @@ BOOL LLTexLayerStaticImageList::loadImageRaw(const std::string& file_name, LLIma return success; } +const std::string LLTexLayerSetBuffer::dumpTextureInfo() const +{ + if (!isAgentAvatarValid()) return ""; + + const BOOL is_high_res = !mNeedsUpload; + const U32 num_low_res = mNumLowresUploads; + const U32 upload_time = (U32)mNeedsUploadTimer.getElapsedTimeF32(); + const std::string local_texture_info = gAgentAvatarp->debugDumpLocalTextureDataInfo(mTexLayerSet); + std::string status = "CREATING "; + if (!uploadNeeded()) status = "DONE "; + if (uploadInProgress()) status = "UPLOADING"; + + std::string text = llformat("[%s] [HiRes:%d LoRes:%d] [Elapsed:%d] %s", + status.c_str(), + is_high_res, num_low_res, + upload_time, + local_texture_info.c_str()); + return text; +} |