summaryrefslogtreecommitdiff
path: root/indra/newview/lltexlayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/lltexlayer.cpp')
-rw-r--r--indra/newview/lltexlayer.cpp1554
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 &param_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 &param_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*)&param_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;
+}