From 523e0e2dff6af1c5bc12e2aefc0ca9b96bf26fd6 Mon Sep 17 00:00:00 2001 From: Armin Weatherwax Date: Tue, 9 Mar 2010 23:00:38 +0100 Subject: Henri Beauchamp: Port of SG-2.0 alpha and tattoo layers (v6) slviewer-0-v12350-AlphaAndTattooSupport-patch.zip contains the necessary avatar_lad.xml and other artwork. The new avatar_lad.xml crashes clients without alpha and tattoo support at startup. --- linden/indra/llinventory/llinventory.cpp | 2 + linden/indra/llinventory/llinventorytype.h | 40 +- linden/indra/llinventory/llwearabletype.h | 4 +- linden/indra/llrender/llrender.cpp | 3 + linden/indra/llrender/llrender.h | 1 + linden/indra/newview/app_settings/settings.xml | 11 + linden/indra/newview/llagent.cpp | 8 +- linden/indra/newview/llfloatercustomize.cpp | 278 +++++++-- linden/indra/newview/llinventoryactions.cpp | 10 + linden/indra/newview/llinventorybridge.cpp | 12 + linden/indra/newview/llinventorybridge.h | 4 + linden/indra/newview/llinventoryview.cpp | 6 + linden/indra/newview/lltexlayer.cpp | 651 +++++++++++---------- linden/indra/newview/lltexlayer.h | 27 +- linden/indra/newview/lltexturectrl.cpp | 6 +- linden/indra/newview/llviewermenu.cpp | 16 + linden/indra/newview/llvoavatar.cpp | 73 ++- linden/indra/newview/llvoavatar.h | 6 +- linden/indra/newview/llvoavatardefines.cpp | 28 +- linden/indra/newview/llvoavatardefines.h | 11 +- linden/indra/newview/llwearable.cpp | 6 + linden/indra/newview/rlvhandler.cpp | 2 +- .../skins/default/xui/en-us/floater_customize.xml | 168 ++++++ .../skins/default/xui/en-us/floater_inventory.xml | 8 + .../xui/en-us/floater_new_outfit_dialog.xml | 10 +- .../skins/default/xui/en-us/menu_inventory.xml | 8 + 26 files changed, 960 insertions(+), 439 deletions(-) diff --git a/linden/indra/llinventory/llinventory.cpp b/linden/indra/llinventory/llinventory.cpp index 755a57c..1508b46 100644 --- a/linden/indra/llinventory/llinventory.cpp +++ b/linden/indra/llinventory/llinventory.cpp @@ -128,6 +128,8 @@ LLInventoryType::NType calc_ntype( case WT_UNDERSHIRT: return LLInventoryType::NIT_UNDERSHIRT; case WT_UNDERPANTS: return LLInventoryType::NIT_UNDERPANTS; case WT_SKIRT: return LLInventoryType::NIT_SKIRT; + case WT_ALPHA: return LLInventoryType::NIT_ALPHA; + case WT_TATTOO: return LLInventoryType::NIT_TATTOO; default: return LLInventoryType::NIT_CLOTHING; } } diff --git a/linden/indra/llinventory/llinventorytype.h b/linden/indra/llinventory/llinventorytype.h index 961fa79..ee429c2 100644 --- a/linden/indra/llinventory/llinventorytype.h +++ b/linden/indra/llinventory/llinventorytype.h @@ -115,49 +115,51 @@ public: NIT_UNDERSHIRT = 1 << 10, NIT_UNDERPANTS = 1 << 11, NIT_SKIRT = 1 << 12, - NIT_CLOTHING = 0x0001ff0, + NIT_ALPHA = 1 << 13, + NIT_TATTOO = 1 << 14, + NIT_CLOTHING = 0x0007ff0, /* Body Parts | Clothing */ - NIT_WEARABLE = 0x0001fff, + NIT_WEARABLE = 0x0007fff, /* Images */ - NIT_TEXTURE = 1 << 13, - NIT_SNAPSHOT = 1 << 14, - NIT_IMAGE = 0x0006000, + NIT_TEXTURE = 1 << 15, + NIT_SNAPSHOT = 1 << 16, + NIT_IMAGE = 0x0018000, /* Calling Cards */ - NIT_CALLCARD_OFF = 1 << 15, - NIT_CALLCARD_ON = 1 << 16, - NIT_CALLCARD = 0x0018000, + NIT_CALLCARD_OFF = 1 << 17, + NIT_CALLCARD_ON = 1 << 18, + NIT_CALLCARD = 0x0060000, /* Landmarks */ - NIT_LANDMARK_UNUSED = 1 << 17, - NIT_LANDMARK_USED = 1 << 18, - NIT_LANDMARK = 0x0060000, + NIT_LANDMARK_UNUSED = 1 << 19, + NIT_LANDMARK_USED = 1 << 20, + NIT_LANDMARK = 0x0180000, /* Sounds */ - NIT_SOUND = 1 << 19, + NIT_SOUND = 1 << 21, /* Animations */ - NIT_ANIMATION = 1 << 20, + NIT_ANIMATION = 1 << 22, /* Gestures */ - NIT_GESTURE = 1 << 21, + NIT_GESTURE = 1 << 23, /* Notecards */ - NIT_NOTECARD = 1 << 22, + NIT_NOTECARD = 1 << 24, /* Scripts */ - NIT_SCRIPT_LSL2 = 1 << 23, + NIT_SCRIPT_LSL2 = 1 << 25, /* Objects */ - NIT_OBJECT = 1 << 24, + NIT_OBJECT = 1 << 26, /* Folders ("Categories" in the old type system) */ - NIT_FOLDER = 1 << 25, + NIT_FOLDER = 1 << 27, /* Bitwise OR-ing of all the above */ - NIT_ALL = 0x3ffffff, + NIT_ALL = 0xfffffff, }; diff --git a/linden/indra/llinventory/llwearabletype.h b/linden/indra/llinventory/llwearabletype.h index b0a40b2..7a5ecef 100644 --- a/linden/indra/llinventory/llwearabletype.h +++ b/linden/indra/llinventory/llwearabletype.h @@ -47,7 +47,9 @@ enum EWearableType // If you change this, update LLWearable::getTypeName(), get WT_UNDERSHIRT = 10, WT_UNDERPANTS = 11, WT_SKIRT = 12, - WT_COUNT = 13, + WT_ALPHA = 13, + WT_TATTOO = 14, + WT_COUNT = 15, WT_INVALID = 255 }; diff --git a/linden/indra/llrender/llrender.cpp b/linden/indra/llrender/llrender.cpp index 93ff822..b1fe153 100644 --- a/linden/indra/llrender/llrender.cpp +++ b/linden/indra/llrender/llrender.cpp @@ -799,6 +799,9 @@ void LLRender::setSceneBlendType(eBlendType type) case BT_MULT: glBlendFunc(GL_DST_COLOR, GL_ZERO); break; + case BT_MULT_ALPHA: + glBlendFunc(GL_DST_ALPHA, GL_ZERO); + break; case BT_MULT_X2: glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); break; diff --git a/linden/indra/llrender/llrender.h b/linden/indra/llrender/llrender.h index ce84e74..da69de8 100644 --- a/linden/indra/llrender/llrender.h +++ b/linden/indra/llrender/llrender.h @@ -252,6 +252,7 @@ public: BT_ADD, BT_ADD_WITH_ALPHA, // Additive blend modulated by the fragment's alpha. BT_MULT, + BT_MULT_ALPHA, BT_MULT_X2, BT_REPLACE } eBlendType; diff --git a/linden/indra/newview/app_settings/settings.xml b/linden/indra/newview/app_settings/settings.xml index 5043264..670de2b 100644 --- a/linden/indra/newview/app_settings/settings.xml +++ b/linden/indra/newview/app_settings/settings.xml @@ -11336,6 +11336,17 @@ Value 5748decc-f629-461c-9a36-a35a221fe21f + UIImgDefaultAlphaUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + UIImgDefaultUnderwearUUID Comment diff --git a/linden/indra/newview/llagent.cpp b/linden/indra/newview/llagent.cpp index 859f7f6..4753bc8 100644 --- a/linden/indra/newview/llagent.cpp +++ b/linden/indra/newview/llagent.cpp @@ -7091,7 +7091,9 @@ void LLAgent::createStandardWearables(BOOL female) FALSE, //WT_GLOVES TRUE, //WT_UNDERSHIRT TRUE, //WT_UNDERPANTS - FALSE //WT_SKIRT + FALSE, //WT_SKIRT + FALSE, //WT_ALPHA + FALSE //WT_TATTOO }; for( S32 i=0; i < WT_COUNT; i++ ) @@ -7602,6 +7604,8 @@ void LLAgent::setWearableOutfit( wearables_to_remove[WT_UNDERPANTS] = (!gAgent.isTeen()) && remove && gRlvHandler.isRemovable(WT_UNDERPANTS); wearables_to_remove[WT_SKIRT] = remove && gRlvHandler.isRemovable(WT_SKIRT); // [/RLVa:KB] + wearables_to_remove[WT_ALPHA] = remove; + wearables_to_remove[WT_TATTOO] = remove; S32 count = wearables.count(); llassert( items.count() == count ); @@ -7900,6 +7904,8 @@ void LLAgent::userRemoveAllClothesStep2( BOOL proceed, void* userdata ) gAgent.removeWearable( WT_UNDERSHIRT ); gAgent.removeWearable( WT_UNDERPANTS ); gAgent.removeWearable( WT_SKIRT ); + gAgent.removeWearable( WT_ALPHA ); + gAgent.removeWearable( WT_TATTOO ); } } diff --git a/linden/indra/newview/llfloatercustomize.cpp b/linden/indra/newview/llfloatercustomize.cpp index 4761491..74b58bd 100644 --- a/linden/indra/newview/llfloatercustomize.cpp +++ b/linden/indra/newview/llfloatercustomize.cpp @@ -355,7 +355,9 @@ enum ESubpart { SUBPART_GLOVES, SUBPART_UNDERSHIRT, SUBPART_UNDERPANTS, - SUBPART_SKIRT + SUBPART_SKIRT, + SUBPART_ALPHA, + SUBPART_TATTOO }; struct LLSubpart @@ -384,6 +386,7 @@ public: void addSubpart(const std::string& name, ESubpart id, LLSubpart* part ); void addTextureDropTarget( ETextureIndex te, const std::string& name, const LLUUID& default_image_id, BOOL allow_no_texture ); + void addInvisibilityCheckbox(ETextureIndex te, const std::string& name); void addColorSwatch( ETextureIndex te, const std::string& name ); const std::string& getLabel() { return LLWearable::typeToTypeLabel( mType ); } @@ -398,6 +401,11 @@ public: void setUIPermissions(U32 perm_mask, BOOL is_complete); + void hideTextureControls(); + bool textureIsInvisible(ETextureIndex te); + void initPreviousTextureList(); + void initPreviousTextureListEntry(ETextureIndex te); + virtual void setVisible( BOOL visible ); // Callbacks @@ -412,6 +420,7 @@ public: static void onBtnTakeOffDialog( S32 option, void* userdata ); static void onBtnCreateNew( void* userdata ); static void onTextureCommit( LLUICtrl* ctrl, void* userdata ); + static void onInvisibilityCommit( LLUICtrl* ctrl, void* userdata ); static void onColorCommit( LLUICtrl* ctrl, void* userdata ); static void onCommitSexChange( LLUICtrl*, void* userdata ); static bool onSelectAutoWearOption(const LLSD& notification, const LLSD& response); @@ -422,8 +431,10 @@ private: EWearableType mType; BOOL mCanTakeOff; std::map mTextureList; + std::map mInvisibilityList; std::map mColorList; std::map mSubpartList; + std::map mPreviousTextureList; ESubpart mCurrentSubpart; }; @@ -658,6 +669,71 @@ bool LLPanelEditWearable::onSelectAutoWearOption(const LLSD& notification, const } return false; } + +bool LLPanelEditWearable::textureIsInvisible(ETextureIndex te) +{ + if (gAgent.getWearable(mType)) + { + LLVOAvatar *avatar = gAgent.getAvatarObject(); + if (avatar) + { + const LLTextureEntry* current_te = avatar->getTE(te); + return (current_te && current_te->getID() == IMG_INVISIBLE); + } + } + return false; +} + +void LLPanelEditWearable::addInvisibilityCheckbox(ETextureIndex te, const std::string& name) +{ + childSetCommitCallback(name, LLPanelEditWearable::onInvisibilityCommit, this); + + mInvisibilityList[name] = te; +} + +// static +void LLPanelEditWearable::onInvisibilityCommit(LLUICtrl* ctrl, void* userdata) +{ + LLPanelEditWearable* self = (LLPanelEditWearable*) userdata; + LLCheckBoxCtrl* checkbox_ctrl = (LLCheckBoxCtrl*) ctrl; + LLVOAvatar *avatar = gAgent.getAvatarObject(); + if (!avatar) + { + return; + } + + ETextureIndex te = (ETextureIndex)(self->mInvisibilityList[ctrl->getName()]); + + bool new_invis_state = checkbox_ctrl->get(); + if (new_invis_state) + { + LLViewerImage* image = gImageList.getImage(IMG_INVISIBLE); + const LLTextureEntry* current_te = avatar->getTE(te); + if (current_te) + { + self->mPreviousTextureList[(S32)te] = current_te->getID(); + } + avatar->setLocTexTE(te, image, TRUE); + avatar->wearableUpdated(self->mType, FALSE); + } + else + { + // Try to restore previous texture, if any. + LLUUID prev_id = self->mPreviousTextureList[(S32)te]; + if (prev_id.isNull() || (prev_id == IMG_INVISIBLE)) + { + prev_id = LLUUID(gSavedSettings.getString("UIImgDefaultAlphaUUID")); + } + if (prev_id.notNull()) + { + LLViewerImage* image = gImageList.getImage(prev_id); + avatar->setLocTexTE(te, image, TRUE); + avatar->wearableUpdated(self->mType, FALSE); + } + + } +} + void LLPanelEditWearable::addColorSwatch( ETextureIndex te, const std::string& name ) { childSetCommitCallback(name, LLPanelEditWearable::onColorCommit, this); @@ -683,11 +759,35 @@ void LLPanelEditWearable::onColorCommit( LLUICtrl* ctrl, void* userdata ) avatar->setClothesColor( te, new_color, TRUE ); LLVisualParamHint::requestHintUpdates(); + avatar->wearableUpdated(self->mType, FALSE); } } } +void LLPanelEditWearable::initPreviousTextureList() +{ + initPreviousTextureListEntry(TEX_LOWER_ALPHA); + initPreviousTextureListEntry(TEX_UPPER_ALPHA); + initPreviousTextureListEntry(TEX_HEAD_ALPHA); + initPreviousTextureListEntry(TEX_EYES_ALPHA); + initPreviousTextureListEntry(TEX_LOWER_ALPHA); +} + +void LLPanelEditWearable::initPreviousTextureListEntry(ETextureIndex te) +{ + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if (!avatar) + { + return; + } + const LLTextureEntry* current_te = avatar->getTE(te); + if (current_te) + { + mPreviousTextureList[te] = current_te->getID(); + } +} + void LLPanelEditWearable::addTextureDropTarget( ETextureIndex te, const std::string& name, const LLUUID& default_image_id, BOOL allow_no_texture ) { @@ -702,6 +802,19 @@ void LLPanelEditWearable::addTextureDropTarget( ETextureIndex te, const std::str texture_ctrl->setNonImmediateFilterPermMask(PERM_NONE);//PERM_COPY | PERM_TRANSFER); } mTextureList[name] = te; + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if (avatar) + { + LLWearable* wearable = gAgent.getWearable(mType); + if (wearable && mType == WT_ALPHA) + { + const LLTextureEntry* current_te = avatar->getTE(te); + if (current_te) + { + mPreviousTextureList[te] = current_te->getID(); + } + } + } } // static @@ -717,11 +830,20 @@ void LLPanelEditWearable::onTextureCommit( LLUICtrl* ctrl, void* userdata ) // Set the new version LLViewerImage* image = gImageList.getImage( texture_ctrl->getImageAssetID() ); - if( image->getID().isNull() ) + if (image->getID().isNull()) { image = gImageList.getImage(IMG_DEFAULT_AVATAR); } - avatar->setLocTexTE( te, image, TRUE ); + self->mTextureList[ctrl->getName()] = te; + if (gAgent.getWearable(self->mType)) + { + avatar->setLocTexTE(te, image, TRUE); + avatar->wearableUpdated(self->mType, FALSE); + } + if (self->mType == WT_ALPHA && image->getID() != IMG_INVISIBLE) + { + self->mPreviousTextureList[te] = image->getID(); + } } } @@ -743,6 +865,8 @@ ESubpart LLPanelEditWearable::getDefaultSubpart() case WT_UNDERSHIRT: return SUBPART_UNDERSHIRT; case WT_UNDERPANTS: return SUBPART_UNDERPANTS; case WT_SKIRT: return SUBPART_SKIRT; + case WT_ALPHA: return SUBPART_ALPHA; + case WT_TATTOO: return SUBPART_TATTOO; default: llassert(0); return SUBPART_SHAPE_WHOLE; } @@ -816,16 +940,7 @@ void LLPanelEditWearable::draw() childSetVisible("title_no_modify", TRUE); childSetTextArg("title_no_modify", "[DESC]", std::string(LLWearable::typeToTypeLabel( mType ))); - for( std::map::iterator iter = mTextureList.begin(); - iter != mTextureList.end(); ++iter ) - { - childSetVisible(iter->first, FALSE ); - } - for( std::map::iterator iter = mColorList.begin(); - iter != mColorList.end(); ++iter ) - { - childSetVisible(iter->first, FALSE ); - } + hideTextureControls(); } else if(has_wearable && !is_complete) { @@ -839,16 +954,7 @@ void LLPanelEditWearable::draw() childSetVisible("path", TRUE); childSetTextArg("path", "[PATH]", path); - for( std::map::iterator iter = mTextureList.begin(); - iter != mTextureList.end(); ++iter ) - { - childSetVisible(iter->first, FALSE ); - } - for( std::map::iterator iter = mColorList.begin(); - iter != mColorList.end(); ++iter ) - { - childSetVisible(iter->first, FALSE ); - } + hideTextureControls(); } else if(has_wearable && is_modifiable) { @@ -908,6 +1014,20 @@ void LLPanelEditWearable::draw() ctrl->set(avatar->getClothesColor( (ETextureIndex)te_index ) ); } } + + for (std::map::iterator iter = mInvisibilityList.begin(); + iter != mInvisibilityList.end(); ++iter) + { + std::string name = iter->first; + ETextureIndex te = (ETextureIndex)iter->second; + childSetVisible(name, is_copyable && is_modifiable && is_complete); + childSetEnabled(name, is_copyable && is_modifiable && is_complete); + LLCheckBoxCtrl* ctrl = getChild(name); + if (ctrl) + { + ctrl->set(textureIsInvisible(te)); + } + } } else { @@ -915,16 +1035,7 @@ void LLPanelEditWearable::draw() childSetVisible("title_not_worn", TRUE); childSetTextArg("title_not_worn", "[DESC]", std::string(LLWearable::typeToTypeLabel( mType ))); - for( std::map::iterator iter = mTextureList.begin(); - iter != mTextureList.end(); ++iter ) - { - childSetVisible(iter->first, FALSE ); - } - for( std::map::iterator iter = mColorList.begin(); - iter != mColorList.end(); ++iter ) - { - childSetVisible(iter->first, FALSE ); - } + hideTextureControls(); } childSetVisible("icon", has_wearable && is_modifiable); @@ -932,11 +1043,34 @@ void LLPanelEditWearable::draw() LLPanel::draw(); } +void LLPanelEditWearable::hideTextureControls() +{ + for (std::map::iterator iter = mTextureList.begin(); + iter != mTextureList.end(); ++iter) + { + childSetVisible(iter->first, FALSE); + } + for (std::map::iterator iter = mColorList.begin(); + iter != mColorList.end(); ++iter) + { + childSetVisible(iter->first, FALSE); + } + for (std::map::iterator iter = mInvisibilityList.begin(); + iter != mInvisibilityList.end(); ++iter) + { + childSetVisible(iter->first, FALSE); + } +} + void LLPanelEditWearable::setWearable(LLWearable* wearable, U32 perm_mask, BOOL is_complete) { if( wearable ) { setUIPermissions(perm_mask, is_complete); + if (mType == WT_ALPHA) + { + initPreviousTextureList(); + } } } @@ -1029,6 +1163,11 @@ void LLPanelEditWearable::setUIPermissions(U32 perm_mask, BOOL is_complete) { childSetVisible(iter->first, is_modifiable && is_complete ); } + for (std::map::iterator iter = mInvisibilityList.begin(); + iter != mInvisibilityList.end(); ++iter) + { + childSetVisible(iter->first, is_copyable && is_modifiable && is_complete); + } } ///////////////////////////////////////////////////////////////////// @@ -1475,6 +1614,8 @@ LLFloaterCustomize::LLFloaterCustomize() factory_map["Undershirt"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_UNDERSHIRT) ) ); factory_map["Underpants"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_UNDERPANTS) ) ); factory_map["Skirt"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_SKIRT) ) ); + factory_map["Alpha"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_ALPHA))); + factory_map["Tattoo"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_TATTOO))); LLUICtrlFactory::getInstance()->buildFloater(this, "floater_customize.xml", &factory_map); } @@ -1506,6 +1647,8 @@ BOOL LLFloaterCustomize::postBuild() childSetTabChangeCallback("customize tab container", "Undershirt", onTabChanged, (void*)WT_UNDERSHIRT, onTabPrecommit ); childSetTabChangeCallback("customize tab container", "Underpants", onTabChanged, (void*)WT_UNDERPANTS, onTabPrecommit ); childSetTabChangeCallback("customize tab container", "Skirt", onTabChanged, (void*)WT_SKIRT, onTabPrecommit ); + childSetTabChangeCallback("customize tab container", "Alpha", onTabChanged, (void*)WT_ALPHA, onTabPrecommit); + childSetTabChangeCallback("customize tab container", "Tattoo", onTabChanged, (void*)WT_TATTOO, onTabPrecommit); // Remove underwear panels for teens if (gAgent.isTeen()) @@ -1532,6 +1675,7 @@ void LLFloaterCustomize::open() LLFloater::open(); // childShowTab depends on gFloaterCustomize being defined and therefore must be called after the constructor. - Nyx childShowTab("customize tab container", "Shape", true); + setCurrentWearableType(WT_SHAPE); } //////////////////////////////////////////////////////////////////////////// @@ -2136,6 +2280,66 @@ void LLFloaterCustomize::initWearablePanels() panel->addColorSwatch( TEX_LOWER_UNDERPANTS, "Color/Tint" ); } + + ///////////////////////////////////////// + // Alpha + panel = mWearablePanelList[WT_ALPHA]; + + if (panel) + { + part = new LLSubpart(); + part->mTargetJoint = "mPelvis"; + part->mEditGroup = "alpha"; + part->mTargetOffset.setVec(0.f, 0.f, 0.1f); + part->mCameraOffset.setVec(-2.5f, 0.5f, 0.8f); + panel->addSubpart(LLStringUtil::null, SUBPART_ALPHA, part); + + panel->addTextureDropTarget(TEX_LOWER_ALPHA, "Lower Alpha", + LLUUID(gSavedSettings.getString("UIImgDefaultAlphaUUID")), + TRUE); + panel->addTextureDropTarget(TEX_UPPER_ALPHA, "Upper Alpha", + LLUUID(gSavedSettings.getString("UIImgDefaultAlphaUUID")), + TRUE); + panel->addTextureDropTarget(TEX_HEAD_ALPHA, "Head Alpha", + LLUUID(gSavedSettings.getString("UIImgDefaultAlphaUUID")), + TRUE); + panel->addTextureDropTarget(TEX_EYES_ALPHA, "Eye Alpha", + LLUUID(gSavedSettings.getString("UIImgDefaultAlphaUUID")), + TRUE); + panel->addTextureDropTarget(TEX_HAIR_ALPHA, "Hair Alpha", + LLUUID(gSavedSettings.getString("UIImgDefaultAlphaUUID")), + TRUE); + + panel->addInvisibilityCheckbox(TEX_LOWER_ALPHA, "lower alpha texture invisible"); + panel->addInvisibilityCheckbox(TEX_UPPER_ALPHA, "upper alpha texture invisible"); + panel->addInvisibilityCheckbox(TEX_HEAD_ALPHA, "head alpha texture invisible"); + panel->addInvisibilityCheckbox(TEX_EYES_ALPHA, "eye alpha texture invisible"); + panel->addInvisibilityCheckbox(TEX_HAIR_ALPHA, "hair alpha texture invisible"); + } + + ///////////////////////////////////////// + // Tattoo + panel = mWearablePanelList[WT_TATTOO]; + + if (panel) + { + part = new LLSubpart(); + part->mTargetJoint = "mPelvis"; + part->mEditGroup = "tattoo"; + part->mTargetOffset.setVec(0.f, 0.f, 0.1f); + part->mCameraOffset.setVec(-2.5f, 0.5f, 0.8f); + panel->addSubpart(LLStringUtil::null, SUBPART_TATTOO, part); + + panel->addTextureDropTarget(TEX_LOWER_TATTOO, "Lower Tattoo", + LLUUID::null, + TRUE); + panel->addTextureDropTarget(TEX_UPPER_TATTOO, "Upper Tattoo", + LLUUID::null, + TRUE); + panel->addTextureDropTarget(TEX_HEAD_TATTOO, "Head Tattoo", + LLUUID::null, + TRUE); + } } //////////////////////////////////////////////////////////////////////////// @@ -2204,7 +2408,8 @@ BOOL LLFloaterCustomize::isDirty() const // static void LLFloaterCustomize::onTabPrecommit( void* userdata, bool from_click ) { - if (gFloaterCustomize && gFloaterCustomize->getCurrentWearableType() != (EWearableType)(intptr_t) userdata) + EWearableType type = (EWearableType)(intptr_t) userdata; + if (type != WT_INVALID && gFloaterCustomize && gFloaterCustomize->getCurrentWearableType() != type) { gFloaterCustomize->askToSaveIfDirty(onCommitChangeTab, userdata); } @@ -2219,7 +2424,10 @@ void LLFloaterCustomize::onTabPrecommit( void* userdata, bool from_click ) void LLFloaterCustomize::onTabChanged( void* userdata, bool from_click ) { EWearableType wearable_type = (EWearableType) (intptr_t)userdata; - LLFloaterCustomize::setCurrentWearableType( wearable_type ); + if (wearable_type != WT_INVALID) + { + LLFloaterCustomize::setCurrentWearableType(wearable_type); + } } void LLFloaterCustomize::onClose(bool app_quitting) diff --git a/linden/indra/newview/llinventoryactions.cpp b/linden/indra/newview/llinventoryactions.cpp index d7af8d1..319b5b2 100644 --- a/linden/indra/newview/llinventoryactions.cpp +++ b/linden/indra/newview/llinventoryactions.cpp @@ -428,6 +428,16 @@ void do_create(LLInventoryModel *model, LLInventoryPanel *ptr, std::string type, LLUUID parent_id = self ? self->getUUID() : gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING); LLFolderBridge::createWearable(parent_id, WT_UNDERPANTS); } + else if ("alpha" == type) + { + LLUUID parent_id = self ? self->getUUID() : gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING); + LLFolderBridge::createWearable(parent_id, WT_ALPHA); + } + else if ("tattoo" == type) + { + LLUUID parent_id = self ? self->getUUID() : gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING); + LLFolderBridge::createWearable(parent_id, WT_TATTOO); + } else if ("shape" == type) { LLUUID parent_id = self ? self->getUUID() : gInventory.findCategoryUUIDForType(LLAssetType::AT_BODYPART); diff --git a/linden/indra/newview/llinventorybridge.cpp b/linden/indra/newview/llinventorybridge.cpp index 42af09a..1f24d14 100644 --- a/linden/indra/newview/llinventorybridge.cpp +++ b/linden/indra/newview/llinventorybridge.cpp @@ -144,6 +144,8 @@ std::string ICON_NAME[ICON_NAME_COUNT] = "inv_item_undershirt.tga", "inv_item_underpants.tga", "inv_item_skirt.tga", + "inv_item_alpha.tga", + "inv_item_tattoo.tga", "inv_item_animation.tga", "inv_item_gesture.tga", @@ -2208,6 +2210,16 @@ void LLFolderBridge::createNewUnderpants(void* user_data) LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERPANTS); } +void LLFolderBridge::createNewAlpha(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_ALPHA); +} + +void LLFolderBridge::createNewTattoo(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_TATTOO); +} + void LLFolderBridge::createNewShape(void* user_data) { LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHAPE); diff --git a/linden/indra/newview/llinventorybridge.h b/linden/indra/newview/llinventorybridge.h index 3f4cfb0..53540b0 100644 --- a/linden/indra/newview/llinventorybridge.h +++ b/linden/indra/newview/llinventorybridge.h @@ -64,6 +64,8 @@ enum EInventoryIcon CLOTHING_UNDERSHIRT_ICON_NAME, CLOTHING_UNDERPANTS_ICON_NAME, CLOTHING_SKIRT_ICON_NAME, + CLOTHING_ALPHA_ICON_NAME, + CLOTHING_TATTOO_ICON_NAME, ANIMATION_ICON_NAME, GESTURE_ICON_NAME, @@ -328,6 +330,8 @@ protected: static void createNewGloves(void* user_data); static void createNewUndershirt(void* user_data); static void createNewUnderpants(void* user_data); + static void createNewAlpha(void* user_data); + static void createNewTattoo(void* user_data); static void createNewShape(void* user_data); static void createNewSkin(void* user_data); static void createNewHair(void* user_data); diff --git a/linden/indra/newview/llinventoryview.cpp b/linden/indra/newview/llinventoryview.cpp index aee67b4..7be1542 100644 --- a/linden/indra/newview/llinventoryview.cpp +++ b/linden/indra/newview/llinventoryview.cpp @@ -1485,6 +1485,12 @@ std::string get_item_icon_name(LLInventoryType::NType inv_ntype, case LLInventoryType::NIT_SKIRT: idx = CLOTHING_SKIRT_ICON_NAME; break; + case LLInventoryType::NIT_ALPHA: + idx = CLOTHING_ALPHA_ICON_NAME; + break; + case LLInventoryType::NIT_TATTOO: + idx = CLOTHING_TATTOO_ICON_NAME; + break; case LLInventoryType::NIT_CLOTHING: idx = CLOTHING_ICON_NAME; diff --git a/linden/indra/newview/lltexlayer.cpp b/linden/indra/newview/lltexlayer.cpp index 968c496..664f4fd 100644 --- a/linden/indra/newview/lltexlayer.cpp +++ b/linden/indra/newview/lltexlayer.cpp @@ -63,7 +63,6 @@ using namespace LLVOAvatarDefines; // static S32 LLTexLayerSetBuffer::sGLByteCount = 0; -S32 LLTexLayerSetBuffer::sGLBumpByteCount = 0; //----------------------------------------------------------------------------- // LLBakedUploadData() @@ -92,7 +91,7 @@ LLBakedUploadData::LLBakedUploadData( LLVOAvatar* avatar, // LLTexLayerSetBuffer // The composite image that a LLTexLayerSet writes to. Each LLTexLayerSet has one. //----------------------------------------------------------------------------- -LLTexLayerSetBuffer::LLTexLayerSetBuffer( LLTexLayerSet* owner, S32 width, S32 height, BOOL has_bump ) +LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* owner, S32 width, S32 height) : // ORDER_LAST => must render these after the hints are created. LLDynamicTexture( width, height, 4, LLDynamicTexture::ORDER_LAST, TRUE ), @@ -102,84 +101,39 @@ LLTexLayerSetBuffer::LLTexLayerSetBuffer( LLTexLayerSet* owner, S32 width, S32 h mTexLayerSet( owner ) { LLTexLayerSetBuffer::sGLByteCount += getSize(); - mHasBump = has_bump ; - mBumpTex = NULL ; - - createBumpTexture() ; } LLTexLayerSetBuffer::~LLTexLayerSetBuffer() { LLTexLayerSetBuffer::sGLByteCount -= getSize(); - - if( mBumpTex.notNull()) + destroyGLTexture(); + for (S32 order = 0; order < ORDER_COUNT; order++) { - mBumpTex = NULL ; - LLTexLayerSetBuffer::sGLBumpByteCount -= mWidth * mHeight * 4; + LLDynamicTexture::sInstances[order].erase(this); // will fail in all but one case. + } + if (mTexLayerSet->mComposite == this) + { + // Destroy the pointer on this now gone buffer. + mTexLayerSet->mComposite = NULL; } } + //virtual void LLTexLayerSetBuffer::restoreGLTexture() { - createBumpTexture() ; LLDynamicTexture::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; - - if(gAuditTexture) - { - mBumpTex->setCategory(LLViewerImageBoostLevel::TEXLAYER_BUMP) ; - mBumpTex->setTextureSize(mWidth * mHeight * 4) ; - mBumpTex->setComponents(4) ; - mBumpTex->incTextureCounter() ; - } - } -} - // 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() @@ -233,8 +187,8 @@ void LLTexLayerSetBuffer::popProjection() BOOL LLTexLayerSetBuffer::needsRender() { LLVOAvatar* avatar = mTexLayerSet->getAvatar(); - BOOL upload_now = mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal(); - BOOL needs_update = gAgent.mNumPendingQueries == 0 && (mNeedsUpdate || upload_now) && !avatar->mAppearanceAnimating; + BOOL upload_now = mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal() && gAgent.mNumPendingQueries == 0; + BOOL needs_update = (mNeedsUpdate || upload_now) && !avatar->mAppearanceAnimating; if (needs_update) { BOOL invalid_skirt = avatar->getBakedTE(mTexLayerSet) == TEX_SKIRT_BAKED && !avatar->isWearingWearableType(WT_SKIRT); @@ -272,8 +226,6 @@ void LLTexLayerSetBuffer::postRender(BOOL success) BOOL LLTexLayerSetBuffer::render() { - U8* baked_bump_data = NULL; - // Default color mask for tex layer render gGL.setColorMask(true, true); @@ -282,34 +234,6 @@ BOOL LLTexLayerSetBuffer::render() BOOL upload_now = (gAgent.mNumPendingQueries == 0 && mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal()); 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 ); @@ -319,13 +243,26 @@ BOOL LLTexLayerSetBuffer::render() { if (!success) { - delete [] baked_bump_data; llinfos << "Failed attempt to bake " << mTexLayerSet->getBodyRegion() << llendl; mUploadPending = FALSE; } else { - readBackAndUpload(baked_bump_data); + if (mTexLayerSet->isVisible()) + { + readBackAndUpload(); + } + else + { + mUploadPending = FALSE; + mNeedsUpload = FALSE; + LLVOAvatar* avatar = mTexLayerSet->getAvatar(); + if (avatar) + { + avatar->setNewBakedTexture(avatar->getBakedTE(mTexLayerSet), IMG_INVISIBLE); + llinfos << "Invisible baked texture set for " << mTexLayerSet->getBodyRegion() << llendl; + } + } } } @@ -360,7 +297,7 @@ BOOL LLTexLayerSetBuffer::updateImmediate() return result; } -void LLTexLayerSetBuffer::readBackAndUpload(U8* baked_bump_data) +void LLTexLayerSetBuffer::readBackAndUpload() { // pointers for storing data to upload U8* baked_color_data = new U8[ mWidth * mHeight * 4 ]; @@ -390,79 +327,23 @@ void LLTexLayerSetBuffer::readBackAndUpload(U8* baked_bump_data) // writes into baked_color_data const char* comment_text = NULL; - S32 baked_image_components = mBumpTex.notNull() ? 5 : 4; // red green blue [bump] clothing + S32 baked_image_components = 5; // red green blue bump clothing LLPointer baked_image = new LLImageRaw( mWidth, mHeight, baked_image_components ); U8* baked_image_data = baked_image->getData(); - if( mBumpTex.notNull() ) - { - comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // 5 channels: rgb, heightfield/alpha, mask + comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // 5 channels: rgb, heightfield/alpha, mask - // Hide the alpha for the eyelashes in a corner of the bump map - if (mTexLayerSet->getBodyRegion() == "head") - { - 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 + S32 i = 0; + for (S32 u = 0; u < mWidth; u++) + { + for (S32 v = 0; v < mHeight; v++) { - 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] = 255; // reserve for alpha - baked_image_data[5*i + 4] = baked_mask_data[i]; - i++; - } - } - } - } - else - { - if (mTexLayerSet->getBodyRegion() == "skirt" || mTexLayerSet->getBodyRegion() == "hair") - { - 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++; - } - } - } - else - { - 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] = 255; // eyes should have no mask - reserve for alpha - 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++; } } @@ -543,7 +424,6 @@ void LLTexLayerSetBuffer::readBackAndUpload(U8* baked_bump_data) } delete [] baked_color_data; - delete [] baked_bump_data; } @@ -554,89 +434,64 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, void* user LLVOAvatar* avatar = gAgent.getAvatarObject(); - if (0 == result && avatar && !avatar->isDead()) + 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->mLayerSet->hasComposite()) { - // Sanity check: only the user's avatar should be uploading textures. - if( baked_upload_data->mAvatar == avatar ) + LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mLayerSet->getComposite(); + + if (layerset_buffer->mUploadID.isNull()) + { + // The upload got canceled, we should be in the + // process of baking a new texture so request an + // upload with the new data + + // BAP: does this really belong in this callback, as + // opposed to where the cancellation takes place? + // suspect this does nothing. + layerset_buffer->requestUpload(); + } + else if (baked_upload_data->mID == layerset_buffer->mUploadID) { - // Composite may have changed since the pointer was stored - need to do some checking. - LLTexLayerSetBuffer* prev_layerset_buffer = baked_upload_data->mLayerSetBuffer; - // Can't just call getComposite() because this will trigger creation if none exists. - LLTexLayerSetBuffer* curr_layerset_buffer = - baked_upload_data->mLayerSet->hasComposite()?baked_upload_data->mLayerSet->getComposite():NULL; + // This is the upload we're currently waiting for. + layerset_buffer->mUploadID.setNull(); + layerset_buffer->mUploadPending = FALSE; - if (prev_layerset_buffer != curr_layerset_buffer) + if (result >= 0) { - llinfos << "Baked texture out of date, composite no longer valid, ignored" << llendl; + ETextureIndex baked_te = avatar->getBakedTE(layerset_buffer->mTexLayerSet); + 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); } else - { - curr_layerset_buffer->mUploadPending = FALSE; - - if (curr_layerset_buffer->mUploadID.isNull()) - { - // The upload got canceled, we should be in the process of baking a new texture - // so request an upload with the new data - curr_layerset_buffer->requestUpload(); - } - else if( baked_upload_data->mID == curr_layerset_buffer->mUploadID ) - { - // This is the upload we're currently waiting for. - curr_layerset_buffer->mUploadID.setNull(); - - if( result >= 0 ) - { - ETextureIndex baked_te = avatar->getBakedTE( curr_layerset_buffer->mTexLayerSet ); - 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 ); - } - else - { - llinfos << "Baked upload failed. Reason: " << result << llendl; - // *FIX: retry upload after n seconds, asset server could be busy - } - } - else - { - llinfos << "Received baked texture out of date, ignored." << llendl; - } - - avatar->dirtyMesh(); + { + // Avatar appearance is changing, ignore the upload results + llinfos << "Baked upload failed. Reason: " << result << llendl; + // *FIX: retry upload after n seconds, asset server could be busy } } + else + { + llinfos << "Received baked texture out of date, ignored." << llendl; + } + + avatar->dirtyMesh(); } else { - // Baked texture failed to upload, but since we didn't set the new baked texture, it means that they'll - // try and rebake it at some point in the future (after login?) + // Baked texture failed to upload (in which case since we + // didn't set the new baked texture, it means that they'll try + // and rebake it at some point in the future (after login?)), + // or this response to upload is out of date, in which case a + // current response should be on the way or already processed. llwarns << "Baked upload failed" << llendl; } 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; - mBumpTex->updateBoundTexMem(); - } - } - 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. @@ -720,15 +575,19 @@ LLTexLayerSet::LLTexLayerSet( LLVOAvatar* avatar ) mComposite( NULL ), mAvatar( avatar ), mUpdatesEnabled( FALSE ), - mHasBump( FALSE ), + mIsVisible(TRUE), + mBakedTexIndex(BAKED_HEAD), mInfo( NULL ) { } LLTexLayerSet::~LLTexLayerSet() { + deleteCaches(); std::for_each(mLayerList.begin(), mLayerList.end(), DeletePointer()); + std::for_each(mMaskLayerList.begin(), mMaskLayerList.end(), DeletePointer()); delete mComposite; + mComposite = NULL; } //----------------------------------------------------------------------------- @@ -751,7 +610,14 @@ BOOL LLTexLayerSet::setInfo(LLTexLayerSetInfo *info) mInfo = NULL; return FALSE; } - mLayerList.push_back( layer ); + if (!layer->isVisibilityMask()) + { + mLayerList.push_back(layer); + } + else + { + mMaskLayerList.push_back(layer); + } } requestUpdate(); @@ -791,6 +657,11 @@ void LLTexLayerSet::deleteCaches() LLTexLayer* layer = *iter; layer->deleteCaches(); } + for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++) + { + LLTexLayer* layer = *iter; + layer->deleteCaches(); + } } // Returns TRUE if at least one packet of data has been received for each of the textures that this layerset depends on. @@ -807,103 +678,130 @@ BOOL LLTexLayerSet::isLocalTextureDataFinal() } -BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height ) +void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, bool forceClear) { - BOOL success = TRUE; + const LLTexLayerSetInfo *info = getInfo(); - LLGLSUIDefault gls_ui; - LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE); - gGL.setColorMask(true, true); - - // composite color layers - for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) - { - LLTexLayer* layer = *iter; - if( layer->getRenderPass() == RP_COLOR ) - { - gGL.flush(); - success &= layer->render( x, y, width, height ); - gGL.flush(); - } - } + gGL.setColorMask(false, true); + gGL.setSceneBlendType(LLRender::BT_REPLACE); // (Optionally) replace alpha with a single component image from a tga file. - if( !getInfo()->mStaticAlphaFileName.empty() ) + if (!info->mStaticAlphaFileName.empty()) { LLGLSNoAlphaTest gls_no_alpha_test; gGL.flush(); - gGL.setColorMask(false, true); - gGL.setSceneBlendType(LLRender::BT_REPLACE); - { - LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticAlphaFileName, TRUE ); - if( image_gl ) + LLImageGL* image_gl = gTexStaticImageList.getImageGL(info->mStaticAlphaFileName, TRUE); + if (image_gl) { LLGLSUIDefault gls_ui; - gGL.getTexUnit(0)->bind(image_gl, TRUE); - gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE ); - gl_rect_2d_simple_tex( width, height ); - } - else - { - success = FALSE; + gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_REPLACE); + gl_rect_2d_simple_tex(width, height); } } gGL.flush(); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); - gGL.setColorMask(true, true); - gGL.setSceneBlendType(LLRender::BT_ALPHA); } - else - if( getInfo()->mClearAlpha ) + else if (forceClear || info->mClearAlpha || (mMaskLayerList.size() > 0)) { // Set the alpha channel to one (clean up after previous blending) + 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 ); - gGL.flush(); - gGL.setColorMask(false, true); gl_rect_2d_simple( width, height ); - + gGL.flush(); - gGL.setColorMask(true, true); } - stop_glerror(); - return success; + // (Optional) Mask out part of the baked texture with alpha masks + // will still have an effect even if mClearAlpha is set or the alpha component was replaced + if (mMaskLayerList.size() > 0) + { + gGL.setSceneBlendType(LLRender::BT_MULT_ALPHA); + gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_REPLACE); + for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++) + { + LLTexLayer* layer = *iter; + gGL.flush(); + layer->blendAlphaTexture(x, y, width, height); + gGL.flush(); + } + } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); + gGL.setColorMask(true, true); + gGL.setSceneBlendType(LLRender::BT_ALPHA); } -BOOL LLTexLayerSet::renderBump( S32 x, S32 y, S32 width, S32 height ) +BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height ) { BOOL success = TRUE; + mIsVisible = TRUE; + + if (mMaskLayerList.size() > 0) + { + for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++) + { + LLTexLayer* layer = *iter; + if (layer->isInvisibleAlphaMask()) + { + mIsVisible = FALSE; + } + } + } 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() == RP_BUMP ) + // composite color layers + for (layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++) { - success &= layer->render( x, y, width, height ); + LLTexLayer* layer = *iter; + if (layer->getRenderPass() == RP_COLOR || layer->getRenderPass() == RP_BUMP) + { + 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; } @@ -943,7 +841,7 @@ void LLTexLayerSet::createComposite() width /= 2; height /= 2; } - mComposite = new LLTexLayerSetBuffer( this, width, height, mHasBump ); + mComposite = new LLTexLayerSetBuffer(this, width, height); } } @@ -1004,6 +902,9 @@ void LLTexLayerSet::gatherAlphaMasks(U8 *data, S32 width, S32 height) } } } + + // Set alpha back to that of our alpha masks. + renderAlphaMaskTextures(mComposite->getOriginX(), mComposite->getOriginY(), width, height, true); } void LLTexLayerSet::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components) @@ -1025,7 +926,8 @@ LLTexLayerInfo::LLTexLayerInfo( ) mFixedColor( 0.f, 0.f, 0.f, 0.f ), mLocalTexture( -1 ), mStaticImageIsMask( FALSE ), - mUseLocalTextureAlphaOnly( FALSE ) + mUseLocalTextureAlphaOnly(FALSE), + mIsVisibilityMask(FALSE) { } @@ -1064,6 +966,14 @@ BOOL LLTexLayerInfo::parseXml(LLXmlTreeNode* node) static LLStdStringHandle global_color_string = LLXmlTree::addAttributeString("global_color"); node->getFastAttributeString( global_color_string, mGlobalColor ); + // Visibility mask (optional) + BOOL is_visibility; + static LLStdStringHandle visibility_mask_string = LLXmlTree::addAttributeString("visibility_mask"); + if (node->getFastAttributeBOOL(visibility_mask_string, is_visibility)) + { + mIsVisibilityMask = is_visibility; + } + // color attribute (optional) LLColor4U color4u; static LLStdStringHandle fixed_color_string = LLXmlTree::addAttributeString("fixed_color"); @@ -1150,9 +1060,41 @@ BOOL LLTexLayerInfo::parseXml(LLXmlTreeNode* node) { mLocalTexture = TEX_HAIR; } + else if ("hair_alpha" == local_texture) + { + mLocalTexture = TEX_HAIR_ALPHA; + } + else if ("head_alpha" == local_texture) + { + mLocalTexture = TEX_HEAD_ALPHA; + } + else if ("upper_alpha" == local_texture) + { + mLocalTexture = TEX_UPPER_ALPHA; + } + else if ("lower_alpha" == local_texture) + { + mLocalTexture = TEX_LOWER_ALPHA; + } + else if ("eyes_alpha" == local_texture) + { + mLocalTexture = TEX_EYES_ALPHA; + } + else if ("head_tattoo" == local_texture) + { + mLocalTexture = TEX_HEAD_TATTOO; + } + else if ("upper_tattoo" == local_texture) + { + mLocalTexture = TEX_UPPER_TATTOO; + } + else if ("lower_tattoo" == local_texture) + { + mLocalTexture = TEX_LOWER_TATTOO; + } else { - llwarns << " element has invalid local_texure attribute: " << mName << " " << local_texture << llendl; + llwarns << " element has invalid local_texture attribute: " << mName << " " << local_texture << llendl; return FALSE; } } @@ -1253,13 +1195,14 @@ LLTexLayer::~LLTexLayer() BOOL LLTexLayer::setInfo(LLTexLayerInfo* info) { - llassert(mInfo == NULL); + //llassert(mInfo == NULL); // nyx says this is probably bogus but needs investigating + if (mInfo != NULL) // above llassert(), but softened into a warning + { + llwarns << "BAD STUFF! mInfo != NULL" << llendl; + } mInfo = info; //mID = info->mID; // No ID - if (info->mRenderPass == RP_BUMP) - mTexLayerSet->setBump(TRUE); - { LLTexLayerInfo::morph_name_list_t::iterator iter; for (iter = mInfo->mMorphNameList.begin(); iter != mInfo->mMorphNameList.end(); iter++) @@ -1351,13 +1294,8 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) LLGLEnable color_mat(GL_COLOR_MATERIAL); gPipeline.disableLights(); - BOOL success = TRUE; - - BOOL color_specified = FALSE; - BOOL alpha_mask_specified = FALSE; - LLColor4 net_color; - color_specified = findNetColor( &net_color ); + BOOL color_specified = findNetColor(&net_color); if (mTexLayerSet->getAvatar()->mIsDummy) { @@ -1365,18 +1303,21 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) net_color = LLVOAvatar::getDummyColor(); } + BOOL success = TRUE; + // If you can't see the layer, don't render it. if( is_approx_zero( net_color.mV[VW] ) ) { return success; } + BOOL alpha_mask_specified = FALSE; alpha_list_t::iterator iter = mParamAlphaList.begin(); if( iter != mParamAlphaList.end() ) { // 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( mMaskedMorphs.empty() ) +/* if( mMaskedMorphs.empty() ) { BOOL skip_layer = TRUE; @@ -1397,7 +1338,7 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) { return success; } - } + }*/ renderAlphaMasks( x, y, width, height, &net_color ); alpha_mask_specified = TRUE; @@ -1412,6 +1353,11 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) gGL.flush(); gGL.setSceneBlendType(LLRender::BT_REPLACE); } + else if (getInfo()->mUseLocalTextureAlphaOnly) + { + // Use the alpha channel only + gGL.setColorMask(false, true); + } if( (getInfo()->mLocalTexture != -1) && !getInfo()->mUseLocalTextureAlphaOnly ) { @@ -1419,13 +1365,17 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) LLImageGL* image_gl = NULL; if( mTexLayerSet->getAvatar()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl ) ) { + if (mTexLayerSet->getAvatar()->getLocalTextureID((ETextureIndex)getInfo()->mLocalTexture) == IMG_DEFAULT_AVATAR) + { + image_gl = NULL; + } if( image_gl ) { LLGLDisable alpha_test(getInfo()->mWriteAllChannels ? GL_ALPHA_TEST : 0); LLTexUnit::eTextureAddressMode old_mode = image_gl->getAddressMode(); - gGL.getTexUnit(0)->bind(image_gl, TRUE); + gGL.getTexUnit(0)->bind(image_gl); gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); gl_rect_2d_simple_tex( width, height ); @@ -1434,10 +1384,6 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } } - else - { - success = FALSE; - } } } @@ -1447,7 +1393,7 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask ); if( image_gl ) { - gGL.getTexUnit(0)->bind(image_gl, TRUE); + gGL.getTexUnit(0)->bind(image_gl); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } @@ -1477,6 +1423,12 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) stop_glerror(); } + if (getInfo()->mUseLocalTextureAlphaOnly) + { + // Restore color + alpha mode. + gGL.setColorMask(true, true); + } + if( !success ) { llinfos << "LLTexLayer::render() partial: " << getInfo()->mName << llendl; @@ -1484,6 +1436,49 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) return success; } +BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height) +{ + BOOL success = TRUE; + + gGL.flush(); + + if (!getInfo()->mStaticImageFileName.empty()) + { + LLImageGL* image_gl = gTexStaticImageList.getImageGL(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); + if (image_gl) + { + LLGLSNoAlphaTest gls_no_alpha_test; + gGL.getTexUnit(0)->bind(image_gl, TRUE); + gl_rect_2d_simple_tex(width, height); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } + else + { + success = FALSE; + } + } + else + { + if (getInfo()->mLocalTexture >=0 && getInfo()->mLocalTexture < TEX_NUM_INDICES) + { + LLImageGL* image_gl = NULL; + if (mTexLayerSet->getAvatar()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl)) + { + 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; + } + } + } + } + + return success; +} + U8* LLTexLayer::getAlphaData() { LLCRC alpha_mask_crc; @@ -1609,7 +1604,7 @@ BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4 // Approximates a min() function gGL.flush(); - gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO); + gGL.setSceneBlendType(LLRender::BT_MULT_ALPHA); // Accumulate the alpha component of the texture if( getInfo()->mLocalTexture != -1 ) @@ -1624,7 +1619,7 @@ BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4 LLTexUnit::eTextureAddressMode old_mode = image_gl->getAddressMode(); - gGL.getTexUnit(0)->bind(image_gl, TRUE); + gGL.getTexUnit(0)->bind(image_gl); gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); gl_rect_2d_simple_tex( width, height ); @@ -1633,10 +1628,6 @@ BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } } - else - { - success = FALSE; - } } } @@ -1650,15 +1641,11 @@ BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4 ( (image_gl->getComponents() == 1) && getInfo()->mStaticImageIsMask ) ) { LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(image_gl, TRUE); + gGL.getTexUnit(0)->bind(image_gl); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } } - else - { - success = FALSE; - } } } @@ -1677,7 +1664,7 @@ BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4 gGL.setColorMask(true, true); - if (!mMorphMasksValid && !mMaskedMorphs.empty()) + if (success && !mMorphMasksValid && !mMaskedMorphs.empty()) { LLCRC alpha_mask_crc; const LLUUID& uuid = mTexLayerSet->getAvatar()->getLocalTextureID((ETextureIndex)getInfo()->mLocalTexture); @@ -1825,6 +1812,26 @@ void LLTexLayer::invalidateMorphMasks() mMorphMasksValid = FALSE; } +BOOL LLTexLayer::isVisibilityMask() const +{ + return mInfo->mIsVisibilityMask; +} + +BOOL LLTexLayer::isInvisibleAlphaMask() +{ + const LLTexLayerInfo *info = getInfo(); + + if (info && info->mLocalTexture >= 0 && info->mLocalTexture < TEX_NUM_INDICES) + { + if (mTexLayerSet->getAvatar()->getLocalTextureID((ETextureIndex)info->mLocalTexture) == IMG_INVISIBLE) + { + return TRUE; + } + } + + return FALSE; +} + //----------------------------------------------------------------------------- // LLTexLayerParamAlphaInfo //----------------------------------------------------------------------------- @@ -2095,14 +2102,14 @@ BOOL LLTexLayerParamAlpha::render( S32 x, S32 y, S32 width, S32 height ) // Create the GL texture, and then hang onto it for future use. if( mNeedsCreateTexture ) { - mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw, 0, TRUE, LLViewerImageBoostLevel::TEXLAYER_CACHE); + mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw); mNeedsCreateTexture = FALSE; gGL.getTexUnit(0)->bind(mCachedProcessedImageGL); mCachedProcessedImageGL->setAddressMode(LLTexUnit::TAM_CLAMP); } LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(mCachedProcessedImageGL, TRUE); + gGL.getTexUnit(0)->bind(mCachedProcessedImageGL); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); stop_glerror(); @@ -2550,7 +2557,7 @@ LLImageGL* LLTexStaticImageList::getImageGL(const std::string& file_name, BOOL i // that once an image is a mask it's always a mask. image_gl->setExplicitFormat( GL_ALPHA8, GL_ALPHA ); } - image_gl->createGLTexture(0, image_raw, 0, TRUE, LLViewerImageBoostLevel::OTHER); + image_gl->createGLTexture(0, image_raw); gGL.getTexUnit(0)->bind(image_gl); image_gl->setAddressMode(LLTexUnit::TAM_CLAMP); diff --git a/linden/indra/newview/lltexlayer.h b/linden/indra/newview/lltexlayer.h index 1924d0b..020ba86 100644 --- a/linden/indra/newview/lltexlayer.h +++ b/linden/indra/newview/lltexlayer.h @@ -41,6 +41,7 @@ #include "lluuid.h" #include "llviewerimage.h" #include "llviewervisualparam.h" +#include "llvoavatardefines.h" #include "llwearable.h" #include "v4color.h" #include "llfloater.h" @@ -186,6 +187,7 @@ protected: std::string mStaticImageFileName; BOOL mStaticImageIsMask; BOOL mUseLocalTextureAlphaOnly; // Ignore RGB channels from the input texture. Use alpha as a mask + BOOL mIsVisibilityMask; typedef std::vector > morph_name_list_t; morph_name_list_t mMorphNameList; @@ -205,14 +207,13 @@ protected: class LLTexLayerSetBuffer : public LLDynamicTexture { public: - LLTexLayerSetBuffer( LLTexLayerSet* owner, S32 width, S32 height, BOOL has_bump ); + LLTexLayerSetBuffer(LLTexLayerSet* owner, S32 width, S32 height); virtual ~LLTexLayerSetBuffer(); virtual void preRender(BOOL clear_depth); virtual void postRender(BOOL success); virtual BOOL render(); BOOL updateImmediate(); - void bindBumpTexture( U32 stage ); bool isInitialized(void) const; BOOL needsRender(); void requestUpdate(); @@ -220,8 +221,7 @@ public: void cancelUpload(); BOOL uploadPending() { return mUploadPending; } BOOL render( S32 x, S32 y, S32 width, S32 height ); - void readBackAndUpload(U8* baked_bump_data); - void createBumpTexture() ; + void readBackAndUpload(); static void onTextureUploadComplete( const LLUUID& uuid, void* userdata, @@ -236,16 +236,13 @@ private: void popProjection(); private: - BOOL mHasBump ; BOOL mNeedsUpdate; BOOL mNeedsUpload; BOOL mUploadPending; LLUUID mUploadID; // Identifys the current upload process (null if none). Used to avoid overlaps (eg, when the user rapidly makes two changes outside of Face Edit) LLTexLayerSet* mTexLayerSet; - LLPointer mBumpTex; // zero if none static S32 sGLByteCount; - static S32 sGLBumpByteCount; }; //----------------------------------------------------------------------------- @@ -254,6 +251,7 @@ private: //----------------------------------------------------------------------------- class LLTexLayerSet { + friend class LLTexLayerSetBuffer; public: LLTexLayerSet( LLVOAvatar* avatar ); ~LLTexLayerSet(); @@ -264,7 +262,7 @@ public: BOOL setInfo(LLTexLayerSetInfo *info); BOOL render( S32 x, S32 y, S32 width, S32 height ); - BOOL renderBump( S32 x, S32 y, S32 width,S32 height ); + void renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, bool forceClear = false); BOOL isBodyRegion( const std::string& region ) { return mInfo->mBodyRegion == region; } LLTexLayerSetBuffer* getComposite(); void requestUpdate(); @@ -283,8 +281,9 @@ public: void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components); const std::string getBodyRegion() { return mInfo->mBodyRegion; } BOOL hasComposite() { return (mComposite != NULL); } - void setBump( BOOL b ) { mHasBump = b; } - BOOL hasBump() { return mHasBump; } + LLVOAvatarDefines::EBakedTextureIndex getBakedTexIndex() { return mBakedTexIndex; } + void setBakedTexIndex(LLVOAvatarDefines::EBakedTextureIndex index) { mBakedTexIndex = index; } + BOOL isVisible() const { return mIsVisible; } public: static BOOL sHasCaches; @@ -292,11 +291,14 @@ public: protected: typedef std::vector layer_list_t; layer_list_t mLayerList; + layer_list_t mMaskLayerList; LLTexLayerSetBuffer* mComposite; // Backlink only; don't make this an LLPointer. LLVOAvatar* mAvatar; BOOL mUpdatesEnabled; - BOOL mHasBump; + BOOL mIsVisible; + + LLVOAvatarDefines::EBakedTextureIndex mBakedTexIndex; LLTexLayerSetInfo *mInfo; }; @@ -348,6 +350,9 @@ public: BOOL renderImageRaw( U8* in_data, S32 in_width, S32 in_height, S32 in_components, S32 width, S32 height, BOOL is_mask ); BOOL renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4* colorp ); BOOL hasAlphaParams() { return (!mParamAlphaList.empty());} + BOOL blendAlphaTexture(S32 x, S32 y, S32 width, S32 height); + BOOL isVisibilityMask() const; + BOOL isInvisibleAlphaMask(); protected: LLTexLayerSet* mTexLayerSet; diff --git a/linden/indra/newview/lltexturectrl.cpp b/linden/indra/newview/lltexturectrl.cpp index f4476fb..635c2b0 100644 --- a/linden/indra/newview/lltexturectrl.cpp +++ b/linden/indra/newview/lltexturectrl.cpp @@ -1322,7 +1322,11 @@ void LLTextureCtrl::draw() mTexturep = gImageList.getImageFromFile(mFallbackImageName); mTexturep->setBoostLevel(LLViewerImageBoostLevel::BOOST_PREVIEW); } - + else // mImageAssetID == LLUUID::null + { + mTexturep = NULL; + } + // Border LLRect border( 0, getRect().getHeight(), getRect().getWidth(), BTN_HEIGHT_SMALL ); gl_rect_2d( border, mBorderColor, FALSE ); diff --git a/linden/indra/newview/llviewermenu.cpp b/linden/indra/newview/llviewermenu.cpp index 5172072..f97fffd 100644 --- a/linden/indra/newview/llviewermenu.cpp +++ b/linden/indra/newview/llviewermenu.cpp @@ -8146,6 +8146,14 @@ class LLEditEnableTakeOff : public view_listener_t { new_value = LLAgent::selfHasWearable((void *)WT_SKIRT); } + if (clothing == "alpha") + { + new_value = LLAgent::selfHasWearable((void *)WT_ALPHA); + } + if (clothing == "tattoo") + { + new_value = LLAgent::selfHasWearable((void *)WT_TATTOO); + } // [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.0.0d) // Why aren't they using LLWearable::typeNameToType()? *confuzzled* @@ -8201,6 +8209,14 @@ class LLEditTakeOff : public view_listener_t { LLAgent::userRemoveWearable((void*)WT_SKIRT); } + else if (clothing == "alpha") + { + LLAgent::userRemoveWearable((void*)WT_ALPHA); + } + else if (clothing == "tattoo") + { + LLAgent::userRemoveWearable((void*)WT_TATTOO); + } else if (clothing == "all") { LLAgent::userRemoveAllClothesConfirm(); diff --git a/linden/indra/newview/llvoavatar.cpp b/linden/indra/newview/llvoavatar.cpp index bcc8472..7c867d2 100644 --- a/linden/indra/newview/llvoavatar.cpp +++ b/linden/indra/newview/llvoavatar.cpp @@ -767,6 +767,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mFullyLoadedInitialized(FALSE), mFullyLoaded(FALSE), mHasBakedHair( FALSE ), + mSupportsAlphaLayers(FALSE), mFirstSetActualBoobGravRan( false ), mFirstSetActualButtGravRan( false ), mFirstSetActualFatGravRan( false ) @@ -4706,14 +4707,9 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) if (pass == AVATAR_RENDER_PASS_SINGLE) { - const bool should_alpha_mask = mHasBakedHair && isTextureDefined(TEX_HEAD_BAKED) && isTextureDefined(TEX_UPPER_BAKED) - && isTextureDefined(TEX_LOWER_BAKED) - && mBakedTextureData[BAKED_HEAD].mIsLoaded - && mBakedTextureData[BAKED_UPPER].mIsLoaded && mBakedTextureData[BAKED_LOWER].mIsLoaded - && mBakedTextureData[BAKED_HEAD].mIsUsed - && mBakedTextureData[BAKED_UPPER].mIsUsed && mBakedTextureData[BAKED_LOWER].mIsUsed - && !LLDrawPoolAlpha::sShowDebugAlpha // Don't alpha mask if "Highlight Transparent" checked - && !(isSelf() && gAgent.cameraCustomizeAvatar()); // don't alpha mask if in customize mode + const bool should_alpha_mask = mSupportsAlphaLayers && mHasBakedHair + && !LLDrawPoolAlpha::sShowDebugAlpha // Don't alpha mask if "Highlight Transparent" checked + && !LLDrawPoolAvatar::sSkipTransparent; LLGLState test(GL_ALPHA_TEST, should_alpha_mask); @@ -4826,27 +4822,25 @@ U32 LLVOAvatar::renderRigid() return 0; } - if (isTextureVisible(TEX_EYES_BAKED) || mIsDummy) - { - // If the meshes need to be drawn, enable alpha masking but not blending - bool should_alpha_mask = mHasBakedHair - && mBakedTextureData[BAKED_EYES].mIsLoaded - && mBakedTextureData[BAKED_EYES].mIsUsed - && !(isSelf() && gAgent.cameraCustomizeAvatar()); + const bool should_alpha_mask = mSupportsAlphaLayers && mHasBakedHair + && !LLDrawPoolAlpha::sShowDebugAlpha // Don't alpha mask if "Highlight Transparent" checked + && !LLDrawPoolAvatar::sSkipTransparent; - LLGLState test(GL_ALPHA_TEST, should_alpha_mask); - if (should_alpha_mask) - { - gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); - } + LLGLState test(GL_ALPHA_TEST, should_alpha_mask); + if (should_alpha_mask) + { + gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); + } + + if (isTextureVisible(TEX_EYES_BAKED) || mIsDummy) + { num_indices += mMeshLOD[MESH_ID_EYEBALL_LEFT]->render(mAdjustedPixelArea, TRUE, mIsDummy); num_indices += mMeshLOD[MESH_ID_EYEBALL_RIGHT]->render(mAdjustedPixelArea, TRUE, mIsDummy); - - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); } + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); return num_indices; } @@ -5019,6 +5013,7 @@ void LLVOAvatar::updateTextures() // Spam if this is a baked texture, not set to default image, without valid host info if (isIndexBakedTexture((ETextureIndex)index) && imagep->getID() != IMG_DEFAULT_AVATAR + && imagep->getID() != IMG_INVISIBLE && !imagep->getTargetHost().isOk()) { LL_WARNS_ONCE("Texture") << "LLVOAvatar::updateTextures No host for texture " @@ -5829,6 +5824,7 @@ BOOL LLVOAvatar::loadAvatar() if (layer_set->isBodyRegion(baked_dict->mName)) { mBakedTextureData[baked_iter->first].mTexLayerSet = layer_set; + layer_set->setBakedTexIndex(baked_iter->first); found_baked_entry = true; break; } @@ -7525,6 +7521,9 @@ void LLVOAvatar::updateMeshTextures() } + // Turn on alpha masking correctly for yourself and other avatars on 1.23+ + mSupportsAlphaLayers = isSelf() || is_layer_baked[BAKED_HAIR]; + // Baked textures should be requested from the sim this avatar is on. JC const LLHost target_host = getObjectHost(); if (!target_host.isOk()) @@ -7554,7 +7553,7 @@ void LLVOAvatar::updateMeshTextures() else { mBakedTextureData[i].mIsLoaded = FALSE; - if ( (i == BAKED_HEAD) || (i == BAKED_UPPER) || (i == BAKED_LOWER) ) + if ((baked_img->getID() != IMG_INVISIBLE) && (i == BAKED_HEAD || i == BAKED_UPPER || i == BAKED_LOWER)) { baked_img->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, TRUE, new LLTextureMaskData( mID )); } @@ -7826,7 +7825,13 @@ void LLVOAvatar::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid ) // Baked textures live on other sims. LLHost target_host = getObjectHost(); setTEImage( te, gImageList.getImageFromHost( uuid, target_host ) ); - updateMeshTextures(); + if (uuid != IMG_INVISIBLE) + { + // Do not update textures when setting a new invisible baked texture as + // it would result in destroying the calling object (setNewBakedTexture() + // is called by LLTexLayerSetBuffer::render()) ! + updateMeshTextures(); + } dirtyMesh(); @@ -7839,7 +7844,7 @@ void LLVOAvatar::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid ) if (text_dict->mIsBakedTexture) { llinfos << "New baked texture: " << text_dict->mName << " UUID: " << uuid <mBakedTextureIndex].mTexLayerSet->requestUpdate(); + //mBakedTextureData[text_dict->mBakedTextureIndex].mTexLayerSet->requestUpdate(); } else { @@ -8087,6 +8092,10 @@ void LLVOAvatar::dumpAvatarTEs( const std::string& context ) { llinfos << " " << text_dict->mName << ": IMG_DEFAULT" << llendl; } + else if (te_image->getID() == IMG_INVISIBLE) + { + llinfos << " " << text_dict->mName << ": IMG_INVISIBLE" << llendl; + } else if( te_image->getID() == IMG_DEFAULT_AVATAR ) { llinfos << " " << text_dict->mName << ": IMG_DEFAULT_AVATAR" << llendl; @@ -8258,11 +8267,11 @@ BOOL LLVOAvatar::isWearingWearableType( EWearableType type ) } //----------------------------------------------------------------------------- -// updatedWearable( EWearableType type ) +// wearableUpdated(EWearableType type, BOOL upload_result) // forces an update to any baked textures relevant to type. -// Should be called only on saving the wearable +// will force an upload of the resulting bake if the second parameter is TRUE //----------------------------------------------------------------------------- -void LLVOAvatar::wearableUpdated( EWearableType type ) +void LLVOAvatar::wearableUpdated(EWearableType type, BOOL upload_result) { for (LLVOAvatarDictionary::wearable_map_t::const_iterator wearable_iter = LLVOAvatarDictionary::getInstance()->getWearables().begin(); wearable_iter != LLVOAvatarDictionary::getInstance()->getWearables().end(); @@ -8281,8 +8290,8 @@ void LLVOAvatar::wearableUpdated( EWearableType type ) { if (mBakedTextureData[index].mTexLayerSet) { - mBakedTextureData[index].mTexLayerSet->requestUpdate(); - mBakedTextureData[index].mTexLayerSet->requestUpload(); + invalidateComposite(mBakedTextureData[index].mTexLayerSet, upload_result); + updateMeshTextures(); } break; } @@ -8381,7 +8390,7 @@ void LLVOAvatar::onFirstTEMessageReceived() LLViewerImage* image = getTEImage( mBakedTextureData[i].mTextureIndex ); mBakedTextureData[i].mLastTextureIndex = image->getID(); // If we have more than one texture for the other baked layers, we'll want to call this for them too. - if ( (i == BAKED_HEAD) || (i == BAKED_UPPER) || (i == BAKED_LOWER) ) + if ((image->getID() != IMG_INVISIBLE) && (i == BAKED_HEAD || i == BAKED_UPPER || i == BAKED_LOWER)) { image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, TRUE, new LLTextureMaskData( mID )); } @@ -8653,7 +8662,7 @@ void LLVOAvatar::onBakedTextureMasksLoaded( BOOL success, LLViewerImage *src_vi, { const ETextureIndex texture_index = iter->first; const LLViewerImage *baked_img = self->getTEImage(texture_index); - if (id == baked_img->getID()) + if (baked_img && id == baked_img->getID()) { const EBakedTextureIndex baked_index = text_dict->mBakedTextureIndex; if (self->mBakedTextureData[baked_index].mTexLayerSet) diff --git a/linden/indra/newview/llvoavatar.h b/linden/indra/newview/llvoavatar.h index 0bf648a..1914f47 100644 --- a/linden/indra/newview/llvoavatar.h +++ b/linden/indra/newview/llvoavatar.h @@ -39,6 +39,7 @@ #include #include +#include "imageids.h" // IMG_INVISIBLE #include "llchat.h" #include "lldrawpoolalpha.h" #include "llviewerobject.h" @@ -342,7 +343,7 @@ public: BOOL teToColorParams( LLVOAvatarDefines::ETextureIndex te, const char* param_name[3] ); BOOL isWearingWearableType( EWearableType type ); - void wearableUpdated( EWearableType type ); + void wearableUpdated(EWearableType type, BOOL upload_result = TRUE); //-------------------------------------------------------------------- // texture compositing @@ -602,6 +603,7 @@ private: BOOL mIsBuilt; // state of deferred character building F32 mSpeedAccum; // measures speed (for diagnostics mostly). + BOOL mSupportsAlphaLayers; // For backwards compatibility, TRUE for 1.23+ clients // LLFrameTimer mUpdateLODTimer; // controls frequency of LOD change calculations BOOL mDirtyMesh; @@ -827,7 +829,7 @@ inline BOOL LLVOAvatar::isTextureDefined(U8 te) const inline BOOL LLVOAvatar::isTextureVisible(U8 te) const { - return ((isTextureDefined(te) || isSelf()) + return ((isTextureDefined(te) || mIsSelf) && (getTEImage(te)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha)); } diff --git a/linden/indra/newview/llvoavatardefines.cpp b/linden/indra/newview/llvoavatardefines.cpp index 371ecbb..359a821 100644 --- a/linden/indra/newview/llvoavatardefines.cpp +++ b/linden/indra/newview/llvoavatardefines.cpp @@ -60,6 +60,14 @@ void LLVOAvatarDictionary::initData() mTextureMap[TEX_UPPER_UNDERSHIRT] = new TextureDictionaryEntry("undershirt", TRUE, BAKED_NUM_INDICES, "UIImgDefaultUnderwearUUID", WT_UNDERSHIRT); mTextureMap[TEX_LOWER_UNDERPANTS] = new TextureDictionaryEntry("underpants", TRUE, BAKED_NUM_INDICES, "UIImgDefaultUnderwearUUID", WT_UNDERPANTS); mTextureMap[TEX_SKIRT] = new TextureDictionaryEntry("skirt", TRUE, BAKED_NUM_INDICES, "UIImgDefaultSkirtUUID", WT_SKIRT); + mTextureMap[TEX_LOWER_ALPHA] = new TextureDictionaryEntry("lower_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", WT_ALPHA); + mTextureMap[TEX_UPPER_ALPHA] = new TextureDictionaryEntry("upper_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", WT_ALPHA); + mTextureMap[TEX_HEAD_ALPHA] = new TextureDictionaryEntry("head_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", WT_ALPHA); + mTextureMap[TEX_EYES_ALPHA] = new TextureDictionaryEntry("eyes_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", WT_ALPHA); + mTextureMap[TEX_HAIR_ALPHA] = new TextureDictionaryEntry("hair_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", WT_ALPHA); + mTextureMap[TEX_HEAD_TATTOO] = new TextureDictionaryEntry("head_tattoo", TRUE, BAKED_NUM_INDICES, "", WT_TATTOO); + mTextureMap[TEX_UPPER_TATTOO] = new TextureDictionaryEntry("upper_tattoo", TRUE, BAKED_NUM_INDICES, "", WT_TATTOO); + mTextureMap[TEX_LOWER_TATTOO] = new TextureDictionaryEntry("lower_tattoo", TRUE, BAKED_NUM_INDICES, "", WT_TATTOO); mTextureMap[TEX_HEAD_BAKED] = new TextureDictionaryEntry("head-baked", FALSE, BAKED_HEAD); mTextureMap[TEX_UPPER_BAKED] = new TextureDictionaryEntry("upper-baked", FALSE, BAKED_UPPER); mTextureMap[TEX_LOWER_BAKED] = new TextureDictionaryEntry("lower-baked", FALSE, BAKED_LOWER); @@ -68,12 +76,12 @@ void LLVOAvatarDictionary::initData() mTextureMap[TEX_SKIRT_BAKED] = new TextureDictionaryEntry("skirt-baked", FALSE, BAKED_SKIRT); // Baked textures - mBakedTextureMap[BAKED_HEAD] = new BakedDictionaryEntry(TEX_HEAD_BAKED, "head", 1, TEX_HEAD_BODYPAINT); - mBakedTextureMap[BAKED_UPPER] = new BakedDictionaryEntry(TEX_UPPER_BAKED, "upper_body", 5, TEX_UPPER_SHIRT,TEX_UPPER_BODYPAINT,TEX_UPPER_JACKET,TEX_UPPER_GLOVES,TEX_UPPER_UNDERSHIRT); - mBakedTextureMap[BAKED_LOWER] = new BakedDictionaryEntry(TEX_LOWER_BAKED, "lower_body", 6, TEX_LOWER_PANTS,TEX_LOWER_BODYPAINT,TEX_LOWER_SHOES,TEX_LOWER_SOCKS,TEX_LOWER_JACKET,TEX_LOWER_UNDERPANTS); - mBakedTextureMap[BAKED_EYES] = new BakedDictionaryEntry(TEX_EYES_BAKED, "eyes", 1, TEX_EYES_IRIS); + mBakedTextureMap[BAKED_HEAD] = new BakedDictionaryEntry(TEX_HEAD_BAKED, "head", 3, TEX_HEAD_BODYPAINT, TEX_HEAD_TATTOO, TEX_HEAD_ALPHA); + mBakedTextureMap[BAKED_UPPER] = new BakedDictionaryEntry(TEX_UPPER_BAKED, "upper_body", 7, TEX_UPPER_SHIRT, TEX_UPPER_BODYPAINT, TEX_UPPER_JACKET, TEX_UPPER_GLOVES, TEX_UPPER_UNDERSHIRT, TEX_UPPER_TATTOO, TEX_UPPER_ALPHA); + mBakedTextureMap[BAKED_LOWER] = new BakedDictionaryEntry(TEX_LOWER_BAKED, "lower_body", 8, TEX_LOWER_PANTS, TEX_LOWER_BODYPAINT, TEX_LOWER_SHOES, TEX_LOWER_SOCKS, TEX_LOWER_JACKET, TEX_LOWER_UNDERPANTS, TEX_LOWER_TATTOO, TEX_LOWER_ALPHA); + mBakedTextureMap[BAKED_EYES] = new BakedDictionaryEntry(TEX_EYES_BAKED, "eyes", 2, TEX_EYES_IRIS, TEX_EYES_ALPHA); mBakedTextureMap[BAKED_SKIRT] = new BakedDictionaryEntry(TEX_SKIRT_BAKED, "skirt", 1, TEX_SKIRT); - mBakedTextureMap[BAKED_HAIR] = new BakedDictionaryEntry(TEX_HAIR_BAKED, "hair", 1, TEX_HAIR); + mBakedTextureMap[BAKED_HAIR] = new BakedDictionaryEntry(TEX_HAIR_BAKED, "hair", 2, TEX_HAIR, TEX_HAIR_ALPHA); // Meshes mMeshMap[MESH_ID_HAIR] = new MeshDictionaryEntry(BAKED_HAIR, "hairMesh", 6, LLViewerJoint::PN_4); @@ -86,12 +94,12 @@ void LLVOAvatarDictionary::initData() mMeshMap[MESH_ID_SKIRT] = new MeshDictionaryEntry(BAKED_SKIRT, "skirtMesh", 5, LLViewerJoint::PN_5); // Wearables - mWearableMap[BAKED_HEAD] = new WearableDictionaryEntry("18ded8d6-bcfc-e415-8539-944c0f5ea7a6", 3, WT_SHAPE, WT_SKIN, WT_HAIR); - mWearableMap[BAKED_UPPER] = new WearableDictionaryEntry("338c29e3-3024-4dbb-998d-7c04cf4fa88f", 6, WT_SHAPE, WT_SKIN, WT_SHIRT, WT_JACKET, WT_GLOVES, WT_UNDERSHIRT); - mWearableMap[BAKED_LOWER] = new WearableDictionaryEntry("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f", 7, WT_SHAPE, WT_SKIN, WT_PANTS, WT_SHOES, WT_SOCKS, WT_JACKET, WT_UNDERPANTS); - mWearableMap[BAKED_EYES] = new WearableDictionaryEntry("b2cf28af-b840-1071-3c6a-78085d8128b5", 1, WT_EYES); + mWearableMap[BAKED_HEAD] = new WearableDictionaryEntry("18ded8d6-bcfc-e415-8539-944c0f5ea7a6", 5, WT_SHAPE, WT_SKIN, WT_HAIR, WT_TATTOO, WT_ALPHA); + mWearableMap[BAKED_UPPER] = new WearableDictionaryEntry("338c29e3-3024-4dbb-998d-7c04cf4fa88f", 8, WT_SHAPE, WT_SKIN, WT_SHIRT, WT_JACKET, WT_GLOVES, WT_UNDERSHIRT, WT_TATTOO, WT_ALPHA); + mWearableMap[BAKED_LOWER] = new WearableDictionaryEntry("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f", 9, WT_SHAPE, WT_SKIN, WT_PANTS, WT_SHOES, WT_SOCKS, WT_JACKET, WT_UNDERPANTS, WT_TATTOO, WT_ALPHA); + mWearableMap[BAKED_EYES] = new WearableDictionaryEntry("b2cf28af-b840-1071-3c6a-78085d8128b5", 2, WT_EYES, WT_ALPHA); mWearableMap[BAKED_SKIRT] = new WearableDictionaryEntry("ea800387-ea1a-14e0-56cb-24f2022f969a", 1, WT_SKIRT); - mWearableMap[BAKED_HAIR] = new WearableDictionaryEntry("0af1ef7c-ad24-11dd-8790-001f5bf833e8", 1, WT_HAIR); + mWearableMap[BAKED_HAIR] = new WearableDictionaryEntry("0af1ef7c-ad24-11dd-8790-001f5bf833e8", 2, WT_HAIR, WT_ALPHA); } /* diff --git a/linden/indra/newview/llvoavatardefines.h b/linden/indra/newview/llvoavatardefines.h index b4da140..1fb516c 100644 --- a/linden/indra/newview/llvoavatardefines.h +++ b/linden/indra/newview/llvoavatardefines.h @@ -70,9 +70,16 @@ enum ETextureIndex TEX_SKIRT, TEX_SKIRT_BAKED, // Pre-composited TEX_HAIR_BAKED, // Pre-composited + TEX_LOWER_ALPHA, + TEX_UPPER_ALPHA, + TEX_HEAD_ALPHA, + TEX_EYES_ALPHA, + TEX_HAIR_ALPHA, + TEX_HEAD_TATTOO, + TEX_UPPER_TATTOO, + TEX_LOWER_TATTOO, TEX_NUM_INDICES -}; // "Note: if TEX_NUM_ENTRIES changes, update AGENT_TEXTURES in llagentinfo.h, mTextureIndexBaked, and BAKED_TEXTURE_COUNT" -// Seraph - Above comment about order is probably obsolete. +}; typedef std::vector texture_vec_t; diff --git a/linden/indra/newview/llwearable.cpp b/linden/indra/newview/llwearable.cpp index 087c74c..b74ef02 100644 --- a/linden/indra/newview/llwearable.cpp +++ b/linden/indra/newview/llwearable.cpp @@ -70,6 +70,8 @@ const std::string LLWearable::sTypeName[ WT_COUNT+1 ] = "undershirt", "underpants", "skirt", + "alpha", + "tattoo", "invalid" }; @@ -89,6 +91,8 @@ const std::string LLWearable::sTypeLabel[ WT_COUNT+1 ] = "Undershirt", "Underpants", "Skirt", + "Alpha", + "Tattoo", "invalid" }; @@ -112,6 +116,8 @@ LLAssetType::EType LLWearable::typeToAssetType(EWearableType wearable_type) case WT_UNDERSHIRT: case WT_UNDERPANTS: case WT_SKIRT: + case WT_ALPHA: + case WT_TATTOO: return LLAssetType::AT_CLOTHING; default: return LLAssetType::AT_NONE; diff --git a/linden/indra/newview/rlvhandler.cpp b/linden/indra/newview/rlvhandler.cpp index 88cd174..7f6643c 100644 --- a/linden/indra/newview/rlvhandler.cpp +++ b/linden/indra/newview/rlvhandler.cpp @@ -1097,7 +1097,7 @@ BOOL RlvHandler::processReplyCommand(const LLUUID& uuid, const RlvCommand& rlvCm const EWearableType layerTypes[] = { - WT_GLOVES, WT_JACKET, WT_PANTS, WT_SHIRT, WT_SHOES, WT_SKIRT, WT_SOCKS, + WT_GLOVES, WT_JACKET, WT_PANTS, WT_SHIRT, WT_SHOES, WT_SKIRT, WT_ALPHA, WT_TATTOO, WT_SOCKS, WT_UNDERPANTS, WT_UNDERSHIRT, WT_SKIN, WT_EYES, WT_HAIR, WT_SHAPE }; diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_customize.xml b/linden/indra/newview/skins/default/xui/en-us/floater_customize.xml index e947ecd..a0dd938 100644 --- a/linden/indra/newview/skins/default/xui/en-us/floater_customize.xml +++ b/linden/indra/newview/skins/default/xui/en-us/floater_customize.xml @@ -1108,6 +1108,174 @@ scratch and wear it. height="20" label="Revert" label_selected="Revert" left="305" mouse_opaque="true" name="Revert" scale_image="true" width="82" /> + + + + + [DESC] + + + [DESC]: cannot modify + + + [DESC]: loading... + + + [DESC]: not worn + + + Located in [PATH] + + + Put on a new tattoo by dragging one from your inventory to your avatar. Alternately, you create a new one from scratch and wear it. + + + You do not have permission to modify this wearable. + + + Tattoo: + + + + +