summaryrefslogtreecommitdiff
path: root/indra/llappearance
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llappearance')
-rw-r--r--indra/llappearance/CMakeLists.txt118
-rw-r--r--indra/llappearance/llavatarappearance.cpp1934
-rw-r--r--indra/llappearance/llavatarappearance.h448
-rw-r--r--indra/llappearance/llavatarappearancedefines.cpp268
-rw-r--r--indra/llappearance/llavatarappearancedefines.h229
-rw-r--r--indra/llappearance/llavatarjoint.cpp326
-rw-r--r--indra/llappearance/llavatarjoint.h140
-rw-r--r--indra/llappearance/llavatarjointmesh.cpp375
-rw-r--r--indra/llappearance/llavatarjointmesh.h145
-rw-r--r--indra/llappearance/lldriverparam.cpp619
-rw-r--r--indra/llappearance/lldriverparam.h144
-rw-r--r--indra/llappearance/lljointpickname.h49
-rw-r--r--indra/llappearance/lllocaltextureobject.cpp213
-rw-r--r--indra/llappearance/lllocaltextureobject.h86
-rw-r--r--indra/llappearance/llpolymesh.cpp1048
-rw-r--r--indra/llappearance/llpolymesh.h369
-rw-r--r--indra/llappearance/llpolymorph.cpp790
-rw-r--r--indra/llappearance/llpolymorph.h197
-rw-r--r--indra/llappearance/llpolyskeletaldistortion.cpp293
-rw-r--r--indra/llappearance/llpolyskeletaldistortion.h131
-rw-r--r--indra/llappearance/lltexglobalcolor.cpp152
-rw-r--r--indra/llappearance/lltexglobalcolor.h83
-rw-r--r--indra/llappearance/lltexlayer.cpp2020
-rw-r--r--indra/llappearance/lltexlayer.h315
-rw-r--r--indra/llappearance/lltexlayerparams.cpp571
-rw-r--r--indra/llappearance/lltexlayerparams.h219
-rw-r--r--indra/llappearance/lltexturemanagerbridge.cpp32
-rw-r--r--indra/llappearance/lltexturemanagerbridge.h46
-rw-r--r--indra/llappearance/llviewervisualparam.cpp163
-rw-r--r--indra/llappearance/llviewervisualparam.h110
-rw-r--r--indra/llappearance/llwearable.cpp781
-rw-r--r--indra/llappearance/llwearable.h142
-rw-r--r--indra/llappearance/llwearabledata.cpp360
-rw-r--r--indra/llappearance/llwearabledata.h109
-rw-r--r--indra/llappearance/llwearabletype.cpp171
-rw-r--r--indra/llappearance/llwearabletype.h86
36 files changed, 13282 insertions, 0 deletions
diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt
new file mode 100644
index 0000000000..0dbd58b7cd
--- /dev/null
+++ b/indra/llappearance/CMakeLists.txt
@@ -0,0 +1,118 @@
+# -*- cmake -*-
+
+project(llappearance)
+
+include(00-Common)
+include(LLCommon)
+include(LLCharacter)
+include(LLImage)
+include(LLInventory)
+include(LLMath)
+include(LLMessage)
+include(LLRender)
+include(LLVFS)
+include(LLWindow)
+include(LLXML)
+include(Linking)
+
+include_directories(
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLCHARACTER_INCLUDE_DIRS}
+ ${LLIMAGE_INCLUDE_DIRS}
+ ${LLINVENTORY_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
+ ${LLVFS_INCLUDE_DIRS}
+ ${LLWINDOW_INCLUDE_DIRS}
+ ${LLXML_INCLUDE_DIRS}
+ )
+
+set(llappearance_SOURCE_FILES
+ llavatarappearance.cpp
+ llavatarjoint.cpp
+ llavatarjointmesh.cpp
+ lldriverparam.cpp
+ lllocaltextureobject.cpp
+ llpolyskeletaldistortion.cpp
+ llpolymesh.cpp
+ llpolymorph.cpp
+ lltexglobalcolor.cpp
+ lltexlayer.cpp
+ lltexlayerparams.cpp
+ lltexturemanagerbridge.cpp
+ llwearable.cpp
+ llwearabledata.cpp
+ llwearabletype.cpp
+ llviewervisualparam.cpp
+ llavatarappearancedefines.cpp
+ )
+
+set(llappearance_HEADER_FILES
+ CMakeLists.txt
+
+ llavatarappearance.h
+ llavatarjoint.h
+ llavatarjointmesh.h
+ lldriverparam.h
+ lljointpickname.h
+ lllocaltextureobject.h
+ llpolyskeletaldistortion.h
+ llpolymesh.h
+ llpolymorph.h
+ lltexglobalcolor.h
+ lltexlayer.h
+ lltexlayerparams.h
+ lltexturemanagerbridge.h
+ llwearable.h
+ llwearabledata.h
+ llwearabletype.h
+ llviewervisualparam.h
+ llavatarappearancedefines.h
+ )
+
+set_source_files_properties(${llappearance_HEADER_FILES}
+ PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND llappearance_SOURCE_FILES ${llappearance_HEADER_FILES})
+
+add_library (llappearance ${llappearance_SOURCE_FILES})
+
+target_link_libraries(llappearance
+ ${LLCHARACTER_LIBRARIES}
+ ${LLINVENTORY_LIBRARIES}
+ ${LLIMAGE_LIBRARIES}
+ ${LLRENDER_LIBRARIES}
+ ${LLVFS_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${LLXML_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ )
+
+if (BUILD_HEADLESS)
+ add_library (llappearanceheadless ${llappearance_SOURCE_FILES})
+
+ target_link_libraries(llappearanceheadless
+ ${LLCHARACTER_LIBRARIES}
+ ${LLINVENTORY_LIBRARIES}
+ ${LLIMAGE_LIBRARIES}
+ ${LLRENDERHEADLESS_LIBRARIES}
+ ${LLVFS_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${LLXML_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ )
+endif (BUILD_HEADLESS)
+
+#add unit tests
+#if (LL_TESTS)
+# INCLUDE(LLAddBuildTest)
+# SET(llappearance_TEST_SOURCE_FILES
+# # no real unit tests yet!
+# )
+# LL_ADD_PROJECT_UNIT_TESTS(llappearance "${llappearance_TEST_SOURCE_FILES}")
+
+ #set(TEST_DEBUG on)
+# set(test_libs llappearance ${LLCOMMON_LIBRARIES})
+#endif (LL_TESTS)
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
new file mode 100644
index 0000000000..2d5744bb5e
--- /dev/null
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -0,0 +1,1934 @@
+/**
+ * @File llavatarappearance.cpp
+ * @brief Implementation of LLAvatarAppearance class
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#if LL_MSVC
+// disable warning about boost::lexical_cast returning uninitialized data
+// when it fails to parse the string
+#pragma warning (disable:4701)
+#endif
+
+#include "linden_common.h"
+
+#include "llavatarappearance.h"
+#include "llavatarappearancedefines.h"
+#include "llavatarjointmesh.h"
+#include "imageids.h"
+#include "lldir.h"
+#include "lldeleteutils.h"
+#include "llpolymorph.h"
+#include "llpolymesh.h"
+#include "llpolyskeletaldistortion.h"
+#include "llstl.h"
+#include "lltexglobalcolor.h"
+#include "llwearabledata.h"
+
+
+#if LL_MSVC
+// disable boost::lexical_cast warning
+#pragma warning (disable:4702)
+#endif
+
+#include <boost/lexical_cast.hpp>
+
+using namespace LLAvatarAppearanceDefines;
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+const std::string AVATAR_DEFAULT_CHAR = "avatar";
+const LLColor4 DUMMY_COLOR = LLColor4(0.5,0.5,0.5,1.0);
+
+/*********************************************************************************
+ ** **
+ ** Begin private LLAvatarAppearance Support classes
+ **
+ **/
+
+//------------------------------------------------------------------------
+// LLAvatarBoneInfo
+// Trans/Scale/Rot etc. info about each avatar bone. Used by LLVOAvatarSkeleton.
+//------------------------------------------------------------------------
+class LLAvatarBoneInfo
+{
+ friend class LLAvatarAppearance;
+ friend class LLAvatarSkeletonInfo;
+public:
+ LLAvatarBoneInfo() : mIsJoint(FALSE) {}
+ ~LLAvatarBoneInfo()
+ {
+ std::for_each(mChildList.begin(), mChildList.end(), DeletePointer());
+ }
+ BOOL parseXml(LLXmlTreeNode* node);
+
+private:
+ std::string mName;
+ BOOL mIsJoint;
+ LLVector3 mPos;
+ LLVector3 mRot;
+ LLVector3 mScale;
+ LLVector3 mPivot;
+ typedef std::vector<LLAvatarBoneInfo*> child_list_t;
+ child_list_t mChildList;
+};
+
+//------------------------------------------------------------------------
+// LLAvatarSkeletonInfo
+// Overall avatar skeleton
+//------------------------------------------------------------------------
+class LLAvatarSkeletonInfo
+{
+ friend class LLAvatarAppearance;
+public:
+ LLAvatarSkeletonInfo() :
+ mNumBones(0), mNumCollisionVolumes(0) {}
+ ~LLAvatarSkeletonInfo()
+ {
+ std::for_each(mBoneInfoList.begin(), mBoneInfoList.end(), DeletePointer());
+ }
+ BOOL parseXml(LLXmlTreeNode* node);
+ S32 getNumBones() const { return mNumBones; }
+ S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
+
+private:
+ S32 mNumBones;
+ S32 mNumCollisionVolumes;
+ typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
+ bone_info_list_t mBoneInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLAvatarXmlInfo
+//-----------------------------------------------------------------------------
+
+LLAvatarAppearance::LLAvatarXmlInfo::LLAvatarXmlInfo()
+ : mTexSkinColorInfo(0), mTexHairColorInfo(0), mTexEyeColorInfo(0)
+{
+}
+
+LLAvatarAppearance::LLAvatarXmlInfo::~LLAvatarXmlInfo()
+{
+ std::for_each(mMeshInfoList.begin(), mMeshInfoList.end(), DeletePointer());
+ std::for_each(mSkeletalDistortionInfoList.begin(), mSkeletalDistortionInfoList.end(), DeletePointer());
+ std::for_each(mAttachmentInfoList.begin(), mAttachmentInfoList.end(), DeletePointer());
+ deleteAndClear(mTexSkinColorInfo);
+ deleteAndClear(mTexHairColorInfo);
+ deleteAndClear(mTexEyeColorInfo);
+ std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
+ std::for_each(mDriverInfoList.begin(), mDriverInfoList.end(), DeletePointer());
+ std::for_each(mMorphMaskInfoList.begin(), mMorphMaskInfoList.end(), DeletePointer());
+}
+
+
+/**
+ **
+ ** End LLAvatarAppearance Support classes
+ ** **
+ *********************************************************************************/
+
+//-----------------------------------------------------------------------------
+// Static Data
+//-----------------------------------------------------------------------------
+LLXmlTree LLAvatarAppearance::sXMLTree;
+LLXmlTree LLAvatarAppearance::sSkeletonXMLTree;
+LLAvatarSkeletonInfo* LLAvatarAppearance::sAvatarSkeletonInfo = NULL;
+LLAvatarAppearance::LLAvatarXmlInfo* LLAvatarAppearance::sAvatarXmlInfo = NULL;
+
+
+LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) :
+ LLCharacter(),
+ mIsDummy(FALSE),
+ mTexSkinColor( NULL ),
+ mTexHairColor( NULL ),
+ mTexEyeColor( NULL ),
+ mPelvisToFoot(0.f),
+ mHeadOffset(),
+ mRoot(NULL),
+ mWearableData(wearable_data)
+{
+ llassert_always(mWearableData);
+ mBakedTextureDatas.resize(LLAvatarAppearanceDefines::BAKED_NUM_INDICES);
+ for (U32 i = 0; i < mBakedTextureDatas.size(); i++ )
+ {
+ mBakedTextureDatas[i].mLastTextureID = IMG_DEFAULT_AVATAR;
+ mBakedTextureDatas[i].mTexLayerSet = NULL;
+ mBakedTextureDatas[i].mIsLoaded = false;
+ mBakedTextureDatas[i].mIsUsed = false;
+ mBakedTextureDatas[i].mMaskTexName = 0;
+ mBakedTextureDatas[i].mTextureIndex = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::bakedToLocalTextureIndex((LLAvatarAppearanceDefines::EBakedTextureIndex)i);
+ }
+
+ mIsBuilt = FALSE;
+
+ mNumCollisionVolumes = 0;
+ mCollisionVolumes = NULL;
+}
+
+// virtual
+void LLAvatarAppearance::initInstance()
+{
+ //-------------------------------------------------------------------------
+ // initialize joint, mesh and shape members
+ //-------------------------------------------------------------------------
+ mRoot = createAvatarJoint();
+ mRoot->setName( "mRoot" );
+
+ for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().begin();
+ iter != LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().end();
+ ++iter)
+ {
+ const EMeshIndex mesh_index = iter->first;
+ const LLAvatarAppearanceDictionary::MeshEntry *mesh_dict = iter->second;
+ LLAvatarJoint* joint = createAvatarJoint();
+ joint->setName(mesh_dict->mName);
+ joint->setMeshID(mesh_index);
+ mMeshLOD.push_back(joint);
+
+ /* mHairLOD.setName("mHairLOD");
+ mHairMesh0.setName("mHairMesh0");
+ mHairMesh0.setMeshID(MESH_ID_HAIR);
+ mHairMesh1.setName("mHairMesh1"); */
+ for (U32 lod = 0; lod < mesh_dict->mLOD; lod++)
+ {
+ LLAvatarJointMesh* mesh = createAvatarJointMesh();
+ std::string mesh_name = "m" + mesh_dict->mName + boost::lexical_cast<std::string>(lod);
+ // We pre-pended an m - need to capitalize first character for camelCase
+ mesh_name[1] = toupper(mesh_name[1]);
+ mesh->setName(mesh_name);
+ mesh->setMeshID(mesh_index);
+ mesh->setPickName(mesh_dict->mPickName);
+ mesh->setIsTransparent(FALSE);
+ switch((int)mesh_index)
+ {
+ case MESH_ID_HAIR:
+ mesh->setIsTransparent(TRUE);
+ break;
+ case MESH_ID_SKIRT:
+ mesh->setIsTransparent(TRUE);
+ break;
+ case MESH_ID_EYEBALL_LEFT:
+ case MESH_ID_EYEBALL_RIGHT:
+ mesh->setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ break;
+ }
+
+ joint->mMeshParts.push_back(mesh);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // associate baked textures with meshes
+ //-------------------------------------------------------------------------
+ for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().begin();
+ iter != LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().end();
+ ++iter)
+ {
+ const EMeshIndex mesh_index = iter->first;
+ const LLAvatarAppearanceDictionary::MeshEntry *mesh_dict = iter->second;
+ const EBakedTextureIndex baked_texture_index = mesh_dict->mBakedID;
+ // Skip it if there's no associated baked texture.
+ if (baked_texture_index == BAKED_NUM_INDICES) continue;
+
+ for (avatar_joint_mesh_list_t::iterator iter = mMeshLOD[mesh_index]->mMeshParts.begin();
+ iter != mMeshLOD[mesh_index]->mMeshParts.end();
+ ++iter)
+ {
+ LLAvatarJointMesh* mesh = (*iter);
+ mBakedTextureDatas[(int)baked_texture_index].mJointMeshes.push_back(mesh);
+ }
+ }
+
+ buildCharacter();
+
+}
+
+// virtual
+LLAvatarAppearance::~LLAvatarAppearance()
+{
+ deleteAndClear(mTexSkinColor);
+ deleteAndClear(mTexHairColor);
+ deleteAndClear(mTexEyeColor);
+
+ for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
+ {
+ deleteAndClear(mBakedTextureDatas[i].mTexLayerSet);
+ mBakedTextureDatas[i].mJointMeshes.clear();
+
+ for (morph_list_t::iterator iter2 = mBakedTextureDatas[i].mMaskedMorphs.begin();
+ iter2 != mBakedTextureDatas[i].mMaskedMorphs.end(); iter2++)
+ {
+ LLMaskedMorph* masked_morph = (*iter2);
+ delete masked_morph;
+ }
+ }
+
+ if (mRoot) mRoot->removeAllChildren();
+ mJointMap.clear();
+
+ clearSkeleton();
+ deleteAndClearArray(mCollisionVolumes);
+
+ deleteAndClear(mTexSkinColor);
+ deleteAndClear(mTexHairColor);
+ deleteAndClear(mTexEyeColor);
+
+ std::for_each(mPolyMeshes.begin(), mPolyMeshes.end(), DeletePairedPointer());
+ mPolyMeshes.clear();
+
+ for (avatar_joint_list_t::iterator jointIter = mMeshLOD.begin();
+ jointIter != mMeshLOD.end();
+ ++jointIter)
+ {
+ LLAvatarJoint* joint = *jointIter;
+ std::for_each(joint->mMeshParts.begin(), joint->mMeshParts.end(), DeletePointer());
+ joint->mMeshParts.clear();
+ }
+ std::for_each(mMeshLOD.begin(), mMeshLOD.end(), DeletePointer());
+ mMeshLOD.clear();
+}
+
+//static
+void LLAvatarAppearance::initClass()
+{
+ std::string xmlFile;
+
+ xmlFile = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,AVATAR_DEFAULT_CHAR) + "_lad.xml";
+ BOOL success = sXMLTree.parseFile( xmlFile, FALSE );
+ if (!success)
+ {
+ llerrs << "Problem reading avatar configuration file:" << xmlFile << llendl;
+ }
+
+ // now sanity check xml file
+ LLXmlTreeNode* root = sXMLTree.getRoot();
+ if (!root)
+ {
+ llerrs << "No root node found in avatar configuration file: " << xmlFile << llendl;
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // <linden_avatar version="1.0"> (root)
+ //-------------------------------------------------------------------------
+ if( !root->hasName( "linden_avatar" ) )
+ {
+ llerrs << "Invalid avatar file header: " << xmlFile << llendl;
+ }
+
+ std::string version;
+ static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
+ if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ {
+ llerrs << "Invalid avatar file version: " << version << " in file: " << xmlFile << llendl;
+ }
+
+ S32 wearable_def_version = 1;
+ static LLStdStringHandle wearable_definition_version_string = LLXmlTree::addAttributeString("wearable_definition_version");
+ root->getFastAttributeS32( wearable_definition_version_string, wearable_def_version );
+ LLWearable::setCurrentDefinitionVersion( wearable_def_version );
+
+ std::string mesh_file_name;
+
+ LLXmlTreeNode* skeleton_node = root->getChildByName( "skeleton" );
+ if (!skeleton_node)
+ {
+ llerrs << "No skeleton in avatar configuration file: " << xmlFile << llendl;
+ return;
+ }
+
+ std::string skeleton_file_name;
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if (!skeleton_node->getFastAttributeString(file_name_string, skeleton_file_name))
+ {
+ llerrs << "No file name in skeleton node in avatar config file: " << xmlFile << llendl;
+ }
+
+ std::string skeleton_path;
+ skeleton_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,skeleton_file_name);
+ if (!parseSkeletonFile(skeleton_path))
+ {
+ llerrs << "Error parsing skeleton file: " << skeleton_path << llendl;
+ }
+
+ // Process XML data
+
+ // avatar_skeleton.xml
+ if (sAvatarSkeletonInfo)
+ { //this can happen if a login attempt failed
+ delete sAvatarSkeletonInfo;
+ }
+ sAvatarSkeletonInfo = new LLAvatarSkeletonInfo;
+ if (!sAvatarSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot()))
+ {
+ llerrs << "Error parsing skeleton XML file: " << skeleton_path << llendl;
+ }
+ // parse avatar_lad.xml
+ if (sAvatarXmlInfo)
+ { //this can happen if a login attempt failed
+ deleteAndClear(sAvatarXmlInfo);
+ }
+ sAvatarXmlInfo = new LLAvatarXmlInfo;
+ if (!sAvatarXmlInfo->parseXmlSkeletonNode(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
+ }
+ if (!sAvatarXmlInfo->parseXmlMeshNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
+ }
+ if (!sAvatarXmlInfo->parseXmlColorNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
+ }
+ if (!sAvatarXmlInfo->parseXmlLayerNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
+ }
+ if (!sAvatarXmlInfo->parseXmlDriverNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
+ }
+ if (!sAvatarXmlInfo->parseXmlMorphNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
+ }
+}
+
+void LLAvatarAppearance::cleanupClass()
+{
+ deleteAndClear(sAvatarXmlInfo);
+ // *TODO: What about sAvatarSkeletonInfo ???
+ sSkeletonXMLTree.cleanup();
+ sXMLTree.cleanup();
+}
+
+using namespace LLAvatarAppearanceDefines;
+
+//------------------------------------------------------------------------
+// The viewer can only suggest a good size for the agent,
+// the simulator will keep it inside a reasonable range.
+void LLAvatarAppearance::computeBodySize()
+{
+ LLVector3 pelvis_scale = mPelvisp->getScale();
+
+ // some of the joints have not been cached
+ LLVector3 skull = mSkullp->getPosition();
+ //LLVector3 skull_scale = mSkullp->getScale();
+
+ LLVector3 neck = mNeckp->getPosition();
+ LLVector3 neck_scale = mNeckp->getScale();
+
+ LLVector3 chest = mChestp->getPosition();
+ LLVector3 chest_scale = mChestp->getScale();
+
+ // the rest of the joints have been cached
+ LLVector3 head = mHeadp->getPosition();
+ LLVector3 head_scale = mHeadp->getScale();
+
+ LLVector3 torso = mTorsop->getPosition();
+ LLVector3 torso_scale = mTorsop->getScale();
+
+ LLVector3 hip = mHipLeftp->getPosition();
+ LLVector3 hip_scale = mHipLeftp->getScale();
+
+ LLVector3 knee = mKneeLeftp->getPosition();
+ LLVector3 knee_scale = mKneeLeftp->getScale();
+
+ LLVector3 ankle = mAnkleLeftp->getPosition();
+ LLVector3 ankle_scale = mAnkleLeftp->getScale();
+
+ LLVector3 foot = mFootLeftp->getPosition();
+
+ F32 old_offset = mAvatarOffset.mV[VZ];
+
+ mAvatarOffset.mV[VZ] = getVisualParamWeight(11001);
+
+ mPelvisToFoot = hip.mV[VZ] * pelvis_scale.mV[VZ] -
+ knee.mV[VZ] * hip_scale.mV[VZ] -
+ ankle.mV[VZ] * knee_scale.mV[VZ] -
+ foot.mV[VZ] * ankle_scale.mV[VZ];
+
+ LLVector3 new_body_size;
+ new_body_size.mV[VZ] = mPelvisToFoot +
+ // the sqrt(2) correction below is an approximate
+ // correction to get to the top of the head
+ F_SQRT2 * (skull.mV[VZ] * head_scale.mV[VZ]) +
+ head.mV[VZ] * neck_scale.mV[VZ] +
+ neck.mV[VZ] * chest_scale.mV[VZ] +
+ chest.mV[VZ] * torso_scale.mV[VZ] +
+ torso.mV[VZ] * pelvis_scale.mV[VZ];
+
+ // TODO -- measure the real depth and width
+ new_body_size.mV[VX] = DEFAULT_AGENT_DEPTH;
+ new_body_size.mV[VY] = DEFAULT_AGENT_WIDTH;
+
+ mAvatarOffset.mV[VX] = 0.0f;
+ mAvatarOffset.mV[VY] = 0.0f;
+
+ if (new_body_size != mBodySize || old_offset != mAvatarOffset.mV[VZ])
+ {
+ mBodySize = new_body_size;
+ bodySizeChanged();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// parseSkeletonFile()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::parseSkeletonFile(const std::string& filename)
+{
+ //-------------------------------------------------------------------------
+ // parse the file
+ //-------------------------------------------------------------------------
+ BOOL parsesuccess = sSkeletonXMLTree.parseFile( filename, FALSE );
+
+ if (!parsesuccess)
+ {
+ llerrs << "Can't parse skeleton file: " << filename << llendl;
+ return FALSE;
+ }
+
+ // now sanity check xml file
+ LLXmlTreeNode* root = sSkeletonXMLTree.getRoot();
+ if (!root)
+ {
+ llerrs << "No root node found in avatar skeleton file: " << filename << llendl;
+ return FALSE;
+ }
+
+ if( !root->hasName( "linden_skeleton" ) )
+ {
+ llerrs << "Invalid avatar skeleton file header: " << filename << llendl;
+ return FALSE;
+ }
+
+ std::string version;
+ static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
+ if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ {
+ llerrs << "Invalid avatar skeleton file version: " << version << " in file: " << filename << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// setupBone()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::setupBone(const LLAvatarBoneInfo* info, LLJoint* parent, S32 &volume_num, S32 &joint_num)
+{
+ LLJoint* joint = NULL;
+
+ if (info->mIsJoint)
+ {
+ joint = getCharacterJoint(joint_num);
+ if (!joint)
+ {
+ llwarns << "Too many bones" << llendl;
+ return FALSE;
+ }
+ joint->setName( info->mName );
+ }
+ else // collision volume
+ {
+ if (volume_num >= (S32)mNumCollisionVolumes)
+ {
+ llwarns << "Too many bones" << llendl;
+ return FALSE;
+ }
+ joint = (&mCollisionVolumes[volume_num]);
+ joint->setName( info->mName );
+ }
+
+ // add to parent
+ if (parent)
+ {
+ parent->addChild( joint );
+ }
+
+ joint->setPosition(info->mPos);
+ joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY],
+ info->mRot.mV[VZ], LLQuaternion::XYZ));
+ joint->setScale(info->mScale);
+
+ joint->setDefaultFromCurrentXform();
+
+ if (info->mIsJoint)
+ {
+ joint->setSkinOffset( info->mPivot );
+ joint_num++;
+ }
+ else // collision volume
+ {
+ volume_num++;
+ }
+
+ // setup children
+ LLAvatarBoneInfo::child_list_t::const_iterator iter;
+ for (iter = info->mChildList.begin(); iter != info->mChildList.end(); ++iter)
+ {
+ LLAvatarBoneInfo *child_info = *iter;
+ if (!setupBone(child_info, joint, volume_num, joint_num))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// allocateCharacterJoints()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::allocateCharacterJoints( U32 num )
+{
+ clearSkeleton();
+
+ for(S32 joint_num = 0; joint_num < (S32)num; joint_num++)
+ {
+ mSkeleton.push_back(createAvatarJoint(joint_num));
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// buildSkeleton()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::buildSkeleton(const LLAvatarSkeletonInfo *info)
+{
+ //-------------------------------------------------------------------------
+ // allocate joints
+ //-------------------------------------------------------------------------
+ if (!allocateCharacterJoints(info->mNumBones))
+ {
+ llerrs << "Can't allocate " << info->mNumBones << " joints" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // allocate volumes
+ //-------------------------------------------------------------------------
+ if (info->mNumCollisionVolumes)
+ {
+ if (!allocateCollisionVolumes(info->mNumCollisionVolumes))
+ {
+ llerrs << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << llendl;
+ return FALSE;
+ }
+ }
+
+ S32 current_joint_num = 0;
+ S32 current_volume_num = 0;
+ LLAvatarSkeletonInfo::bone_info_list_t::const_iterator iter;
+ for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); ++iter)
+ {
+ LLAvatarBoneInfo *info = *iter;
+ if (!setupBone(info, NULL, current_volume_num, current_joint_num))
+ {
+ llerrs << "Error parsing bone in skeleton file" << llendl;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// clearSkeleton()
+//-----------------------------------------------------------------------------
+void LLAvatarAppearance::clearSkeleton()
+{
+ std::for_each(mSkeleton.begin(), mSkeleton.end(), DeletePointer());
+ mSkeleton.clear();
+}
+
+//-----------------------------------------------------------------------------
+// LLAvatarAppearance::buildCharacter()
+// Deferred initialization and rebuild of the avatar.
+//-----------------------------------------------------------------------------
+void LLAvatarAppearance::buildCharacter()
+{
+ //-------------------------------------------------------------------------
+ // remove all references to our existing skeleton
+ // so we can rebuild it
+ //-------------------------------------------------------------------------
+ flushAllMotions();
+
+ //-------------------------------------------------------------------------
+ // remove all of mRoot's children
+ //-------------------------------------------------------------------------
+ mRoot->removeAllChildren();
+ mJointMap.clear();
+ mIsBuilt = FALSE;
+
+ //-------------------------------------------------------------------------
+ // clear mesh data
+ //-------------------------------------------------------------------------
+ for (avatar_joint_list_t::iterator jointIter = mMeshLOD.begin();
+ jointIter != mMeshLOD.end(); ++jointIter)
+ {
+ LLAvatarJoint* joint = *jointIter;
+ for (avatar_joint_mesh_list_t::iterator meshIter = joint->mMeshParts.begin();
+ meshIter != joint->mMeshParts.end(); ++meshIter)
+ {
+ LLAvatarJointMesh * mesh = *meshIter;
+ mesh->setMesh(NULL);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // (re)load our skeleton and meshes
+ //-------------------------------------------------------------------------
+ LLTimer timer;
+
+ BOOL status = loadAvatar();
+ stop_glerror();
+
+// gPrintMessagesThisFrame = TRUE;
+ lldebugs << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << llendl;
+
+ if (!status)
+ {
+ if (isSelf())
+ {
+ llerrs << "Unable to load user's avatar" << llendl;
+ }
+ else
+ {
+ llwarns << "Unable to load other's avatar" << llendl;
+ }
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize "well known" joint pointers
+ //-------------------------------------------------------------------------
+ mPelvisp = mRoot->findJoint("mPelvis");
+ mTorsop = mRoot->findJoint("mTorso");
+ mChestp = mRoot->findJoint("mChest");
+ mNeckp = mRoot->findJoint("mNeck");
+ mHeadp = mRoot->findJoint("mHead");
+ mSkullp = mRoot->findJoint("mSkull");
+ mHipLeftp = mRoot->findJoint("mHipLeft");
+ mHipRightp = mRoot->findJoint("mHipRight");
+ mKneeLeftp = mRoot->findJoint("mKneeLeft");
+ mKneeRightp = mRoot->findJoint("mKneeRight");
+ mAnkleLeftp = mRoot->findJoint("mAnkleLeft");
+ mAnkleRightp = mRoot->findJoint("mAnkleRight");
+ mFootLeftp = mRoot->findJoint("mFootLeft");
+ mFootRightp = mRoot->findJoint("mFootRight");
+ mWristLeftp = mRoot->findJoint("mWristLeft");
+ mWristRightp = mRoot->findJoint("mWristRight");
+ mEyeLeftp = mRoot->findJoint("mEyeLeft");
+ mEyeRightp = mRoot->findJoint("mEyeRight");
+
+ //-------------------------------------------------------------------------
+ // Make sure "well known" pointers exist
+ //-------------------------------------------------------------------------
+ if (!(mPelvisp &&
+ mTorsop &&
+ mChestp &&
+ mNeckp &&
+ mHeadp &&
+ mSkullp &&
+ mHipLeftp &&
+ mHipRightp &&
+ mKneeLeftp &&
+ mKneeRightp &&
+ mAnkleLeftp &&
+ mAnkleRightp &&
+ mFootLeftp &&
+ mFootRightp &&
+ mWristLeftp &&
+ mWristRightp &&
+ mEyeLeftp &&
+ mEyeRightp))
+ {
+ llerrs << "Failed to create avatar." << llendl;
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize the pelvis
+ //-------------------------------------------------------------------------
+ mPelvisp->setPosition( LLVector3(0.0f, 0.0f, 0.0f) );
+
+ mIsBuilt = TRUE;
+ stop_glerror();
+
+}
+
+BOOL LLAvatarAppearance::loadAvatar()
+{
+// LLFastTimer t(FTM_LOAD_AVATAR);
+
+ // avatar_skeleton.xml
+ if( !buildSkeleton(sAvatarSkeletonInfo) )
+ {
+ llwarns << "avatar file: buildSkeleton() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <skeleton>
+ if( !loadSkeletonNode() )
+ {
+ llwarns << "avatar file: loadNodeSkeleton() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <mesh>
+ if( !loadMeshNodes() )
+ {
+ llwarns << "avatar file: loadNodeMesh() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <global_color>
+ if( sAvatarXmlInfo->mTexSkinColorInfo )
+ {
+ mTexSkinColor = new LLTexGlobalColor( this );
+ if( !mTexSkinColor->setInfo( sAvatarXmlInfo->mTexSkinColorInfo ) )
+ {
+ llwarns << "avatar file: mTexSkinColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"skin_color\" not found" << llendl;
+ return FALSE;
+ }
+ if( sAvatarXmlInfo->mTexHairColorInfo )
+ {
+ mTexHairColor = new LLTexGlobalColor( this );
+ if( !mTexHairColor->setInfo( sAvatarXmlInfo->mTexHairColorInfo ) )
+ {
+ llwarns << "avatar file: mTexHairColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"hair_color\" not found" << llendl;
+ return FALSE;
+ }
+ if( sAvatarXmlInfo->mTexEyeColorInfo )
+ {
+ mTexEyeColor = new LLTexGlobalColor( this );
+ if( !mTexEyeColor->setInfo( sAvatarXmlInfo->mTexEyeColorInfo ) )
+ {
+ llwarns << "avatar file: mTexEyeColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"eye_color\" not found" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <layer_set>
+ if (sAvatarXmlInfo->mLayerInfoList.empty())
+ {
+ llwarns << "avatar file: missing <layer_set> node" << llendl;
+ return FALSE;
+ }
+
+ if (sAvatarXmlInfo->mMorphMaskInfoList.empty())
+ {
+ llwarns << "avatar file: missing <morph_masks> node" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <morph_masks>
+ for (LLAvatarXmlInfo::morph_info_list_t::iterator iter = sAvatarXmlInfo->mMorphMaskInfoList.begin();
+ iter != sAvatarXmlInfo->mMorphMaskInfoList.end();
+ ++iter)
+ {
+ LLAvatarXmlInfo::LLAvatarMorphInfo *info = *iter;
+
+ EBakedTextureIndex baked = LLAvatarAppearanceDictionary::findBakedByRegionName(info->mRegion);
+ if (baked != BAKED_NUM_INDICES)
+ {
+ LLVisualParam* morph_param;
+ const std::string *name = &info->mName;
+ morph_param = getVisualParam(name->c_str());
+ if (morph_param)
+ {
+ BOOL invert = info->mInvert;
+ addMaskedMorph(baked, morph_param, invert, info->mLayer);
+ }
+ }
+
+ }
+
+ loadLayersets();
+
+ // avatar_lad.xml : <driver_parameters>
+ for (LLAvatarXmlInfo::driver_info_list_t::iterator iter = sAvatarXmlInfo->mDriverInfoList.begin();
+ iter != sAvatarXmlInfo->mDriverInfoList.end();
+ ++iter)
+ {
+ LLDriverParamInfo *info = *iter;
+ LLDriverParam* driver_param = new LLDriverParam( this );
+ if (driver_param->setInfo(info))
+ {
+ addVisualParam( driver_param );
+ driver_param->setParamLocation(isSelf() ? LOC_AV_SELF : LOC_AV_OTHER);
+ LLVisualParam*(LLAvatarAppearance::*avatar_function)(S32)const = &LLAvatarAppearance::getVisualParam;
+ if( !driver_param->linkDrivenParams(boost::bind(avatar_function,(LLAvatarAppearance*)this,_1 ), false))
+ {
+ llwarns << "could not link driven params for avatar " << getID().asString() << " param id: " << driver_param->getID() << llendl;
+ continue;
+ }
+ }
+ else
+ {
+ delete driver_param;
+ llwarns << "avatar file: driver_param->parseData() failed" << llendl;
+ return FALSE;
+ }
+ }
+
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadSkeletonNode(): loads <skeleton> node from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::loadSkeletonNode ()
+{
+ mRoot->addChild( mSkeleton[0] );
+
+ // make meshes children before calling parent version of the function
+ for (avatar_joint_list_t::iterator iter = mMeshLOD.begin();
+ iter != mMeshLOD.end();
+ ++iter)
+ {
+ LLAvatarJoint *joint = *iter;
+ joint->mUpdateXform = FALSE;
+ joint->setMeshesToChildren();
+ }
+
+ mRoot->addChild(mMeshLOD[MESH_ID_HEAD]);
+ mRoot->addChild(mMeshLOD[MESH_ID_EYELASH]);
+ mRoot->addChild(mMeshLOD[MESH_ID_UPPER_BODY]);
+ mRoot->addChild(mMeshLOD[MESH_ID_LOWER_BODY]);
+ mRoot->addChild(mMeshLOD[MESH_ID_SKIRT]);
+ mRoot->addChild(mMeshLOD[MESH_ID_HEAD]);
+
+ LLAvatarJoint *skull = (LLAvatarJoint*)mRoot->findJoint("mSkull");
+ if (skull)
+ {
+ skull->addChild(mMeshLOD[MESH_ID_HAIR] );
+ }
+
+ LLAvatarJoint *eyeL = (LLAvatarJoint*)mRoot->findJoint("mEyeLeft");
+ if (eyeL)
+ {
+ eyeL->addChild( mMeshLOD[MESH_ID_EYEBALL_LEFT] );
+ }
+
+ LLAvatarJoint *eyeR = (LLAvatarJoint*)mRoot->findJoint("mEyeRight");
+ if (eyeR)
+ {
+ eyeR->addChild( mMeshLOD[MESH_ID_EYEBALL_RIGHT] );
+ }
+
+ // SKELETAL DISTORTIONS
+ {
+ LLAvatarXmlInfo::skeletal_distortion_info_list_t::iterator iter;
+ for (iter = sAvatarXmlInfo->mSkeletalDistortionInfoList.begin();
+ iter != sAvatarXmlInfo->mSkeletalDistortionInfoList.end();
+ ++iter)
+ {
+ LLPolySkeletalDistortionInfo *info = (LLPolySkeletalDistortionInfo*)*iter;
+ LLPolySkeletalDistortion *param = new LLPolySkeletalDistortion(this);
+ if (!param->setInfo(info))
+ {
+ delete param;
+ return FALSE;
+ }
+ else
+ {
+ addVisualParam(param);
+ param->setParamLocation(isSelf() ? LOC_AV_SELF : LOC_AV_OTHER);
+ }
+ }
+ }
+
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadMeshNodes(): loads <mesh> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::loadMeshNodes()
+{
+ for (LLAvatarXmlInfo::mesh_info_list_t::const_iterator meshinfo_iter = sAvatarXmlInfo->mMeshInfoList.begin();
+ meshinfo_iter != sAvatarXmlInfo->mMeshInfoList.end();
+ ++meshinfo_iter)
+ {
+ const LLAvatarXmlInfo::LLAvatarMeshInfo *info = *meshinfo_iter;
+ const std::string &type = info->mType;
+ S32 lod = info->mLOD;
+
+ LLAvatarJointMesh* mesh = NULL;
+ U8 mesh_id = 0;
+ BOOL found_mesh_id = FALSE;
+
+ /* if (type == "hairMesh")
+ switch(lod)
+ case 0:
+ mesh = &mHairMesh0; */
+ for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator mesh_iter = LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().begin();
+ mesh_iter != LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().end();
+ ++mesh_iter)
+ {
+ const EMeshIndex mesh_index = mesh_iter->first;
+ const LLAvatarAppearanceDictionary::MeshEntry *mesh_dict = mesh_iter->second;
+ if (type.compare(mesh_dict->mName) == 0)
+ {
+ mesh_id = mesh_index;
+ found_mesh_id = TRUE;
+ break;
+ }
+ }
+
+ if (found_mesh_id)
+ {
+ if (lod < (S32)mMeshLOD[mesh_id]->mMeshParts.size())
+ {
+ mesh = mMeshLOD[mesh_id]->mMeshParts[lod];
+ }
+ else
+ {
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "Ignoring unrecognized mesh type: " << type << llendl;
+ return FALSE;
+ }
+
+ // llinfos << "Parsing mesh data for " << type << "..." << llendl;
+
+ // If this isn't set to white (1.0), avatars will *ALWAYS* be darker than their surroundings.
+ // Do not touch!!!
+ mesh->setColor( LLColor4::white );
+
+ LLPolyMesh *poly_mesh = NULL;
+
+ if (!info->mReferenceMeshName.empty())
+ {
+ polymesh_map_t::const_iterator polymesh_iter = mPolyMeshes.find(info->mReferenceMeshName);
+ if (polymesh_iter != mPolyMeshes.end())
+ {
+ poly_mesh = LLPolyMesh::getMesh(info->mMeshFileName, polymesh_iter->second);
+ poly_mesh->setAvatar(this);
+ }
+ else
+ {
+ // This should never happen
+ LL_WARNS("Avatar") << "Could not find avatar mesh: " << info->mReferenceMeshName << LL_ENDL;
+ }
+ }
+ else
+ {
+ poly_mesh = LLPolyMesh::getMesh(info->mMeshFileName);
+ poly_mesh->setAvatar(this);
+ }
+
+ if( !poly_mesh )
+ {
+ llwarns << "Failed to load mesh of type " << type << llendl;
+ return FALSE;
+ }
+
+ // Multimap insert
+ mPolyMeshes.insert(std::make_pair(info->mMeshFileName, poly_mesh));
+
+ mesh->setMesh( poly_mesh );
+ mesh->setLOD( info->mMinPixelArea );
+
+ for (LLAvatarXmlInfo::LLAvatarMeshInfo::morph_info_list_t::const_iterator xmlinfo_iter = info->mPolyMorphTargetInfoList.begin();
+ xmlinfo_iter != info->mPolyMorphTargetInfoList.end();
+ ++xmlinfo_iter)
+ {
+ const LLAvatarXmlInfo::LLAvatarMeshInfo::morph_info_pair_t *info_pair = &(*xmlinfo_iter);
+ LLPolyMorphTarget *param = new LLPolyMorphTarget(mesh->getMesh());
+ if (!param->setInfo((LLPolyMorphTargetInfo*)info_pair->first))
+ {
+ delete param;
+ return FALSE;
+ }
+ else
+ {
+ if (info_pair->second)
+ {
+ addSharedVisualParam(param);
+ param->setParamLocation(isSelf() ? LOC_AV_SELF : LOC_AV_OTHER);
+ }
+ else
+ {
+ addVisualParam(param);
+ param->setParamLocation(isSelf() ? LOC_AV_SELF : LOC_AV_OTHER);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadLayerSets()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::loadLayersets()
+{
+ BOOL success = TRUE;
+ for (LLAvatarXmlInfo::layer_info_list_t::const_iterator layerset_iter = sAvatarXmlInfo->mLayerInfoList.begin();
+ layerset_iter != sAvatarXmlInfo->mLayerInfoList.end();
+ ++layerset_iter)
+ {
+ LLTexLayerSetInfo *layerset_info = *layerset_iter;
+ if (isSelf())
+ {
+ // Construct a layerset for each one specified in avatar_lad.xml and initialize it as such.
+ LLTexLayerSet* layer_set = createTexLayerSet();
+
+ if (!layer_set->setInfo(layerset_info))
+ {
+ stop_glerror();
+ delete layer_set;
+ llwarns << "avatar file: layer_set->setInfo() failed" << llendl;
+ return FALSE;
+ }
+
+ // scan baked textures and associate the layerset with the appropriate one
+ EBakedTextureIndex baked_index = BAKED_NUM_INDICES;
+ for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin();
+ baked_iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end();
+ ++baked_iter)
+ {
+ const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = baked_iter->second;
+ if (layer_set->isBodyRegion(baked_dict->mName))
+ {
+ baked_index = baked_iter->first;
+ // ensure both structures are aware of each other
+ mBakedTextureDatas[baked_index].mTexLayerSet = layer_set;
+ layer_set->setBakedTexIndex(baked_index);
+ break;
+ }
+ }
+ // if no baked texture was found, warn and cleanup
+ if (baked_index == BAKED_NUM_INDICES)
+ {
+ llwarns << "<layer_set> has invalid body_region attribute" << llendl;
+ delete layer_set;
+ return FALSE;
+ }
+
+ // scan morph masks and let any affected layers know they have an associated morph
+ for (LLAvatarAppearance::morph_list_t::const_iterator morph_iter = mBakedTextureDatas[baked_index].mMaskedMorphs.begin();
+ morph_iter != mBakedTextureDatas[baked_index].mMaskedMorphs.end();
+ ++morph_iter)
+ {
+ LLMaskedMorph *morph = *morph_iter;
+ LLTexLayerInterface* layer = layer_set->findLayerByName(morph->mLayer);
+ if (layer)
+ {
+ layer->setHasMorph(TRUE);
+ }
+ else
+ {
+ llwarns << "Could not find layer named " << morph->mLayer << " to set morph flag" << llendl;
+ success = FALSE;
+ }
+ }
+ }
+ else // !isSelf()
+ {
+ // Construct a layerset for each one specified in avatar_lad.xml and initialize it as such.
+ LLTexLayerSetInfo *layerset_info = *layerset_iter;
+ layerset_info->createVisualParams(this);
+ }
+ }
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// getCharacterJoint()
+//-----------------------------------------------------------------------------
+LLJoint *LLAvatarAppearance::getCharacterJoint( U32 num )
+{
+ if ((S32)num >= mSkeleton.size()
+ || (S32)num < 0)
+ {
+ return NULL;
+ }
+ return mSkeleton[num];
+}
+
+
+//-----------------------------------------------------------------------------
+// getVolumePos()
+//-----------------------------------------------------------------------------
+LLVector3 LLAvatarAppearance::getVolumePos(S32 joint_index, LLVector3& volume_offset)
+{
+ if (joint_index > mNumCollisionVolumes)
+ {
+ return LLVector3::zero;
+ }
+
+ return mCollisionVolumes[joint_index].getVolumePos(volume_offset);
+}
+
+//-----------------------------------------------------------------------------
+// findCollisionVolume()
+//-----------------------------------------------------------------------------
+LLJoint* LLAvatarAppearance::findCollisionVolume(U32 volume_id)
+{
+ if ((S32)volume_id > mNumCollisionVolumes)
+ {
+ return NULL;
+ }
+
+ return &mCollisionVolumes[volume_id];
+}
+
+//-----------------------------------------------------------------------------
+// findCollisionVolume()
+//-----------------------------------------------------------------------------
+S32 LLAvatarAppearance::getCollisionVolumeID(std::string &name)
+{
+ for (S32 i = 0; i < mNumCollisionVolumes; i++)
+ {
+ if (mCollisionVolumes[i].getName() == name)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// LLAvatarAppearance::getHeadMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh* LLAvatarAppearance::getHeadMesh()
+{
+ return mMeshLOD[MESH_ID_HEAD]->mMeshParts[0]->getMesh();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLAvatarAppearance::getUpperBodyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh* LLAvatarAppearance::getUpperBodyMesh()
+{
+ return mMeshLOD[MESH_ID_UPPER_BODY]->mMeshParts[0]->getMesh();
+}
+
+
+
+// virtual
+BOOL LLAvatarAppearance::isValid() const
+{
+ // This should only be called on ourself.
+ if (!isSelf())
+ {
+ llerrs << "Called LLAvatarAppearance::isValid() on when isSelf() == false" << llendl;
+ }
+ return TRUE;
+}
+
+
+// adds a morph mask to the appropriate baked texture structure
+void LLAvatarAppearance::addMaskedMorph(EBakedTextureIndex index, LLVisualParam* morph_target, BOOL invert, std::string layer)
+{
+ if (index < BAKED_NUM_INDICES)
+ {
+ LLMaskedMorph *morph = new LLMaskedMorph(morph_target, invert, layer);
+ mBakedTextureDatas[index].mMaskedMorphs.push_front(morph);
+ }
+}
+
+
+//static
+BOOL LLAvatarAppearance::teToColorParams( ETextureIndex te, U32 *param_name )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT:
+ param_name[0] = 803; //"shirt_red";
+ param_name[1] = 804; //"shirt_green";
+ param_name[2] = 805; //"shirt_blue";
+ break;
+
+ case TEX_LOWER_PANTS:
+ param_name[0] = 806; //"pants_red";
+ param_name[1] = 807; //"pants_green";
+ param_name[2] = 808; //"pants_blue";
+ break;
+
+ case TEX_LOWER_SHOES:
+ param_name[0] = 812; //"shoes_red";
+ param_name[1] = 813; //"shoes_green";
+ param_name[2] = 817; //"shoes_blue";
+ break;
+
+ case TEX_LOWER_SOCKS:
+ param_name[0] = 818; //"socks_red";
+ param_name[1] = 819; //"socks_green";
+ param_name[2] = 820; //"socks_blue";
+ break;
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET:
+ param_name[0] = 834; //"jacket_red";
+ param_name[1] = 835; //"jacket_green";
+ param_name[2] = 836; //"jacket_blue";
+ break;
+
+ case TEX_UPPER_GLOVES:
+ param_name[0] = 827; //"gloves_red";
+ param_name[1] = 829; //"gloves_green";
+ param_name[2] = 830; //"gloves_blue";
+ break;
+
+ case TEX_UPPER_UNDERSHIRT:
+ param_name[0] = 821; //"undershirt_red";
+ param_name[1] = 822; //"undershirt_green";
+ param_name[2] = 823; //"undershirt_blue";
+ break;
+
+ case TEX_LOWER_UNDERPANTS:
+ param_name[0] = 824; //"underpants_red";
+ param_name[1] = 825; //"underpants_green";
+ param_name[2] = 826; //"underpants_blue";
+ break;
+
+ case TEX_SKIRT:
+ param_name[0] = 921; //"skirt_red";
+ param_name[1] = 922; //"skirt_green";
+ param_name[2] = 923; //"skirt_blue";
+ break;
+
+ case TEX_HEAD_TATTOO:
+ case TEX_LOWER_TATTOO:
+ case TEX_UPPER_TATTOO:
+ param_name[0] = 1071; //"tattoo_red";
+ param_name[1] = 1072; //"tattoo_green";
+ param_name[2] = 1073; //"tattoo_blue";
+ break;
+
+ default:
+ llassert(0);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void LLAvatarAppearance::setClothesColor( ETextureIndex te, const LLColor4& new_color, BOOL upload_bake )
+{
+ U32 param_name[3];
+ if( teToColorParams( te, param_name ) )
+ {
+ setVisualParamWeight( param_name[0], new_color.mV[VX], upload_bake );
+ setVisualParamWeight( param_name[1], new_color.mV[VY], upload_bake );
+ setVisualParamWeight( param_name[2], new_color.mV[VZ], upload_bake );
+ }
+}
+
+LLColor4 LLAvatarAppearance::getClothesColor( ETextureIndex te )
+{
+ LLColor4 color;
+ U32 param_name[3];
+ if( teToColorParams( te, param_name ) )
+ {
+ color.mV[VX] = getVisualParamWeight( param_name[0] );
+ color.mV[VY] = getVisualParamWeight( param_name[1] );
+ color.mV[VZ] = getVisualParamWeight( param_name[2] );
+ }
+ return color;
+}
+
+// static
+LLColor4 LLAvatarAppearance::getDummyColor()
+{
+ return DUMMY_COLOR;
+}
+
+LLColor4 LLAvatarAppearance::getGlobalColor( const std::string& color_name ) const
+{
+ if (color_name=="skin_color" && mTexSkinColor)
+ {
+ return mTexSkinColor->getColor();
+ }
+ else if(color_name=="hair_color" && mTexHairColor)
+ {
+ return mTexHairColor->getColor();
+ }
+ if(color_name=="eye_color" && mTexEyeColor)
+ {
+ return mTexEyeColor->getColor();
+ }
+ else
+ {
+// return LLColor4( .5f, .5f, .5f, .5f );
+ return LLColor4( 0.f, 1.f, 1.f, 1.f ); // good debugging color
+ }
+}
+
+// Unlike most wearable functions, this works for both self and other.
+// virtual
+BOOL LLAvatarAppearance::isWearingWearableType(LLWearableType::EType type) const
+{
+ return mWearableData->getWearableCount(type) > 0;
+}
+
+LLTexLayerSet* LLAvatarAppearance::getAvatarLayerSet(EBakedTextureIndex baked_index) const
+{
+ /* switch(index)
+ case TEX_HEAD_BAKED:
+ case TEX_HEAD_BODYPAINT:
+ return mHeadLayerSet; */
+ return mBakedTextureDatas[baked_index].mTexLayerSet;
+}
+
+//-----------------------------------------------------------------------------
+// allocateCollisionVolumes()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::allocateCollisionVolumes( U32 num )
+{
+ deleteAndClearArray(mCollisionVolumes);
+ mNumCollisionVolumes = 0;
+
+ mCollisionVolumes = new LLAvatarJointCollisionVolume[num];
+ if (!mCollisionVolumes)
+ {
+ return FALSE;
+ }
+
+ mNumCollisionVolumes = num;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLAvatarBoneInfo::parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
+{
+ if (node->hasName("bone"))
+ {
+ mIsJoint = TRUE;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ llwarns << "Bone without name" << llendl;
+ return FALSE;
+ }
+ }
+ else if (node->hasName("collision_volume"))
+ {
+ mIsJoint = FALSE;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ mName = "Collision Volume";
+ }
+ }
+ else
+ {
+ llwarns << "Invalid node " << node->getName() << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos");
+ if (!node->getFastAttributeVector3(pos_string, mPos))
+ {
+ llwarns << "Bone without position" << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot");
+ if (!node->getFastAttributeVector3(rot_string, mRot))
+ {
+ llwarns << "Bone without rotation" << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ if (!node->getFastAttributeVector3(scale_string, mScale))
+ {
+ llwarns << "Bone without scale" << llendl;
+ return FALSE;
+ }
+
+ if (mIsJoint)
+ {
+ static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
+ if (!node->getFastAttributeVector3(pivot_string, mPivot))
+ {
+ llwarns << "Bone without pivot" << llendl;
+ return FALSE;
+ }
+ }
+
+ // parse children
+ LLXmlTreeNode* child;
+ for( child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ LLAvatarBoneInfo *child_info = new LLAvatarBoneInfo;
+ if (!child_info->parseXml(child))
+ {
+ delete child_info;
+ return FALSE;
+ }
+ mChildList.push_back(child_info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLAvatarSkeletonInfo::parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
+{
+ static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones");
+ if (!node->getFastAttributeS32(num_bones_string, mNumBones))
+ {
+ llwarns << "Couldn't find number of bones." << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes");
+ node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes);
+
+ LLXmlTreeNode* child;
+ for( child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ LLAvatarBoneInfo *info = new LLAvatarBoneInfo;
+ if (!info->parseXml(child))
+ {
+ delete info;
+ llwarns << "Error parsing bone in skeleton file" << llendl;
+ return FALSE;
+ }
+ mBoneInfoList.push_back(info);
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlSkeletonNode(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* node = root->getChildByName( "skeleton" );
+ if( !node )
+ {
+ llwarns << "avatar file: missing <skeleton>" << llendl;
+ return FALSE;
+ }
+
+ LLXmlTreeNode* child;
+
+ // SKELETON DISTORTIONS
+ for (child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (!child->getChildByName("param_skeleton"))
+ {
+ if (child->getChildByName("param_morph"))
+ {
+ llwarns << "Can't specify morph param in skeleton definition." << llendl;
+ }
+ else
+ {
+ llwarns << "Unknown param type." << llendl;
+ }
+ continue;
+ }
+
+ LLPolySkeletalDistortionInfo *info = new LLPolySkeletalDistortionInfo;
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+
+ mSkeletalDistortionInfoList.push_back(info);
+ }
+
+ // ATTACHMENT POINTS
+ for (child = node->getChildByName( "attachment_point" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ LLAvatarAttachmentInfo* info = new LLAvatarAttachmentInfo();
+
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!child->getFastAttributeString(name_string, info->mName))
+ {
+ llwarns << "No name supplied for attachment point." << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle joint_string = LLXmlTree::addAttributeString("joint");
+ if (!child->getFastAttributeString(joint_string, info->mJointName))
+ {
+ llwarns << "No bone declared in attachment point " << info->mName << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle position_string = LLXmlTree::addAttributeString("position");
+ if (child->getFastAttributeVector3(position_string, info->mPosition))
+ {
+ info->mHasPosition = TRUE;
+ }
+
+ static LLStdStringHandle rotation_string = LLXmlTree::addAttributeString("rotation");
+ if (child->getFastAttributeVector3(rotation_string, info->mRotationEuler))
+ {
+ info->mHasRotation = TRUE;
+ }
+ static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
+ if (child->getFastAttributeS32(group_string, info->mGroup))
+ {
+ if (info->mGroup == -1)
+ info->mGroup = -1111; // -1 = none parsed, < -1 = bad value
+ }
+
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ if (!child->getFastAttributeS32(id_string, info->mAttachmentID))
+ {
+ llwarns << "No id supplied for attachment point " << info->mName << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle slot_string = LLXmlTree::addAttributeString("pie_slice");
+ child->getFastAttributeS32(slot_string, info->mPieMenuSlice);
+
+ static LLStdStringHandle visible_in_first_person_string = LLXmlTree::addAttributeString("visible_in_first_person");
+ child->getFastAttributeBOOL(visible_in_first_person_string, info->mVisibleFirstPerson);
+
+ static LLStdStringHandle hud_attachment_string = LLXmlTree::addAttributeString("hud");
+ child->getFastAttributeBOOL(hud_attachment_string, info->mIsHUDAttachment);
+
+ mAttachmentInfoList.push_back(info);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlMeshNodes(): parses <mesh> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlMeshNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* node = root->getChildByName( "mesh" );
+ node;
+ node = root->getNextNamedChild())
+ {
+ LLAvatarMeshInfo *info = new LLAvatarMeshInfo;
+
+ // attribute: type
+ static LLStdStringHandle type_string = LLXmlTree::addAttributeString("type");
+ if( !node->getFastAttributeString( type_string, info->mType ) )
+ {
+ llwarns << "Avatar file: <mesh> is missing type attribute. Ignoring element. " << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle lod_string = LLXmlTree::addAttributeString("lod");
+ if (!node->getFastAttributeS32( lod_string, info->mLOD ))
+ {
+ llwarns << "Avatar file: <mesh> is missing lod attribute. Ignoring element. " << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if( !node->getFastAttributeString( file_name_string, info->mMeshFileName ) )
+ {
+ llwarns << "Avatar file: <mesh> is missing file_name attribute. Ignoring: " << info->mType << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle reference_string = LLXmlTree::addAttributeString("reference");
+ node->getFastAttributeString( reference_string, info->mReferenceMeshName );
+
+ // attribute: min_pixel_area
+ static LLStdStringHandle min_pixel_area_string = LLXmlTree::addAttributeString("min_pixel_area");
+ static LLStdStringHandle min_pixel_width_string = LLXmlTree::addAttributeString("min_pixel_width");
+ if (!node->getFastAttributeF32( min_pixel_area_string, info->mMinPixelArea ))
+ {
+ F32 min_pixel_area = 0.1f;
+ if (node->getFastAttributeF32( min_pixel_width_string, min_pixel_area ))
+ {
+ // this is square root of pixel area (sensible to use linear space in defining lods)
+ min_pixel_area = min_pixel_area * min_pixel_area;
+ }
+ info->mMinPixelArea = min_pixel_area;
+ }
+
+ // Parse visual params for this node only if we haven't already
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (!child->getChildByName("param_morph"))
+ {
+ if (child->getChildByName("param_skeleton"))
+ {
+ llwarns << "Can't specify skeleton param in a mesh definition." << llendl;
+ }
+ else
+ {
+ llwarns << "Unknown param type." << llendl;
+ }
+ continue;
+ }
+
+ LLPolyMorphTargetInfo *morphinfo = new LLPolyMorphTargetInfo();
+ if (!morphinfo->parseXml(child))
+ {
+ delete morphinfo;
+ delete info;
+ return -1;
+ }
+ BOOL shared = FALSE;
+ static LLStdStringHandle shared_string = LLXmlTree::addAttributeString("shared");
+ child->getFastAttributeBOOL(shared_string, shared);
+
+ info->mPolyMorphTargetInfoList.push_back(LLAvatarMeshInfo::morph_info_pair_t(morphinfo, shared));
+ }
+
+ mMeshInfoList.push_back(info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlColorNodes(): parses <global_color> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlColorNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* color_node = root->getChildByName( "global_color" );
+ color_node;
+ color_node = root->getNextNamedChild())
+ {
+ std::string global_color_name;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (color_node->getFastAttributeString( name_string, global_color_name ) )
+ {
+ if( global_color_name == "skin_color" )
+ {
+ if (mTexSkinColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of skin_color" << llendl;
+ return FALSE;
+ }
+ mTexSkinColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexSkinColorInfo->parseXml( color_node ) )
+ {
+ deleteAndClear(mTexSkinColorInfo);
+ llwarns << "avatar file: mTexSkinColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else if( global_color_name == "hair_color" )
+ {
+ if (mTexHairColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of hair_color" << llendl;
+ return FALSE;
+ }
+ mTexHairColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexHairColorInfo->parseXml( color_node ) )
+ {
+ deleteAndClear(mTexHairColorInfo);
+ llwarns << "avatar file: mTexHairColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else if( global_color_name == "eye_color" )
+ {
+ if (mTexEyeColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of eye_color" << llendl;
+ return FALSE;
+ }
+ mTexEyeColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexEyeColorInfo->parseXml( color_node ) )
+ {
+ llwarns << "avatar file: mTexEyeColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlLayerNodes(): parses <layer_set> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlLayerNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* layer_node = root->getChildByName( "layer_set" );
+ layer_node;
+ layer_node = root->getNextNamedChild())
+ {
+ LLTexLayerSetInfo* layer_info = new LLTexLayerSetInfo();
+ if( layer_info->parseXml( layer_node ) )
+ {
+ mLayerInfoList.push_back(layer_info);
+ }
+ else
+ {
+ delete layer_info;
+ llwarns << "avatar file: layer_set->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlDriverNodes(): parses <driver_parameters> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlDriverNodes(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* driver = root->getChildByName( "driver_parameters" );
+ if( driver )
+ {
+ for (LLXmlTreeNode* grand_child = driver->getChildByName( "param" );
+ grand_child;
+ grand_child = driver->getNextNamedChild())
+ {
+ if( grand_child->getChildByName( "param_driver" ) )
+ {
+ LLDriverParamInfo* driver_info = new LLDriverParamInfo();
+ if( driver_info->parseXml( grand_child ) )
+ {
+ mDriverInfoList.push_back(driver_info);
+ }
+ else
+ {
+ delete driver_info;
+ llwarns << "avatar file: driver_param->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlDriverNodes(): parses <driver_parameters> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlMorphNodes(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* masks = root->getChildByName( "morph_masks" );
+ if( !masks )
+ {
+ return FALSE;
+ }
+
+ for (LLXmlTreeNode* grand_child = masks->getChildByName( "mask" );
+ grand_child;
+ grand_child = masks->getNextNamedChild())
+ {
+ LLAvatarMorphInfo* info = new LLAvatarMorphInfo();
+
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("morph_name");
+ if (!grand_child->getFastAttributeString(name_string, info->mName))
+ {
+ llwarns << "No name supplied for morph mask." << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle region_string = LLXmlTree::addAttributeString("body_region");
+ if (!grand_child->getFastAttributeString(region_string, info->mRegion))
+ {
+ llwarns << "No region supplied for morph mask." << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle layer_string = LLXmlTree::addAttributeString("layer");
+ if (!grand_child->getFastAttributeString(layer_string, info->mLayer))
+ {
+ llwarns << "No layer supplied for morph mask." << llendl;
+ delete info;
+ continue;
+ }
+
+ // optional parameter. don't throw a warning if not present.
+ static LLStdStringHandle invert_string = LLXmlTree::addAttributeString("invert");
+ grand_child->getFastAttributeBOOL(invert_string, info->mInvert);
+
+ mMorphMaskInfoList.push_back(info);
+ }
+
+ return TRUE;
+}
+
+//virtual
+LLAvatarAppearance::LLMaskedMorph::LLMaskedMorph(LLVisualParam *morph_target, BOOL invert, std::string layer) :
+ mMorphTarget(morph_target),
+ mInvert(invert),
+ mLayer(layer)
+{
+ LLPolyMorphTarget *target = dynamic_cast<LLPolyMorphTarget*>(morph_target);
+ if (target)
+ {
+ target->addPendingMorphMask();
+ }
+}
+
+
+
diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h
new file mode 100644
index 0000000000..bce2540258
--- /dev/null
+++ b/indra/llappearance/llavatarappearance.h
@@ -0,0 +1,448 @@
+/**
+ * @file llavatarappearance.h
+ * @brief Declaration of LLAvatarAppearance class
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_AVATAR_APPEARANCE_H
+#define LL_AVATAR_APPEARANCE_H
+
+#include "llcharacter.h"
+#include "llavatarappearancedefines.h"
+#include "llavatarjointmesh.h"
+#include "lldriverparam.h"
+#include "lltexlayer.h"
+#include "llviewervisualparam.h"
+#include "llxmltree.h"
+
+class LLTexLayerSet;
+class LLTexGlobalColor;
+class LLTexGlobalColorInfo;
+class LLWearableData;
+class LLAvatarBoneInfo;
+class LLAvatarSkeletonInfo;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLAvatarAppearance
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLAvatarAppearance : public LLCharacter
+{
+ LOG_CLASS(LLAvatarAppearance);
+
+protected:
+ struct LLAvatarXmlInfo;
+
+/********************************************************************************
+ ** **
+ ** INITIALIZATION
+ **/
+private:
+ // Hide default constructor.
+ LLAvatarAppearance() {}
+
+public:
+ LLAvatarAppearance(LLWearableData* wearable_data);
+ virtual ~LLAvatarAppearance();
+
+ static void initClass(); // initializes static members
+ static void cleanupClass(); // Cleanup data that's only init'd once per class.
+ virtual void initInstance(); // Called after construction to initialize the instance.
+ virtual BOOL loadSkeletonNode();
+ BOOL loadMeshNodes();
+ BOOL loadLayersets();
+
+
+/** Initialization
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** INHERITED
+ **/
+
+ //--------------------------------------------------------------------
+ // LLCharacter interface and related
+ //--------------------------------------------------------------------
+public:
+ /*virtual*/ LLJoint* getCharacterJoint(U32 num);
+
+ /*virtual*/ const char* getAnimationPrefix() { return "avatar"; }
+ /*virtual*/ LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset);
+ /*virtual*/ LLJoint* findCollisionVolume(U32 volume_id);
+ /*virtual*/ S32 getCollisionVolumeID(std::string &name);
+ /*virtual*/ LLPolyMesh* getHeadMesh();
+ /*virtual*/ LLPolyMesh* getUpperBodyMesh();
+
+/** Inherited
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** STATE
+ **/
+public:
+ virtual bool isSelf() const { return false; } // True if this avatar is for this viewer's agent
+ virtual BOOL isValid() const;
+ virtual BOOL isUsingServerBakes() const = 0;
+ virtual BOOL isUsingLocalAppearance() const = 0;
+ virtual BOOL isEditingAppearance() const = 0;
+
+ bool isBuilt() const { return mIsBuilt; }
+
+
+/** State
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** SKELETON
+ **/
+
+protected:
+ virtual LLAvatarJoint* createAvatarJoint() = 0;
+ virtual LLAvatarJoint* createAvatarJoint(S32 joint_num) = 0;
+ virtual LLAvatarJointMesh* createAvatarJointMesh() = 0;
+public:
+ F32 getPelvisToFoot() const { return mPelvisToFoot; }
+ /*virtual*/ LLJoint* getRootJoint() { return mRoot; }
+
+ LLVector3 mHeadOffset; // current head position
+ LLAvatarJoint *mRoot;
+
+ typedef std::map<std::string, LLJoint*> joint_map_t;
+ joint_map_t mJointMap;
+
+ void computeBodySize();
+
+
+protected:
+ static BOOL parseSkeletonFile(const std::string& filename);
+ virtual void buildCharacter();
+ virtual BOOL loadAvatar();
+ virtual void bodySizeChanged() = 0;
+
+ BOOL setupBone(const LLAvatarBoneInfo* info, LLJoint* parent, S32 &current_volume_num, S32 &current_joint_num);
+ BOOL allocateCharacterJoints(U32 num);
+ BOOL buildSkeleton(const LLAvatarSkeletonInfo *info);
+protected:
+ void clearSkeleton();
+ BOOL mIsBuilt; // state of deferred character building
+ typedef std::vector<LLAvatarJoint*> avatar_joint_list_t;
+ avatar_joint_list_t mSkeleton;
+
+ //--------------------------------------------------------------------
+ // Pelvis height adjustment members.
+ //--------------------------------------------------------------------
+public:
+ LLVector3 mBodySize;
+ LLVector3 mAvatarOffset;
+protected:
+ F32 mPelvisToFoot;
+
+ //--------------------------------------------------------------------
+ // Cached pointers to well known joints
+ //--------------------------------------------------------------------
+public:
+ LLJoint* mPelvisp;
+ LLJoint* mTorsop;
+ LLJoint* mChestp;
+ LLJoint* mNeckp;
+ LLJoint* mHeadp;
+ LLJoint* mSkullp;
+ LLJoint* mEyeLeftp;
+ LLJoint* mEyeRightp;
+ LLJoint* mHipLeftp;
+ LLJoint* mHipRightp;
+ LLJoint* mKneeLeftp;
+ LLJoint* mKneeRightp;
+ LLJoint* mAnkleLeftp;
+ LLJoint* mAnkleRightp;
+ LLJoint* mFootLeftp;
+ LLJoint* mFootRightp;
+ LLJoint* mWristLeftp;
+ LLJoint* mWristRightp;
+
+ //--------------------------------------------------------------------
+ // XML parse tree
+ //--------------------------------------------------------------------
+protected:
+ static LLXmlTree sXMLTree; // avatar config file
+ static LLXmlTree sSkeletonXMLTree; // avatar skeleton file
+
+ static LLAvatarSkeletonInfo* sAvatarSkeletonInfo;
+ static LLAvatarXmlInfo* sAvatarXmlInfo;
+
+
+/** Skeleton
+ ** **
+ *******************************************************************************/
+
+
+/********************************************************************************
+ ** **
+ ** RENDERING
+ **/
+public:
+ BOOL mIsDummy; // for special views
+
+ //--------------------------------------------------------------------
+ // Morph masks
+ //--------------------------------------------------------------------
+public:
+ void addMaskedMorph(LLAvatarAppearanceDefines::EBakedTextureIndex index, LLVisualParam* morph_target, BOOL invert, std::string layer);
+ virtual void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components, LLAvatarAppearanceDefines::EBakedTextureIndex index = LLAvatarAppearanceDefines::BAKED_NUM_INDICES) = 0;
+
+/** Rendering
+ ** **
+ *******************************************************************************/
+
+ //--------------------------------------------------------------------
+ // Composites
+ //--------------------------------------------------------------------
+public:
+ virtual void invalidateComposite(LLTexLayerSet* layerset, BOOL upload_result) = 0;
+
+/********************************************************************************
+ ** **
+ ** MESHES
+ **/
+
+public:
+ virtual void updateMeshTextures() = 0;
+ virtual void dirtyMesh() = 0; // Dirty the avatar mesh
+protected:
+ virtual void dirtyMesh(S32 priority) = 0; // Dirty the avatar mesh, with priority
+
+protected:
+ typedef std::multimap<std::string, LLPolyMesh*> polymesh_map_t;
+ polymesh_map_t mPolyMeshes;
+ avatar_joint_list_t mMeshLOD;
+
+/** Meshes
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** APPEARANCE
+ **/
+
+ //--------------------------------------------------------------------
+ // Clothing colors (convenience functions to access visual parameters)
+ //--------------------------------------------------------------------
+public:
+ void setClothesColor(LLAvatarAppearanceDefines::ETextureIndex te, const LLColor4& new_color, BOOL upload_bake);
+ LLColor4 getClothesColor(LLAvatarAppearanceDefines::ETextureIndex te);
+ static BOOL teToColorParams(LLAvatarAppearanceDefines::ETextureIndex te, U32 *param_name);
+
+ //--------------------------------------------------------------------
+ // Global colors
+ //--------------------------------------------------------------------
+public:
+ LLColor4 getGlobalColor(const std::string& color_name ) const;
+ virtual void onGlobalColorChanged(const LLTexGlobalColor* global_color, BOOL upload_bake) = 0;
+protected:
+ LLTexGlobalColor* mTexSkinColor;
+ LLTexGlobalColor* mTexHairColor;
+ LLTexGlobalColor* mTexEyeColor;
+
+ //--------------------------------------------------------------------
+ // Visibility
+ //--------------------------------------------------------------------
+public:
+ static LLColor4 getDummyColor();
+/** Appearance
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** WEARABLES
+ **/
+
+public:
+ LLWearableData* getWearableData() { return mWearableData; }
+ const LLWearableData* getWearableData() const { return mWearableData; }
+ virtual BOOL isTextureDefined(LLAvatarAppearanceDefines::ETextureIndex te, U32 index = 0 ) const = 0;
+ virtual BOOL isWearingWearableType(LLWearableType::EType type ) const;
+
+private:
+ LLWearableData* mWearableData;
+
+/********************************************************************************
+ ** **
+ ** BAKED TEXTURES
+ **/
+public:
+ LLTexLayerSet* getAvatarLayerSet(LLAvatarAppearanceDefines::EBakedTextureIndex baked_index) const;
+
+protected:
+ virtual LLTexLayerSet* createTexLayerSet() = 0;
+
+protected:
+ class LLMaskedMorph;
+ typedef std::deque<LLMaskedMorph *> morph_list_t;
+ struct BakedTextureData
+ {
+ LLUUID mLastTextureID;
+ LLTexLayerSet* mTexLayerSet; // Only exists for self
+ bool mIsLoaded;
+ bool mIsUsed;
+ LLAvatarAppearanceDefines::ETextureIndex mTextureIndex;
+ U32 mMaskTexName;
+ // Stores pointers to the joint meshes that this baked texture deals with
+ avatar_joint_mesh_list_t mJointMeshes;
+ morph_list_t mMaskedMorphs;
+ };
+ typedef std::vector<BakedTextureData> bakedtexturedata_vec_t;
+ bakedtexturedata_vec_t mBakedTextureDatas;
+
+/********************************************************************************
+ ** **
+ ** PHYSICS
+ **/
+
+ //--------------------------------------------------------------------
+ // Collision volumes
+ //--------------------------------------------------------------------
+public:
+ S32 mNumCollisionVolumes;
+ LLAvatarJointCollisionVolume* mCollisionVolumes;
+protected:
+ BOOL allocateCollisionVolumes(U32 num);
+
+/** Physics
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** SUPPORT CLASSES
+ **/
+
+ struct LLAvatarXmlInfo
+ {
+ LLAvatarXmlInfo();
+ ~LLAvatarXmlInfo();
+
+ BOOL parseXmlSkeletonNode(LLXmlTreeNode* root);
+ BOOL parseXmlMeshNodes(LLXmlTreeNode* root);
+ BOOL parseXmlColorNodes(LLXmlTreeNode* root);
+ BOOL parseXmlLayerNodes(LLXmlTreeNode* root);
+ BOOL parseXmlDriverNodes(LLXmlTreeNode* root);
+ BOOL parseXmlMorphNodes(LLXmlTreeNode* root);
+
+ struct LLAvatarMeshInfo
+ {
+ typedef std::pair<LLViewerVisualParamInfo*,BOOL> morph_info_pair_t; // LLPolyMorphTargetInfo stored here
+ typedef std::vector<morph_info_pair_t> morph_info_list_t;
+
+ LLAvatarMeshInfo() : mLOD(0), mMinPixelArea(.1f) {}
+ ~LLAvatarMeshInfo()
+ {
+ morph_info_list_t::iterator iter;
+ for (iter = mPolyMorphTargetInfoList.begin(); iter != mPolyMorphTargetInfoList.end(); iter++)
+ {
+ delete iter->first;
+ }
+ mPolyMorphTargetInfoList.clear();
+ }
+
+ std::string mType;
+ S32 mLOD;
+ std::string mMeshFileName;
+ std::string mReferenceMeshName;
+ F32 mMinPixelArea;
+ morph_info_list_t mPolyMorphTargetInfoList;
+ };
+ typedef std::vector<LLAvatarMeshInfo*> mesh_info_list_t;
+ mesh_info_list_t mMeshInfoList;
+
+ typedef std::vector<LLViewerVisualParamInfo*> skeletal_distortion_info_list_t; // LLPolySkeletalDistortionInfo stored here
+ skeletal_distortion_info_list_t mSkeletalDistortionInfoList;
+
+ struct LLAvatarAttachmentInfo
+ {
+ LLAvatarAttachmentInfo()
+ : mGroup(-1), mAttachmentID(-1), mPieMenuSlice(-1), mVisibleFirstPerson(FALSE),
+ mIsHUDAttachment(FALSE), mHasPosition(FALSE), mHasRotation(FALSE) {}
+ std::string mName;
+ std::string mJointName;
+ LLVector3 mPosition;
+ LLVector3 mRotationEuler;
+ S32 mGroup;
+ S32 mAttachmentID;
+ S32 mPieMenuSlice;
+ BOOL mVisibleFirstPerson;
+ BOOL mIsHUDAttachment;
+ BOOL mHasPosition;
+ BOOL mHasRotation;
+ };
+ typedef std::vector<LLAvatarAttachmentInfo*> attachment_info_list_t;
+ attachment_info_list_t mAttachmentInfoList;
+
+ LLTexGlobalColorInfo *mTexSkinColorInfo;
+ LLTexGlobalColorInfo *mTexHairColorInfo;
+ LLTexGlobalColorInfo *mTexEyeColorInfo;
+
+ typedef std::vector<LLTexLayerSetInfo*> layer_info_list_t;
+ layer_info_list_t mLayerInfoList;
+
+ typedef std::vector<LLDriverParamInfo*> driver_info_list_t;
+ driver_info_list_t mDriverInfoList;
+
+ struct LLAvatarMorphInfo
+ {
+ LLAvatarMorphInfo()
+ : mInvert(FALSE) {}
+ std::string mName;
+ std::string mRegion;
+ std::string mLayer;
+ BOOL mInvert;
+ };
+
+ typedef std::vector<LLAvatarMorphInfo*> morph_info_list_t;
+ morph_info_list_t mMorphMaskInfoList;
+ };
+
+
+ class LLMaskedMorph
+ {
+ public:
+ LLMaskedMorph(LLVisualParam *morph_target, BOOL invert, std::string layer);
+
+ LLVisualParam *mMorphTarget;
+ BOOL mInvert;
+ std::string mLayer;
+ };
+/** Support Classes
+ ** **
+ *******************************************************************************/
+};
+
+#endif // LL_AVATAR_APPEARANCE_H
diff --git a/indra/llappearance/llavatarappearancedefines.cpp b/indra/llappearance/llavatarappearancedefines.cpp
new file mode 100644
index 0000000000..f1c78946a1
--- /dev/null
+++ b/indra/llappearance/llavatarappearancedefines.cpp
@@ -0,0 +1,268 @@
+/**
+ * @file llavatarappearancedefines.cpp
+ * @brief Implementation of LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llavatarappearancedefines.h"
+
+const S32 LLAvatarAppearanceDefines::SCRATCH_TEX_WIDTH = 512;
+const S32 LLAvatarAppearanceDefines::SCRATCH_TEX_HEIGHT = 512;
+const S32 LLAvatarAppearanceDefines::IMPOSTOR_PERIOD = 2;
+
+using namespace LLAvatarAppearanceDefines;
+
+/*********************************************************************************
+ * Edit this function to add/remove/change textures and mesh definitions for avatars.
+ */
+
+LLAvatarAppearanceDictionary::Textures::Textures()
+{
+ addEntry(TEX_HEAD_BODYPAINT, new TextureEntry("head_bodypaint", TRUE, BAKED_NUM_INDICES, "", LLWearableType::WT_SKIN));
+ addEntry(TEX_UPPER_SHIRT, new TextureEntry("upper_shirt", TRUE, BAKED_NUM_INDICES, "UIImgDefaultShirtUUID", LLWearableType::WT_SHIRT));
+ addEntry(TEX_LOWER_PANTS, new TextureEntry("lower_pants", TRUE, BAKED_NUM_INDICES, "UIImgDefaultPantsUUID", LLWearableType::WT_PANTS));
+ addEntry(TEX_EYES_IRIS, new TextureEntry("eyes_iris", TRUE, BAKED_NUM_INDICES, "UIImgDefaultEyesUUID", LLWearableType::WT_EYES));
+ addEntry(TEX_HAIR, new TextureEntry("hair_grain", TRUE, BAKED_NUM_INDICES, "UIImgDefaultHairUUID", LLWearableType::WT_HAIR));
+ addEntry(TEX_UPPER_BODYPAINT, new TextureEntry("upper_bodypaint", TRUE, BAKED_NUM_INDICES, "", LLWearableType::WT_SKIN));
+ addEntry(TEX_LOWER_BODYPAINT, new TextureEntry("lower_bodypaint", TRUE, BAKED_NUM_INDICES, "", LLWearableType::WT_SKIN));
+ addEntry(TEX_LOWER_SHOES, new TextureEntry("lower_shoes", TRUE, BAKED_NUM_INDICES, "UIImgDefaultShoesUUID", LLWearableType::WT_SHOES));
+ addEntry(TEX_LOWER_SOCKS, new TextureEntry("lower_socks", TRUE, BAKED_NUM_INDICES, "UIImgDefaultSocksUUID", LLWearableType::WT_SOCKS));
+ addEntry(TEX_UPPER_JACKET, new TextureEntry("upper_jacket", TRUE, BAKED_NUM_INDICES, "UIImgDefaultJacketUUID", LLWearableType::WT_JACKET));
+ addEntry(TEX_LOWER_JACKET, new TextureEntry("lower_jacket", TRUE, BAKED_NUM_INDICES, "UIImgDefaultJacketUUID", LLWearableType::WT_JACKET));
+ addEntry(TEX_UPPER_GLOVES, new TextureEntry("upper_gloves", TRUE, BAKED_NUM_INDICES, "UIImgDefaultGlovesUUID", LLWearableType::WT_GLOVES));
+ addEntry(TEX_UPPER_UNDERSHIRT, new TextureEntry("upper_undershirt", TRUE, BAKED_NUM_INDICES, "UIImgDefaultUnderwearUUID", LLWearableType::WT_UNDERSHIRT));
+ addEntry(TEX_LOWER_UNDERPANTS, new TextureEntry("lower_underpants", TRUE, BAKED_NUM_INDICES, "UIImgDefaultUnderwearUUID", LLWearableType::WT_UNDERPANTS));
+ addEntry(TEX_SKIRT, new TextureEntry("skirt", TRUE, BAKED_NUM_INDICES, "UIImgDefaultSkirtUUID", LLWearableType::WT_SKIRT));
+
+ addEntry(TEX_LOWER_ALPHA, new TextureEntry("lower_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", LLWearableType::WT_ALPHA));
+ addEntry(TEX_UPPER_ALPHA, new TextureEntry("upper_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", LLWearableType::WT_ALPHA));
+ addEntry(TEX_HEAD_ALPHA, new TextureEntry("head_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", LLWearableType::WT_ALPHA));
+ addEntry(TEX_EYES_ALPHA, new TextureEntry("eyes_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", LLWearableType::WT_ALPHA));
+ addEntry(TEX_HAIR_ALPHA, new TextureEntry("hair_alpha", TRUE, BAKED_NUM_INDICES, "UIImgDefaultAlphaUUID", LLWearableType::WT_ALPHA));
+
+ addEntry(TEX_HEAD_TATTOO, new TextureEntry("head_tattoo", TRUE, BAKED_NUM_INDICES, "", LLWearableType::WT_TATTOO));
+ addEntry(TEX_UPPER_TATTOO, new TextureEntry("upper_tattoo", TRUE, BAKED_NUM_INDICES, "", LLWearableType::WT_TATTOO));
+ addEntry(TEX_LOWER_TATTOO, new TextureEntry("lower_tattoo", TRUE, BAKED_NUM_INDICES, "", LLWearableType::WT_TATTOO));
+
+ addEntry(TEX_HEAD_BAKED, new TextureEntry("head-baked", FALSE, BAKED_HEAD, "head"));
+ addEntry(TEX_UPPER_BAKED, new TextureEntry("upper-baked", FALSE, BAKED_UPPER, "upper"));
+ addEntry(TEX_LOWER_BAKED, new TextureEntry("lower-baked", FALSE, BAKED_LOWER, "lower"));
+ addEntry(TEX_EYES_BAKED, new TextureEntry("eyes-baked", FALSE, BAKED_EYES, "eyes"));
+ addEntry(TEX_HAIR_BAKED, new TextureEntry("hair-baked", FALSE, BAKED_HAIR, "hair"));
+ addEntry(TEX_SKIRT_BAKED, new TextureEntry("skirt-baked", FALSE, BAKED_SKIRT, "skirt"));
+}
+
+LLAvatarAppearanceDictionary::BakedTextures::BakedTextures()
+{
+ // Baked textures
+ addEntry(BAKED_HEAD, new BakedEntry(TEX_HEAD_BAKED,
+ "head", "a4b9dc38-e13b-4df9-b284-751efb0566ff",
+ 3, TEX_HEAD_BODYPAINT, TEX_HEAD_TATTOO, TEX_HEAD_ALPHA,
+ 5, LLWearableType::WT_SHAPE, LLWearableType::WT_SKIN, LLWearableType::WT_HAIR, LLWearableType::WT_TATTOO, LLWearableType::WT_ALPHA));
+
+ addEntry(BAKED_UPPER, new BakedEntry(TEX_UPPER_BAKED,
+ "upper_body", "5943ff64-d26c-4a90-a8c0-d61f56bd98d4",
+ 7, TEX_UPPER_SHIRT,TEX_UPPER_BODYPAINT, TEX_UPPER_JACKET,
+ TEX_UPPER_GLOVES, TEX_UPPER_UNDERSHIRT, TEX_UPPER_TATTOO, TEX_UPPER_ALPHA,
+ 8, LLWearableType::WT_SHAPE, LLWearableType::WT_SKIN, LLWearableType::WT_SHIRT, LLWearableType::WT_JACKET, LLWearableType::WT_GLOVES, LLWearableType::WT_UNDERSHIRT, LLWearableType::WT_TATTOO, LLWearableType::WT_ALPHA));
+
+ addEntry(BAKED_LOWER, new BakedEntry(TEX_LOWER_BAKED,
+ "lower_body", "2944ee70-90a7-425d-a5fb-d749c782ed7d",
+ 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,
+ 9, LLWearableType::WT_SHAPE, LLWearableType::WT_SKIN, LLWearableType::WT_PANTS, LLWearableType::WT_SHOES, LLWearableType::WT_SOCKS, LLWearableType::WT_JACKET, LLWearableType::WT_UNDERPANTS, LLWearableType::WT_TATTOO, LLWearableType::WT_ALPHA));
+
+ addEntry(BAKED_EYES, new BakedEntry(TEX_EYES_BAKED,
+ "eyes", "27b1bc0f-979f-4b13-95fe-b981c2ba9788",
+ 2, TEX_EYES_IRIS, TEX_EYES_ALPHA,
+ 2, LLWearableType::WT_EYES, LLWearableType::WT_ALPHA));
+
+ addEntry(BAKED_SKIRT, new BakedEntry(TEX_SKIRT_BAKED,
+ "skirt", "03e7e8cb-1368-483b-b6f3-74850838ba63",
+ 1, TEX_SKIRT,
+ 1, LLWearableType::WT_SKIRT));
+
+ addEntry(BAKED_HAIR, new BakedEntry(TEX_HAIR_BAKED,
+ "hair", "a60e85a9-74e8-48d8-8a2d-8129f28d9b61",
+ 2, TEX_HAIR, TEX_HAIR_ALPHA,
+ 2, LLWearableType::WT_HAIR, LLWearableType::WT_ALPHA));
+}
+
+LLAvatarAppearanceDictionary::MeshEntries::MeshEntries()
+{
+ // MeshEntries
+ addEntry(MESH_ID_HAIR, new MeshEntry(BAKED_HAIR, "hairMesh", 6, PN_4));
+ addEntry(MESH_ID_HEAD, new MeshEntry(BAKED_HEAD, "headMesh", 5, PN_5));
+ addEntry(MESH_ID_EYELASH, new MeshEntry(BAKED_HEAD, "eyelashMesh", 1, PN_0)); // no baked mesh associated currently
+ addEntry(MESH_ID_UPPER_BODY, new MeshEntry(BAKED_UPPER, "upperBodyMesh", 5, PN_1));
+ addEntry(MESH_ID_LOWER_BODY, new MeshEntry(BAKED_LOWER, "lowerBodyMesh", 5, PN_2));
+ addEntry(MESH_ID_EYEBALL_LEFT, new MeshEntry(BAKED_EYES, "eyeBallLeftMesh", 2, PN_3));
+ addEntry(MESH_ID_EYEBALL_RIGHT, new MeshEntry(BAKED_EYES, "eyeBallRightMesh", 2, PN_3));
+ addEntry(MESH_ID_SKIRT, new MeshEntry(BAKED_SKIRT, "skirtMesh", 5, PN_5));
+}
+
+/*
+ *
+ *********************************************************************************/
+
+LLAvatarAppearanceDictionary::LLAvatarAppearanceDictionary()
+{
+ createAssociations();
+}
+
+//virtual
+LLAvatarAppearanceDictionary::~LLAvatarAppearanceDictionary()
+{
+}
+
+// Baked textures are composites of textures; for each such composited texture,
+// map it to the baked texture.
+void LLAvatarAppearanceDictionary::createAssociations()
+{
+ for (BakedTextures::const_iterator iter = mBakedTextures.begin(); iter != mBakedTextures.end(); iter++)
+ {
+ const EBakedTextureIndex baked_index = (iter->first);
+ const BakedEntry *dict = (iter->second);
+
+ // For each texture that this baked texture index affects, associate those textures
+ // with this baked texture index.
+ for (texture_vec_t::const_iterator local_texture_iter = dict->mLocalTextures.begin();
+ local_texture_iter != dict->mLocalTextures.end();
+ local_texture_iter++)
+ {
+ const ETextureIndex local_texture_index = (ETextureIndex) *local_texture_iter;
+ mTextures[local_texture_index]->mIsUsedByBakedTexture = true;
+ mTextures[local_texture_index]->mBakedTextureIndex = baked_index;
+ }
+ }
+
+}
+
+LLAvatarAppearanceDictionary::TextureEntry::TextureEntry(const std::string &name,
+ bool is_local_texture,
+ EBakedTextureIndex baked_texture_index,
+ const std::string &default_image_name,
+ LLWearableType::EType wearable_type) :
+ LLDictionaryEntry(name),
+ mIsLocalTexture(is_local_texture),
+ mIsBakedTexture(!is_local_texture),
+ mIsUsedByBakedTexture(baked_texture_index != BAKED_NUM_INDICES),
+ mBakedTextureIndex(baked_texture_index),
+ mDefaultImageName(default_image_name),
+ mWearableType(wearable_type)
+{
+}
+
+LLAvatarAppearanceDictionary::MeshEntry::MeshEntry(EBakedTextureIndex baked_index,
+ const std::string &name,
+ U8 level,
+ LLJointPickName pick) :
+ LLDictionaryEntry(name),
+ mBakedID(baked_index),
+ mLOD(level),
+ mPickName(pick)
+{
+}
+LLAvatarAppearanceDictionary::BakedEntry::BakedEntry(ETextureIndex tex_index,
+ const std::string &name,
+ const std::string &hash_name,
+ U32 num_local_textures,
+ ... ) :
+ LLDictionaryEntry(name),
+ mWearablesHashID(LLUUID(hash_name)),
+ mTextureIndex(tex_index)
+{
+ va_list argp;
+
+ va_start(argp, num_local_textures);
+
+ // Read in local textures
+ for (U8 i=0; i < num_local_textures; i++)
+ {
+ ETextureIndex t = (ETextureIndex)va_arg(argp,int);
+ mLocalTextures.push_back(t);
+ }
+
+ // Read in number of wearables
+ const U32 num_wearables = (U32)va_arg(argp,int);
+ // Read in wearables
+ for (U8 i=0; i < num_wearables; i++)
+ {
+ LLWearableType::EType t = (LLWearableType::EType)va_arg(argp,int);
+ mWearables.push_back(t);
+ }
+}
+
+// static
+ETextureIndex LLAvatarAppearanceDictionary::bakedToLocalTextureIndex(EBakedTextureIndex index)
+{
+ return LLAvatarAppearanceDictionary::getInstance()->getBakedTexture(index)->mTextureIndex;
+}
+
+// static
+EBakedTextureIndex LLAvatarAppearanceDictionary::findBakedByRegionName(std::string name)
+{
+ U8 index = 0;
+ while (index < BAKED_NUM_INDICES)
+ {
+ const BakedEntry *be = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex) index);
+ if (be && be->mName.compare(name) == 0)
+ {
+ // baked texture found
+ return (EBakedTextureIndex) index;
+ }
+ index++;
+ }
+ // baked texture could not be found
+ return BAKED_NUM_INDICES;
+}
+
+// static
+EBakedTextureIndex LLAvatarAppearanceDictionary::findBakedByImageName(std::string name)
+{
+ U8 index = 0;
+ while (index < BAKED_NUM_INDICES)
+ {
+ const BakedEntry *be = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex) index);
+ if (be)
+ {
+ const TextureEntry *te = LLAvatarAppearanceDictionary::getInstance()->getTexture(be->mTextureIndex);
+ if (te && te->mDefaultImageName.compare(name) == 0)
+ {
+ // baked texture found
+ return (EBakedTextureIndex) index;
+ }
+ }
+ index++;
+ }
+ // baked texture could not be found
+ return BAKED_NUM_INDICES;
+}
+
+// static
+LLWearableType::EType LLAvatarAppearanceDictionary::getTEWearableType(ETextureIndex index )
+{
+ return getInstance()->getTexture(index)->mWearableType;
+}
+
diff --git a/indra/llappearance/llavatarappearancedefines.h b/indra/llappearance/llavatarappearancedefines.h
new file mode 100644
index 0000000000..496f85c107
--- /dev/null
+++ b/indra/llappearance/llavatarappearancedefines.h
@@ -0,0 +1,229 @@
+/**
+ * @file llavatarappearancedefines.h
+ * @brief Various LLAvatarAppearance related definitions
+ * LLViewerObject
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_AVATARAPPEARANCE_DEFINES_H
+#define LL_AVATARAPPEARANCE_DEFINES_H
+
+#include <vector>
+#include "lljointpickname.h"
+#include "lldictionary.h"
+#include "llwearabletype.h"
+#include "lluuid.h"
+
+namespace LLAvatarAppearanceDefines
+{
+
+extern const S32 SCRATCH_TEX_WIDTH;
+extern const S32 SCRATCH_TEX_HEIGHT;
+extern const S32 IMPOSTOR_PERIOD;
+
+//--------------------------------------------------------------------
+// Enums
+//--------------------------------------------------------------------
+enum ETextureIndex
+{
+ TEX_INVALID = -1,
+ TEX_HEAD_BODYPAINT = 0,
+ TEX_UPPER_SHIRT,
+ TEX_LOWER_PANTS,
+ TEX_EYES_IRIS,
+ TEX_HAIR,
+ TEX_UPPER_BODYPAINT,
+ TEX_LOWER_BODYPAINT,
+ TEX_LOWER_SHOES,
+ TEX_HEAD_BAKED, // Pre-composited
+ TEX_UPPER_BAKED, // Pre-composited
+ TEX_LOWER_BAKED, // Pre-composited
+ TEX_EYES_BAKED, // Pre-composited
+ TEX_LOWER_SOCKS,
+ TEX_UPPER_JACKET,
+ TEX_LOWER_JACKET,
+ TEX_UPPER_GLOVES,
+ TEX_UPPER_UNDERSHIRT,
+ TEX_LOWER_UNDERPANTS,
+ 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
+};
+
+enum EBakedTextureIndex
+{
+ BAKED_HEAD = 0,
+ BAKED_UPPER,
+ BAKED_LOWER,
+ BAKED_EYES,
+ BAKED_SKIRT,
+ BAKED_HAIR,
+ BAKED_NUM_INDICES
+};
+
+// Reference IDs for each mesh. Used as indices for vector of joints
+enum EMeshIndex
+{
+ MESH_ID_HAIR = 0,
+ MESH_ID_HEAD,
+ MESH_ID_EYELASH,
+ MESH_ID_UPPER_BODY,
+ MESH_ID_LOWER_BODY,
+ MESH_ID_EYEBALL_LEFT,
+ MESH_ID_EYEBALL_RIGHT,
+ MESH_ID_SKIRT,
+ MESH_ID_NUM_INDICES
+};
+
+//--------------------------------------------------------------------
+// Vector Types
+//--------------------------------------------------------------------
+typedef std::vector<ETextureIndex> texture_vec_t;
+typedef std::vector<EBakedTextureIndex> bakedtexture_vec_t;
+typedef std::vector<EMeshIndex> mesh_vec_t;
+typedef std::vector<LLWearableType::EType> wearables_vec_t;
+
+//------------------------------------------------------------------------
+// LLAvatarAppearanceDictionary
+//
+// Holds dictionary static entries for textures, baked textures, meshes, etc.; i.e.
+// information that is common to all avatars.
+//
+// This holds const data - it is initialized once and the contents never change after that.
+//------------------------------------------------------------------------
+class LLAvatarAppearanceDictionary : public LLSingleton<LLAvatarAppearanceDictionary>
+{
+ //--------------------------------------------------------------------
+ // Constructors and Destructors
+ //--------------------------------------------------------------------
+public:
+ LLAvatarAppearanceDictionary();
+ virtual ~LLAvatarAppearanceDictionary();
+private:
+ void createAssociations();
+
+ //--------------------------------------------------------------------
+ // Local and baked textures
+ //--------------------------------------------------------------------
+public:
+ struct TextureEntry : public LLDictionaryEntry
+ {
+ TextureEntry(const std::string &name, // this must match the xml name used by LLTexLayerInfo::parseXml
+ bool is_local_texture,
+ EBakedTextureIndex baked_texture_index = BAKED_NUM_INDICES,
+ const std::string& default_image_name = "",
+ LLWearableType::EType wearable_type = LLWearableType::WT_INVALID);
+ const std::string mDefaultImageName;
+ const LLWearableType::EType mWearableType;
+ // It's either a local texture xor baked
+ BOOL mIsLocalTexture;
+ BOOL mIsBakedTexture;
+ // If it's a local texture, it may be used by a baked texture
+ BOOL mIsUsedByBakedTexture;
+ EBakedTextureIndex mBakedTextureIndex;
+ };
+
+ struct Textures : public LLDictionary<ETextureIndex, TextureEntry>
+ {
+ Textures();
+ } mTextures;
+ const TextureEntry* getTexture(ETextureIndex index) const { return mTextures.lookup(index); }
+ const Textures& getTextures() const { return mTextures; }
+
+ //--------------------------------------------------------------------
+ // Meshes
+ //--------------------------------------------------------------------
+public:
+ struct MeshEntry : public LLDictionaryEntry
+ {
+ MeshEntry(EBakedTextureIndex baked_index,
+ const std::string &name, // names of mesh types as they are used in avatar_lad.xml
+ U8 level,
+ LLJointPickName pick);
+ // Levels of Detail for each mesh. Must match levels of detail present in avatar_lad.xml
+ // Otherwise meshes will be unable to be found, or levels of detail will be ignored
+ const U8 mLOD;
+ const EBakedTextureIndex mBakedID;
+ const LLJointPickName mPickName;
+ };
+
+ struct MeshEntries : public LLDictionary<EMeshIndex, MeshEntry>
+ {
+ MeshEntries();
+ } mMeshEntries;
+ const MeshEntry* getMeshEntry(EMeshIndex index) const { return mMeshEntries.lookup(index); }
+ const MeshEntries& getMeshEntries() const { return mMeshEntries; }
+
+ //--------------------------------------------------------------------
+ // Baked Textures
+ //--------------------------------------------------------------------
+public:
+ struct BakedEntry : public LLDictionaryEntry
+ {
+ BakedEntry(ETextureIndex tex_index,
+ const std::string &name, // unused, but necessary for templating.
+ const std::string &hash_name,
+ U32 num_local_textures, ... ); // # local textures, local texture list, # wearables, wearable list
+ // Local Textures
+ const ETextureIndex mTextureIndex;
+ texture_vec_t mLocalTextures;
+ // Wearables
+ const LLUUID mWearablesHashID;
+ wearables_vec_t mWearables;
+ };
+
+ struct BakedTextures: public LLDictionary<EBakedTextureIndex, BakedEntry>
+ {
+ BakedTextures();
+ } mBakedTextures;
+ const BakedEntry* getBakedTexture(EBakedTextureIndex index) const { return mBakedTextures.lookup(index); }
+ const BakedTextures& getBakedTextures() const { return mBakedTextures; }
+
+ //--------------------------------------------------------------------
+ // Convenience Functions
+ //--------------------------------------------------------------------
+public:
+ // Convert from baked texture to associated texture; e.g. BAKED_HEAD -> TEX_HEAD_BAKED
+ static ETextureIndex bakedToLocalTextureIndex(EBakedTextureIndex t);
+
+ // find a baked texture index based on its name
+ static EBakedTextureIndex findBakedByRegionName(std::string name);
+ static EBakedTextureIndex findBakedByImageName(std::string name);
+
+ // Given a texture entry, determine which wearable type owns it.
+ static LLWearableType::EType getTEWearableType(ETextureIndex index);
+
+}; // End LLAvatarAppearanceDictionary
+
+} // End namespace LLAvatarAppearanceDefines
+
+#endif //LL_AVATARAPPEARANCE_DEFINES_H
diff --git a/indra/llappearance/llavatarjoint.cpp b/indra/llappearance/llavatarjoint.cpp
new file mode 100644
index 0000000000..6ab341af64
--- /dev/null
+++ b/indra/llappearance/llavatarjoint.cpp
@@ -0,0 +1,326 @@
+/**
+ * @file llavatarjoint.cpp
+ * @brief Implementation of LLAvatarJoint class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llavatarjoint.h"
+
+#include "llgl.h"
+#include "llrender.h"
+#include "llmath.h"
+#include "llglheaders.h"
+#include "llavatarappearance.h"
+
+const F32 DEFAULT_AVATAR_JOINT_LOD = 0.0f;
+
+//-----------------------------------------------------------------------------
+// Static Data
+//-----------------------------------------------------------------------------
+BOOL LLAvatarJoint::sDisableLOD = FALSE;
+
+//-----------------------------------------------------------------------------
+// LLAvatarJoint()
+// Class Constructors
+//-----------------------------------------------------------------------------
+LLAvatarJoint::LLAvatarJoint() :
+ LLJoint()
+{
+ init();
+}
+
+LLAvatarJoint::LLAvatarJoint(const std::string &name, LLJoint *parent) :
+ LLJoint(name, parent)
+{
+ init();
+}
+
+LLAvatarJoint::LLAvatarJoint(S32 joint_num) :
+ LLJoint(joint_num)
+{
+ init();
+}
+
+
+void LLAvatarJoint::init()
+{
+ mValid = FALSE;
+ mComponents = SC_JOINT | SC_BONE | SC_AXES;
+ mMinPixelArea = DEFAULT_AVATAR_JOINT_LOD;
+ mPickName = PN_DEFAULT;
+ mVisible = TRUE;
+ mMeshID = 0;
+ mIsTransparent = FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLAvatarJoint()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLAvatarJoint::~LLAvatarJoint()
+{
+}
+
+
+//--------------------------------------------------------------------
+// setValid()
+//--------------------------------------------------------------------
+void LLAvatarJoint::setValid( BOOL valid, BOOL recursive )
+{
+ //----------------------------------------------------------------
+ // set visibility for this joint
+ //----------------------------------------------------------------
+ mValid = valid;
+
+ //----------------------------------------------------------------
+ // set visibility for children
+ //----------------------------------------------------------------
+ if (recursive)
+ {
+ for (child_list_t::iterator iter = mChildren.begin();
+ iter != mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* joint = (LLAvatarJoint*)(*iter);
+ joint->setValid(valid, TRUE);
+ }
+ }
+
+}
+
+//--------------------------------------------------------------------
+// setSkeletonComponents()
+//--------------------------------------------------------------------
+void LLAvatarJoint::setSkeletonComponents( U32 comp, BOOL recursive )
+{
+ mComponents = comp;
+ if (recursive)
+ {
+ for (child_list_t::iterator iter = mChildren.begin();
+ iter != mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* joint = dynamic_cast<LLAvatarJoint*>(*iter);
+ joint->setSkeletonComponents(comp, recursive);
+ }
+ }
+}
+
+void LLAvatarJoint::setVisible(BOOL visible, BOOL recursive)
+{
+ mVisible = visible;
+
+ if (recursive)
+ {
+ for (child_list_t::iterator iter = mChildren.begin();
+ iter != mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* joint = (LLAvatarJoint*)(*iter);
+ joint->setVisible(visible, recursive);
+ }
+ }
+}
+
+void LLAvatarJoint::updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area)
+{
+ for (child_list_t::iterator iter = mChildren.begin();
+ iter != mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* joint = dynamic_cast<LLAvatarJoint*>(*iter);
+ joint->updateFaceSizes(num_vertices, num_indices, pixel_area);
+ }
+}
+
+void LLAvatarJoint::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind, bool terse_update)
+{
+ for (child_list_t::iterator iter = mChildren.begin();
+ iter != mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* joint = dynamic_cast<LLAvatarJoint*>(*iter);
+ joint->updateFaceData(face, pixel_area, damp_wind, terse_update);
+ }
+}
+
+void LLAvatarJoint::updateJointGeometry()
+{
+ for (child_list_t::iterator iter = mChildren.begin();
+ iter != mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* joint = dynamic_cast<LLAvatarJoint*>(*iter);
+ joint->updateJointGeometry();
+ }
+}
+
+
+BOOL LLAvatarJoint::updateLOD(F32 pixel_area, BOOL activate)
+{
+ BOOL lod_changed = FALSE;
+ BOOL found_lod = FALSE;
+
+ for (child_list_t::iterator iter = mChildren.begin();
+ iter != mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* joint = dynamic_cast<LLAvatarJoint*>(*iter);
+ F32 jointLOD = joint->getLOD();
+
+ if (found_lod || jointLOD == DEFAULT_AVATAR_JOINT_LOD)
+ {
+ // we've already found a joint to enable, so enable the rest as alternatives
+ lod_changed |= joint->updateLOD(pixel_area, TRUE);
+ }
+ else
+ {
+ if (pixel_area >= jointLOD || sDisableLOD)
+ {
+ lod_changed |= joint->updateLOD(pixel_area, TRUE);
+ found_lod = TRUE;
+ }
+ else
+ {
+ lod_changed |= joint->updateLOD(pixel_area, FALSE);
+ }
+ }
+ }
+ return lod_changed;
+}
+
+void LLAvatarJoint::dump()
+{
+ for (child_list_t::iterator iter = mChildren.begin();
+ iter != mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* joint = dynamic_cast<LLAvatarJoint*>(*iter);
+ joint->dump();
+ }
+}
+
+
+void LLAvatarJoint::setMeshesToChildren()
+{
+ removeAllChildren();
+ for (avatar_joint_mesh_list_t::iterator iter = mMeshParts.begin();
+ iter != mMeshParts.end(); iter++)
+ {
+ addChild((*iter));
+ }
+}
+//-----------------------------------------------------------------------------
+// LLAvatarJointCollisionVolume()
+//-----------------------------------------------------------------------------
+
+LLAvatarJointCollisionVolume::LLAvatarJointCollisionVolume()
+{
+ mUpdateXform = FALSE;
+}
+
+/*virtual*/
+U32 LLAvatarJointCollisionVolume::render( F32 pixelArea, BOOL first_pass, BOOL is_dummy )
+{
+ llerrs << "Cannot call render() on LLAvatarJointCollisionVolume" << llendl;
+ return 0;
+}
+
+LLVector3 LLAvatarJointCollisionVolume::getVolumePos(LLVector3 &offset)
+{
+ mUpdateXform = TRUE;
+
+ LLVector3 result = offset;
+ result.scaleVec(getScale());
+ result.rotVec(getWorldRotation());
+ result += getWorldPosition();
+
+ return result;
+}
+
+void LLAvatarJointCollisionVolume::renderCollision()
+{
+ updateWorldMatrix();
+
+ gGL.pushMatrix();
+ gGL.multMatrix( &mXform.getWorldMatrix().mMatrix[0][0] );
+
+ gGL.diffuseColor3f( 0.f, 0.f, 1.f );
+
+ gGL.begin(LLRender::LINES);
+
+ LLVector3 v[] =
+ {
+ LLVector3(1,0,0),
+ LLVector3(-1,0,0),
+ LLVector3(0,1,0),
+ LLVector3(0,-1,0),
+
+ LLVector3(0,0,-1),
+ LLVector3(0,0,1),
+ };
+
+ //sides
+ gGL.vertex3fv(v[0].mV);
+ gGL.vertex3fv(v[2].mV);
+
+ gGL.vertex3fv(v[0].mV);
+ gGL.vertex3fv(v[3].mV);
+
+ gGL.vertex3fv(v[1].mV);
+ gGL.vertex3fv(v[2].mV);
+
+ gGL.vertex3fv(v[1].mV);
+ gGL.vertex3fv(v[3].mV);
+
+
+ //top
+ gGL.vertex3fv(v[0].mV);
+ gGL.vertex3fv(v[4].mV);
+
+ gGL.vertex3fv(v[1].mV);
+ gGL.vertex3fv(v[4].mV);
+
+ gGL.vertex3fv(v[2].mV);
+ gGL.vertex3fv(v[4].mV);
+
+ gGL.vertex3fv(v[3].mV);
+ gGL.vertex3fv(v[4].mV);
+
+
+ //bottom
+ gGL.vertex3fv(v[0].mV);
+ gGL.vertex3fv(v[5].mV);
+
+ gGL.vertex3fv(v[1].mV);
+ gGL.vertex3fv(v[5].mV);
+
+ gGL.vertex3fv(v[2].mV);
+ gGL.vertex3fv(v[5].mV);
+
+ gGL.vertex3fv(v[3].mV);
+ gGL.vertex3fv(v[5].mV);
+
+ gGL.end();
+
+ gGL.popMatrix();
+}
+
+
+// End
diff --git a/indra/llappearance/llavatarjoint.h b/indra/llappearance/llavatarjoint.h
new file mode 100644
index 0000000000..fec91503c7
--- /dev/null
+++ b/indra/llappearance/llavatarjoint.h
@@ -0,0 +1,140 @@
+/**
+ * @file llavatarjoint.h
+ * @brief Implementation of LLAvatarJoint class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLAVATARJOINT_H
+#define LL_LLAVATARJOINT_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "lljoint.h"
+#include "lljointpickname.h"
+
+class LLFace;
+class LLAvatarJointMesh;
+
+extern const F32 DEFAULT_AVATAR_JOINT_LOD;
+
+//-----------------------------------------------------------------------------
+// class LLViewerJoint
+//-----------------------------------------------------------------------------
+class LLAvatarJoint :
+ public LLJoint
+{
+public:
+ LLAvatarJoint();
+ LLAvatarJoint(S32 joint_num);
+ // *TODO: Only used for LLVOAvatarSelf::mScreenp. *DOES NOT INITIALIZE mResetAfterRestoreOldXform*
+ LLAvatarJoint(const std::string &name, LLJoint *parent = NULL);
+ virtual ~LLAvatarJoint();
+
+ // Gets the validity of this joint
+ BOOL getValid() { return mValid; }
+
+ // Sets the validity of this joint
+ virtual void setValid( BOOL valid, BOOL recursive=FALSE );
+
+ // Returns true if this object is transparent.
+ // This is used to determine in which order to draw objects.
+ virtual BOOL isTransparent() { return mIsTransparent; }
+
+ // Returns true if this object should inherit scale modifiers from its immediate parent
+ virtual BOOL inheritScale() { return FALSE; }
+
+ enum Components
+ {
+ SC_BONE = 1,
+ SC_JOINT = 2,
+ SC_AXES = 4
+ };
+
+ // Selects which skeleton components to draw
+ void setSkeletonComponents( U32 comp, BOOL recursive = TRUE );
+
+ // Returns which skeleton components are enables for drawing
+ U32 getSkeletonComponents() { return mComponents; }
+
+ // Sets the level of detail for this node as a minimum
+ // pixel area threshold. If the current pixel area for this
+ // object is less than the specified threshold, the node is
+ // not traversed. In addition, if a value is specified (not
+ // default of 0.0), and the pixel area is larger than the
+ // specified minimum, the node is rendered, but no other siblings
+ // of this node under the same parent will be.
+ F32 getLOD() { return mMinPixelArea; }
+ void setLOD( F32 pixelArea ) { mMinPixelArea = pixelArea; }
+
+ void setPickName(LLJointPickName name) { mPickName = name; }
+ LLJointPickName getPickName() { return mPickName; }
+
+ void setVisible( BOOL visible, BOOL recursive );
+
+ // Takes meshes in mMeshParts and sets each one as a child joint
+ void setMeshesToChildren();
+
+ // LLViewerJoint interface
+ virtual U32 render( F32 pixelArea, BOOL first_pass = TRUE, BOOL is_dummy = FALSE ) = 0;
+ virtual void updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area);
+ virtual void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE, bool terse_update = false);
+ virtual BOOL updateLOD(F32 pixel_area, BOOL activate);
+ virtual void updateJointGeometry();
+ virtual void dump();
+
+
+public:
+ static BOOL sDisableLOD;
+ avatar_joint_mesh_list_t mMeshParts; //LLViewerJointMesh*
+ void setMeshID( S32 id ) {mMeshID = id;}
+
+protected:
+ void init();
+
+ BOOL mValid;
+ BOOL mIsTransparent;
+ U32 mComponents;
+ F32 mMinPixelArea;
+ LLJointPickName mPickName;
+ BOOL mVisible;
+ S32 mMeshID;
+};
+
+class LLAvatarJointCollisionVolume : public LLAvatarJoint
+{
+public:
+ LLAvatarJointCollisionVolume();
+ virtual ~LLAvatarJointCollisionVolume() {};
+
+ /*virtual*/ BOOL inheritScale() { return TRUE; }
+ /*virtual*/ U32 render( F32 pixelArea, BOOL first_pass = TRUE, BOOL is_dummy = FALSE );
+
+ void renderCollision();
+
+ LLVector3 getVolumePos(LLVector3 &offset);
+};
+
+#endif // LL_LLAVATARJOINT_H
+
+
diff --git a/indra/llappearance/llavatarjointmesh.cpp b/indra/llappearance/llavatarjointmesh.cpp
new file mode 100644
index 0000000000..4a5cff1dc3
--- /dev/null
+++ b/indra/llappearance/llavatarjointmesh.cpp
@@ -0,0 +1,375 @@
+/**
+ * @file LLAvatarJointMesh.cpp
+ * @brief Implementation of LLAvatarJointMesh class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+#include "imageids.h"
+#include "llfasttimer.h"
+#include "llrender.h"
+
+#include "llavatarjointmesh.h"
+#include "llavatarappearance.h"
+//#include "llapr.h"
+//#include "llbox.h"
+//#include "lldrawable.h"
+//#include "lldrawpoolavatar.h"
+//#include "lldrawpoolbump.h"
+//#include "lldynamictexture.h"
+//#include "llface.h"
+//#include "llgldbg.h"
+//#include "llglheaders.h"
+#include "lltexlayer.h"
+//#include "llviewercamera.h"
+//#include "llviewercontrol.h"
+//#include "llviewertexturelist.h"
+//#include "llsky.h"
+//#include "pipeline.h"
+//#include "llviewershadermgr.h"
+#include "llmath.h"
+#include "v4math.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "llmatrix4a.h"
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLAvatarJointMesh::LLSkinJoint
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLSkinJoint
+//-----------------------------------------------------------------------------
+LLSkinJoint::LLSkinJoint()
+{
+ mJoint = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLSkinJoint
+//-----------------------------------------------------------------------------
+LLSkinJoint::~LLSkinJoint()
+{
+ mJoint = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLSkinJoint::setupSkinJoint()
+//-----------------------------------------------------------------------------
+BOOL LLSkinJoint::setupSkinJoint( LLAvatarJoint *joint)
+{
+ // find the named joint
+ mJoint = joint;
+ if ( !mJoint )
+ {
+ llinfos << "Can't find joint" << llendl;
+ }
+
+ // compute the inverse root skin matrix
+ mRootToJointSkinOffset.clearVec();
+
+ LLVector3 rootSkinOffset;
+ while (joint)
+ {
+ rootSkinOffset += joint->getSkinOffset();
+ joint = (LLAvatarJoint*)joint->getParent();
+ }
+
+ mRootToJointSkinOffset = -rootSkinOffset;
+ mRootToParentJointSkinOffset = mRootToJointSkinOffset;
+ mRootToParentJointSkinOffset += mJoint->getSkinOffset();
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLAvatarJointMesh
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+BOOL LLAvatarJointMesh::sPipelineRender = FALSE;
+EAvatarRenderPass LLAvatarJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
+U32 LLAvatarJointMesh::sClothingMaskImageName = 0;
+LLColor4 LLAvatarJointMesh::sClothingInnerColor;
+
+//-----------------------------------------------------------------------------
+// LLAvatarJointMesh()
+//-----------------------------------------------------------------------------
+LLAvatarJointMesh::LLAvatarJointMesh()
+ :
+ mTexture( NULL ),
+ mLayerSet( NULL ),
+ mTestImageName( 0 ),
+ mFaceIndexCount(0)
+{
+
+ mColor[0] = 1.0f;
+ mColor[1] = 1.0f;
+ mColor[2] = 1.0f;
+ mColor[3] = 1.0f;
+ mShiny = 0.0f;
+ mCullBackFaces = TRUE;
+
+ mMesh = NULL;
+
+ mNumSkinJoints = 0;
+ mSkinJoints = NULL;
+
+ mFace = NULL;
+
+ mMeshID = 0;
+ mUpdateXform = FALSE;
+
+ mValid = FALSE;
+
+ mIsTransparent = FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLAvatarJointMesh()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLAvatarJointMesh::~LLAvatarJointMesh()
+{
+ mMesh = NULL;
+ mTexture = NULL;
+ freeSkinData();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLAvatarJointMesh::allocateSkinData()
+//-----------------------------------------------------------------------------
+BOOL LLAvatarJointMesh::allocateSkinData( U32 numSkinJoints )
+{
+ mSkinJoints = new LLSkinJoint[ numSkinJoints ];
+ mNumSkinJoints = numSkinJoints;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLAvatarJointMesh::freeSkinData()
+//-----------------------------------------------------------------------------
+void LLAvatarJointMesh::freeSkinData()
+{
+ mNumSkinJoints = 0;
+ delete [] mSkinJoints;
+ mSkinJoints = NULL;
+}
+
+//--------------------------------------------------------------------
+// LLAvatarJointMesh::getColor()
+//--------------------------------------------------------------------
+void LLAvatarJointMesh::getColor( F32 *red, F32 *green, F32 *blue, F32 *alpha )
+{
+ *red = mColor[0];
+ *green = mColor[1];
+ *blue = mColor[2];
+ *alpha = mColor[3];
+}
+
+//--------------------------------------------------------------------
+// LLAvatarJointMesh::setColor()
+//--------------------------------------------------------------------
+void LLAvatarJointMesh::setColor( F32 red, F32 green, F32 blue, F32 alpha )
+{
+ mColor[0] = red;
+ mColor[1] = green;
+ mColor[2] = blue;
+ mColor[3] = alpha;
+}
+
+void LLAvatarJointMesh::setColor( const LLColor4& color )
+{
+ mColor = color;
+}
+
+
+//--------------------------------------------------------------------
+// LLAvatarJointMesh::getTexture()
+//--------------------------------------------------------------------
+//LLViewerTexture *LLAvatarJointMesh::getTexture()
+//{
+// return mTexture;
+//}
+
+//--------------------------------------------------------------------
+// LLAvatarJointMesh::setTexture()
+//--------------------------------------------------------------------
+void LLAvatarJointMesh::setTexture( LLGLTexture *texture )
+{
+ mTexture = texture;
+
+ // texture and dynamic_texture are mutually exclusive
+ if( texture )
+ {
+ mLayerSet = NULL;
+ //texture->bindTexture(0);
+ //texture->setClamp(TRUE, TRUE);
+ }
+}
+
+
+BOOL LLAvatarJointMesh::hasGLTexture() const
+{
+ return mTexture.notNull() && mTexture->hasGLTexture();
+}
+
+//--------------------------------------------------------------------
+// LLAvatarJointMesh::setLayerSet()
+// Sets the shape texture (takes precedence over normal texture)
+//--------------------------------------------------------------------
+void LLAvatarJointMesh::setLayerSet( LLTexLayerSet* layer_set )
+{
+ mLayerSet = layer_set;
+
+ // texture and dynamic_texture are mutually exclusive
+ if( layer_set )
+ {
+ mTexture = NULL;
+ }
+}
+
+BOOL LLAvatarJointMesh::hasComposite() const
+{
+ return (mLayerSet && mLayerSet->hasComposite());
+}
+
+
+//--------------------------------------------------------------------
+// LLAvatarJointMesh::getMesh()
+//--------------------------------------------------------------------
+LLPolyMesh *LLAvatarJointMesh::getMesh()
+{
+ return mMesh;
+}
+
+//-----------------------------------------------------------------------------
+// LLAvatarJointMesh::setMesh()
+//-----------------------------------------------------------------------------
+void LLAvatarJointMesh::setMesh( LLPolyMesh *mesh )
+{
+ // set the mesh pointer
+ mMesh = mesh;
+
+ // release any existing skin joints
+ freeSkinData();
+
+ if ( mMesh == NULL )
+ {
+ return;
+ }
+
+ // acquire the transform from the mesh object
+ setPosition( mMesh->getPosition() );
+ setRotation( mMesh->getRotation() );
+ setScale( mMesh->getScale() );
+
+ // create skin joints if necessary
+ if ( mMesh->hasWeights() && !mMesh->isLOD())
+ {
+ U32 numJointNames = mMesh->getNumJointNames();
+
+ allocateSkinData( numJointNames );
+ std::string *jointNames = mMesh->getJointNames();
+
+ U32 jn;
+ for (jn = 0; jn < numJointNames; jn++)
+ {
+ //llinfos << "Setting up joint " << jointNames[jn] << llendl;
+ LLAvatarJoint* joint = (LLAvatarJoint*)(getRoot()->findJoint(jointNames[jn]) );
+ mSkinJoints[jn].setupSkinJoint( joint );
+ }
+ }
+
+ // setup joint array
+ if (!mMesh->isLOD())
+ {
+ setupJoint((LLAvatarJoint*)getRoot());
+ }
+
+// llinfos << "joint render entries: " << mMesh->mJointRenderData.count() << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// setupJoint()
+//-----------------------------------------------------------------------------
+void LLAvatarJointMesh::setupJoint(LLAvatarJoint* current_joint)
+{
+// llinfos << "Mesh: " << getName() << llendl;
+
+// S32 joint_count = 0;
+ U32 sj;
+ for (sj=0; sj<mNumSkinJoints; sj++)
+ {
+ LLSkinJoint &js = mSkinJoints[sj];
+
+ if (js.mJoint != current_joint)
+ {
+ continue;
+ }
+
+ // we've found a skinjoint for this joint..
+
+ // is the last joint in the array our parent?
+ if(mMesh->mJointRenderData.count() && mMesh->mJointRenderData[mMesh->mJointRenderData.count() - 1]->mWorldMatrix == &current_joint->getParent()->getWorldMatrix())
+ {
+ // ...then just add ourselves
+ LLAvatarJoint* jointp = js.mJoint;
+ mMesh->mJointRenderData.put(new LLJointRenderData(&jointp->getWorldMatrix(), &js));
+// llinfos << "joint " << joint_count << js.mJoint->getName() << llendl;
+// joint_count++;
+ }
+ // otherwise add our parent and ourselves
+ else
+ {
+ mMesh->mJointRenderData.put(new LLJointRenderData(&current_joint->getParent()->getWorldMatrix(), NULL));
+// llinfos << "joint " << joint_count << current_joint->getParent()->getName() << llendl;
+// joint_count++;
+ mMesh->mJointRenderData.put(new LLJointRenderData(&current_joint->getWorldMatrix(), &js));
+// llinfos << "joint " << joint_count << current_joint->getName() << llendl;
+// joint_count++;
+ }
+ }
+
+ // depth-first traversal
+ for (LLJoint::child_list_t::iterator iter = current_joint->mChildren.begin();
+ iter != current_joint->mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* child_joint = (LLAvatarJoint*)(*iter);
+ setupJoint(child_joint);
+ }
+}
+
+
+// End
diff --git a/indra/llappearance/llavatarjointmesh.h b/indra/llappearance/llavatarjointmesh.h
new file mode 100644
index 0000000000..6486932cdf
--- /dev/null
+++ b/indra/llappearance/llavatarjointmesh.h
@@ -0,0 +1,145 @@
+/**
+ * @file llavatarjointmesh.h
+ * @brief Declaration of LLAvatarJointMesh class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLAVATARJOINTMESH_H
+#define LL_LLAVATARJOINTMESH_H
+
+#include "llavatarjoint.h"
+#include "llgltexture.h"
+#include "llpolymesh.h"
+#include "v4color.h"
+
+class LLDrawable;
+class LLFace;
+class LLCharacter;
+class LLTexLayerSet;
+
+typedef enum e_avatar_render_pass
+{
+ AVATAR_RENDER_PASS_SINGLE,
+ AVATAR_RENDER_PASS_CLOTHING_INNER,
+ AVATAR_RENDER_PASS_CLOTHING_OUTER
+} EAvatarRenderPass;
+
+class LLSkinJoint
+{
+public:
+ LLSkinJoint();
+ ~LLSkinJoint();
+ BOOL setupSkinJoint( LLAvatarJoint *joint);
+
+ LLAvatarJoint *mJoint;
+ LLVector3 mRootToJointSkinOffset;
+ LLVector3 mRootToParentJointSkinOffset;
+};
+
+//-----------------------------------------------------------------------------
+// class LLViewerJointMesh
+//-----------------------------------------------------------------------------
+class LLAvatarJointMesh : public virtual LLAvatarJoint
+{
+protected:
+ LLColor4 mColor; // color value
+// LLColor4 mSpecular; // specular color (always white for now)
+ F32 mShiny; // shiny value
+ LLPointer<LLGLTexture> mTexture; // ptr to a global texture
+ LLTexLayerSet* mLayerSet; // ptr to a layer set owned by the avatar
+ U32 mTestImageName; // handle to a temporary texture for previewing uploads
+ LLPolyMesh* mMesh; // ptr to a global polymesh
+ BOOL mCullBackFaces; // true by default
+ LLFace* mFace; // ptr to a face w/ AGP copy of mesh
+
+ U32 mFaceIndexCount;
+
+ U32 mNumSkinJoints;
+ LLSkinJoint* mSkinJoints;
+ S32 mMeshID;
+
+public:
+ static BOOL sPipelineRender;
+ //RN: this is here for testing purposes
+ static U32 sClothingMaskImageName;
+ static EAvatarRenderPass sRenderPass;
+ static LLColor4 sClothingInnerColor;
+
+public:
+ // Constructor
+ LLAvatarJointMesh();
+
+ // Destructor
+ virtual ~LLAvatarJointMesh();
+
+ // Gets the shape color
+ void getColor( F32 *red, F32 *green, F32 *blue, F32 *alpha );
+
+ // Sets the shape color
+ void setColor( F32 red, F32 green, F32 blue, F32 alpha );
+ void setColor( const LLColor4& color );
+
+ // Sets the shininess
+ void setSpecular( const LLColor4& color, F32 shiny ) { /*mSpecular = color;*/ mShiny = shiny; };
+
+ // Sets the shape texture
+ void setTexture( LLGLTexture *texture );
+
+ BOOL hasGLTexture() const;
+
+ void setTestTexture( U32 name ) { mTestImageName = name; }
+
+ // Sets layer set responsible for a dynamic shape texture (takes precedence over normal texture)
+ void setLayerSet( LLTexLayerSet* layer_set );
+
+ BOOL hasComposite() const;
+
+ // Gets the poly mesh
+ LLPolyMesh *getMesh();
+
+ // Sets the poly mesh
+ void setMesh( LLPolyMesh *mesh );
+
+ // Sets up joint matrix data for rendering
+ void setupJoint(LLAvatarJoint* current_joint);
+
+ // Render time method to upload batches of joint matrices
+ void uploadJointMatrices();
+
+ // Sets ID for picking
+ void setMeshID( S32 id ) {mMeshID = id;}
+
+ // Gets ID for picking
+ S32 getMeshID() { return mMeshID; }
+
+ void setIsTransparent(BOOL is_transparent) { mIsTransparent = is_transparent; }
+
+private:
+ // Allocate skin data
+ BOOL allocateSkinData( U32 numSkinJoints );
+
+ // Free skin data
+ void freeSkinData();
+};
+
+#endif // LL_LLAVATARJOINTMESH_H
diff --git a/indra/llappearance/lldriverparam.cpp b/indra/llappearance/lldriverparam.cpp
new file mode 100644
index 0000000000..1f7e8b8652
--- /dev/null
+++ b/indra/llappearance/lldriverparam.cpp
@@ -0,0 +1,619 @@
+/**
+ * @file lldriverparam.cpp
+ * @brief A visual parameter that drives (controls) other visual parameters.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldriverparam.h"
+
+#include "llavatarappearance.h"
+#include "llwearable.h"
+#include "llwearabledata.h"
+
+//-----------------------------------------------------------------------------
+// LLDriverParamInfo
+//-----------------------------------------------------------------------------
+
+LLDriverParamInfo::LLDriverParamInfo() :
+ mDriverParam(NULL)
+{
+}
+
+BOOL LLDriverParamInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_driver" ) );
+
+ if( !LLViewerVisualParamInfo::parseXml( node ))
+ return FALSE;
+
+ LLXmlTreeNode* param_driver_node = node->getChildByName( "param_driver" );
+ if( !param_driver_node )
+ return FALSE;
+
+ for (LLXmlTreeNode* child = param_driver_node->getChildByName( "driven" );
+ child;
+ child = param_driver_node->getNextNamedChild())
+ {
+ S32 driven_id;
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ if( child->getFastAttributeS32( id_string, driven_id ) )
+ {
+ F32 min1 = mMinWeight;
+ F32 max1 = mMaxWeight;
+ F32 max2 = max1;
+ F32 min2 = max1;
+
+ // driven ________ //
+ // ^ /| |\ //
+ // | / | | \ //
+ // | / | | \ //
+ // | / | | \ //
+ // | / | | \ //
+ //-------|----|-------|----|-------> driver //
+ // | min1 max1 max2 min2
+
+ static LLStdStringHandle min1_string = LLXmlTree::addAttributeString("min1");
+ child->getFastAttributeF32( min1_string, min1 ); // optional
+ static LLStdStringHandle max1_string = LLXmlTree::addAttributeString("max1");
+ child->getFastAttributeF32( max1_string, max1 ); // optional
+ static LLStdStringHandle max2_string = LLXmlTree::addAttributeString("max2");
+ child->getFastAttributeF32( max2_string, max2 ); // optional
+ static LLStdStringHandle min2_string = LLXmlTree::addAttributeString("min2");
+ child->getFastAttributeF32( min2_string, min2 ); // optional
+
+ // Push these on the front of the deque, so that we can construct
+ // them in order later (faster)
+ mDrivenInfoList.push_front( LLDrivenEntryInfo( driven_id, min1, max1, max2, min2 ) );
+ }
+ else
+ {
+ llerrs << "<driven> Unable to resolve driven parameter: " << driven_id << llendl;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//virtual
+void LLDriverParamInfo::toStream(std::ostream &out)
+{
+ LLViewerVisualParamInfo::toStream(out);
+ out << "driver" << "\t";
+ out << mDrivenInfoList.size() << "\t";
+ for (entry_info_list_t::iterator iter = mDrivenInfoList.begin(); iter != mDrivenInfoList.end(); iter++)
+ {
+ LLDrivenEntryInfo driven = *iter;
+ out << driven.mDrivenID << "\t";
+ }
+
+ out << std::endl;
+
+ if(mDriverParam && mDriverParam->getAvatarAppearance()->isSelf() &&
+ mDriverParam->getAvatarAppearance()->isValid())
+ {
+ for (entry_info_list_t::iterator iter = mDrivenInfoList.begin(); iter != mDrivenInfoList.end(); iter++)
+ {
+ LLDrivenEntryInfo driven = *iter;
+ LLViewerVisualParam *param =
+ (LLViewerVisualParam*)mDriverParam->getAvatarAppearance()->getVisualParam(driven.mDrivenID);
+ if (param)
+ {
+ param->getInfo()->toStream(out);
+ if (param->getWearableType() != mWearableType)
+ {
+ if(param->getCrossWearable())
+ {
+ out << "cross-wearable" << "\t";
+ }
+ else
+ {
+ out << "ERROR!" << "\t";
+ }
+ }
+ else
+ {
+ out << "valid" << "\t";
+ }
+ }
+ else
+ {
+ llwarns << "could not get parameter " << driven.mDrivenID << " from avatar "
+ << mDriverParam->getAvatarAppearance()
+ << " for driver parameter " << getID() << llendl;
+ }
+ out << std::endl;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLDriverParam
+//-----------------------------------------------------------------------------
+
+LLDriverParam::LLDriverParam(LLAvatarAppearance *appearance, LLWearable* wearable /* = NULL */) :
+ mCurrentDistortionParam( NULL ),
+ mAvatarAppearance(appearance),
+ mWearablep(wearable)
+{
+ llassert(mAvatarAppearance);
+ if (mWearablep)
+ {
+ llassert(mAvatarAppearance->isSelf());
+ }
+ mDefaultVec.clear();
+}
+
+LLDriverParam::~LLDriverParam()
+{
+}
+
+BOOL LLDriverParam::setInfo(LLDriverParamInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ info->mDriverParam = this;
+
+ setWeight(getDefaultWeight(), FALSE );
+
+ return TRUE;
+}
+
+/*virtual*/ LLViewerVisualParam* LLDriverParam::cloneParam(LLWearable* wearable) const
+{
+ llassert(wearable);
+ LLDriverParam *new_param = new LLDriverParam(mAvatarAppearance, wearable);
+ // FIXME DRANO this clobbers mWearablep, which means any code
+ // currently using mWearablep is wrong, or at least untested.
+ *new_param = *this;
+ //new_param->mWearablep = wearable;
+// new_param->mDriven.clear(); // clear driven list to avoid overwriting avatar driven params from wearables.
+ return new_param;
+}
+
+void LLDriverParam::setWeight(F32 weight, BOOL upload_bake)
+{
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ if (mIsAnimating)
+ {
+ // allow overshoot when animating
+ mCurWeight = weight;
+ }
+ else
+ {
+ mCurWeight = llclamp(weight, min_weight, max_weight);
+ }
+
+ // driven ________
+ // ^ /| |\ ^
+ // | / | | \ |
+ // | / | | \ |
+ // | / | | \ |
+ // | / | | \ |
+ //-------|----|-------|----|-------> driver
+ // | min1 max1 max2 min2
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ LLDrivenEntryInfo* info = driven->mInfo;
+
+ F32 driven_weight = 0.f;
+ F32 driven_min = driven->mParam->getMinWeight();
+ F32 driven_max = driven->mParam->getMaxWeight();
+
+ if (mIsAnimating)
+ {
+ // driven param doesn't interpolate (textures, for example)
+ if (!driven->mParam->getAnimating())
+ {
+ continue;
+ }
+ if( mCurWeight < info->mMin1 )
+ {
+ if (info->mMin1 == min_weight)
+ {
+ if (info->mMin1 == info->mMax1)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ //up slope extrapolation
+ F32 t = (mCurWeight - info->mMin1) / (info->mMax1 - info->mMin1 );
+ driven_weight = driven_min + t * (driven_max - driven_min);
+ }
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+
+ setDrivenWeight(driven,driven_weight,upload_bake);
+ continue;
+ }
+ else
+ if ( mCurWeight > info->mMin2 )
+ {
+ if (info->mMin2 == max_weight)
+ {
+ if (info->mMin2 == info->mMax2)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ //down slope extrapolation
+ F32 t = (mCurWeight - info->mMax2) / (info->mMin2 - info->mMax2 );
+ driven_weight = driven_max + t * (driven_min - driven_max);
+ }
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+
+ setDrivenWeight(driven,driven_weight,upload_bake);
+ continue;
+ }
+ }
+
+ driven_weight = getDrivenWeight(driven, mCurWeight);
+ setDrivenWeight(driven,driven_weight,upload_bake);
+ }
+}
+
+F32 LLDriverParam::getTotalDistortion()
+{
+ F32 sum = 0.f;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum += driven->mParam->getTotalDistortion();
+ }
+
+ return sum;
+}
+
+const LLVector4a &LLDriverParam::getAvgDistortion()
+{
+ // It's not actually correct to take the average of averages, but it good enough here.
+ LLVector4a sum;
+ sum.clear();
+ S32 count = 0;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum.add(driven->mParam->getAvgDistortion());
+ count++;
+ }
+ sum.mul( 1.f/(F32)count);
+
+ mDefaultVec = sum;
+ return mDefaultVec;
+}
+
+F32 LLDriverParam::getMaxDistortion()
+{
+ F32 max = 0.f;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ F32 param_max = driven->mParam->getMaxDistortion();
+ if( param_max > max )
+ {
+ max = param_max;
+ }
+ }
+
+ return max;
+}
+
+
+LLVector4a LLDriverParam::getVertexDistortion(S32 index, LLPolyMesh *poly_mesh)
+{
+ LLVector4a sum;
+ sum.clear();
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum.add(driven->mParam->getVertexDistortion( index, poly_mesh ));
+ }
+ return sum;
+}
+
+const LLVector4a* LLDriverParam::getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ mCurrentDistortionParam = NULL;
+ const LLVector4a* v = NULL;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ v = driven->mParam->getFirstDistortion( index, poly_mesh );
+ if( v )
+ {
+ mCurrentDistortionParam = driven->mParam;
+ break;
+ }
+ }
+
+ return v;
+};
+
+const LLVector4a* LLDriverParam::getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ llassert( mCurrentDistortionParam );
+ if( !mCurrentDistortionParam )
+ {
+ return NULL;
+ }
+
+ LLDrivenEntry* driven = NULL;
+ entry_list_t::iterator iter;
+
+ // Set mDriven iteration to the right point
+ for( iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ driven = &(*iter);
+ if( driven->mParam == mCurrentDistortionParam )
+ {
+ break;
+ }
+ }
+
+ llassert(driven);
+ if (!driven)
+ {
+ return NULL; // shouldn't happen, but...
+ }
+
+ // We're already in the middle of a param's distortions, so get the next one.
+ const LLVector4a* v = driven->mParam->getNextDistortion( index, poly_mesh );
+ if( (!v) && (iter != mDriven.end()) )
+ {
+ // This param is finished, so start the next param. It might not have any
+ // distortions, though, so we have to loop to find the next param that does.
+ for( iter++; iter != mDriven.end(); iter++ )
+ {
+ driven = &(*iter);
+ v = driven->mParam->getFirstDistortion( index, poly_mesh );
+ if( v )
+ {
+ mCurrentDistortionParam = driven->mParam;
+ break;
+ }
+ }
+ }
+
+ return v;
+};
+
+S32 LLDriverParam::getDrivenParamsCount() const
+{
+ return mDriven.size();
+}
+
+const LLViewerVisualParam* LLDriverParam::getDrivenParam(S32 index) const
+{
+ if (0 > index || index >= mDriven.size())
+ {
+ return NULL;
+ }
+ return mDriven[index].mParam;
+}
+
+//-----------------------------------------------------------------------------
+// setAnimationTarget()
+//-----------------------------------------------------------------------------
+void LLDriverParam::setAnimationTarget( F32 target_value, BOOL upload_bake )
+{
+ LLVisualParam::setAnimationTarget(target_value, upload_bake);
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ F32 driven_weight = getDrivenWeight(driven, mTargetWeight);
+
+ // this isn't normally necessary, as driver params handle interpolation of their driven params
+ // but texture params need to know to assume their final value at beginning of interpolation
+ driven->mParam->setAnimationTarget(driven_weight, upload_bake);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// stopAnimating()
+//-----------------------------------------------------------------------------
+void LLDriverParam::stopAnimating(BOOL upload_bake)
+{
+ LLVisualParam::stopAnimating(upload_bake);
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ driven->mParam->setAnimating(FALSE);
+ }
+}
+
+/*virtual*/
+BOOL LLDriverParam::linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params)
+{
+ BOOL success = TRUE;
+ LLDriverParamInfo::entry_info_list_t::iterator iter;
+ for (iter = getInfo()->mDrivenInfoList.begin(); iter != getInfo()->mDrivenInfoList.end(); ++iter)
+ {
+ LLDrivenEntryInfo *driven_info = &(*iter);
+ S32 driven_id = driven_info->mDrivenID;
+
+ // check for already existing links. Do not overwrite.
+ BOOL found = FALSE;
+ for (entry_list_t::iterator driven_iter = mDriven.begin(); driven_iter != mDriven.end() && !found; ++driven_iter)
+ {
+ if (driven_iter->mInfo->mDrivenID == driven_id)
+ {
+ found = TRUE;
+ }
+ }
+
+ if (!found)
+ {
+ LLViewerVisualParam* param = (LLViewerVisualParam*)mapper(driven_id);
+ if (param) param->setParamLocation(this->getParamLocation());
+ bool push = param && (!only_cross_params || param->getCrossWearable());
+ if (push)
+ {
+ mDriven.push_back(LLDrivenEntry( param, driven_info ));
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ return success;
+}
+
+void LLDriverParam::resetDrivenParams()
+{
+ mDriven.clear();
+ mDriven.reserve(getInfo()->mDrivenInfoList.size());
+}
+
+void LLDriverParam::updateCrossDrivenParams(LLWearableType::EType driven_type)
+{
+ bool needs_update = (getWearableType()==driven_type);
+
+ // if the driver has a driven entry for the passed-in wearable type, we need to refresh the value
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ if (driven && driven->mParam && driven->mParam->getCrossWearable() && driven->mParam->getWearableType() == driven_type)
+ {
+ needs_update = true;
+ }
+ }
+
+
+ if (needs_update)
+ {
+ LLWearableType::EType driver_type = (LLWearableType::EType)getWearableType();
+
+ // If we've gotten here, we've added a new wearable of type "type"
+ // Thus this wearable needs to get updates from the driver wearable.
+ // The call to setVisualParamWeight seems redundant, but is necessary
+ // as the number of driven wearables has changed since the last update. -Nyx
+ LLWearable *wearable = mAvatarAppearance->getWearableData()->getTopWearable(driver_type);
+ if (wearable)
+ {
+ wearable->setVisualParamWeight(mID, wearable->getVisualParamWeight(mID), false);
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getDrivenWeight()
+//-----------------------------------------------------------------------------
+F32 LLDriverParam::getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight)
+{
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ const LLDrivenEntryInfo* info = driven->mInfo;
+
+ F32 driven_weight = 0.f;
+ F32 driven_min = driven->mParam->getMinWeight();
+ F32 driven_max = driven->mParam->getMaxWeight();
+
+ if( input_weight <= info->mMin1 )
+ {
+ if( info->mMin1 == info->mMax1 &&
+ info->mMin1 <= min_weight)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+ }
+ else
+ if( input_weight <= info->mMax1 )
+ {
+ F32 t = (input_weight - info->mMin1) / (info->mMax1 - info->mMin1 );
+ driven_weight = driven_min + t * (driven_max - driven_min);
+ }
+ else
+ if( input_weight <= info->mMax2 )
+ {
+ driven_weight = driven_max;
+ }
+ else
+ if( input_weight <= info->mMin2 )
+ {
+ F32 t = (input_weight - info->mMax2) / (info->mMin2 - info->mMax2 );
+ driven_weight = driven_max + t * (driven_min - driven_max);
+ }
+ else
+ {
+ if (info->mMax2 >= max_weight)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+ }
+
+ return driven_weight;
+}
+
+void LLDriverParam::setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight, bool upload_bake)
+{
+ bool use_self = false;
+ if(mWearablep &&
+ mAvatarAppearance->isValid() &&
+ driven->mParam->getCrossWearable())
+ {
+ LLWearable* wearable = dynamic_cast<LLWearable*> (mWearablep);
+ if (mAvatarAppearance->getWearableData()->isOnTop(wearable))
+ {
+ use_self = true;
+ }
+ }
+
+ if (use_self)
+ {
+ // call setWeight through LLVOAvatarSelf so other wearables can be updated with the correct values
+ mAvatarAppearance->setVisualParamWeight( (LLVisualParam*)driven->mParam, driven_weight, upload_bake );
+ }
+ else
+ {
+ driven->mParam->setWeight( driven_weight, upload_bake );
+ }
+}
diff --git a/indra/llappearance/lldriverparam.h b/indra/llappearance/lldriverparam.h
new file mode 100644
index 0000000000..040c9cf5be
--- /dev/null
+++ b/indra/llappearance/lldriverparam.h
@@ -0,0 +1,144 @@
+/**
+ * @file lldriverparam.h
+ * @brief A visual parameter that drives (controls) other visual parameters.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLDRIVERPARAM_H
+#define LL_LLDRIVERPARAM_H
+
+#include "llviewervisualparam.h"
+#include "llwearabletype.h"
+
+class LLAvatarAppearance;
+class LLDriverParam;
+class LLWearable;
+
+//-----------------------------------------------------------------------------
+
+struct LLDrivenEntryInfo
+{
+ LLDrivenEntryInfo( S32 id, F32 min1, F32 max1, F32 max2, F32 min2 )
+ : mDrivenID( id ), mMin1( min1 ), mMax1( max1 ), mMax2( max2 ), mMin2( min2 ) {}
+ S32 mDrivenID;
+ F32 mMin1;
+ F32 mMax1;
+ F32 mMax2;
+ F32 mMin2;
+};
+
+struct LLDrivenEntry
+{
+ LLDrivenEntry( LLViewerVisualParam* param, LLDrivenEntryInfo *info )
+ : mParam( param ), mInfo( info ) {}
+ LLViewerVisualParam* mParam;
+ LLDrivenEntryInfo* mInfo;
+};
+
+//-----------------------------------------------------------------------------
+
+class LLDriverParamInfo : public LLViewerVisualParamInfo
+{
+ friend class LLDriverParam;
+public:
+ LLDriverParamInfo();
+ /*virtual*/ ~LLDriverParamInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+ /*virtual*/ void toStream(std::ostream &out);
+
+protected:
+ typedef std::deque<LLDrivenEntryInfo> entry_info_list_t;
+ entry_info_list_t mDrivenInfoList;
+ LLDriverParam* mDriverParam; // backpointer
+};
+
+//-----------------------------------------------------------------------------
+
+LL_ALIGN_PREFIX(16)
+class LLDriverParam : public LLViewerVisualParam
+{
+private:
+ // Hide the default constructor. Force construction with LLAvatarAppearance.
+ LLDriverParam() {}
+public:
+ LLDriverParam(LLAvatarAppearance *appearance, LLWearable* wearable = NULL);
+ ~LLDriverParam();
+
+ void* operator new(size_t size)
+ {
+ return ll_aligned_malloc_16(size);
+ }
+
+ void operator delete(void* ptr)
+ {
+ ll_aligned_free_16(ptr);
+ }
+
+ // Special: These functions are overridden by child classes
+ LLDriverParamInfo* getInfo() const { return (LLDriverParamInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLDriverParamInfo *info);
+
+ LLAvatarAppearance* getAvatarAppearance() { return mAvatarAppearance; }
+ const LLAvatarAppearance* getAvatarAppearance() const { return mAvatarAppearance; }
+
+ void updateCrossDrivenParams(LLWearableType::EType driven_type);
+
+ /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const;
+
+ // LLVisualParam Virtual functions
+ /*virtual*/ void apply( ESex sex ) {} // apply is called separately for each driven param.
+ /*virtual*/ void setWeight(F32 weight, BOOL upload_bake);
+ /*virtual*/ void setAnimationTarget( F32 target_value, BOOL upload_bake );
+ /*virtual*/ void stopAnimating(BOOL upload_bake);
+ /*virtual*/ BOOL linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params);
+ /*virtual*/ void resetDrivenParams();
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion();
+ /*virtual*/ const LLVector4a& getAvgDistortion();
+ /*virtual*/ F32 getMaxDistortion();
+ /*virtual*/ LLVector4a getVertexDistortion(S32 index, LLPolyMesh *poly_mesh);
+ /*virtual*/ const LLVector4a* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh);
+ /*virtual*/ const LLVector4a* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
+
+ S32 getDrivenParamsCount() const;
+ const LLViewerVisualParam* getDrivenParam(S32 index) const;
+
+protected:
+ F32 getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight);
+ void setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight, bool upload_bake);
+
+
+ LL_ALIGN_16(LLVector4a mDefaultVec); // temp holder
+ typedef std::vector<LLDrivenEntry> entry_list_t;
+ entry_list_t mDriven;
+ LLViewerVisualParam* mCurrentDistortionParam;
+ // Backlink only; don't make this an LLPointer.
+ LLAvatarAppearance* mAvatarAppearance;
+ LLWearable* mWearablep;
+} LL_ALIGN_POSTFIX(16);
+
+#endif // LL_LLDRIVERPARAM_H
diff --git a/indra/llappearance/lljointpickname.h b/indra/llappearance/lljointpickname.h
new file mode 100644
index 0000000000..1d41a761fc
--- /dev/null
+++ b/indra/llappearance/lljointpickname.h
@@ -0,0 +1,49 @@
+/**
+ * @file lljointpickname.h
+ * @brief Defines OpenGL seleciton stack names
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#ifndef LL_LLJOINTPICKNAME_H
+#define LL_LLJOINTPICKNAME_H
+
+class LLAvatarJointMesh;
+
+// Sets the OpenGL selection stack name that is pushed and popped
+// with this joint state. The default value indicates that no name
+// should be pushed/popped.
+enum LLJointPickName
+{
+ PN_DEFAULT = -1,
+ PN_0 = 0,
+ PN_1 = 1,
+ PN_2 = 2,
+ PN_3 = 3,
+ PN_4 = 4,
+ PN_5 = 5
+};
+
+typedef std::vector<LLAvatarJointMesh*> avatar_joint_mesh_list_t;
+
+#endif // LL_LLJOINTPICKNAME_H
diff --git a/indra/llappearance/lllocaltextureobject.cpp b/indra/llappearance/lllocaltextureobject.cpp
new file mode 100644
index 0000000000..7e36a06797
--- /dev/null
+++ b/indra/llappearance/lllocaltextureobject.cpp
@@ -0,0 +1,213 @@
+/**
+ * @file lllocaltextureobject.cpp
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lllocaltextureobject.h"
+
+#include "llimage.h"
+#include "llrender.h"
+#include "lltexlayer.h"
+#include "llgltexture.h"
+#include "lluuid.h"
+#include "llwearable.h"
+
+
+LLLocalTextureObject::LLLocalTextureObject() :
+ mIsBakedReady(FALSE),
+ mDiscard(MAX_DISCARD_LEVEL+1)
+{
+ mImage = NULL;
+}
+
+LLLocalTextureObject::LLLocalTextureObject(LLGLTexture* image, const LLUUID& id) :
+ mIsBakedReady(FALSE),
+ mDiscard(MAX_DISCARD_LEVEL+1)
+{
+ mImage = image;
+ gGL.getTexUnit(0)->bind(mImage);
+ mID = id;
+}
+
+LLLocalTextureObject::LLLocalTextureObject(const LLLocalTextureObject& lto) :
+ mImage(lto.mImage),
+ mID(lto.mID),
+ mIsBakedReady(lto.mIsBakedReady),
+ mDiscard(lto.mDiscard)
+{
+ U32 num_layers = lto.getNumTexLayers();
+ mTexLayers.reserve(num_layers);
+ for (U32 index = 0; index < num_layers; index++)
+ {
+ LLTexLayer* original_layer = lto.getTexLayer(index);
+ if (!original_layer)
+ {
+ llerrs << "could not clone Local Texture Object: unable to extract texlayer!" << llendl;
+ continue;
+ }
+
+ LLTexLayer* new_layer = new LLTexLayer(*original_layer);
+ new_layer->setLTO(this);
+ mTexLayers.push_back(new_layer);
+ }
+}
+
+LLLocalTextureObject::~LLLocalTextureObject()
+{
+}
+
+LLGLTexture* LLLocalTextureObject::getImage() const
+{
+ return mImage;
+}
+
+LLTexLayer* LLLocalTextureObject::getTexLayer(U32 index) const
+{
+ if (index >= getNumTexLayers())
+ {
+ return NULL;
+ }
+
+ return mTexLayers[index];
+}
+
+LLTexLayer* LLLocalTextureObject::getTexLayer(const std::string &name)
+{
+ for( tex_layer_vec_t::iterator iter = mTexLayers.begin(); iter != mTexLayers.end(); iter++)
+ {
+ LLTexLayer *layer = *iter;
+ if (layer->getName().compare(name) == 0)
+ {
+ return layer;
+ }
+ }
+
+ return NULL;
+}
+
+U32 LLLocalTextureObject::getNumTexLayers() const
+{
+ return mTexLayers.size();
+}
+
+LLUUID LLLocalTextureObject::getID() const
+{
+ return mID;
+}
+
+S32 LLLocalTextureObject::getDiscard() const
+{
+ return mDiscard;
+}
+
+BOOL LLLocalTextureObject::getBakedReady() const
+{
+ return mIsBakedReady;
+}
+
+void LLLocalTextureObject::setImage(LLGLTexture* new_image)
+{
+ mImage = new_image;
+}
+
+BOOL LLLocalTextureObject::setTexLayer(LLTexLayer *new_tex_layer, U32 index)
+{
+ if (index >= getNumTexLayers() )
+ {
+ return FALSE;
+ }
+
+ if (new_tex_layer == NULL)
+ {
+ return removeTexLayer(index);
+ }
+
+ LLTexLayer *layer = new LLTexLayer(*new_tex_layer);
+ layer->setLTO(this);
+
+ if (mTexLayers[index])
+ {
+ delete mTexLayers[index];
+ }
+ mTexLayers[index] = layer;
+
+ return TRUE;
+}
+
+BOOL LLLocalTextureObject::addTexLayer(LLTexLayer *new_tex_layer, LLWearable *wearable)
+{
+ if (new_tex_layer == NULL)
+ {
+ return FALSE;
+ }
+
+ LLTexLayer *layer = new LLTexLayer(*new_tex_layer, wearable);
+ layer->setLTO(this);
+ mTexLayers.push_back(layer);
+ return TRUE;
+}
+
+BOOL LLLocalTextureObject::addTexLayer(LLTexLayerTemplate *new_tex_layer, LLWearable *wearable)
+{
+ if (new_tex_layer == NULL)
+ {
+ return FALSE;
+ }
+
+ LLTexLayer *layer = new LLTexLayer(*new_tex_layer, this, wearable);
+ layer->setLTO(this);
+ mTexLayers.push_back(layer);
+ return TRUE;
+}
+
+BOOL LLLocalTextureObject::removeTexLayer(U32 index)
+{
+ if (index >= getNumTexLayers())
+ {
+ return FALSE;
+ }
+ tex_layer_vec_t::iterator iter = mTexLayers.begin();
+ iter += index;
+
+ delete *iter;
+ mTexLayers.erase(iter);
+ return TRUE;
+}
+
+void LLLocalTextureObject::setID(LLUUID new_id)
+{
+ mID = new_id;
+}
+
+void LLLocalTextureObject::setDiscard(S32 new_discard)
+{
+ mDiscard = new_discard;
+}
+
+void LLLocalTextureObject::setBakedReady(BOOL ready)
+{
+ mIsBakedReady = ready;
+}
+
diff --git a/indra/llappearance/lllocaltextureobject.h b/indra/llappearance/lllocaltextureobject.h
new file mode 100644
index 0000000000..9b9f41fd19
--- /dev/null
+++ b/indra/llappearance/lllocaltextureobject.h
@@ -0,0 +1,86 @@
+/**
+ * @file lllocaltextureobject.h
+ * @brief LLLocalTextureObject class header file
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LOCALTEXTUREOBJECT_H
+#define LL_LOCALTEXTUREOBJECT_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "llpointer.h"
+#include "llgltexture.h"
+
+class LLTexLayer;
+class LLTexLayerTemplate;
+class LLWearable;
+
+// Stores all relevant information for a single texture
+// assumed to have ownership of all objects referred to -
+// will delete objects when being replaced or if object is destroyed.
+class LLLocalTextureObject
+{
+public:
+ LLLocalTextureObject();
+ LLLocalTextureObject(LLGLTexture* image, const LLUUID& id);
+ LLLocalTextureObject(const LLLocalTextureObject& lto);
+ ~LLLocalTextureObject();
+
+ LLGLTexture* getImage() const;
+ LLTexLayer* getTexLayer(U32 index) const;
+ LLTexLayer* getTexLayer(const std::string &name);
+ U32 getNumTexLayers() const;
+ LLUUID getID() const;
+ S32 getDiscard() const;
+ BOOL getBakedReady() const;
+
+ void setImage(LLGLTexture* new_image);
+ BOOL setTexLayer(LLTexLayer *new_tex_layer, U32 index);
+ BOOL addTexLayer(LLTexLayer *new_tex_layer, LLWearable *wearable);
+ BOOL addTexLayer(LLTexLayerTemplate *new_tex_layer, LLWearable *wearable);
+ BOOL removeTexLayer(U32 index);
+
+ void setID(LLUUID new_id);
+ void setDiscard(S32 new_discard);
+ void setBakedReady(BOOL ready);
+
+protected:
+
+private:
+
+ LLPointer<LLGLTexture> mImage;
+ // NOTE: LLLocalTextureObject should be the exclusive owner of mTexEntry and mTexLayer
+ // using shared pointers here only for smart assignment & cleanup
+ // do NOT create new shared pointers to these objects, or keep pointers to them around
+ typedef std::vector<LLTexLayer*> tex_layer_vec_t;
+ tex_layer_vec_t mTexLayers;
+
+ LLUUID mID;
+
+ BOOL mIsBakedReady;
+ S32 mDiscard;
+};
+
+ #endif // LL_LOCALTEXTUREOBJECT_H
+
diff --git a/indra/llappearance/llpolymesh.cpp b/indra/llappearance/llpolymesh.cpp
new file mode 100644
index 0000000000..a01457246e
--- /dev/null
+++ b/indra/llappearance/llpolymesh.cpp
@@ -0,0 +1,1048 @@
+/**
+ * @file llpolymesh.cpp
+ * @brief Implementation of LLPolyMesh class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+#include "llpolymesh.h"
+#include "llfasttimer.h"
+#include "llmemory.h"
+
+//#include "llviewercontrol.h"
+#include "llxmltree.h"
+#include "llavatarappearance.h"
+#include "llwearable.h"
+#include "lldir.h"
+#include "llvolume.h"
+#include "llendianswizzle.h"
+
+
+#define HEADER_ASCII "Linden Mesh 1.0"
+#define HEADER_BINARY "Linden Binary Mesh 1.0"
+
+//extern LLControlGroup gSavedSettings; // read only
+
+LLPolyMorphData *clone_morph_param_duplicate(const LLPolyMorphData *src_data,
+ const std::string &name);
+LLPolyMorphData *clone_morph_param_direction(const LLPolyMorphData *src_data,
+ const LLVector3 &direction,
+ const std::string &name);
+LLPolyMorphData *clone_morph_param_cleavage(const LLPolyMorphData *src_data,
+ F32 scale,
+ const std::string &name);
+
+//-----------------------------------------------------------------------------
+// Global table of loaded LLPolyMeshes
+//-----------------------------------------------------------------------------
+LLPolyMesh::LLPolyMeshSharedDataTable LLPolyMesh::sGlobalSharedMeshList;
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData()
+//-----------------------------------------------------------------------------
+LLPolyMeshSharedData::LLPolyMeshSharedData()
+{
+ mNumVertices = 0;
+ mBaseCoords = NULL;
+ mBaseNormals = NULL;
+ mBaseBinormals = NULL;
+ mTexCoords = NULL;
+ mDetailTexCoords = NULL;
+ mWeights = NULL;
+ mHasWeights = FALSE;
+ mHasDetailTexCoords = FALSE;
+
+ mNumFaces = 0;
+ mFaces = NULL;
+
+ mNumJointNames = 0;
+ mJointNames = NULL;
+
+ mTriangleIndices = NULL;
+ mNumTriangleIndices = 0;
+
+ mReferenceData = NULL;
+
+ mLastIndexOffset = -1;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMeshSharedData()
+//-----------------------------------------------------------------------------
+LLPolyMeshSharedData::~LLPolyMeshSharedData()
+{
+ freeMeshData();
+ for_each(mMorphData.begin(), mMorphData.end(), DeletePointer());
+ mMorphData.clear();
+}
+
+//-----------------------------------------------------------------------------
+// setupLOD()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::setupLOD(LLPolyMeshSharedData* reference_data)
+{
+ mReferenceData = reference_data;
+
+ if (reference_data)
+ {
+ mBaseCoords = reference_data->mBaseCoords;
+ mBaseNormals = reference_data->mBaseNormals;
+ mBaseBinormals = reference_data->mBaseBinormals;
+ mTexCoords = reference_data->mTexCoords;
+ mDetailTexCoords = reference_data->mDetailTexCoords;
+ mWeights = reference_data->mWeights;
+ mHasWeights = reference_data->mHasWeights;
+ mHasDetailTexCoords = reference_data->mHasDetailTexCoords;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::freeMeshData()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::freeMeshData()
+{
+ if (!mReferenceData)
+ {
+ mNumVertices = 0;
+
+ ll_aligned_free_16(mBaseCoords);
+ mBaseCoords = NULL;
+
+ ll_aligned_free_16(mBaseNormals);
+ mBaseNormals = NULL;
+
+ ll_aligned_free_16(mBaseBinormals);
+ mBaseBinormals = NULL;
+
+ ll_aligned_free_16(mTexCoords);
+ mTexCoords = NULL;
+
+ ll_aligned_free_16(mDetailTexCoords);
+ mDetailTexCoords = NULL;
+
+ ll_aligned_free_16(mWeights);
+ mWeights = NULL;
+ }
+
+ mNumFaces = 0;
+ delete [] mFaces;
+ mFaces = NULL;
+
+ mNumJointNames = 0;
+ delete [] mJointNames;
+ mJointNames = NULL;
+
+ delete [] mTriangleIndices;
+ mTriangleIndices = NULL;
+
+// mVertFaceMap.deleteAllData();
+}
+
+// compate_int is used by the qsort function to sort the index array
+int compare_int(const void *a, const void *b);
+
+//-----------------------------------------------------------------------------
+// genIndices()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::genIndices(S32 index_offset)
+{
+ if (index_offset == mLastIndexOffset)
+ {
+ return;
+ }
+
+ delete []mTriangleIndices;
+ mTriangleIndices = new U32[mNumTriangleIndices];
+
+ S32 cur_index = 0;
+ for (S32 i = 0; i < mNumFaces; i++)
+ {
+ mTriangleIndices[cur_index] = mFaces[i][0] + index_offset;
+ cur_index++;
+ mTriangleIndices[cur_index] = mFaces[i][1] + index_offset;
+ cur_index++;
+ mTriangleIndices[cur_index] = mFaces[i][2] + index_offset;
+ cur_index++;
+ }
+
+ mLastIndexOffset = index_offset;
+}
+
+//--------------------------------------------------------------------
+// LLPolyMeshSharedData::getNumKB()
+//--------------------------------------------------------------------
+U32 LLPolyMeshSharedData::getNumKB()
+{
+ U32 num_kb = sizeof(LLPolyMesh);
+
+ if (!isLOD())
+ {
+ num_kb += mNumVertices *
+ ( sizeof(LLVector3) + // coords
+ sizeof(LLVector3) + // normals
+ sizeof(LLVector2) ); // texCoords
+ }
+
+ if (mHasDetailTexCoords && !isLOD())
+ {
+ num_kb += mNumVertices * sizeof(LLVector2); // detailTexCoords
+ }
+
+ if (mHasWeights && !isLOD())
+ {
+ num_kb += mNumVertices * sizeof(float); // weights
+ }
+
+ num_kb += mNumFaces * sizeof(LLPolyFace); // faces
+
+ num_kb /= 1024;
+ return num_kb;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateVertexData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateVertexData( U32 numVertices )
+{
+ U32 i;
+ mBaseCoords = (LLVector4a*) ll_aligned_malloc_16(numVertices*sizeof(LLVector4a));
+ mBaseNormals = (LLVector4a*) ll_aligned_malloc_16(numVertices*sizeof(LLVector4a));
+ mBaseBinormals = (LLVector4a*) ll_aligned_malloc_16(numVertices*sizeof(LLVector4a));
+ mTexCoords = (LLVector2*) ll_aligned_malloc_16(numVertices*sizeof(LLVector2));
+ mDetailTexCoords = (LLVector2*) ll_aligned_malloc_16(numVertices*sizeof(LLVector2));
+ mWeights = (F32*) ll_aligned_malloc_16(numVertices*sizeof(F32));
+ for (i = 0; i < numVertices; i++)
+ {
+ mBaseCoords[i].clear();
+ mBaseNormals[i].clear();
+ mBaseBinormals[i].clear();
+ mTexCoords[i].clear();
+ mWeights[i] = 0.f;
+ }
+ mNumVertices = numVertices;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateFaceData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateFaceData( U32 numFaces )
+{
+ mFaces = new LLPolyFace[ numFaces ];
+ mNumFaces = numFaces;
+ mNumTriangleIndices = mNumFaces * 3;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateJointNames()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateJointNames( U32 numJointNames )
+{
+ mJointNames = new std::string[ numJointNames ];
+ mNumJointNames = numJointNames;
+ return TRUE;
+}
+
+//--------------------------------------------------------------------
+// LLPolyMeshSharedData::loadMesh()
+//--------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::loadMesh( const std::string& fileName )
+{
+ //-------------------------------------------------------------------------
+ // Open the file
+ //-------------------------------------------------------------------------
+ if(fileName.empty())
+ {
+ llerrs << "Filename is Empty!" << llendl;
+ return FALSE;
+ }
+ LLFILE* fp = LLFile::fopen(fileName, "rb"); /*Flawfinder: ignore*/
+ if (!fp)
+ {
+ llerrs << "can't open: " << fileName << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // Read a chunk
+ //-------------------------------------------------------------------------
+ char header[128]; /*Flawfinder: ignore*/
+ if (fread(header, sizeof(char), 128, fp) != 128)
+ {
+ llwarns << "Short read" << llendl;
+ }
+
+ //-------------------------------------------------------------------------
+ // Check for proper binary header
+ //-------------------------------------------------------------------------
+ BOOL status = FALSE;
+ if ( strncmp(header, HEADER_BINARY, strlen(HEADER_BINARY)) == 0 ) /*Flawfinder: ignore*/
+ {
+ lldebugs << "Loading " << fileName << llendl;
+
+ //----------------------------------------------------------------
+ // File Header (seek past it)
+ //----------------------------------------------------------------
+ fseek(fp, 24, SEEK_SET);
+
+ //----------------------------------------------------------------
+ // HasWeights
+ //----------------------------------------------------------------
+ U8 hasWeights;
+ size_t numRead = fread(&hasWeights, sizeof(U8), 1, fp);
+ if (numRead != 1)
+ {
+ llerrs << "can't read HasWeights flag from " << fileName << llendl;
+ return FALSE;
+ }
+ if (!isLOD())
+ {
+ mHasWeights = (hasWeights==0) ? FALSE : TRUE;
+ }
+
+ //----------------------------------------------------------------
+ // HasDetailTexCoords
+ //----------------------------------------------------------------
+ U8 hasDetailTexCoords;
+ numRead = fread(&hasDetailTexCoords, sizeof(U8), 1, fp);
+ if (numRead != 1)
+ {
+ llerrs << "can't read HasDetailTexCoords flag from " << fileName << llendl;
+ return FALSE;
+ }
+
+ //----------------------------------------------------------------
+ // Position
+ //----------------------------------------------------------------
+ LLVector3 position;
+ numRead = fread(position.mV, sizeof(float), 3, fp);
+ llendianswizzle(position.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Position from " << fileName << llendl;
+ return FALSE;
+ }
+ setPosition( position );
+
+ //----------------------------------------------------------------
+ // Rotation
+ //----------------------------------------------------------------
+ LLVector3 rotationAngles;
+ numRead = fread(rotationAngles.mV, sizeof(float), 3, fp);
+ llendianswizzle(rotationAngles.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read RotationAngles from " << fileName << llendl;
+ return FALSE;
+ }
+
+ U8 rotationOrder;
+ numRead = fread(&rotationOrder, sizeof(U8), 1, fp);
+
+ if (numRead != 1)
+ {
+ llerrs << "can't read RotationOrder from " << fileName << llendl;
+ return FALSE;
+ }
+
+ rotationOrder = 0;
+
+ setRotation( mayaQ( rotationAngles.mV[0],
+ rotationAngles.mV[1],
+ rotationAngles.mV[2],
+ (LLQuaternion::Order)rotationOrder ) );
+
+ //----------------------------------------------------------------
+ // Scale
+ //----------------------------------------------------------------
+ LLVector3 scale;
+ numRead = fread(scale.mV, sizeof(float), 3, fp);
+ llendianswizzle(scale.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Scale from " << fileName << llendl;
+ return FALSE;
+ }
+ setScale( scale );
+
+ //-------------------------------------------------------------------------
+ // Release any existing mesh geometry
+ //-------------------------------------------------------------------------
+ freeMeshData();
+
+ U16 numVertices = 0;
+
+ //----------------------------------------------------------------
+ // NumVertices
+ //----------------------------------------------------------------
+ if (!isLOD())
+ {
+ numRead = fread(&numVertices, sizeof(U16), 1, fp);
+ llendianswizzle(&numVertices, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumVertices from " << fileName << llendl;
+ return FALSE;
+ }
+
+ allocateVertexData( numVertices );
+
+ for (U16 i = 0; i < numVertices; ++i)
+ {
+ //----------------------------------------------------------------
+ // Coords
+ //----------------------------------------------------------------
+ numRead = fread(&mBaseCoords[i], sizeof(float), 3, fp);
+ llendianswizzle(&mBaseCoords[i], sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Coordinates from " << fileName << llendl;
+ return FALSE;
+ }
+ }
+
+ for (U16 i = 0; i < numVertices; ++i)
+ {
+ //----------------------------------------------------------------
+ // Normals
+ //----------------------------------------------------------------
+ numRead = fread(&mBaseNormals[i], sizeof(float), 3, fp);
+ llendianswizzle(&mBaseNormals[i], sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << " can't read Normals from " << fileName << llendl;
+ return FALSE;
+ }
+ }
+
+ for (U16 i = 0; i < numVertices; ++i)
+ {
+ //----------------------------------------------------------------
+ // Binormals
+ //----------------------------------------------------------------
+ numRead = fread(&mBaseBinormals[i], sizeof(float), 3, fp);
+ llendianswizzle(&mBaseBinormals[i], sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << " can't read Binormals from " << fileName << llendl;
+ return FALSE;
+ }
+ }
+
+ //----------------------------------------------------------------
+ // TexCoords
+ //----------------------------------------------------------------
+ numRead = fread(mTexCoords, 2*sizeof(float), numVertices, fp);
+ llendianswizzle(mTexCoords, sizeof(float), 2*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read TexCoords from " << fileName << llendl;
+ return FALSE;
+ }
+
+ //----------------------------------------------------------------
+ // DetailTexCoords
+ //----------------------------------------------------------------
+ if (mHasDetailTexCoords)
+ {
+ numRead = fread(mDetailTexCoords, 2*sizeof(float), numVertices, fp);
+ llendianswizzle(mDetailTexCoords, sizeof(float), 2*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read DetailTexCoords from " << fileName << llendl;
+ return FALSE;
+ }
+ }
+
+ //----------------------------------------------------------------
+ // Weights
+ //----------------------------------------------------------------
+ if (mHasWeights)
+ {
+ numRead = fread(mWeights, sizeof(float), numVertices, fp);
+ llendianswizzle(mWeights, sizeof(float), numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read Weights from " << fileName << llendl;
+ return FALSE;
+ }
+ }
+ }
+
+ //----------------------------------------------------------------
+ // NumFaces
+ //----------------------------------------------------------------
+ U16 numFaces;
+ numRead = fread(&numFaces, sizeof(U16), 1, fp);
+ llendianswizzle(&numFaces, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumFaces from " << fileName << llendl;
+ return FALSE;
+ }
+ allocateFaceData( numFaces );
+
+
+ //----------------------------------------------------------------
+ // Faces
+ //----------------------------------------------------------------
+ U32 i;
+ U32 numTris = 0;
+ for (i = 0; i < numFaces; i++)
+ {
+ S16 face[3];
+ numRead = fread(face, sizeof(U16), 3, fp);
+ llendianswizzle(face, sizeof(U16), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Face[" << i << "] from " << fileName << llendl;
+ return FALSE;
+ }
+ if (mReferenceData)
+ {
+ llassert(face[0] < mReferenceData->mNumVertices);
+ llassert(face[1] < mReferenceData->mNumVertices);
+ llassert(face[2] < mReferenceData->mNumVertices);
+ }
+
+ if (isLOD())
+ {
+ // store largest index in case of LODs
+ for (S32 j = 0; j < 3; j++)
+ {
+ if (face[j] > mNumVertices - 1)
+ {
+ mNumVertices = face[j] + 1;
+ }
+ }
+ }
+ mFaces[i][0] = face[0];
+ mFaces[i][1] = face[1];
+ mFaces[i][2] = face[2];
+
+// S32 j;
+// for(j = 0; j < 3; j++)
+// {
+// LLDynamicArray<S32> *face_list = mVertFaceMap.getIfThere(face[j]);
+// if (!face_list)
+// {
+// face_list = new LLDynamicArray<S32>;
+// mVertFaceMap.addData(face[j], face_list);
+// }
+// face_list->put(i);
+// }
+
+ numTris++;
+ }
+
+ lldebugs << "verts: " << numVertices
+ << ", faces: " << numFaces
+ << ", tris: " << numTris
+ << llendl;
+
+ //----------------------------------------------------------------
+ // NumSkinJoints
+ //----------------------------------------------------------------
+ if (!isLOD())
+ {
+ U16 numSkinJoints = 0;
+ if ( mHasWeights )
+ {
+ numRead = fread(&numSkinJoints, sizeof(U16), 1, fp);
+ llendianswizzle(&numSkinJoints, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumSkinJoints from " << fileName << llendl;
+ return FALSE;
+ }
+ allocateJointNames( numSkinJoints );
+ }
+
+ //----------------------------------------------------------------
+ // SkinJoints
+ //----------------------------------------------------------------
+ for (i=0; i < numSkinJoints; i++)
+ {
+ char jointName[64+1];
+ numRead = fread(jointName, sizeof(jointName)-1, 1, fp);
+ jointName[sizeof(jointName)-1] = '\0'; // ensure nul-termination
+ if (numRead != 1)
+ {
+ llerrs << "can't read Skin[" << i << "].Name from " << fileName << llendl;
+ return FALSE;
+ }
+
+ std::string *jn = &mJointNames[i];
+ *jn = jointName;
+ }
+
+ //-------------------------------------------------------------------------
+ // look for morph section
+ //-------------------------------------------------------------------------
+ char morphName[64+1];
+ morphName[sizeof(morphName)-1] = '\0'; // ensure nul-termination
+ while(fread(&morphName, sizeof(char), 64, fp) == 64)
+ {
+ if (!strcmp(morphName, "End Morphs"))
+ {
+ // we reached the end of the morphs
+ break;
+ }
+ LLPolyMorphData* morph_data = new LLPolyMorphData(std::string(morphName));
+
+ BOOL result = morph_data->loadBinary(fp, this);
+
+ if (!result)
+ {
+ delete morph_data;
+ continue;
+ }
+
+ mMorphData.insert(morph_data);
+
+ if (!strcmp(morphName, "Breast_Female_Cleavage"))
+ {
+ mMorphData.insert(clone_morph_param_cleavage(morph_data,
+ .75f,
+ "Breast_Physics_LeftRight_Driven"));
+ }
+
+ if (!strcmp(morphName, "Breast_Female_Cleavage"))
+ {
+ mMorphData.insert(clone_morph_param_duplicate(morph_data,
+ "Breast_Physics_InOut_Driven"));
+ }
+ if (!strcmp(morphName, "Breast_Gravity"))
+ {
+ mMorphData.insert(clone_morph_param_duplicate(morph_data,
+ "Breast_Physics_UpDown_Driven"));
+ }
+
+ if (!strcmp(morphName, "Big_Belly_Torso"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0,0.05f),
+ "Belly_Physics_Torso_UpDown_Driven"));
+ }
+
+ if (!strcmp(morphName, "Big_Belly_Legs"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0,0.05f),
+ "Belly_Physics_Legs_UpDown_Driven"));
+ }
+
+ if (!strcmp(morphName, "skirt_belly"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0,0.05f),
+ "Belly_Physics_Skirt_UpDown_Driven"));
+ }
+
+ if (!strcmp(morphName, "Small_Butt"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0,0.05f),
+ "Butt_Physics_UpDown_Driven"));
+ }
+ if (!strcmp(morphName, "Small_Butt"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0.03f,0),
+ "Butt_Physics_LeftRight_Driven"));
+ }
+ }
+
+ S32 numRemaps;
+ if (fread(&numRemaps, sizeof(S32), 1, fp) == 1)
+ {
+ llendianswizzle(&numRemaps, sizeof(S32), 1);
+ for (S32 i = 0; i < numRemaps; i++)
+ {
+ S32 remapSrc;
+ S32 remapDst;
+ if (fread(&remapSrc, sizeof(S32), 1, fp) != 1)
+ {
+ llerrs << "can't read source vertex in vertex remap data" << llendl;
+ break;
+ }
+ if (fread(&remapDst, sizeof(S32), 1, fp) != 1)
+ {
+ llerrs << "can't read destination vertex in vertex remap data" << llendl;
+ break;
+ }
+ llendianswizzle(&remapSrc, sizeof(S32), 1);
+ llendianswizzle(&remapDst, sizeof(S32), 1);
+
+ mSharedVerts[remapSrc] = remapDst;
+ }
+ }
+ }
+
+ status = TRUE;
+ }
+ else
+ {
+ llerrs << "invalid mesh file header: " << fileName << llendl;
+ status = FALSE;
+ }
+
+ if (0 == mNumJointNames)
+ {
+ allocateJointNames(1);
+ }
+
+ fclose( fp );
+
+ return status;
+}
+
+//-----------------------------------------------------------------------------
+// getSharedVert()
+//-----------------------------------------------------------------------------
+const S32 *LLPolyMeshSharedData::getSharedVert(S32 vert)
+{
+ if (mSharedVerts.count(vert) > 0)
+ {
+ return &mSharedVerts[vert];
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getUV()
+//-----------------------------------------------------------------------------
+const LLVector2 &LLPolyMeshSharedData::getUVs(U32 index)
+{
+ // TODO: convert all index variables to S32
+ llassert((S32)index < mNumVertices);
+
+ return mTexCoords[index];
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh::LLPolyMesh(LLPolyMeshSharedData *shared_data, LLPolyMesh *reference_mesh)
+{
+ llassert(shared_data);
+
+ mSharedData = shared_data;
+ mReferenceMesh = reference_mesh;
+ mAvatarp = NULL;
+ mVertexData = NULL;
+
+ mCurVertexCount = 0;
+ mFaceIndexCount = 0;
+ mFaceIndexOffset = 0;
+ mFaceVertexCount = 0;
+ mFaceVertexOffset = 0;
+
+ if (shared_data->isLOD() && reference_mesh)
+ {
+ mCoords = reference_mesh->mCoords;
+ mNormals = reference_mesh->mNormals;
+ mScaledNormals = reference_mesh->mScaledNormals;
+ mBinormals = reference_mesh->mBinormals;
+ mScaledBinormals = reference_mesh->mScaledBinormals;
+ mTexCoords = reference_mesh->mTexCoords;
+ mClothingWeights = reference_mesh->mClothingWeights;
+ }
+ else
+ {
+ // Allocate memory without initializing every vector
+ // NOTE: This makes asusmptions about the size of LLVector[234]
+ S32 nverts = mSharedData->mNumVertices;
+ //make sure it's an even number of verts for alignment
+ nverts += nverts%2;
+ S32 nfloats = nverts * (
+ 4 + //coords
+ 4 + //normals
+ 4 + //weights
+ 2 + //coords
+ 4 + //scaled normals
+ 4 + //binormals
+ 4); //scaled binormals
+
+ //use 16 byte aligned vertex data to make LLPolyMesh SSE friendly
+ mVertexData = (F32*) ll_aligned_malloc_16(nfloats*4);
+ S32 offset = 0;
+ mCoords = (LLVector4a*)(mVertexData + offset); offset += 4*nverts;
+ mNormals = (LLVector4a*)(mVertexData + offset); offset += 4*nverts;
+ mClothingWeights = (LLVector4a*)(mVertexData + offset); offset += 4*nverts;
+ mTexCoords = (LLVector2*)(mVertexData + offset); offset += 2*nverts;
+ mScaledNormals = (LLVector4a*)(mVertexData + offset); offset += 4*nverts;
+ mBinormals = (LLVector4a*)(mVertexData + offset); offset += 4*nverts;
+ mScaledBinormals = (LLVector4a*)(mVertexData + offset); offset += 4*nverts;
+ initializeForMorph();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh::~LLPolyMesh()
+{
+ S32 i;
+ for (i = 0; i < mJointRenderData.count(); i++)
+ {
+ delete mJointRenderData[i];
+ mJointRenderData[i] = NULL;
+ }
+
+ ll_aligned_free_16(mVertexData);
+
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh::getMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh *LLPolyMesh::getMesh(const std::string &name, LLPolyMesh* reference_mesh)
+{
+ //-------------------------------------------------------------------------
+ // search for an existing mesh by this name
+ //-------------------------------------------------------------------------
+ LLPolyMeshSharedData* meshSharedData = get_if_there(sGlobalSharedMeshList, name, (LLPolyMeshSharedData*)NULL);
+ if (meshSharedData)
+ {
+// llinfos << "Polymesh " << name << " found in global mesh table." << llendl;
+ LLPolyMesh *poly_mesh = new LLPolyMesh(meshSharedData, reference_mesh);
+ return poly_mesh;
+ }
+
+ //-------------------------------------------------------------------------
+ // if not found, create a new one, add it to the list
+ //-------------------------------------------------------------------------
+ std::string full_path;
+ full_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,name);
+
+ LLPolyMeshSharedData *mesh_data = new LLPolyMeshSharedData();
+ if (reference_mesh)
+ {
+ mesh_data->setupLOD(reference_mesh->getSharedData());
+ }
+ if ( ! mesh_data->loadMesh( full_path ) )
+ {
+ delete mesh_data;
+ return NULL;
+ }
+
+ LLPolyMesh *poly_mesh = new LLPolyMesh(mesh_data, reference_mesh);
+
+// llinfos << "Polymesh " << name << " added to global mesh table." << llendl;
+ sGlobalSharedMeshList[name] = poly_mesh->mSharedData;
+
+ return poly_mesh;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh::freeAllMeshes()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::freeAllMeshes()
+{
+ // delete each item in the global lists
+ for_each(sGlobalSharedMeshList.begin(), sGlobalSharedMeshList.end(), DeletePairedPointer());
+ sGlobalSharedMeshList.clear();
+}
+
+LLPolyMeshSharedData *LLPolyMesh::getSharedData() const
+{
+ return mSharedData;
+}
+
+
+//--------------------------------------------------------------------
+// LLPolyMesh::dumpDiagInfo()
+//--------------------------------------------------------------------
+void LLPolyMesh::dumpDiagInfo()
+{
+ // keep track of totals
+ U32 total_verts = 0;
+ U32 total_faces = 0;
+ U32 total_kb = 0;
+
+ std::string buf;
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ llinfos << " Global PolyMesh Table (DEBUG only)" << llendl;
+ llinfos << " Verts Faces Mem(KB) Name" << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+
+ // print each loaded mesh, and it's memory usage
+ for(LLPolyMeshSharedDataTable::iterator iter = sGlobalSharedMeshList.begin();
+ iter != sGlobalSharedMeshList.end(); ++iter)
+ {
+ const std::string& mesh_name = iter->first;
+ LLPolyMeshSharedData* mesh = iter->second;
+
+ S32 num_verts = mesh->mNumVertices;
+ S32 num_faces = mesh->mNumFaces;
+ U32 num_kb = mesh->getNumKB();
+
+ buf = llformat("%8d %8d %8d %s", num_verts, num_faces, num_kb, mesh_name.c_str());
+ llinfos << buf << llendl;
+
+ total_verts += num_verts;
+ total_faces += num_faces;
+ total_kb += num_kb;
+ }
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ buf = llformat("%8d %8d %8d TOTAL", total_verts, total_faces, total_kb );
+ llinfos << buf << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableCoords()
+//-----------------------------------------------------------------------------
+LLVector4a *LLPolyMesh::getWritableCoords()
+{
+ return mCoords;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableNormals()
+//-----------------------------------------------------------------------------
+LLVector4a *LLPolyMesh::getWritableNormals()
+{
+ return mNormals;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableBinormals()
+//-----------------------------------------------------------------------------
+LLVector4a *LLPolyMesh::getWritableBinormals()
+{
+ return mBinormals;
+}
+
+
+//-----------------------------------------------------------------------------
+// getWritableClothingWeights()
+//-----------------------------------------------------------------------------
+LLVector4a *LLPolyMesh::getWritableClothingWeights()
+{
+ return mClothingWeights;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableTexCoords()
+//-----------------------------------------------------------------------------
+LLVector2 *LLPolyMesh::getWritableTexCoords()
+{
+ return mTexCoords;
+}
+
+//-----------------------------------------------------------------------------
+// getScaledNormals()
+//-----------------------------------------------------------------------------
+LLVector4a *LLPolyMesh::getScaledNormals()
+{
+ return mScaledNormals;
+}
+
+//-----------------------------------------------------------------------------
+// getScaledBinormals()
+//-----------------------------------------------------------------------------
+LLVector4a *LLPolyMesh::getScaledBinormals()
+{
+ return mScaledBinormals;
+}
+
+
+//-----------------------------------------------------------------------------
+// initializeForMorph()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::initializeForMorph()
+{
+ LLVector4a::memcpyNonAliased16((F32*) mCoords, (F32*) mSharedData->mBaseCoords, sizeof(LLVector4a) * mSharedData->mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mScaledNormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mBinormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mScaledBinormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) mSharedData->mTexCoords, sizeof(LLVector2) * (mSharedData->mNumVertices + mSharedData->mNumVertices%2));
+
+ for (U32 i = 0; i < mSharedData->mNumVertices; ++i)
+ {
+ mClothingWeights[i].clear();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData* LLPolyMesh::getMorphData(const std::string& morph_name)
+{
+ if (!mSharedData)
+ return NULL;
+ for (LLPolyMeshSharedData::morphdata_list_t::iterator iter = mSharedData->mMorphData.begin();
+ iter != mSharedData->mMorphData.end(); ++iter)
+ {
+ LLPolyMorphData *morph_data = *iter;
+ if (morph_data->getName() == morph_name)
+ {
+ return morph_data;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// removeMorphData()
+//-----------------------------------------------------------------------------
+// // erasing but not deleting seems bad, but fortunately we don't actually use this...
+// void LLPolyMesh::removeMorphData(LLPolyMorphData *morph_target)
+// {
+// if (!mSharedData)
+// return;
+// mSharedData->mMorphData.erase(morph_target);
+// }
+
+//-----------------------------------------------------------------------------
+// deleteAllMorphData()
+//-----------------------------------------------------------------------------
+// void LLPolyMesh::deleteAllMorphData()
+// {
+// if (!mSharedData)
+// return;
+
+// for_each(mSharedData->mMorphData.begin(), mSharedData->mMorphData.end(), DeletePointer());
+// mSharedData->mMorphData.clear();
+// }
+
+//-----------------------------------------------------------------------------
+// getWritableWeights()
+//-----------------------------------------------------------------------------
+F32* LLPolyMesh::getWritableWeights() const
+{
+ return mSharedData->mWeights;
+}
+
+// End
diff --git a/indra/llappearance/llpolymesh.h b/indra/llappearance/llpolymesh.h
new file mode 100644
index 0000000000..ef1dfb1adb
--- /dev/null
+++ b/indra/llappearance/llpolymesh.h
@@ -0,0 +1,369 @@
+/**
+ * @file llpolymesh.h
+ * @brief Implementation of LLPolyMesh class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPOLYMESHINTERFACE_H
+#define LL_LLPOLYMESHINTERFACE_H
+
+#include <string>
+#include <map>
+#include "llstl.h"
+
+#include "v3math.h"
+#include "v2math.h"
+#include "llquaternion.h"
+#include "llpolymorph.h"
+#include "lljoint.h"
+//#include "lldarray.h"
+
+class LLSkinJoint;
+class LLAvatarAppearance;
+class LLWearable;
+
+//#define USE_STRIPS // Use tri-strips for rendering.
+
+//-----------------------------------------------------------------------------
+// LLPolyFace
+// A set of 4 vertex indices.
+// An LLPolyFace can represent either a triangle or quad.
+// If the last index is -1, it's a triangle.
+//-----------------------------------------------------------------------------
+typedef S32 LLPolyFace[3];
+
+//struct PrimitiveGroup;
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh
+// A polyhedra consisting of any number of triangles and quads.
+// All instances contain a set of faces, and optionally may include
+// faces grouped into named face sets.
+//-----------------------------------------------------------------------------
+class LLPolyMorphTarget;
+
+class LLPolyMeshSharedData
+{
+ friend class LLPolyMesh;
+private:
+ // transform data
+ LLVector3 mPosition;
+ LLQuaternion mRotation;
+ LLVector3 mScale;
+
+ // vertex data
+ S32 mNumVertices;
+ LLVector4a *mBaseCoords;
+ LLVector4a *mBaseNormals;
+ LLVector4a *mBaseBinormals;
+ LLVector2 *mTexCoords;
+ LLVector2 *mDetailTexCoords;
+ F32 *mWeights;
+
+ BOOL mHasWeights;
+ BOOL mHasDetailTexCoords;
+
+ // face data
+ S32 mNumFaces;
+ LLPolyFace *mFaces;
+
+ // face set data
+ U32 mNumJointNames;
+ std::string* mJointNames;
+
+ // morph targets
+ typedef std::set<LLPolyMorphData*> morphdata_list_t;
+ morphdata_list_t mMorphData;
+
+ std::map<S32, S32> mSharedVerts;
+
+ LLPolyMeshSharedData* mReferenceData;
+ S32 mLastIndexOffset;
+
+public:
+ // Temporarily...
+ // Triangle indices
+ U32 mNumTriangleIndices;
+ U32 *mTriangleIndices;
+
+public:
+ LLPolyMeshSharedData();
+ ~LLPolyMeshSharedData();
+
+private:
+ void setupLOD(LLPolyMeshSharedData* reference_data);
+
+ // Frees all mesh memory resources
+ void freeMeshData();
+
+ void setPosition( const LLVector3 &pos ) { mPosition = pos; }
+ void setRotation( const LLQuaternion &rot ) { mRotation = rot; }
+ void setScale( const LLVector3 &scale ) { mScale = scale; }
+
+ BOOL allocateVertexData( U32 numVertices );
+
+ BOOL allocateFaceData( U32 numFaces );
+
+ BOOL allocateJointNames( U32 numJointNames );
+
+ // Retrieve the number of KB of memory used by this instance
+ U32 getNumKB();
+
+ // Load mesh data from file
+ BOOL loadMesh( const std::string& fileName );
+
+public:
+ void genIndices(S32 offset);
+
+ const LLVector2 &getUVs(U32 index);
+
+ const S32 *getSharedVert(S32 vert);
+
+ BOOL isLOD() { return (mReferenceData != NULL); }
+};
+
+
+class LLJointRenderData
+{
+public:
+ LLJointRenderData(const LLMatrix4* world_matrix, LLSkinJoint* skin_joint) : mWorldMatrix(world_matrix), mSkinJoint(skin_joint) {}
+ ~LLJointRenderData(){}
+
+ const LLMatrix4* mWorldMatrix;
+ LLSkinJoint* mSkinJoint;
+};
+
+
+class LLPolyMesh
+{
+public:
+
+ // Constructor
+ LLPolyMesh(LLPolyMeshSharedData *shared_data, LLPolyMesh *reference_mesh);
+
+ // Destructor
+ ~LLPolyMesh();
+
+ // Requests a mesh by name.
+ // If the mesh already exists in the global mesh table, it is returned,
+ // otherwise it is loaded from file, added to the table, and returned.
+ static LLPolyMesh *getMesh( const std::string &name, LLPolyMesh* reference_mesh = NULL);
+
+ // Frees all loaded meshes.
+ // This should only be called once you know there are no outstanding
+ // references to these objects. Generally, upon exit of the application.
+ static void freeAllMeshes();
+
+ //--------------------------------------------------------------------
+ // Transform Data Access
+ //--------------------------------------------------------------------
+ // Get position
+ const LLVector3 &getPosition() {
+ llassert (mSharedData);
+ return mSharedData->mPosition;
+ }
+
+ // Get rotation
+ const LLQuaternion &getRotation() {
+ llassert (mSharedData);
+ return mSharedData->mRotation;
+ }
+
+ // Get scale
+ const LLVector3 &getScale() {
+ llassert (mSharedData);
+ return mSharedData->mScale;
+ }
+
+ //--------------------------------------------------------------------
+ // Vertex Data Access
+ //--------------------------------------------------------------------
+ // Get number of vertices
+ U32 getNumVertices() {
+ llassert (mSharedData);
+ return mSharedData->mNumVertices;
+ }
+
+ // Returns whether or not the mesh has detail texture coords
+ BOOL hasDetailTexCoords() {
+ llassert (mSharedData);
+ return mSharedData->mHasDetailTexCoords;
+ }
+
+ // Returns whether or not the mesh has vertex weights
+ BOOL hasWeights() const{
+ llassert (mSharedData);
+ return mSharedData->mHasWeights;
+ }
+
+ // Get coords
+ const LLVector4a *getCoords() const{
+ return mCoords;
+ }
+
+ // non const version
+ LLVector4a *getWritableCoords();
+
+ // Get normals
+ const LLVector4a *getNormals() const{
+ return mNormals;
+ }
+
+ // Get normals
+ const LLVector4a *getBinormals() const{
+ return mBinormals;
+ }
+
+ // Get base mesh normals
+ const LLVector4a *getBaseNormals() const{
+ llassert(mSharedData);
+ return mSharedData->mBaseNormals;
+ }
+
+ // Get base mesh normals
+ const LLVector4a *getBaseBinormals() const{
+ llassert(mSharedData);
+ return mSharedData->mBaseBinormals;
+ }
+
+ // intermediate morphed normals and output normals
+ LLVector4a *getWritableNormals();
+ LLVector4a *getScaledNormals();
+
+ LLVector4a *getWritableBinormals();
+ LLVector4a *getScaledBinormals();
+
+ // Get texCoords
+ const LLVector2 *getTexCoords() const {
+ return mTexCoords;
+ }
+
+ // non const version
+ LLVector2 *getWritableTexCoords();
+
+ // Get detailTexCoords
+ const LLVector2 *getDetailTexCoords() const {
+ llassert (mSharedData);
+ return mSharedData->mDetailTexCoords;
+ }
+
+ // Get weights
+ const F32 *getWeights() const {
+ llassert (mSharedData);
+ return mSharedData->mWeights;
+ }
+
+ F32 *getWritableWeights() const;
+
+ LLVector4a *getWritableClothingWeights();
+
+ const LLVector4a *getClothingWeights()
+ {
+ return mClothingWeights;
+ }
+
+ //--------------------------------------------------------------------
+ // Face Data Access
+ //--------------------------------------------------------------------
+ // Get number of faces
+ S32 getNumFaces() {
+ llassert (mSharedData);
+ return mSharedData->mNumFaces;
+ }
+
+ // Get faces
+ LLPolyFace *getFaces() {
+ llassert (mSharedData);
+ return mSharedData->mFaces;
+ }
+
+ U32 getNumJointNames() {
+ llassert (mSharedData);
+ return mSharedData->mNumJointNames;
+ }
+
+ std::string *getJointNames() {
+ llassert (mSharedData);
+ return mSharedData->mJointNames;
+ }
+
+ LLPolyMorphData* getMorphData(const std::string& morph_name);
+// void removeMorphData(LLPolyMorphData *morph_target);
+// void deleteAllMorphData();
+
+ LLPolyMeshSharedData *getSharedData() const;
+ LLPolyMesh *getReferenceMesh() { return mReferenceMesh ? mReferenceMesh : this; }
+
+ // Get indices
+ U32* getIndices() { return mSharedData ? mSharedData->mTriangleIndices : NULL; }
+
+ BOOL isLOD() { return mSharedData && mSharedData->isLOD(); }
+
+ void setAvatar(LLAvatarAppearance* avatarp) { mAvatarp = avatarp; }
+ LLAvatarAppearance* getAvatar() { return mAvatarp; }
+
+ LLDynamicArray<LLJointRenderData*> mJointRenderData;
+
+ U32 mFaceVertexOffset;
+ U32 mFaceVertexCount;
+ U32 mFaceIndexOffset;
+ U32 mFaceIndexCount;
+ U32 mCurVertexCount;
+private:
+ void initializeForMorph();
+
+ // Dumps diagnostic information about the global mesh table
+ static void dumpDiagInfo();
+
+protected:
+ // mesh data shared across all instances of a given mesh
+ LLPolyMeshSharedData *mSharedData;
+ // Single array of floats for allocation / deletion
+ F32 *mVertexData;
+ // deformed vertices (resulting from application of morph targets)
+ LLVector4a *mCoords;
+ // deformed normals (resulting from application of morph targets)
+ LLVector4a *mScaledNormals;
+ // output normals (after normalization)
+ LLVector4a *mNormals;
+ // deformed binormals (resulting from application of morph targets)
+ LLVector4a *mScaledBinormals;
+ // output binormals (after normalization)
+ LLVector4a *mBinormals;
+ // weight values that mark verts as clothing/skin
+ LLVector4a *mClothingWeights;
+ // output texture coordinates
+ LLVector2 *mTexCoords;
+
+ LLPolyMesh *mReferenceMesh;
+
+ // global mesh list
+ typedef std::map<std::string, LLPolyMeshSharedData*> LLPolyMeshSharedDataTable;
+ static LLPolyMeshSharedDataTable sGlobalSharedMeshList;
+
+ // Backlink only; don't make this an LLPointer.
+ LLAvatarAppearance* mAvatarp;
+};
+
+#endif // LL_LLPOLYMESHINTERFACE_H
+
diff --git a/indra/llappearance/llpolymorph.cpp b/indra/llappearance/llpolymorph.cpp
new file mode 100644
index 0000000000..8a17819083
--- /dev/null
+++ b/indra/llappearance/llpolymorph.cpp
@@ -0,0 +1,790 @@
+/**
+ * @file llpolymorph.cpp
+ * @brief Implementation of LLPolyMesh class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+
+#include "llpolymorph.h"
+#include "llavatarappearance.h"
+#include "llavatarjoint.h"
+#include "llwearable.h"
+#include "llxmltree.h"
+#include "llendianswizzle.h"
+#include "llpolymesh.h"
+
+//#include "../tools/imdebug/imdebug.h"
+
+const F32 NORMAL_SOFTEN_FACTOR = 0.65f;
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData::LLPolyMorphData(const std::string& morph_name)
+ : mName(morph_name)
+{
+ mNumIndices = 0;
+ mCurrentIndex = 0;
+ mTotalDistortion = 0.f;
+ mAvgDistortion.clear();
+ mMaxDistortion = 0.f;
+ mVertexIndices = NULL;
+ mCoords = NULL;
+ mNormals = NULL;
+ mBinormals = NULL;
+ mTexCoords = NULL;
+
+ mMesh = NULL;
+}
+
+LLPolyMorphData::LLPolyMorphData(const LLPolyMorphData &rhs) :
+ mName(rhs.mName),
+ mNumIndices(rhs.mNumIndices),
+ mTotalDistortion(rhs.mTotalDistortion),
+ mAvgDistortion(rhs.mAvgDistortion),
+ mMaxDistortion(rhs.mMaxDistortion),
+ mVertexIndices(NULL),
+ mCoords(NULL),
+ mNormals(NULL),
+ mBinormals(NULL),
+ mTexCoords(NULL)
+{
+ const S32 numVertices = mNumIndices;
+
+ U32 size = sizeof(LLVector4a)*numVertices;
+
+ mCoords = static_cast<LLVector4a*>( ll_aligned_malloc_16(size) );
+ mNormals = static_cast<LLVector4a*>( ll_aligned_malloc_16(size) );
+ mBinormals = static_cast<LLVector4a*>( ll_aligned_malloc_16(size) );
+ mTexCoords = new LLVector2[numVertices];
+ mVertexIndices = new U32[numVertices];
+
+ for (S32 v=0; v < numVertices; v++)
+ {
+ mCoords[v] = rhs.mCoords[v];
+ mNormals[v] = rhs.mNormals[v];
+ mBinormals[v] = rhs.mBinormals[v];
+ mTexCoords[v] = rhs.mTexCoords[v];
+ mVertexIndices[v] = rhs.mVertexIndices[v];
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData::~LLPolyMorphData()
+{
+ freeData();
+}
+
+//-----------------------------------------------------------------------------
+// loadBinary()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphData::loadBinary(LLFILE *fp, LLPolyMeshSharedData *mesh)
+{
+ S32 numVertices;
+ S32 numRead;
+
+ numRead = fread(&numVertices, sizeof(S32), 1, fp);
+ llendianswizzle(&numVertices, sizeof(S32), 1);
+ if (numRead != 1)
+ {
+ llwarns << "Can't read number of morph target vertices" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // free any existing data
+ //-------------------------------------------------------------------------
+ freeData();
+
+ //-------------------------------------------------------------------------
+ // allocate vertices
+ //-------------------------------------------------------------------------
+
+ U32 size = sizeof(LLVector4a)*numVertices;
+
+ mCoords = static_cast<LLVector4a*>(ll_aligned_malloc_16(size));
+ mNormals = static_cast<LLVector4a*>(ll_aligned_malloc_16(size));
+ mBinormals = static_cast<LLVector4a*>(ll_aligned_malloc_16(size));
+
+ mTexCoords = new LLVector2[numVertices];
+ // Actually, we are allocating more space than we need for the skiplist
+ mVertexIndices = new U32[numVertices];
+ mNumIndices = 0;
+ mTotalDistortion = 0.f;
+ mMaxDistortion = 0.f;
+ mAvgDistortion.clear();
+ mMesh = mesh;
+
+ //-------------------------------------------------------------------------
+ // read vertices
+ //-------------------------------------------------------------------------
+ for(S32 v = 0; v < numVertices; v++)
+ {
+ numRead = fread(&mVertexIndices[v], sizeof(U32), 1, fp);
+ llendianswizzle(&mVertexIndices[v], sizeof(U32), 1);
+ if (numRead != 1)
+ {
+ llwarns << "Can't read morph target vertex number" << llendl;
+ return FALSE;
+ }
+
+ if (mVertexIndices[v] > 10000)
+ {
+ llerrs << "Bad morph index: " << mVertexIndices[v] << llendl;
+ }
+
+
+ numRead = fread(&mCoords[v], sizeof(F32), 3, fp);
+ llendianswizzle(&mCoords[v], sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target vertex coordinates" << llendl;
+ return FALSE;
+ }
+
+ F32 magnitude = mCoords[v].getLength3().getF32();
+
+ mTotalDistortion += magnitude;
+ LLVector4a t;
+ t.setAbs(mCoords[v]);
+ mAvgDistortion.add(t);
+
+ if (magnitude > mMaxDistortion)
+ {
+ mMaxDistortion = magnitude;
+ }
+
+ numRead = fread(&mNormals[v], sizeof(F32), 3, fp);
+ llendianswizzle(&mNormals[v], sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target normal" << llendl;
+ return FALSE;
+ }
+
+ numRead = fread(&mBinormals[v], sizeof(F32), 3, fp);
+ llendianswizzle(&mBinormals[v], sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target binormal" << llendl;
+ return FALSE;
+ }
+
+
+ numRead = fread(&mTexCoords[v].mV, sizeof(F32), 2, fp);
+ llendianswizzle(&mTexCoords[v].mV, sizeof(F32), 2);
+ if (numRead != 2)
+ {
+ llwarns << "Can't read morph target uv" << llendl;
+ return FALSE;
+ }
+
+ mNumIndices++;
+ }
+
+ mAvgDistortion.mul(1.f/(F32)mNumIndices);
+ mAvgDistortion.normalize3fast();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// freeData()
+//-----------------------------------------------------------------------------
+void LLPolyMorphData::freeData()
+{
+ if (mCoords != NULL)
+ {
+ ll_aligned_free_16(mCoords);
+ mCoords = NULL;
+ }
+
+ if (mNormals != NULL)
+ {
+ ll_aligned_free_16(mNormals);
+ mNormals = NULL;
+ }
+
+ if (mBinormals != NULL)
+ {
+ ll_aligned_free_16(mBinormals);
+ mBinormals = NULL;
+ }
+
+ if (mTexCoords != NULL)
+ {
+ delete [] mTexCoords;
+ mTexCoords = NULL;
+ }
+
+ if (mVertexIndices != NULL)
+ {
+ delete [] mVertexIndices;
+ mVertexIndices = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTargetInfo()
+//-----------------------------------------------------------------------------
+LLPolyMorphTargetInfo::LLPolyMorphTargetInfo()
+ : mIsClothingMorph(FALSE)
+{
+}
+
+BOOL LLPolyMorphTargetInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_morph" ) );
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ // Get mixed-case name
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mMorphName ) )
+ {
+ llwarns << "Avatar file: <param> is missing name attribute" << llendl;
+ return FALSE; // Continue, ignoring this tag
+ }
+
+ static LLStdStringHandle clothing_morph_string = LLXmlTree::addAttributeString("clothing_morph");
+ node->getFastAttributeBOOL(clothing_morph_string, mIsClothingMorph);
+
+ LLXmlTreeNode *paramNode = node->getChildByName("param_morph");
+
+ if (NULL == paramNode)
+ {
+ llwarns << "Failed to getChildByName(\"param_morph\")"
+ << llendl;
+ return FALSE;
+ }
+
+ for (LLXmlTreeNode* child_node = paramNode->getFirstChild();
+ child_node;
+ child_node = paramNode->getNextChild())
+ {
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (child_node->hasName("volume_morph"))
+ {
+ std::string volume_name;
+ if (child_node->getFastAttributeString(name_string, volume_name))
+ {
+ LLVector3 scale;
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ child_node->getFastAttributeVector3(scale_string, scale);
+
+ LLVector3 pos;
+ static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos");
+ child_node->getFastAttributeVector3(pos_string, pos);
+
+ mVolumeInfoList.push_back(LLPolyVolumeMorphInfo(volume_name,scale,pos));
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget()
+//-----------------------------------------------------------------------------
+LLPolyMorphTarget::LLPolyMorphTarget(LLPolyMesh *poly_mesh)
+ : mMorphData(NULL), mMesh(poly_mesh),
+ mVertMask(NULL),
+ mLastSex(SEX_FEMALE),
+ mNumMorphMasksPending(0)
+{
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMorphTarget()
+//-----------------------------------------------------------------------------
+LLPolyMorphTarget::~LLPolyMorphTarget()
+{
+ if (mVertMask)
+ {
+ delete mVertMask;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+
+ LLAvatarAppearance* avatarp = mMesh->getAvatar();
+ LLPolyMorphTargetInfo::volume_info_list_t::iterator iter;
+ for (iter = getInfo()->mVolumeInfoList.begin(); iter != getInfo()->mVolumeInfoList.end(); iter++)
+ {
+ LLPolyVolumeMorphInfo *volume_info = &(*iter);
+ for (S32 i = 0; i < avatarp->mNumCollisionVolumes; i++)
+ {
+ if (avatarp->mCollisionVolumes[i].getName() == volume_info->mName)
+ {
+ mVolumeMorphs.push_back(LLPolyVolumeMorph(&avatarp->mCollisionVolumes[i],
+ volume_info->mScale,
+ volume_info->mPos));
+ break;
+ }
+ }
+ }
+
+ std::string morph_param_name = getInfo()->mMorphName;
+
+ mMorphData = mMesh->getMorphData(morph_param_name);
+ if (!mMorphData)
+ {
+ const std::string driven_tag = "_Driven";
+ U32 pos = morph_param_name.find(driven_tag);
+ if (pos > 0)
+ {
+ morph_param_name = morph_param_name.substr(0,pos);
+ mMorphData = mMesh->getMorphData(morph_param_name);
+ }
+ }
+ if (!mMorphData)
+ {
+ llwarns << "No morph target named " << morph_param_name << " found in mesh." << llendl;
+ return FALSE; // Continue, ignoring this tag
+ }
+ return TRUE;
+}
+
+/*virtual*/ LLViewerVisualParam* LLPolyMorphTarget::cloneParam(LLWearable* wearable) const
+{
+ LLPolyMorphTarget *new_param = new LLPolyMorphTarget(mMesh);
+ *new_param = *this;
+ return new_param;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphTarget::parseData(LLXmlTreeNode* node)
+{
+ LLPolyMorphTargetInfo* info = new LLPolyMorphTargetInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// getVertexDistortion()
+//-----------------------------------------------------------------------------
+LLVector4a LLPolyMorphTarget::getVertexDistortion(S32 requested_index, LLPolyMesh *mesh)
+{
+ if (!mMorphData || mMesh != mesh) return LLVector4a::getZero();
+
+ for(U32 index = 0; index < mMorphData->mNumIndices; index++)
+ {
+ if (mMorphData->mVertexIndices[index] == (U32)requested_index)
+ {
+ return mMorphData->mCoords[index];
+ }
+ }
+
+ return LLVector4a::getZero();
+}
+
+//-----------------------------------------------------------------------------
+// getFirstDistortion()
+//-----------------------------------------------------------------------------
+const LLVector4a *LLPolyMorphTarget::getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ if (!mMorphData) return &LLVector4a::getZero();
+
+ LLVector4a* resultVec;
+ mMorphData->mCurrentIndex = 0;
+ if (mMorphData->mNumIndices)
+ {
+ resultVec = &mMorphData->mCoords[mMorphData->mCurrentIndex];
+ if (index != NULL)
+ {
+ *index = mMorphData->mVertexIndices[mMorphData->mCurrentIndex];
+ }
+ if (poly_mesh != NULL)
+ {
+ *poly_mesh = mMesh;
+ }
+
+ return resultVec;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getNextDistortion()
+//-----------------------------------------------------------------------------
+const LLVector4a *LLPolyMorphTarget::getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ if (!mMorphData) return &LLVector4a::getZero();
+
+ LLVector4a* resultVec;
+ mMorphData->mCurrentIndex++;
+ if (mMorphData->mCurrentIndex < mMorphData->mNumIndices)
+ {
+ resultVec = &mMorphData->mCoords[mMorphData->mCurrentIndex];
+ if (index != NULL)
+ {
+ *index = mMorphData->mVertexIndices[mMorphData->mCurrentIndex];
+ }
+ if (poly_mesh != NULL)
+ {
+ *poly_mesh = mMesh;
+ }
+ return resultVec;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getTotalDistortion()
+//-----------------------------------------------------------------------------
+F32 LLPolyMorphTarget::getTotalDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mTotalDistortion;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getAvgDistortion()
+//-----------------------------------------------------------------------------
+const LLVector4a& LLPolyMorphTarget::getAvgDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mAvgDistortion;
+ }
+ else
+ {
+ return LLVector4a::getZero();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getMaxDistortion()
+//-----------------------------------------------------------------------------
+F32 LLPolyMorphTarget::getMaxDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mMaxDistortion;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// apply()
+//-----------------------------------------------------------------------------
+static LLFastTimer::DeclareTimer FTM_APPLY_MORPH_TARGET("Apply Morph");
+
+void LLPolyMorphTarget::apply( ESex avatar_sex )
+{
+ if (!mMorphData || mNumMorphMasksPending > 0)
+ {
+ return;
+ }
+
+ LLFastTimer t(FTM_APPLY_MORPH_TARGET);
+
+ mLastSex = avatar_sex;
+
+ // Check for NaN condition (NaN is detected if a variable doesn't equal itself.
+ if (mCurWeight != mCurWeight)
+ {
+ mCurWeight = 0.0;
+ }
+ if (mLastWeight != mLastWeight)
+ {
+ mLastWeight = mCurWeight+.001;
+ }
+
+ // perform differential update of morph
+ F32 delta_weight = ( getSex() & avatar_sex ) ? (mCurWeight - mLastWeight) : (getDefaultWeight() - mLastWeight);
+ // store last weight
+ mLastWeight += delta_weight;
+
+ if (delta_weight != 0.f)
+ {
+ llassert(!mMesh->isLOD());
+ LLVector4a *coords = mMesh->getWritableCoords();
+
+ LLVector4a *scaled_normals = mMesh->getScaledNormals();
+ LLVector4a *normals = mMesh->getWritableNormals();
+
+ LLVector4a *scaled_binormals = mMesh->getScaledBinormals();
+ LLVector4a *binormals = mMesh->getWritableBinormals();
+
+ LLVector4a *clothing_weights = mMesh->getWritableClothingWeights();
+ LLVector2 *tex_coords = mMesh->getWritableTexCoords();
+
+ F32 *maskWeightArray = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
+
+ for(U32 vert_index_morph = 0; vert_index_morph < mMorphData->mNumIndices; vert_index_morph++)
+ {
+ S32 vert_index_mesh = mMorphData->mVertexIndices[vert_index_morph];
+
+ F32 maskWeight = 1.f;
+ if (maskWeightArray)
+ {
+ maskWeight = maskWeightArray[vert_index_morph];
+ }
+
+
+ LLVector4a pos = mMorphData->mCoords[vert_index_morph];
+ pos.mul(delta_weight*maskWeight);
+ coords[vert_index_mesh].add(pos);
+
+ if (getInfo()->mIsClothingMorph && clothing_weights)
+ {
+ LLVector4a clothing_offset = mMorphData->mCoords[vert_index_morph];
+ clothing_offset.mul(delta_weight * maskWeight);
+ LLVector4a* clothing_weight = &clothing_weights[vert_index_mesh];
+ clothing_weight->add(clothing_offset);
+ clothing_weight->getF32ptr()[VW] = maskWeight;
+ }
+
+ // calculate new normals based on half angles
+ LLVector4a norm = mMorphData->mNormals[vert_index_morph];
+ norm.mul(delta_weight*maskWeight*NORMAL_SOFTEN_FACTOR);
+ scaled_normals[vert_index_mesh].add(norm);
+ norm = scaled_normals[vert_index_mesh];
+ norm.normalize3fast();
+ normals[vert_index_mesh] = norm;
+
+ // calculate new binormals
+ LLVector4a binorm = mMorphData->mBinormals[vert_index_morph];
+ binorm.mul(delta_weight*maskWeight*NORMAL_SOFTEN_FACTOR);
+ scaled_binormals[vert_index_mesh].add(binorm);
+ LLVector4a tangent;
+ tangent.setCross3(scaled_binormals[vert_index_mesh], norm);
+ LLVector4a& normalized_binormal = binormals[vert_index_mesh];
+ normalized_binormal.setCross3(norm, tangent);
+ normalized_binormal.normalize3fast();
+
+ tex_coords[vert_index_mesh] += mMorphData->mTexCoords[vert_index_morph] * delta_weight * maskWeight;
+ }
+
+ // now apply volume changes
+ for( volume_list_t::iterator iter = mVolumeMorphs.begin(); iter != mVolumeMorphs.end(); iter++ )
+ {
+ LLPolyVolumeMorph* volume_morph = &(*iter);
+ LLVector3 scale_delta = volume_morph->mScale * delta_weight;
+ LLVector3 pos_delta = volume_morph->mPos * delta_weight;
+
+ volume_morph->mVolume->setScale(volume_morph->mVolume->getScale() + scale_delta);
+ volume_morph->mVolume->setPosition(volume_morph->mVolume->getPosition() + pos_delta);
+ }
+ }
+
+ if (mNext)
+ {
+ mNext->apply(avatar_sex);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// applyMask()
+//-----------------------------------------------------------------------------
+void LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S32 num_components, BOOL invert)
+{
+ LLVector4a *clothing_weights = getInfo()->mIsClothingMorph ? mMesh->getWritableClothingWeights() : NULL;
+
+ if (!mVertMask)
+ {
+ mVertMask = new LLPolyVertexMask(mMorphData);
+ mNumMorphMasksPending--;
+ }
+ else
+ {
+ // remove effect of previous mask
+ F32 *maskWeights = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
+
+ if (maskWeights)
+ {
+ LLVector4a *coords = mMesh->getWritableCoords();
+ LLVector4a *scaled_normals = mMesh->getScaledNormals();
+ LLVector4a *scaled_binormals = mMesh->getScaledBinormals();
+ LLVector2 *tex_coords = mMesh->getWritableTexCoords();
+
+ LLVector4Logical clothing_mask;
+ clothing_mask.clear();
+ clothing_mask.setElement<0>();
+ clothing_mask.setElement<1>();
+ clothing_mask.setElement<2>();
+
+
+ for(U32 vert = 0; vert < mMorphData->mNumIndices; vert++)
+ {
+ F32 lastMaskWeight = mLastWeight * maskWeights[vert];
+ S32 out_vert = mMorphData->mVertexIndices[vert];
+
+ // remove effect of existing masked morph
+ LLVector4a t;
+ t = mMorphData->mCoords[vert];
+ t.mul(lastMaskWeight);
+ coords[out_vert].sub(t);
+
+ t = mMorphData->mNormals[vert];
+ t.mul(lastMaskWeight*NORMAL_SOFTEN_FACTOR);
+ scaled_normals[out_vert].sub(t);
+
+ t = mMorphData->mBinormals[vert];
+ t.mul(lastMaskWeight*NORMAL_SOFTEN_FACTOR);
+ scaled_binormals[out_vert].sub(t);
+
+ tex_coords[out_vert] -= mMorphData->mTexCoords[vert] * lastMaskWeight;
+
+ if (clothing_weights)
+ {
+ LLVector4a clothing_offset = mMorphData->mCoords[vert];
+ clothing_offset.mul(lastMaskWeight);
+ LLVector4a* clothing_weight = &clothing_weights[out_vert];
+ LLVector4a t;
+ t.setSub(*clothing_weight, clothing_offset);
+ clothing_weight->setSelectWithMask(clothing_mask, t, *clothing_weight);
+ }
+ }
+ }
+ }
+
+ // set last weight to 0, since we've removed the effect of this morph
+ mLastWeight = 0.f;
+
+ mVertMask->generateMask(maskTextureData, width, height, num_components, invert, clothing_weights);
+
+ apply(mLastSex);
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+LLPolyVertexMask::LLPolyVertexMask(LLPolyMorphData* morph_data)
+{
+ mWeights = new F32[morph_data->mNumIndices];
+ mMorphData = morph_data;
+ mWeightsGenerated = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+LLPolyVertexMask::~LLPolyVertexMask()
+{
+ delete[] mWeights;
+}
+
+//-----------------------------------------------------------------------------
+// generateMask()
+//-----------------------------------------------------------------------------
+void LLPolyVertexMask::generateMask(U8 *maskTextureData, S32 width, S32 height, S32 num_components, BOOL invert, LLVector4a *clothing_weights)
+{
+// RN debug output that uses Image Debugger (http://www.cs.unc.edu/~baxter/projects/imdebug/)
+// BOOL debugImg = FALSE;
+// if (debugImg)
+// {
+// if (invert)
+// {
+// imdebug("lum rbga=rgba b=8 w=%d h=%d *-1 %p", width, height, maskTextureData);
+// }
+// else
+// {
+// imdebug("lum rbga=rgba b=8 w=%d h=%d %p", width, height, maskTextureData);
+// }
+// }
+ for (U32 index = 0; index < mMorphData->mNumIndices; index++)
+ {
+ S32 vertIndex = mMorphData->mVertexIndices[index];
+ const S32 *sharedVertIndex = mMorphData->mMesh->getSharedVert(vertIndex);
+ LLVector2 uvCoords;
+
+ if (sharedVertIndex)
+ {
+ uvCoords = mMorphData->mMesh->getUVs(*sharedVertIndex);
+ }
+ else
+ {
+ uvCoords = mMorphData->mMesh->getUVs(vertIndex);
+ }
+ U32 s = llclamp((U32)(uvCoords.mV[VX] * (F32)(width - 1)), (U32)0, (U32)width - 1);
+ U32 t = llclamp((U32)(uvCoords.mV[VY] * (F32)(height - 1)), (U32)0, (U32)height - 1);
+
+ mWeights[index] = ((F32) maskTextureData[((t * width + s) * num_components) + (num_components - 1)]) / 255.f;
+
+ if (invert)
+ {
+ mWeights[index] = 1.f - mWeights[index];
+ }
+
+ // now apply step function
+ // mWeights[index] = mWeights[index] > 0.95f ? 1.f : 0.f;
+
+ if (clothing_weights)
+ {
+ clothing_weights[vertIndex].getF32ptr()[VW] = mWeights[index];
+ }
+ }
+ mWeightsGenerated = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// getMaskForMorphIndex()
+//-----------------------------------------------------------------------------
+F32* LLPolyVertexMask::getMorphMaskWeights()
+{
+ if (!mWeightsGenerated)
+ {
+ return NULL;
+ }
+
+ return mWeights;
+}
diff --git a/indra/llappearance/llpolymorph.h b/indra/llappearance/llpolymorph.h
new file mode 100644
index 0000000000..ee380ae7c3
--- /dev/null
+++ b/indra/llappearance/llpolymorph.h
@@ -0,0 +1,197 @@
+/**
+ * @file llpolymorph.h
+ * @brief Implementation of LLPolyMesh class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPOLYMORPH_H
+#define LL_LLPOLYMORPH_H
+
+#include <string>
+#include <vector>
+
+#include "llviewervisualparam.h"
+
+class LLAvatarJointCollisionVolume;
+class LLPolyMeshSharedData;
+class LLVector2;
+class LLAvatarJointCollisionVolume;
+class LLWearable;
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphData()
+//-----------------------------------------------------------------------------
+LL_ALIGN_PREFIX(16)
+class LLPolyMorphData
+{
+public:
+ LLPolyMorphData(const std::string& morph_name);
+ ~LLPolyMorphData();
+ LLPolyMorphData(const LLPolyMorphData &rhs);
+
+ void* operator new(size_t size)
+ {
+ return ll_aligned_malloc_16(size);
+ }
+
+ void operator delete(void* ptr)
+ {
+ ll_aligned_free_16(ptr);
+ }
+
+ BOOL loadBinary(LLFILE* fp, LLPolyMeshSharedData *mesh);
+ const std::string& getName() { return mName; }
+
+public:
+ std::string mName;
+
+ // morphology
+ U32 mNumIndices;
+ U32* mVertexIndices;
+ U32 mCurrentIndex;
+ LLVector4a* mCoords;
+ LLVector4a* mNormals;
+ LLVector4a* mBinormals;
+ LLVector2* mTexCoords;
+
+ F32 mTotalDistortion; // vertex distortion summed over entire morph
+ F32 mMaxDistortion; // maximum single vertex distortion in a given morph
+ LL_ALIGN_16(LLVector4a mAvgDistortion); // average vertex distortion, to infer directionality of the morph
+ LLPolyMeshSharedData* mMesh;
+
+private:
+ void freeData();
+} LL_ALIGN_POSTFIX(16);
+
+
+//-----------------------------------------------------------------------------
+// LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+class LLPolyVertexMask
+{
+public:
+ LLPolyVertexMask(LLPolyMorphData* morph_data);
+ ~LLPolyVertexMask();
+
+ void generateMask(U8 *maskData, S32 width, S32 height, S32 num_components, BOOL invert, LLVector4a *clothing_weights);
+ F32* getMorphMaskWeights();
+
+
+protected:
+ F32* mWeights;
+ LLPolyMorphData *mMorphData;
+ BOOL mWeightsGenerated;
+
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget Data structs
+//-----------------------------------------------------------------------------
+struct LLPolyVolumeMorphInfo
+{
+ LLPolyVolumeMorphInfo(std::string &name, LLVector3 &scale, LLVector3 &pos)
+ : mName(name), mScale(scale), mPos(pos) {};
+
+ std::string mName;
+ LLVector3 mScale;
+ LLVector3 mPos;
+};
+
+struct LLPolyVolumeMorph
+{
+ LLPolyVolumeMorph(LLAvatarJointCollisionVolume* volume, LLVector3 scale, LLVector3 pos)
+ : mVolume(volume), mScale(scale), mPos(pos) {};
+
+ LLAvatarJointCollisionVolume* mVolume;
+ LLVector3 mScale;
+ LLVector3 mPos;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTargetInfo
+// Shared information for LLPolyMorphTargets
+//-----------------------------------------------------------------------------
+class LLPolyMorphTargetInfo : public LLViewerVisualParamInfo
+{
+ friend class LLPolyMorphTarget;
+public:
+ LLPolyMorphTargetInfo();
+ /*virtual*/ ~LLPolyMorphTargetInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ std::string mMorphName;
+ BOOL mIsClothingMorph;
+ typedef std::vector<LLPolyVolumeMorphInfo> volume_info_list_t;
+ volume_info_list_t mVolumeInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget
+// A set of vertex data associated with morph target.
+// These morph targets must be topologically consistent with a given Polymesh
+// (share face sets)
+//-----------------------------------------------------------------------------
+class LLPolyMorphTarget : public LLViewerVisualParam
+{
+public:
+ LLPolyMorphTarget(LLPolyMesh *poly_mesh);
+ ~LLPolyMorphTarget();
+
+ // Special: These functions are overridden by child classes
+ LLPolyMorphTargetInfo* getInfo() const { return (LLPolyMorphTargetInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLPolyMorphTargetInfo *info);
+
+ /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const;
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex sex );
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion();
+ /*virtual*/ const LLVector4a& getAvgDistortion();
+ /*virtual*/ F32 getMaxDistortion();
+ /*virtual*/ LLVector4a getVertexDistortion(S32 index, LLPolyMesh *poly_mesh);
+ /*virtual*/ const LLVector4a* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh);
+ /*virtual*/ const LLVector4a* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
+
+ void applyMask(U8 *maskData, S32 width, S32 height, S32 num_components, BOOL invert);
+ void addPendingMorphMask() { mNumMorphMasksPending++; }
+
+protected:
+ LLPolyMorphData* mMorphData;
+ LLPolyMesh* mMesh;
+ LLPolyVertexMask * mVertMask;
+ ESex mLastSex;
+ // number of morph masks that haven't been generated, must be 0 before this morph is applied
+ BOOL mNumMorphMasksPending;
+
+ typedef std::vector<LLPolyVolumeMorph> volume_list_t;
+ volume_list_t mVolumeMorphs;
+
+};
+
+#endif // LL_LLPOLYMORPH_H
diff --git a/indra/llappearance/llpolyskeletaldistortion.cpp b/indra/llappearance/llpolyskeletaldistortion.cpp
new file mode 100644
index 0000000000..4ba16691c2
--- /dev/null
+++ b/indra/llappearance/llpolyskeletaldistortion.cpp
@@ -0,0 +1,293 @@
+/**
+ * @file llpolyskeletaldistortion.cpp
+ * @brief Implementation of LLPolySkeletalDistortion classes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llpreprocessor.h"
+#include "llerrorlegacy.h"
+//#include "llcommon.h"
+//#include "llmemory.h"
+#include "llavatarappearance.h"
+#include "llavatarjoint.h"
+#include "llpolymorph.h"
+//#include "llviewercontrol.h"
+//#include "llxmltree.h"
+//#include "llvoavatar.h"
+#include "llwearable.h"
+//#include "lldir.h"
+//#include "llvolume.h"
+//#include "llendianswizzle.h"
+
+#include "llpolyskeletaldistortion.h"
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDistortionInfo()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortionInfo::LLPolySkeletalDistortionInfo()
+{
+}
+
+BOOL LLPolySkeletalDistortionInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_skeleton" ) );
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ LLXmlTreeNode* skeletalParam = node->getChildByName("param_skeleton");
+
+ if (NULL == skeletalParam)
+ {
+ llwarns << "Failed to getChildByName(\"param_skeleton\")"
+ << llendl;
+ return FALSE;
+ }
+
+ for( LLXmlTreeNode* bone = skeletalParam->getFirstChild(); bone; bone = skeletalParam->getNextChild() )
+ {
+ if (bone->hasName("bone"))
+ {
+ std::string name;
+ LLVector3 scale;
+ LLVector3 pos;
+ BOOL haspos = FALSE;
+
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!bone->getFastAttributeString(name_string, name))
+ {
+ llwarns << "No bone name specified for skeletal param." << llendl;
+ continue;
+ }
+
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ if (!bone->getFastAttributeVector3(scale_string, scale))
+ {
+ llwarns << "No scale specified for bone " << name << "." << llendl;
+ continue;
+ }
+
+ // optional offset deformation (translation)
+ static LLStdStringHandle offset_string = LLXmlTree::addAttributeString("offset");
+ if (bone->getFastAttributeVector3(offset_string, pos))
+ {
+ haspos = TRUE;
+ }
+ mBoneInfoList.push_back(LLPolySkeletalBoneInfo(name, scale, pos, haspos));
+ }
+ else
+ {
+ llwarns << "Unrecognized element " << bone->getName() << " in skeletal distortion" << llendl;
+ continue;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDistortion()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortion::LLPolySkeletalDistortion(LLAvatarAppearance *avatarp)
+{
+ mAvatar = avatarp;
+ mDefaultVec.splat(0.001f);
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolySkeletalDistortion()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortion::~LLPolySkeletalDistortion()
+{
+}
+
+BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+
+ LLPolySkeletalDistortionInfo::bone_info_list_t::iterator iter;
+ for (iter = getInfo()->mBoneInfoList.begin(); iter != getInfo()->mBoneInfoList.end(); iter++)
+ {
+ LLPolySkeletalBoneInfo *bone_info = &(*iter);
+ LLJoint* joint = mAvatar->getJoint(bone_info->mBoneName);
+ if (!joint)
+ {
+ llwarns << "Joint " << bone_info->mBoneName << " not found." << llendl;
+ continue;
+ }
+
+ if (mJointScales.find(joint) != mJointScales.end())
+ {
+ llwarns << "Scale deformation already supplied for joint " << joint->getName() << "." << llendl;
+ }
+
+ // store it
+ mJointScales[joint] = bone_info->mScaleDeformation;
+
+ // apply to children that need to inherit it
+ for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin();
+ iter != joint->mChildren.end(); ++iter)
+ {
+ LLAvatarJoint* child_joint = (LLAvatarJoint*)(*iter);
+ if (child_joint->inheritScale())
+ {
+ LLVector3 childDeformation = LLVector3(child_joint->getScale());
+ childDeformation.scaleVec(bone_info->mScaleDeformation);
+ mJointScales[child_joint] = childDeformation;
+ }
+ }
+
+ if (bone_info->mHasPositionDeformation)
+ {
+ if (mJointOffsets.find(joint) != mJointOffsets.end())
+ {
+ llwarns << "Offset deformation already supplied for joint " << joint->getName() << "." << llendl;
+ }
+ mJointOffsets[joint] = bone_info->mPositionDeformation;
+ }
+ }
+ return TRUE;
+}
+
+/*virtual*/ LLViewerVisualParam* LLPolySkeletalDistortion::cloneParam(LLWearable* wearable) const
+{
+ LLPolySkeletalDistortion *new_param = new LLPolySkeletalDistortion(mAvatar);
+ *new_param = *this;
+ return new_param;
+}
+
+//-----------------------------------------------------------------------------
+// apply()
+//-----------------------------------------------------------------------------
+static LLFastTimer::DeclareTimer FTM_POLYSKELETAL_DISTORTION_APPLY("Skeletal Distortion");
+
+void LLPolySkeletalDistortion::apply( ESex avatar_sex )
+{
+ LLFastTimer t(FTM_POLYSKELETAL_DISTORTION_APPLY);
+
+ F32 effective_weight = ( getSex() & avatar_sex ) ? mCurWeight : getDefaultWeight();
+
+ LLJoint* joint;
+ joint_vec_map_t::iterator iter;
+
+ for (iter = mJointScales.begin();
+ iter != mJointScales.end();
+ iter++)
+ {
+ joint = iter->first;
+ LLVector3 newScale = joint->getScale();
+ LLVector3 scaleDelta = iter->second;
+ newScale = newScale + (effective_weight * scaleDelta) - (mLastWeight * scaleDelta);
+ joint->setScale(newScale);
+ }
+
+ for (iter = mJointOffsets.begin();
+ iter != mJointOffsets.end();
+ iter++)
+ {
+ joint = iter->first;
+ LLVector3 newPosition = joint->getPosition();
+ LLVector3 positionDelta = iter->second;
+ newPosition = newPosition + (effective_weight * positionDelta) - (mLastWeight * positionDelta);
+ joint->setPosition(newPosition);
+ }
+
+ if (mLastWeight != mCurWeight && !mIsAnimating)
+ {
+ mAvatar->setSkeletonSerialNum(mAvatar->getSkeletonSerialNum() + 1);
+ }
+ mLastWeight = mCurWeight;
+}
+
+
+LLPolyMorphData *clone_morph_param_duplicate(const LLPolyMorphData *src_data,
+ const std::string &name)
+{
+ LLPolyMorphData* cloned_morph_data = new LLPolyMorphData(*src_data);
+ cloned_morph_data->mName = name;
+ for (U32 v=0; v < cloned_morph_data->mNumIndices; v++)
+ {
+ cloned_morph_data->mCoords[v] = src_data->mCoords[v];
+ cloned_morph_data->mNormals[v] = src_data->mNormals[v];
+ cloned_morph_data->mBinormals[v] = src_data->mBinormals[v];
+ }
+ return cloned_morph_data;
+}
+
+LLPolyMorphData *clone_morph_param_direction(const LLPolyMorphData *src_data,
+ const LLVector3 &direction,
+ const std::string &name)
+{
+ LLPolyMorphData* cloned_morph_data = new LLPolyMorphData(*src_data);
+ cloned_morph_data->mName = name;
+ LLVector4a dir;
+ dir.load3(direction.mV);
+
+ for (U32 v=0; v < cloned_morph_data->mNumIndices; v++)
+ {
+ cloned_morph_data->mCoords[v] = dir;
+ cloned_morph_data->mNormals[v].clear();
+ cloned_morph_data->mBinormals[v].clear();
+ }
+ return cloned_morph_data;
+}
+
+LLPolyMorphData *clone_morph_param_cleavage(const LLPolyMorphData *src_data,
+ F32 scale,
+ const std::string &name)
+{
+ LLPolyMorphData* cloned_morph_data = new LLPolyMorphData(*src_data);
+ cloned_morph_data->mName = name;
+
+ LLVector4a sc;
+ sc.splat(scale);
+
+ LLVector4a nsc;
+ nsc.set(scale, -scale, scale, scale);
+
+ for (U32 v=0; v < cloned_morph_data->mNumIndices; v++)
+ {
+ if (cloned_morph_data->mCoords[v][1] < 0)
+ {
+ cloned_morph_data->mCoords[v].setMul(src_data->mCoords[v],nsc);
+ cloned_morph_data->mNormals[v].setMul(src_data->mNormals[v],nsc);
+ cloned_morph_data->mBinormals[v].setMul(src_data->mBinormals[v],nsc);
+ }
+ else
+ {
+ cloned_morph_data->mCoords[v].setMul(src_data->mCoords[v],sc);
+ cloned_morph_data->mNormals[v].setMul(src_data->mNormals[v], sc);
+ cloned_morph_data->mBinormals[v].setMul(src_data->mBinormals[v],sc);
+ }
+ }
+ return cloned_morph_data;
+}
+
+// End
diff --git a/indra/llappearance/llpolyskeletaldistortion.h b/indra/llappearance/llpolyskeletaldistortion.h
new file mode 100644
index 0000000000..774bc7dfa2
--- /dev/null
+++ b/indra/llappearance/llpolyskeletaldistortion.h
@@ -0,0 +1,131 @@
+/**
+ * @file llpolyskeletaldistortion.h
+ * @brief Implementation of LLPolyMesh class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPOLYSKELETALDISTORTION_H
+#define LL_LLPOLYSKELETALDISTORTION_H
+
+#include "llcommon.h"
+
+#include <string>
+#include <map>
+#include "llstl.h"
+
+#include "v3math.h"
+#include "v2math.h"
+#include "llquaternion.h"
+//#include "llpolymorph.h"
+#include "lljoint.h"
+#include "llviewervisualparam.h"
+//#include "lldarray.h"
+
+//class LLSkinJoint;
+class LLAvatarAppearance;
+
+//#define USE_STRIPS // Use tri-strips for rendering.
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDeformationInfo
+// Shared information for LLPolySkeletalDeformations
+//-----------------------------------------------------------------------------
+struct LLPolySkeletalBoneInfo
+{
+ LLPolySkeletalBoneInfo(std::string &name, LLVector3 &scale, LLVector3 &pos, BOOL haspos)
+ : mBoneName(name),
+ mScaleDeformation(scale),
+ mPositionDeformation(pos),
+ mHasPositionDeformation(haspos) {}
+ std::string mBoneName;
+ LLVector3 mScaleDeformation;
+ LLVector3 mPositionDeformation;
+ BOOL mHasPositionDeformation;
+};
+
+LL_ALIGN_PREFIX(16)
+class LLPolySkeletalDistortionInfo : public LLViewerVisualParamInfo
+{
+ friend class LLPolySkeletalDistortion;
+public:
+
+ LLPolySkeletalDistortionInfo();
+ /*virtual*/ ~LLPolySkeletalDistortionInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ typedef std::vector<LLPolySkeletalBoneInfo> bone_info_list_t;
+ bone_info_list_t mBoneInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDeformation
+// A set of joint scale data for deforming the avatar mesh
+//-----------------------------------------------------------------------------
+class LLPolySkeletalDistortion : public LLViewerVisualParam
+{
+public:
+ void* operator new(size_t size)
+ {
+ return ll_aligned_malloc_16(size);
+ }
+
+ void operator delete(void* ptr)
+ {
+ ll_aligned_free_16(ptr);
+ }
+
+ LLPolySkeletalDistortion(LLAvatarAppearance *avatarp);
+ ~LLPolySkeletalDistortion();
+
+ // Special: These functions are overridden by child classes
+ LLPolySkeletalDistortionInfo* getInfo() const { return (LLPolySkeletalDistortionInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLPolySkeletalDistortionInfo *info);
+
+ /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const;
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex sex );
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 0.1f; }
+ /*virtual*/ const LLVector4a& getAvgDistortion() { return mDefaultVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 0.1f; }
+ /*virtual*/ LLVector4a getVertexDistortion(S32 index, LLPolyMesh *poly_mesh){return LLVector4a(0.001f, 0.001f, 0.001f);}
+ /*virtual*/ const LLVector4a* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh){index = 0; poly_mesh = NULL; return &mDefaultVec;};
+ /*virtual*/ const LLVector4a* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh){index = 0; poly_mesh = NULL; return NULL;};
+
+protected:
+ LL_ALIGN_16(LLVector4a mDefaultVec);
+ typedef std::map<LLJoint*, LLVector3> joint_vec_map_t;
+ joint_vec_map_t mJointScales;
+ joint_vec_map_t mJointOffsets;
+ // Backlink only; don't make this an LLPointer.
+ LLAvatarAppearance *mAvatar;
+} LL_ALIGN_POSTFIX(16);
+
+#endif // LL_LLPOLYSKELETALDISTORTION_H
+
diff --git a/indra/llappearance/lltexglobalcolor.cpp b/indra/llappearance/lltexglobalcolor.cpp
new file mode 100644
index 0000000000..f38b982104
--- /dev/null
+++ b/indra/llappearance/lltexglobalcolor.cpp
@@ -0,0 +1,152 @@
+/**
+ * @file lltexlayerglobalcolor.cpp
+ * @brief Color for texture layers.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llavatarappearance.h"
+#include "lltexlayer.h"
+#include "lltexglobalcolor.h"
+
+class LLWearable;
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColor
+//-----------------------------------------------------------------------------
+
+LLTexGlobalColor::LLTexGlobalColor(LLAvatarAppearance* appearance)
+ :
+ mAvatarAppearance(appearance),
+ mInfo(NULL)
+{
+}
+
+LLTexGlobalColor::~LLTexGlobalColor()
+{
+ // mParamColorList are LLViewerVisualParam's and get deleted with ~LLCharacter()
+ //std::for_each(mParamColorList.begin(), mParamColorList.end(), DeletePointer());
+}
+
+BOOL LLTexGlobalColor::setInfo(LLTexGlobalColorInfo *info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ mParamGlobalColorList.reserve(mInfo->mParamColorInfoList.size());
+ for (param_color_info_list_t::iterator iter = mInfo->mParamColorInfoList.begin();
+ iter != mInfo->mParamColorInfoList.end();
+ iter++)
+ {
+ LLTexParamGlobalColor* param_color = new LLTexParamGlobalColor(this);
+ if (!param_color->setInfo(*iter, TRUE))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mParamGlobalColorList.push_back(param_color);
+ }
+
+ return TRUE;
+}
+
+LLColor4 LLTexGlobalColor::getColor() const
+{
+ // Sum of color params
+ if (mParamGlobalColorList.empty())
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+
+ LLColor4 net_color(0.f, 0.f, 0.f, 0.f);
+ LLTexLayer::calculateTexLayerColor(mParamGlobalColorList, net_color);
+ return net_color;
+}
+
+const std::string& LLTexGlobalColor::getName() const
+{
+ return mInfo->mName;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexParamGlobalColor
+//-----------------------------------------------------------------------------
+LLTexParamGlobalColor::LLTexParamGlobalColor(LLTexGlobalColor* tex_global_color) :
+ LLTexLayerParamColor(tex_global_color->getAvatarAppearance()),
+ mTexGlobalColor(tex_global_color)
+{
+}
+
+/*virtual*/ LLViewerVisualParam* LLTexParamGlobalColor::cloneParam(LLWearable* wearable) const
+{
+ LLTexParamGlobalColor *new_param = new LLTexParamGlobalColor(mTexGlobalColor);
+ *new_param = *this;
+ return new_param;
+}
+
+void LLTexParamGlobalColor::onGlobalColorChanged(bool upload_bake)
+{
+ mAvatarAppearance->onGlobalColorChanged(mTexGlobalColor, upload_bake);
+}
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColorInfo
+//-----------------------------------------------------------------------------
+
+LLTexGlobalColorInfo::LLTexGlobalColorInfo()
+{
+}
+
+
+LLTexGlobalColorInfo::~LLTexGlobalColorInfo()
+{
+ for_each(mParamColorInfoList.begin(), mParamColorInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexGlobalColorInfo::parseXml(LLXmlTreeNode* node)
+{
+ // name attribute
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ llwarns << "<global_color> element is missing name attribute." << llendl;
+ return FALSE;
+ }
+ // <param> sub-element
+ for (LLXmlTreeNode* child = node->getChildByName("param");
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (child->getChildByName("param_color"))
+ {
+ // <param><param_color/></param>
+ LLTexLayerParamColorInfo* info = new LLTexLayerParamColorInfo();
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mParamColorInfoList.push_back(info);
+ }
+ }
+ return TRUE;
+}
diff --git a/indra/llappearance/lltexglobalcolor.h b/indra/llappearance/lltexglobalcolor.h
new file mode 100644
index 0000000000..2867479876
--- /dev/null
+++ b/indra/llappearance/lltexglobalcolor.h
@@ -0,0 +1,83 @@
+/**
+ * @file lltexglobalcolor.h
+ * @brief This is global texture color info used by llavatarappearance.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXGLOBALCOLOR_H
+#define LL_LLTEXGLOBALCOLOR_H
+
+#include "lltexlayer.h"
+#include "lltexlayerparams.h"
+
+class LLAvatarAppearance;
+class LLWearable;
+class LLTexGlobalColorInfo;
+
+class LLTexGlobalColor
+{
+public:
+ LLTexGlobalColor( LLAvatarAppearance* appearance );
+ ~LLTexGlobalColor();
+
+ LLTexGlobalColorInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexGlobalColorInfo *info);
+
+ LLAvatarAppearance* getAvatarAppearance() const { return mAvatarAppearance; }
+ LLColor4 getColor() const;
+ const std::string& getName() const;
+
+private:
+ param_color_list_t mParamGlobalColorList;
+ LLAvatarAppearance* mAvatarAppearance; // just backlink, don't LLPointer
+ LLTexGlobalColorInfo *mInfo;
+};
+
+// Used by llavatarappearance to determine skin/eye/hair color.
+class LLTexGlobalColorInfo
+{
+ friend class LLTexGlobalColor;
+public:
+ LLTexGlobalColorInfo();
+ ~LLTexGlobalColorInfo();
+
+ BOOL parseXml(LLXmlTreeNode* node);
+
+private:
+ param_color_info_list_t mParamColorInfoList;
+ std::string mName;
+};
+
+class LLTexParamGlobalColor : public LLTexLayerParamColor
+{
+public:
+ LLTexParamGlobalColor(LLTexGlobalColor *tex_color);
+ /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const;
+protected:
+ /*virtual*/ void onGlobalColorChanged(bool upload_bake);
+private:
+ LLTexGlobalColor* mTexGlobalColor;
+};
+
+#endif
diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp
new file mode 100644
index 0000000000..c075035226
--- /dev/null
+++ b/indra/llappearance/lltexlayer.cpp
@@ -0,0 +1,2020 @@
+/**
+ * @file lltexlayer.cpp
+ * @brief A texture layer. Used for avatars.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltexlayer.h"
+
+#include "llavatarappearance.h"
+#include "llcrc.h"
+#include "imageids.h"
+#include "llimagej2c.h"
+#include "llimagetga.h"
+#include "lldir.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "lltexlayerparams.h"
+#include "lltexturemanagerbridge.h"
+//#include "llrender2dutils.h"
+#include "..\llui\llui.h"
+#include "llwearable.h"
+#include "llwearabledata.h"
+#include "llvertexbuffer.h"
+#include "llviewervisualparam.h"
+
+//#include "../tools/imdebug/imdebug.h"
+
+using namespace LLAvatarAppearanceDefines;
+
+// runway consolidate
+extern std::string self_av_string();
+
+class LLTexLayerInfo
+{
+ friend class LLTexLayer;
+ friend class LLTexLayerTemplate;
+ friend class LLTexLayerInterface;
+public:
+ LLTexLayerInfo();
+ ~LLTexLayerInfo();
+
+ BOOL parseXml(LLXmlTreeNode* node);
+ BOOL createVisualParams(LLAvatarAppearance *appearance);
+ 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;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetBuffer
+// The composite image that a LLViewerTexLayerSet writes to. Each LLViewerTexLayerSet has one.
+//-----------------------------------------------------------------------------
+
+LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner) :
+ mTexLayerSet(owner)
+{
+}
+
+LLTexLayerSetBuffer::~LLTexLayerSetBuffer()
+{
+}
+
+void LLTexLayerSetBuffer::pushProjection() const
+{
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+ gGL.ortho(0.0f, getCompositeWidth(), 0.0f, getCompositeHeight(), -1.0f, 1.0f);
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+}
+
+void LLTexLayerSetBuffer::popProjection() const
+{
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.popMatrix();
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.popMatrix();
+}
+
+// virtual
+void LLTexLayerSetBuffer::preRenderTexLayerSet()
+{
+ // Set up an ortho projection
+ pushProjection();
+}
+
+// virtual
+void LLTexLayerSetBuffer::postRenderTexLayerSet(BOOL success)
+{
+ popProjection();
+}
+
+BOOL LLTexLayerSetBuffer::renderTexLayerSet()
+{
+ // Default color mask for tex layer render
+ gGL.setColorMask(true, true);
+
+ BOOL success = TRUE;
+
+ bool use_shaders = LLGLSLShader::sNoFixedFunction;
+
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.bind();
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ else
+ {
+ gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.00f);
+ }
+
+ LLVertexBuffer::unbind();
+
+ // Composite the color data
+ LLGLSUIDefault gls_ui;
+ success &= mTexLayerSet->render( getCompositeOriginX(), getCompositeOriginY(),
+ getCompositeWidth(), getCompositeHeight() );
+ gGL.flush();
+
+ midRenderTexLayerSet(success);
+
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.unbind();
+ }
+
+ LLVertexBuffer::unbind();
+
+ // reset GL state
+ gGL.setColorMask(true, true);
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetInfo
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+
+LLTexLayerSetInfo::LLTexLayerSetInfo() :
+ mBodyRegion( "" ),
+ mWidth( 512 ),
+ mHeight( 512 ),
+ mClearAlpha( TRUE )
+{
+}
+
+LLTexLayerSetInfo::~LLTexLayerSetInfo( )
+{
+ std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexLayerSetInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "layer_set" ) );
+ if( !node->hasName( "layer_set" ) )
+ {
+ return FALSE;
+ }
+
+ // body_region
+ static LLStdStringHandle body_region_string = LLXmlTree::addAttributeString("body_region");
+ if( !node->getFastAttributeString( body_region_string, mBodyRegion ) )
+ {
+ llwarns << "<layer_set> is missing body_region attribute" << llendl;
+ return FALSE;
+ }
+
+ // width, height
+ static LLStdStringHandle width_string = LLXmlTree::addAttributeString("width");
+ if( !node->getFastAttributeS32( width_string, mWidth ) )
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle height_string = LLXmlTree::addAttributeString("height");
+ if( !node->getFastAttributeS32( height_string, mHeight ) )
+ {
+ return FALSE;
+ }
+
+ // Optional alpha component to apply after all compositing is complete.
+ static LLStdStringHandle alpha_tga_file_string = LLXmlTree::addAttributeString("alpha_tga_file");
+ node->getFastAttributeString( alpha_tga_file_string, mStaticAlphaFileName );
+
+ static LLStdStringHandle clear_alpha_string = LLXmlTree::addAttributeString("clear_alpha");
+ node->getFastAttributeBOOL( clear_alpha_string, mClearAlpha );
+
+ // <layer>
+ for (LLXmlTreeNode* child = node->getChildByName( "layer" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ LLTexLayerInfo* info = new LLTexLayerInfo();
+ if( !info->parseXml( child ))
+ {
+ delete info;
+ return FALSE;
+ }
+ mLayerInfoList.push_back( info );
+ }
+ return TRUE;
+}
+
+// creates visual params without generating layersets or layers
+void LLTexLayerSetInfo::createVisualParams(LLAvatarAppearance *appearance)
+{
+ //layer_info_list_t mLayerInfoList;
+ for (layer_info_list_t::iterator layer_iter = mLayerInfoList.begin();
+ layer_iter != mLayerInfoList.end();
+ layer_iter++)
+ {
+ LLTexLayerInfo *layer_info = *layer_iter;
+ layer_info->createVisualParams(appearance);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSet
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::sHasCaches = FALSE;
+
+LLTexLayerSet::LLTexLayerSet(LLAvatarAppearance* const appearance) :
+ mAvatarAppearance( appearance ),
+ mIsVisible( TRUE ),
+ mBakedTexIndex(LLAvatarAppearanceDefines::BAKED_HEAD),
+ mInfo( NULL )
+{
+}
+
+// virtual
+LLTexLayerSet::~LLTexLayerSet()
+{
+ deleteCaches();
+ std::for_each(mLayerList.begin(), mLayerList.end(), DeletePointer());
+ std::for_each(mMaskLayerList.begin(), mMaskLayerList.end(), DeletePointer());
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::setInfo(const LLTexLayerSetInfo *info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ mLayerList.reserve(info->mLayerInfoList.size());
+ for (LLTexLayerSetInfo::layer_info_list_t::const_iterator iter = info->mLayerInfoList.begin();
+ iter != info->mLayerInfoList.end();
+ iter++)
+ {
+ LLTexLayerInterface *layer = NULL;
+ if ( (*iter)->isUserSettable() )
+ {
+ layer = new LLTexLayerTemplate( this, getAvatarAppearance() );
+ }
+ 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 appearance
+ if (!layer->setInfo(*iter, NULL))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ if (!layer->isVisibilityMask())
+ {
+ mLayerList.push_back( layer );
+ }
+ else
+ {
+ mMaskLayerList.push_back(layer);
+ }
+ }
+
+ requestUpdate();
+
+ stop_glerror();
+
+ return TRUE;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::parseData(LLXmlTreeNode* node)
+{
+ LLTexLayerSetInfo *info = new LLTexLayerSetInfo;
+
+ if (!info->parseXml(node))
+ {
+ delete info;
+ return FALSE;
+ }
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+void LLTexLayerSet::deleteCaches()
+{
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayerInterface* layer = *iter;
+ layer->deleteCaches();
+ }
+ for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++)
+ {
+ LLTexLayerInterface* layer = *iter;
+ layer->deleteCaches();
+ }
+}
+
+
+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++)
+ {
+ LLTexLayerInterface* layer = *iter;
+ if (layer->isInvisibleAlphaMask())
+ {
+ mIsVisible = FALSE;
+ }
+ }
+ }
+
+ bool use_shaders = LLGLSLShader::sNoFixedFunction;
+
+ LLGLSUIDefault gls_ui;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+ gGL.setColorMask(true, true);
+
+ // clear buffer area to ensure we don't pick up UI elements
+ {
+ gGL.flush();
+ LLGLDisable no_alpha(GL_ALPHA_TEST);
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.0f);
+ }
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f( 0.f, 0.f, 0.f, 1.f );
+
+ gl_rect_2d_simple( width, height );
+
+ gGL.flush();
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+
+ if (mIsVisible)
+ {
+ // composite color layers
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ 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();
+
+ gGL.setSceneBlendType(LLRender::BT_REPLACE);
+ LLGLDisable no_alpha(GL_ALPHA_TEST);
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ }
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f( 0.f, 0.f, 0.f, 0.f );
+
+ gl_rect_2d_simple( width, height );
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+ gGL.flush();
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+
+ return success;
+}
+
+
+BOOL LLTexLayerSet::isBodyRegion(const std::string& region) const
+{
+ return mInfo->mBodyRegion == region;
+}
+
+const std::string LLTexLayerSet::getBodyRegionName() const
+{
+ return mInfo->mBodyRegion;
+}
+
+
+// virtual
+void LLTexLayerSet::asLLSD(LLSD& sd) const
+{
+ sd["visible"] = LLSD::Boolean(isVisible());
+ LLSD layer_list_sd;
+ layer_list_t::const_iterator layer_iter = mLayerList.begin();
+ layer_list_t::const_iterator layer_end = mLayerList.end();
+ for(; layer_iter != layer_end; ++layer_iter);
+ {
+ LLSD layer_sd;
+ //LLTexLayerInterface* layer = (*layer_iter);
+ //if (layer)
+ //{
+ // layer->asLLSD(layer_sd);
+ //}
+ layer_list_sd.append(layer_sd);
+ }
+ LLSD mask_list_sd;
+ LLSD info_sd;
+ sd["layers"] = layer_list_sd;
+ sd["masks"] = mask_list_sd;
+ sd["info"] = info_sd;
+}
+
+
+void LLTexLayerSet::destroyComposite()
+{
+ if( mComposite )
+ {
+ mComposite = NULL;
+ }
+}
+
+LLTexLayerSetBuffer* LLTexLayerSet::getComposite()
+{
+ if (!mComposite)
+ {
+ createComposite();
+ }
+ return mComposite;
+}
+
+const LLTexLayerSetBuffer* LLTexLayerSet::getComposite() const
+{
+ return mComposite;
+}
+
+static LLFastTimer::DeclareTimer FTM_GATHER_MORPH_MASK_ALPHA("gatherMorphMaskAlpha");
+void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 origin_x, S32 origin_y, S32 width, S32 height)
+{
+ LLFastTimer t(FTM_GATHER_MORPH_MASK_ALPHA);
+ memset(data, 255, width * height);
+
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayerInterface* layer = *iter;
+ layer->gatherAlphaMasks(data, origin_x, origin_y, width, height);
+ }
+
+ // Set alpha back to that of our alpha masks.
+ renderAlphaMaskTextures(origin_x, origin_y, width, height, true);
+}
+
+static LLFastTimer::DeclareTimer FTM_RENDER_ALPHA_MASK_TEXTURES("renderAlphaMaskTextures");
+void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, bool forceClear)
+{
+ LLFastTimer t(FTM_RENDER_ALPHA_MASK_TEXTURES);
+ const LLTexLayerSetInfo *info = getInfo();
+
+ bool use_shaders = LLGLSLShader::sNoFixedFunction;
+
+ 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())
+ {
+ gGL.flush();
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(info->mStaticAlphaFileName, TRUE);
+ if( tex )
+ {
+ LLGLSUIDefault gls_ui;
+ gGL.getTexUnit(0)->bind(tex);
+ gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE );
+ gl_rect_2d_simple_tex( width, height );
+ }
+ }
+ gGL.flush();
+ }
+ 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);
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ }
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f( 0.f, 0.f, 0.f, 1.f );
+
+ gl_rect_2d_simple( width, height );
+
+ gGL.flush();
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+
+ // (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++)
+ {
+ LLTexLayerInterface* 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);
+}
+
+void LLTexLayerSet::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components)
+{
+ mAvatarAppearance->applyMorphMask(tex_data, width, height, num_components, mBakedTexIndex);
+}
+
+BOOL LLTexLayerSet::isMorphValid() const
+{
+ for(layer_list_t::const_iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ const LLTexLayerInterface* layer = *iter;
+ if (layer && !layer->isMorphValid())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLTexLayerSet::invalidateMorphMasks()
+{
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayerInterface* layer = *iter;
+ if (layer)
+ {
+ layer->invalidateMorphMasks();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLTexLayerInfo
+//-----------------------------------------------------------------------------
+LLTexLayerInfo::LLTexLayerInfo() :
+ mWriteAllChannels( FALSE ),
+ mRenderPass(LLTexLayer::RP_COLOR),
+ mFixedColor( 0.f, 0.f, 0.f, 0.f ),
+ mLocalTexture( -1 ),
+ mStaticImageIsMask( FALSE ),
+ mUseLocalTextureAlphaOnly(FALSE),
+ mIsVisibilityMask(FALSE)
+{
+}
+
+LLTexLayerInfo::~LLTexLayerInfo( )
+{
+ std::for_each(mParamColorInfoList.begin(), mParamColorInfoList.end(), DeletePointer());
+ std::for_each(mParamAlphaInfoList.begin(), mParamAlphaInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexLayerInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "layer" ) );
+
+ // name attribute
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mName ) )
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle write_all_channels_string = LLXmlTree::addAttributeString("write_all_channels");
+ node->getFastAttributeBOOL( write_all_channels_string, mWriteAllChannels );
+
+ std::string render_pass_name;
+ static LLStdStringHandle render_pass_string = LLXmlTree::addAttributeString("render_pass");
+ if( node->getFastAttributeString( render_pass_string, render_pass_name ) )
+ {
+ if( render_pass_name == "bump" )
+ {
+ mRenderPass = LLTexLayer::RP_BUMP;
+ }
+ }
+
+ // Note: layers can have either a "global_color" attrib, a "fixed_color" attrib, or a <param_color> child.
+ // global color attribute (optional)
+ 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");
+ if( node->getFastAttributeColor4U( fixed_color_string, color4u ) )
+ {
+ mFixedColor.setVec( color4u );
+ }
+
+ // <texture> optional sub-element
+ for (LLXmlTreeNode* texture_node = node->getChildByName( "texture" );
+ texture_node;
+ texture_node = node->getNextNamedChild())
+ {
+ std::string local_texture_name;
+ static LLStdStringHandle tga_file_string = LLXmlTree::addAttributeString("tga_file");
+ static LLStdStringHandle local_texture_string = LLXmlTree::addAttributeString("local_texture");
+ static LLStdStringHandle file_is_mask_string = LLXmlTree::addAttributeString("file_is_mask");
+ static LLStdStringHandle local_texture_alpha_only_string = LLXmlTree::addAttributeString("local_texture_alpha_only");
+ if( texture_node->getFastAttributeString( tga_file_string, mStaticImageFileName ) )
+ {
+ texture_node->getFastAttributeBOOL( file_is_mask_string, mStaticImageIsMask );
+ }
+ else if (texture_node->getFastAttributeString(local_texture_string, local_texture_name))
+ {
+ texture_node->getFastAttributeBOOL( local_texture_alpha_only_string, mUseLocalTextureAlphaOnly );
+
+ /* if ("upper_shirt" == local_texture_name)
+ mLocalTexture = TEX_UPPER_SHIRT; */
+ mLocalTexture = TEX_NUM_INDICES;
+ for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin();
+ iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end();
+ iter++)
+ {
+ const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second;
+ if (local_texture_name == texture_dict->mName)
+ {
+ mLocalTexture = iter->first;
+ break;
+ }
+ }
+ if (mLocalTexture == TEX_NUM_INDICES)
+ {
+ llwarns << "<texture> element has invalid local_texture attribute: " << mName << " " << local_texture_name << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<texture> element is missing a required attribute. " << mName << llendl;
+ return FALSE;
+ }
+ }
+
+ for (LLXmlTreeNode* maskNode = node->getChildByName( "morph_mask" );
+ maskNode;
+ maskNode = node->getNextNamedChild())
+ {
+ std::string morph_name;
+ static LLStdStringHandle morph_name_string = LLXmlTree::addAttributeString("morph_name");
+ if (maskNode->getFastAttributeString(morph_name_string, morph_name))
+ {
+ BOOL invert = FALSE;
+ static LLStdStringHandle invert_string = LLXmlTree::addAttributeString("invert");
+ maskNode->getFastAttributeBOOL(invert_string, invert);
+ mMorphNameList.push_back(std::pair<std::string,BOOL>(morph_name,invert));
+ }
+ }
+
+ // <param> optional sub-element (color or alpha params)
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if( child->getChildByName( "param_color" ) )
+ {
+ // <param><param_color/></param>
+ LLTexLayerParamColorInfo* info = new LLTexLayerParamColorInfo();
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mParamColorInfoList.push_back(info);
+ }
+ else if( child->getChildByName( "param_alpha" ) )
+ {
+ // <param><param_alpha/></param>
+ LLTexLayerParamAlphaInfo* info = new LLTexLayerParamAlphaInfo( );
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mParamAlphaInfoList.push_back(info);
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLTexLayerInfo::createVisualParams(LLAvatarAppearance *appearance)
+{
+ BOOL success = TRUE;
+ for (param_color_info_list_t::iterator color_info_iter = mParamColorInfoList.begin();
+ color_info_iter != mParamColorInfoList.end();
+ color_info_iter++)
+ {
+ LLTexLayerParamColorInfo * color_info = *color_info_iter;
+ LLTexLayerParamColor* param_color = new LLTexLayerParamColor(appearance);
+ 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;
+ success = FALSE;
+ }
+ }
+
+ for (param_alpha_info_list_t::iterator alpha_info_iter = mParamAlphaInfoList.begin();
+ alpha_info_iter != mParamAlphaInfoList.end();
+ alpha_info_iter++)
+ {
+ LLTexLayerParamAlphaInfo * alpha_info = *alpha_info_iter;
+ LLTexLayerParamAlpha* param_alpha = new LLTexLayerParamAlpha(appearance);
+ 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;
+ success = FALSE;
+ }
+ }
+
+ 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;
+}
+
+ETextureIndex LLTexLayerInterface::getLocalTextureIndex() const
+{
+ return (ETextureIndex) mInfo->mLocalTexture;
+}
+
+LLWearableType::EType LLTexLayerInterface::getWearableType() const
+{
+ ETextureIndex te = getLocalTextureIndex();
+ if (TEX_INVALID == te)
+ {
+ LLWearableType::EType type = LLWearableType::WT_INVALID;
+ param_color_list_t::const_iterator color_iter = mParamColorList.begin();
+ param_alpha_list_t::const_iterator alpha_iter = mParamAlphaList.begin();
+
+ for (; color_iter != mParamColorList.end(); color_iter++)
+ {
+ LLTexLayerParamColor* param = *color_iter;
+ if (param)
+ {
+ LLWearableType::EType new_type = (LLWearableType::EType)param->getWearableType();
+ if (new_type != LLWearableType::WT_INVALID && new_type != type)
+ {
+ if (type != LLWearableType::WT_INVALID)
+ {
+ return LLWearableType::WT_INVALID;
+ }
+ type = new_type;
+ }
+ }
+ }
+
+ for (; alpha_iter != mParamAlphaList.end(); alpha_iter++)
+ {
+ LLTexLayerParamAlpha* param = *alpha_iter;
+ if (param)
+ {
+ LLWearableType::EType new_type = (LLWearableType::EType)param->getWearableType();
+ if (new_type != LLWearableType::WT_INVALID && new_type != type)
+ {
+ if (type != LLWearableType::WT_INVALID)
+ {
+ return LLWearableType::WT_INVALID;
+ }
+ type = new_type;
+ }
+ }
+ }
+
+ return type;
+ }
+ return LLAvatarAppearanceDictionary::getTEWearableType(te);
+}
+
+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:
+// * 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)
+//-----------------------------------------------------------------------------
+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)
+{
+}
+
+LLTexLayer::~LLTexLayer()
+{
+ // mParamAlphaList and mParamColorList are LLViewerVisualParam's and get
+ // deleted with ~LLCharacter()
+ //std::for_each(mParamAlphaList.begin(), mParamAlphaList.end(), DeletePointer());
+ //std::for_each(mParamColorList.begin(), mParamColorList.end(), DeletePointer());
+
+ for( alpha_cache_t::iterator iter = mAlphaCache.begin();
+ iter != mAlphaCache.end(); iter++ )
+ {
+ U8* alpha_data = iter->second;
+ delete [] alpha_data;
+ }
+
+}
+
+void LLTexLayer::asLLSD(LLSD& sd) const
+{
+ // *TODO: Finish
+ sd["id"] = getUUID();
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayer::setInfo(const LLTexLayerInfo* info, LLWearable* wearable )
+{
+ return LLTexLayerInterface::setInfo(info, wearable);
+}
+
+//static
+void LLTexLayer::calculateTexLayerColor(const param_color_list_t &param_list, LLColor4 &net_color)
+{
+ 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;
+ case LLTexLayerParamColor::OP_MULTIPLY:
+ net_color = net_color * param_net;
+ break;
+ case LLTexLayerParamColor::OP_BLEND:
+ net_color = lerp(net_color, param_net, param->getWeight());
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+ }
+ net_color.clamp();
+}
+
+/*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++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ param->deleteCaches();
+ }
+}
+
+BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height)
+{
+ LLGLEnable color_mat(GL_COLOR_MATERIAL);
+ // *TODO: Is this correct?
+ //gPipeline.disableLights();
+ stop_glerror();
+ glDisable(GL_LIGHTING);
+ stop_glerror();
+
+ bool use_shaders = LLGLSLShader::sNoFixedFunction;
+
+ LLColor4 net_color;
+ BOOL color_specified = findNetColor(&net_color);
+
+ if (mTexLayerSet->getAvatarAppearance()->mIsDummy)
+ {
+ color_specified = true;
+ net_color = LLAvatarAppearance::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;
+ param_alpha_list_t::const_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 (!mHasMorph)
+ {
+ BOOL skip_layer = TRUE;
+
+ while( iter != mParamAlphaList.end() )
+ {
+ const LLTexLayerParamAlpha* param = *iter;
+
+ if( !param->getSkip() )
+ {
+ skip_layer = FALSE;
+ break;
+ }
+
+ iter++;
+ }
+
+ if( skip_layer )
+ {
+ return success;
+ }
+ }//*/
+
+ const bool force_render = true;
+ renderMorphMasks(x, y, width, height, net_color, force_render);
+ alpha_mask_specified = TRUE;
+ gGL.flush();
+ gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ONE_MINUS_DEST_ALPHA);
+ }
+
+ gGL.color4fv( net_color.mV);
+
+ if( getInfo()->mWriteAllChannels )
+ {
+ gGL.flush();
+ gGL.setSceneBlendType(LLRender::BT_REPLACE);
+ }
+
+ if( (getInfo()->mLocalTexture != -1) && !getInfo()->mUseLocalTextureAlphaOnly )
+ {
+ {
+ LLGLTexture* tex = NULL;
+ if (mLocalTextureObject && mLocalTextureObject->getImage())
+ {
+ tex = mLocalTextureObject->getImage();
+ if (mLocalTextureObject->getID() == IMG_DEFAULT_AVATAR)
+ {
+ tex = NULL;
+ }
+ }
+ else
+ {
+ llinfos << "lto not defined or image not defined: " << getInfo()->getLocalTexture() << " lto: " << mLocalTextureObject << llendl;
+ }
+// if( mTexLayerSet->getAvatarAppearance()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl ) )
+ {
+ if( tex )
+ {
+ bool no_alpha_test = getInfo()->mWriteAllChannels;
+ LLGLDisable alpha_test(no_alpha_test ? GL_ALPHA_TEST : 0);
+ if (no_alpha_test)
+ {
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ }
+ }
+
+ 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 );
+
+ gGL.getTexUnit(0)->setTextureAddressMode(old_mode);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ if (no_alpha_test)
+ {
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+ }
+ }
+// else
+// {
+// success = FALSE;
+// }
+ }
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
+ if( tex )
+ {
+ gGL.getTexUnit(0)->bind(tex, TRUE);
+ gl_rect_2d_simple_tex( width, height );
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ if(((-1 == getInfo()->mLocalTexture) ||
+ getInfo()->mUseLocalTextureAlphaOnly) &&
+ getInfo()->mStaticImageFileName.empty() &&
+ color_specified )
+ {
+ LLGLDisable no_alpha(GL_ALPHA_TEST);
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.000f);
+ }
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4fv( net_color.mV );
+ gl_rect_2d_simple( width, height );
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+
+ if( alpha_mask_specified || getInfo()->mWriteAllChannels )
+ {
+ // Restore standard blend func value
+ gGL.flush();
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ stop_glerror();
+ }
+
+ if( !success )
+ {
+ llinfos << "LLTexLayer::render() partial: " << getInfo()->mName << llendl;
+ }
+ return success;
+}
+
+const U8* LLTexLayer::getAlphaData() const
+{
+ LLCRC alpha_mask_crc;
+ 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::const_iterator iter2 = mAlphaCache.find(cache_index);
+ return (iter2 == mAlphaCache.end()) ? 0 : iter2->second;
+}
+
+BOOL LLTexLayer::findNetColor(LLColor4* net_color) const
+{
+ // Color is either:
+ // * one or more color parameters (weighted colors) (which may make use of a global color or fixed color)
+ // * a reference to a global color
+ // * a fixed color with non-zero alpha
+ // * opaque white (the default)
+
+ if( !mParamColorList.empty() )
+ {
+ if( !getGlobalColor().empty() )
+ {
+ net_color->setVec( mTexLayerSet->getAvatarAppearance()->getGlobalColor( getInfo()->mGlobalColor ) );
+ }
+ else if (getInfo()->mFixedColor.mV[VW])
+ {
+ net_color->setVec( getInfo()->mFixedColor );
+ }
+ else
+ {
+ net_color->setVec( 0.f, 0.f, 0.f, 0.f );
+ }
+
+ calculateTexLayerColor(mParamColorList, *net_color);
+ return TRUE;
+ }
+
+ if( !getGlobalColor().empty() )
+ {
+ net_color->setVec( mTexLayerSet->getAvatarAppearance()->getGlobalColor( getGlobalColor() ) );
+ return TRUE;
+ }
+
+ if( getInfo()->mFixedColor.mV[VW] )
+ {
+ net_color->setVec( getInfo()->mFixedColor );
+ return TRUE;
+ }
+
+ net_color->setToWhite();
+
+ return FALSE; // No need to draw a separate colored polygon
+}
+
+BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
+{
+ BOOL success = TRUE;
+
+ gGL.flush();
+
+ bool use_shaders = LLGLSLShader::sNoFixedFunction;
+
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask );
+ if( tex )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ }
+ gGL.getTexUnit(0)->bind(tex, TRUE);
+ gl_rect_2d_simple_tex( width, height );
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ if (getInfo()->mLocalTexture >=0 && getInfo()->mLocalTexture < TEX_NUM_INDICES)
+ {
+ LLGLTexture* tex = mLocalTextureObject->getImage();
+ if (tex)
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ }
+ gGL.getTexUnit(0)->bind(tex);
+ gl_rect_2d_simple_tex( width, height );
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ success = TRUE;
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+ }
+ }
+
+ return success;
+}
+
+/*virtual*/ void LLTexLayer::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height)
+{
+ addAlphaMask(data, originX, originY, width, height);
+}
+
+static LLFastTimer::DeclareTimer FTM_RENDER_MORPH_MASKS("renderMorphMasks");
+void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, bool force_render)
+{
+ if (!force_render && !hasMorph())
+ {
+ lldebugs << "skipping renderMorphMasks for " << getUUID() << llendl;
+ return;
+ }
+ LLFastTimer t(FTM_RENDER_MORPH_MASKS);
+ BOOL success = TRUE;
+
+ llassert( !mParamAlphaList.empty() );
+
+ bool use_shaders = LLGLSLShader::sNoFixedFunction;
+
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ }
+
+ gGL.setColorMask(false, true);
+
+ LLTexLayerParamAlpha* first_param = *mParamAlphaList.begin();
+ // Note: if the first param is a mulitply, multiply against the current buffer's alpha
+ if( !first_param || !first_param->getMultiplyBlend() )
+ {
+ LLGLDisable no_alpha(GL_ALPHA_TEST);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ // Clear the alpha
+ gGL.flush();
+ gGL.setSceneBlendType(LLRender::BT_REPLACE);
+
+ gGL.color4f( 0.f, 0.f, 0.f, 0.f );
+ gl_rect_2d_simple( width, height );
+ }
+
+ // Accumulate alphas
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ gGL.color4f( 1.f, 1.f, 1.f, 1.f );
+ for (param_alpha_list_t::iterator iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++)
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ success &= param->render( x, y, width, height );
+ if (!success && !force_render)
+ {
+ lldebugs << "Failed to render param " << param->getID() << " ; skipping morph mask." << llendl;
+ return;
+ }
+ }
+
+ // Approximates a min() function
+ gGL.flush();
+ gGL.setSceneBlendType(LLRender::BT_MULT_ALPHA);
+
+ // Accumulate the alpha component of the texture
+ if( getInfo()->mLocalTexture != -1 )
+ {
+ LLGLTexture* tex = mLocalTextureObject->getImage();
+ if( tex && (tex->getComponents() == 4) )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ 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 );
+
+ gGL.getTexUnit(0)->setTextureAddressMode(old_mode);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ }
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() && getInfo()->mStaticImageIsMask )
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
+ if( tex )
+ {
+ if( (tex->getComponents() == 4) || (tex->getComponents() == 1) )
+ {
+ 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);
+ }
+ else
+ {
+ llwarns << "Skipping rendering of " << getInfo()->mStaticImageFileName
+ << "; expected 1 or 4 components." << llendl;
+ }
+ }
+ }
+
+ // 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 );
+ if ( !is_approx_equal(layer_color.mV[VW], 1.f) )
+ {
+ LLGLDisable no_alpha(GL_ALPHA_TEST);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4fv(layer_color.mV);
+ gl_rect_2d_simple( width, height );
+ }
+
+ if (use_shaders)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+
+ LLGLSUIDefault gls_ui;
+
+ gGL.setColorMask(true, true);
+
+ if (hasMorph() && success)
+ {
+ LLCRC alpha_mask_crc;
+ 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;
+ F32 param_weight = param->getWeight();
+ alpha_mask_crc.update((U8*)&param_weight, sizeof(F32));
+ }
+
+ U32 cache_index = alpha_mask_crc.getCRC();
+ U8* alpha_data = get_if_there(mAlphaCache,cache_index,(U8*)NULL);
+ if (!alpha_data)
+ {
+ // clear out a slot if we have filled our cache
+ S32 max_cache_entries = getTexLayerSet()->getAvatarAppearance()->isSelf() ? 4 : 1;
+ while ((S32)mAlphaCache.size() >= max_cache_entries)
+ {
+ alpha_cache_t::iterator iter2 = mAlphaCache.begin(); // arbitrarily grab the first entry
+ alpha_data = iter2->second;
+ delete [] alpha_data;
+ mAlphaCache.erase(iter2);
+ }
+ alpha_data = new U8[width * height];
+ mAlphaCache[cache_index] = alpha_data;
+ glReadPixels(x, y, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_data);
+ }
+
+ getTexLayerSet()->getAvatarAppearance()->dirtyMesh();
+
+ mMorphMasksValid = TRUE;
+ getTexLayerSet()->applyMorphMask(alpha_data, width, height, 1);
+ }
+}
+
+static LLFastTimer::DeclareTimer FTM_ADD_ALPHA_MASK("addAlphaMask");
+void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height)
+{
+ LLFastTimer t(FTM_ADD_ALPHA_MASK);
+ S32 size = width * height;
+ const U8* alphaData = getAlphaData();
+ if (!alphaData && hasAlphaParams())
+ {
+ LLColor4 net_color;
+ findNetColor( &net_color );
+ // TODO: eliminate need for layer morph mask valid flag
+ invalidateMorphMasks();
+ const bool force_render = false;
+ renderMorphMasks(originX, originY, width, height, net_color, force_render);
+ alphaData = getAlphaData();
+ }
+ if (alphaData)
+ {
+ for( S32 i = 0; i < size; i++ )
+ {
+ U8 curAlpha = data[i];
+ U16 resultAlpha = curAlpha;
+ resultAlpha *= ( ((U16)alphaData[i]) + 1);
+ resultAlpha = resultAlpha >> 8;
+ data[i] = (U8)resultAlpha;
+ }
+ }
+}
+
+/*virtual*/ BOOL LLTexLayer::isInvisibleAlphaMask() const
+{
+ if (mLocalTextureObject)
+ {
+ if (mLocalTextureObject->getID() == IMG_INVISIBLE)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+LLUUID LLTexLayer::getUUID() const
+{
+ LLUUID uuid;
+ if( getInfo()->mLocalTexture != -1 )
+ {
+ LLGLTexture* tex = mLocalTextureObject->getImage();
+ if (tex)
+ {
+ uuid = mLocalTextureObject->getID();
+ }
+ }
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
+ if( tex )
+ {
+ uuid = tex->getID();
+ }
+ }
+ return uuid;
+}
+
+
+//-----------------------------------------------------------------------------
+// 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, LLAvatarAppearance* const appearance) :
+ LLTexLayerInterface(layer_set),
+ mAvatarAppearance( appearance )
+{
+}
+
+LLTexLayerTemplate::LLTexLayerTemplate(const LLTexLayerTemplate &layer) :
+ LLTexLayerInterface(layer),
+ mAvatarAppearance(layer.getAvatarAppearance())
+{
+}
+
+LLTexLayerTemplate::~LLTexLayerTemplate()
+{
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+/*virtual*/ BOOL LLTexLayerTemplate::setInfo(const LLTexLayerInfo* info, LLWearable* wearable )
+{
+ return LLTexLayerInterface::setInfo(info, wearable);
+}
+
+U32 LLTexLayerTemplate::updateWearableCache() const
+{
+ mWearableCache.clear();
+
+ LLWearableType::EType wearable_type = getWearableType();
+ if (LLWearableType::WT_INVALID == wearable_type)
+ {
+ //this isn't a cloneable layer
+ return 0;
+ }
+ U32 num_wearables = getAvatarAppearance()->getWearableData()->getWearableCount(wearable_type);
+ U32 added = 0;
+ for (U32 i = 0; i < num_wearables; i++)
+ {
+ LLWearable* wearable = getAvatarAppearance()->getWearableData()->getWearable(wearable_type, i);
+ if (!wearable)
+ {
+ 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;
+}
+
+/*virtual*/ BOOL LLTexLayerTemplate::render(S32 x, S32 y, S32 width, S32 height)
+{
+ if(!mInfo)
+ {
+ return FALSE ;
+ }
+
+ 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(mAvatarAppearance);
+ layer->setLTO(lto);
+ success &= layer->render(x,y,width,height);
+ }
+ }
+
+ return success;
+}
+
+/*virtual*/ BOOL LLTexLayerTemplate::blendAlphaTexture( S32 x, S32 y, S32 width, S32 height) // Multiplies a single alpha texture against the frame buffer
+{
+ 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;
+}
+
+/*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)
+{
+ mHasMorph = newval;
+ U32 num_wearables = updateWearableCache();
+ for (U32 i = 0; i < num_wearables; i++)
+ {
+ LLTexLayer *layer = getLayer(i);
+ if (layer)
+ {
+ layer->setHasMorph(newval);
+ }
+ }
+}
+
+/*virtual*/ void LLTexLayerTemplate::deleteCaches()
+{
+ U32 num_wearables = updateWearableCache();
+ for (U32 i = 0; i < num_wearables; i++)
+ {
+ LLTexLayer *layer = getLayer(i);
+ if (layer)
+ {
+ layer->deleteCaches();
+ }
+ }
+}
+
+/*virtual*/ BOOL LLTexLayerTemplate::isInvisibleAlphaMask() const
+{
+ 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;
+}
+
+
+//-----------------------------------------------------------------------------
+// finds a specific layer based on a passed in name
+//-----------------------------------------------------------------------------
+LLTexLayerInterface* LLTexLayerSet::findLayerByName(const std::string& name)
+{
+ for (layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayerInterface* layer = *iter;
+ if (layer->getName() == name)
+ {
+ return layer;
+ }
+ }
+ for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++ )
+ {
+ LLTexLayerInterface* layer = *iter;
+ if (layer->getName() == name)
+ {
+ return layer;
+ }
+ }
+ return NULL;
+}
+
+void LLTexLayerSet::cloneTemplates(LLLocalTextureObject *lto, LLAvatarAppearanceDefines::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
+//-----------------------------------------------------------------------------
+
+LLTexLayerStaticImageList::LLTexLayerStaticImageList() :
+ mGLBytes(0),
+ mTGABytes(0),
+ mImageNames(16384)
+{
+}
+
+LLTexLayerStaticImageList::~LLTexLayerStaticImageList()
+{
+ deleteCachedImages();
+}
+
+void LLTexLayerStaticImageList::dumpByteCount() const
+{
+ llinfos << "Avatar Static Textures " <<
+ "KB GL:" << (mGLBytes / 1024) <<
+ "KB TGA:" << (mTGABytes / 1024) << "KB" << llendl;
+}
+
+void LLTexLayerStaticImageList::deleteCachedImages()
+{
+ if( mGLBytes || mTGABytes )
+ {
+ llinfos << "Clearing Static Textures " <<
+ "KB GL:" << (mGLBytes / 1024) <<
+ "KB TGA:" << (mTGABytes / 1024) << "KB" << llendl;
+
+ //mStaticImageLists uses LLPointers, clear() will cause deletion
+
+ mStaticImageListTGA.clear();
+ mStaticImageList.clear();
+
+ mGLBytes = 0;
+ mTGABytes = 0;
+ }
+}
+
+// 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 getTexture()
+
+// Returns an LLImageTGA that contains the encoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+static LLFastTimer::DeclareTimer FTM_LOAD_STATIC_TGA("getImageTGA");
+LLImageTGA* LLTexLayerStaticImageList::getImageTGA(const std::string& file_name)
+{
+ LLFastTimer t(FTM_LOAD_STATIC_TGA);
+ const char *namekey = mImageNames.addString(file_name);
+ image_tga_map_t::const_iterator iter = mStaticImageListTGA.find(namekey);
+ if( iter != mStaticImageListTGA.end() )
+ {
+ return iter->second;
+ }
+ else
+ {
+ std::string path;
+ path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name);
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA( path );
+ if( image_tga->getDataSize() > 0 )
+ {
+ mStaticImageListTGA[ namekey ] = image_tga;
+ mTGABytes += image_tga->getDataSize();
+ return image_tga;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+}
+
+// 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.
+static LLFastTimer::DeclareTimer FTM_LOAD_STATIC_TEXTURE("getTexture");
+LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name, BOOL is_mask)
+{
+ LLFastTimer t(FTM_LOAD_STATIC_TEXTURE);
+ LLPointer<LLGLTexture> tex;
+ const char *namekey = mImageNames.addString(file_name);
+
+ texture_map_t::const_iterator iter = mStaticImageList.find(namekey);
+ if( iter != mStaticImageList.end() )
+ {
+ tex = iter->second;
+ }
+ else
+ {
+ llassert(gTextureManagerBridgep);
+ tex = gTextureManagerBridgep->getLocalTexture( FALSE );
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw;
+ if( loadImageRaw( file_name, image_raw ) )
+ {
+ if( (image_raw->getComponents() == 1) && is_mask )
+ {
+ // Convert grayscale alpha masks from single channel into RGBA.
+ // Fill RGB with black to allow fixed function gl calls
+ // to match shader implementation.
+ LLPointer<LLImageRaw> alpha_image_raw = image_raw;
+ image_raw = new LLImageRaw(image_raw->getWidth(),
+ image_raw->getHeight(),
+ 4);
+
+ image_raw->copyUnscaledAlphaMask(alpha_image_raw, LLColor4U::black);
+ }
+ tex->createGLTexture(0, image_raw, 0, TRUE, LLGLTexture::LOCAL);
+
+ gGL.getTexUnit(0)->bind(tex);
+ tex->setAddressMode(LLTexUnit::TAM_CLAMP);
+
+ mStaticImageList [ namekey ] = tex;
+ mGLBytes += (S32)tex->getWidth() * tex->getHeight() * tex->getComponents();
+ }
+ else
+ {
+ tex = NULL;
+ }
+ }
+
+ return tex;
+}
+
+// Reads a .tga file, decodes it, and puts the decoded data in image_raw.
+// Returns TRUE if successful.
+static LLFastTimer::DeclareTimer FTM_LOAD_IMAGE_RAW("loadImageRaw");
+BOOL LLTexLayerStaticImageList::loadImageRaw(const std::string& file_name, LLImageRaw* image_raw)
+{
+ LLFastTimer t(FTM_LOAD_IMAGE_RAW);
+ BOOL success = FALSE;
+ std::string path;
+ path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name);
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA( path );
+ if( image_tga->getDataSize() > 0 )
+ {
+ // Copy data from tga to raw.
+ success = image_tga->decode( image_raw );
+ }
+
+ return success;
+}
+
diff --git a/indra/llappearance/lltexlayer.h b/indra/llappearance/lltexlayer.h
new file mode 100644
index 0000000000..959d6e499a
--- /dev/null
+++ b/indra/llappearance/lltexlayer.h
@@ -0,0 +1,315 @@
+/**
+ * @file lltexlayer.h
+ * @brief Texture layer classes. Used for avatars.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXLAYER_H
+#define LL_LLTEXLAYER_H
+
+#include <deque>
+#include "llglslshader.h"
+#include "llgltexture.h"
+#include "llavatarappearancedefines.h"
+#include "lltexlayerparams.h"
+
+class LLAvatarAppearance;
+class LLImageTGA;
+class LLImageRaw;
+class LLLocalTextureObject;
+class LLXmlTreeNode;
+class LLTexLayerSet;
+class LLTexLayerSetInfo;
+class LLTexLayerInfo;
+class LLTexLayerSetBuffer;
+class LLWearable;
+class LLViewerVisualParam;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerInterface
+//
+// Interface class to generalize functionality shared by LLTexLayer
+// and LLTexLayerTemplate.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTexLayerInterface
+{
+public:
+ enum ERenderPass
+ {
+ RP_COLOR,
+ RP_BUMP,
+ RP_SHINE
+ };
+
+ LLTexLayerInterface(LLTexLayerSet* const layer_set);
+ LLTexLayerInterface(const LLTexLayerInterface &layer, LLWearable *wearable);
+ virtual ~LLTexLayerInterface() {}
+
+ virtual BOOL render(S32 x, S32 y, S32 width, S32 height) = 0;
+ virtual void deleteCaches() = 0;
+ virtual BOOL blendAlphaTexture(S32 x, S32 y, S32 width, S32 height) = 0;
+ virtual BOOL isInvisibleAlphaMask() const = 0;
+
+ const LLTexLayerInfo* getInfo() const { return mInfo; }
+ virtual BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable); // sets mInfo, calls initialization functions
+ LLWearableType::EType getWearableType() const;
+ LLAvatarAppearanceDefines::ETextureIndex getLocalTextureIndex() const;
+
+ const std::string& getName() const;
+ const LLTexLayerSet* const getTexLayerSet() const { return mTexLayerSet; }
+ LLTexLayerSet* const getTexLayerSet() { return mTexLayerSet; }
+
+ void invalidateMorphMasks();
+ virtual void setHasMorph(BOOL newval) { mHasMorph = newval; }
+ BOOL hasMorph() const { return mHasMorph; }
+ BOOL isMorphValid() const { return mMorphMasksValid; }
+
+ void requestUpdate();
+ virtual void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height) = 0;
+ BOOL hasAlphaParams() const { return !mParamAlphaList.empty(); }
+
+ ERenderPass getRenderPass() const;
+ BOOL isVisibilityMask() const;
+
+ virtual void asLLSD(LLSD& sd) const {}
+
+protected:
+ const std::string& getGlobalColor() const;
+ LLViewerVisualParam* getVisualParamPtr(S32 index) const;
+
+protected:
+ LLTexLayerSet* const mTexLayerSet;
+ const LLTexLayerInfo* mInfo;
+ BOOL mMorphMasksValid;
+ BOOL mHasMorph;
+
+ // Layers can have either mParamColorList, mGlobalColor, or mFixedColor. They are looked for in that order.
+ param_color_list_t mParamColorList;
+ param_alpha_list_t mParamAlphaList;
+ // mGlobalColor name stored in mInfo
+ // mFixedColor value stored in mInfo
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerTemplate
+//
+// Only exists for llvoavatarself.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTexLayerTemplate : public LLTexLayerInterface
+{
+public:
+ LLTexLayerTemplate(LLTexLayerSet* const layer_set, LLAvatarAppearance* const appearance);
+ LLTexLayerTemplate(const LLTexLayerTemplate &layer);
+ /*virtual*/ ~LLTexLayerTemplate();
+ /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height);
+ /*virtual*/ BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable); // This sets mInfo and calls initialization functions
+ /*virtual*/ BOOL blendAlphaTexture(S32 x, S32 y, S32 width, S32 height); // Multiplies a single alpha texture against the frame buffer
+ /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height);
+ /*virtual*/ void setHasMorph(BOOL newval);
+ /*virtual*/ void deleteCaches();
+ /*virtual*/ BOOL isInvisibleAlphaMask() const;
+protected:
+ U32 updateWearableCache() const;
+ LLTexLayer* getLayer(U32 i) const;
+ LLAvatarAppearance* getAvatarAppearance() const { return mAvatarAppearance; }
+private:
+ LLAvatarAppearance* const mAvatarAppearance; // note: backlink only; don't make this an LLPointer.
+ typedef std::vector<LLWearable*> wearable_cache_t;
+ mutable wearable_cache_t mWearableCache; // mutable b/c most get- require updating this cache
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayer
+//
+// A single texture layer. Only exists for llvoavatarself.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTexLayer : public LLTexLayerInterface
+{
+public:
+ LLTexLayer(LLTexLayerSet* const layer_set);
+ LLTexLayer(const LLTexLayer &layer, LLWearable *wearable);
+ LLTexLayer(const LLTexLayerTemplate &layer_template, LLLocalTextureObject *lto, LLWearable *wearable);
+ /*virtual*/ ~LLTexLayer();
+
+ /*virtual*/ BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable); // This sets mInfo and calls initialization functions
+ /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height);
+
+ /*virtual*/ void deleteCaches();
+ const U8* getAlphaData() const;
+
+ BOOL findNetColor(LLColor4* color) const;
+ /*virtual*/ BOOL blendAlphaTexture(S32 x, S32 y, S32 width, S32 height); // Multiplies a single alpha texture against the frame buffer
+ /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height);
+ void renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, bool force_render);
+ void addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height);
+ /*virtual*/ BOOL isInvisibleAlphaMask() const;
+
+ void setLTO(LLLocalTextureObject *lto) { mLocalTextureObject = lto; }
+ LLLocalTextureObject* getLTO() { return mLocalTextureObject; }
+
+ /*virtual*/ void asLLSD(LLSD& sd) const;
+
+ static void calculateTexLayerColor(const param_color_list_t &param_list, LLColor4 &net_color);
+protected:
+ LLUUID getUUID() const;
+ typedef std::map<U32, U8*> alpha_cache_t;
+ alpha_cache_t mAlphaCache;
+ LLLocalTextureObject* mLocalTextureObject;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerSet
+//
+// An ordered set of texture layers that gets composited into a single texture.
+// Only exists for llvoavatarself.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTexLayerSet
+{
+ friend class LLTexLayerSetBuffer;
+public:
+ LLTexLayerSet(LLAvatarAppearance* const appearance);
+ virtual ~LLTexLayerSet();
+
+ LLTexLayerSetBuffer* getComposite();
+ const LLTexLayerSetBuffer* getComposite() const; // Do not create one if it doesn't exist.
+ virtual void createComposite() = 0;
+ void destroyComposite();
+ void gatherMorphMaskAlpha(U8 *data, S32 origin_x, S32 origin_y, S32 width, S32 height);
+
+ const LLTexLayerSetInfo* getInfo() const { return mInfo; }
+ BOOL setInfo(const LLTexLayerSetInfo *info); // This sets mInfo and calls initialization functions
+
+ BOOL render(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) const;
+ void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components);
+ BOOL isMorphValid() const;
+ virtual void requestUpdate() = 0;
+ void invalidateMorphMasks();
+ void deleteCaches();
+ LLTexLayerInterface* findLayerByName(const std::string& name);
+ void cloneTemplates(LLLocalTextureObject *lto, LLAvatarAppearanceDefines::ETextureIndex tex_index, LLWearable* wearable);
+
+ LLAvatarAppearance* getAvatarAppearance() const { return mAvatarAppearance; }
+ const std::string getBodyRegionName() const;
+ BOOL hasComposite() const { return (mComposite.notNull()); }
+ LLAvatarAppearanceDefines::EBakedTextureIndex getBakedTexIndex() const { return mBakedTexIndex; }
+ void setBakedTexIndex(LLAvatarAppearanceDefines::EBakedTextureIndex index) { mBakedTexIndex = index; }
+ BOOL isVisible() const { return mIsVisible; }
+
+ static BOOL sHasCaches;
+
+ virtual void asLLSD(LLSD& sd) const;
+
+protected:
+ typedef std::vector<LLTexLayerInterface *> layer_list_t;
+ layer_list_t mLayerList;
+ layer_list_t mMaskLayerList;
+ LLPointer<LLTexLayerSetBuffer> mComposite;
+ LLAvatarAppearance* const mAvatarAppearance; // note: backlink only; don't make this an LLPointer.
+ BOOL mIsVisible;
+
+ LLAvatarAppearanceDefines::EBakedTextureIndex mBakedTexIndex;
+ const LLTexLayerSetInfo* mInfo;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerSetInfo
+//
+// Contains shared layer set data.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTexLayerSetInfo
+{
+ friend class LLTexLayerSet;
+public:
+ LLTexLayerSetInfo();
+ ~LLTexLayerSetInfo();
+ BOOL parseXml(LLXmlTreeNode* node);
+ void createVisualParams(LLAvatarAppearance *appearance);
+ S32 getWidth() const { return mWidth; }
+ S32 getHeight() const { return mHeight; }
+protected:
+ std::string mBodyRegion;
+ S32 mWidth;
+ S32 mHeight;
+ std::string mStaticAlphaFileName;
+ BOOL mClearAlpha; // Set alpha to 1 for this layerset (if there is no mStaticAlphaFileName)
+ typedef std::vector<LLTexLayerInfo*> layer_info_list_t;
+ layer_info_list_t mLayerInfoList;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerSetBuffer
+//
+// The composite image that a LLTexLayerSet writes to. Each LLTexLayerSet has one.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTexLayerSetBuffer : public virtual LLRefCount
+{
+ LOG_CLASS(LLTexLayerSetBuffer);
+
+public:
+ LLTexLayerSetBuffer(LLTexLayerSet* const owner);
+ virtual ~LLTexLayerSetBuffer();
+
+protected:
+ void pushProjection() const;
+ void popProjection() const;
+ virtual void preRenderTexLayerSet();
+ virtual void midRenderTexLayerSet(BOOL success) {}
+ virtual void postRenderTexLayerSet(BOOL success);
+ virtual S32 getCompositeOriginX() const = 0;
+ virtual S32 getCompositeOriginY() const = 0;
+ virtual S32 getCompositeWidth() const = 0;
+ virtual S32 getCompositeHeight() const = 0;
+ BOOL renderTexLayerSet();
+
+ LLTexLayerSet* const mTexLayerSet;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerStaticImageList
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTexLayerStaticImageList : public LLSingleton<LLTexLayerStaticImageList>
+{
+public:
+ LLTexLayerStaticImageList();
+ ~LLTexLayerStaticImageList();
+ LLGLTexture* getTexture(const std::string& file_name, BOOL is_mask);
+ LLImageTGA* getImageTGA(const std::string& file_name);
+ void deleteCachedImages();
+ void dumpByteCount() const;
+protected:
+ BOOL loadImageRaw(const std::string& file_name, LLImageRaw* image_raw);
+private:
+ LLStringTable mImageNames;
+ typedef std::map<const char*, LLPointer<LLGLTexture> > texture_map_t;
+ texture_map_t mStaticImageList;
+ typedef std::map<const char*, LLPointer<LLImageTGA> > image_tga_map_t;
+ image_tga_map_t mStaticImageListTGA;
+ S32 mGLBytes;
+ S32 mTGABytes;
+};
+
+#endif // LL_LLTEXLAYER_H
diff --git a/indra/llappearance/lltexlayerparams.cpp b/indra/llappearance/lltexlayerparams.cpp
new file mode 100644
index 0000000000..1adc294705
--- /dev/null
+++ b/indra/llappearance/lltexlayerparams.cpp
@@ -0,0 +1,571 @@
+/**
+ * @file lltexlayerparams.cpp
+ * @brief Texture layer parameters
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltexlayerparams.h"
+
+#include "llavatarappearance.h"
+#include "llimagetga.h"
+#include "llquantize.h"
+#include "lltexlayer.h"
+#include "lltexturemanagerbridge.h"
+//#include "llrender2dutils.h"
+#include "..\llui\llui.h"
+#include "llwearable.h"
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParam
+//-----------------------------------------------------------------------------
+LLTexLayerParam::LLTexLayerParam(LLTexLayerInterface *layer) :
+ mTexLayer(layer),
+ mAvatarAppearance(NULL)
+{
+ if (mTexLayer != NULL)
+ {
+ mAvatarAppearance = mTexLayer->getTexLayerSet()->getAvatarAppearance();
+ }
+ else
+ {
+ llerrs << "LLTexLayerParam constructor passed with NULL reference for layer!" << llendl;
+ }
+}
+
+LLTexLayerParam::LLTexLayerParam(LLAvatarAppearance *appearance) :
+ mTexLayer(NULL),
+ mAvatarAppearance(appearance)
+{
+}
+
+
+BOOL LLTexLayerParam::setInfo(LLViewerVisualParamInfo *info, BOOL add_to_appearance)
+{
+ LLViewerVisualParam::setInfo(info);
+
+ if (add_to_appearance)
+ {
+ mAvatarAppearance->addVisualParam( this);
+ this->setParamLocation(mAvatarAppearance->isSelf() ? LOC_AV_SELF : LOC_AV_OTHER);
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlpha
+//-----------------------------------------------------------------------------
+
+// static
+LLTexLayerParamAlpha::param_alpha_ptr_list_t LLTexLayerParamAlpha::sInstances;
+
+// static
+void LLTexLayerParamAlpha::dumpCacheByteCount()
+{
+ S32 gl_bytes = 0;
+ getCacheByteCount( &gl_bytes);
+ llinfos << "Processed Alpha Texture Cache GL:" << (gl_bytes/1024) << "KB" << llendl;
+}
+
+// static
+void LLTexLayerParamAlpha::getCacheByteCount(S32* gl_bytes)
+{
+ *gl_bytes = 0;
+
+ for (param_alpha_ptr_list_t::iterator iter = sInstances.begin();
+ iter != sInstances.end(); iter++)
+ {
+ LLTexLayerParamAlpha* instance = *iter;
+ LLGLTexture* tex = instance->mCachedProcessedTexture;
+ if (tex)
+ {
+ S32 bytes = (S32)tex->getWidth() * tex->getHeight() * tex->getComponents();
+
+ if (tex->hasGLTexture())
+ {
+ *gl_bytes += bytes;
+ }
+ }
+ }
+}
+
+LLTexLayerParamAlpha::LLTexLayerParamAlpha(LLTexLayerInterface* layer) :
+ LLTexLayerParam(layer),
+ mCachedProcessedTexture(NULL),
+ mNeedsCreateTexture(FALSE),
+ mStaticImageInvalid(FALSE),
+ mAvgDistortionVec(1.f, 1.f, 1.f),
+ mCachedEffectiveWeight(0.f)
+{
+ sInstances.push_front(this);
+}
+
+LLTexLayerParamAlpha::LLTexLayerParamAlpha(LLAvatarAppearance* appearance) :
+ LLTexLayerParam(appearance),
+ mCachedProcessedTexture(NULL),
+ mNeedsCreateTexture(FALSE),
+ mStaticImageInvalid(FALSE),
+ mAvgDistortionVec(1.f, 1.f, 1.f),
+ mCachedEffectiveWeight(0.f)
+{
+ sInstances.push_front(this);
+}
+
+
+LLTexLayerParamAlpha::~LLTexLayerParamAlpha()
+{
+ deleteCaches();
+ sInstances.remove(this);
+}
+
+/*virtual*/ LLViewerVisualParam* LLTexLayerParamAlpha::cloneParam(LLWearable* wearable) const
+{
+ LLTexLayerParamAlpha *new_param = new LLTexLayerParamAlpha(mTexLayer);
+ *new_param = *this;
+ return new_param;
+}
+
+void LLTexLayerParamAlpha::deleteCaches()
+{
+ mStaticImageTGA = NULL; // deletes image
+ mCachedProcessedTexture = NULL;
+ mStaticImageRaw = NULL;
+ mNeedsCreateTexture = FALSE;
+}
+
+BOOL LLTexLayerParamAlpha::getMultiplyBlend() const
+{
+ return ((LLTexLayerParamAlphaInfo *)getInfo())->mMultiplyBlend;
+}
+
+void LLTexLayerParamAlpha::setWeight(F32 weight, BOOL upload_bake)
+{
+ if (mIsAnimating || mTexLayer == NULL)
+ {
+ return;
+ }
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ F32 new_weight = llclamp(weight, min_weight, max_weight);
+ U8 cur_u8 = F32_to_U8(mCurWeight, min_weight, max_weight);
+ U8 new_u8 = F32_to_U8(new_weight, min_weight, max_weight);
+ if (cur_u8 != new_u8)
+ {
+ mCurWeight = new_weight;
+
+ if ((mAvatarAppearance->getSex() & getSex()) &&
+ (mAvatarAppearance->isSelf() && !mIsDummy)) // only trigger a baked texture update if we're changing a wearable's visual param.
+ {
+ mAvatarAppearance->invalidateComposite(mTexLayer->getTexLayerSet(), upload_bake);
+ mTexLayer->invalidateMorphMasks();
+ }
+ }
+}
+
+void LLTexLayerParamAlpha::setAnimationTarget(F32 target_value, BOOL upload_bake)
+{
+ // do not animate dummy parameters
+ if (mIsDummy)
+ {
+ setWeight(target_value, upload_bake);
+ return;
+ }
+
+ mTargetWeight = target_value;
+ setWeight(target_value, upload_bake);
+ mIsAnimating = TRUE;
+ if (mNext)
+ {
+ mNext->setAnimationTarget(target_value, upload_bake);
+ }
+}
+
+void LLTexLayerParamAlpha::animate(F32 delta, BOOL upload_bake)
+{
+ if (mNext)
+ {
+ mNext->animate(delta, upload_bake);
+ }
+}
+
+BOOL LLTexLayerParamAlpha::getSkip() const
+{
+ if (!mTexLayer)
+ {
+ return TRUE;
+ }
+
+ const LLAvatarAppearance *appearance = mTexLayer->getTexLayerSet()->getAvatarAppearance();
+
+ if (((LLTexLayerParamAlphaInfo *)getInfo())->mSkipIfZeroWeight)
+ {
+ F32 effective_weight = (appearance->getSex() & getSex()) ? mCurWeight : getDefaultWeight();
+ if (is_approx_zero(effective_weight))
+ {
+ return TRUE;
+ }
+ }
+
+ LLWearableType::EType type = (LLWearableType::EType)getWearableType();
+ if ((type != LLWearableType::WT_INVALID) && !appearance->isWearingWearableType(type))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static LLFastTimer::DeclareTimer FTM_TEX_LAYER_PARAM_ALPHA("alpha render");
+BOOL LLTexLayerParamAlpha::render(S32 x, S32 y, S32 width, S32 height)
+{
+ LLFastTimer t(FTM_TEX_LAYER_PARAM_ALPHA);
+ BOOL success = TRUE;
+
+ if (!mTexLayer)
+ {
+ return success;
+ }
+
+ F32 effective_weight = (mTexLayer->getTexLayerSet()->getAvatarAppearance()->getSex() & getSex()) ? mCurWeight : getDefaultWeight();
+ BOOL weight_changed = effective_weight != mCachedEffectiveWeight;
+ if (getSkip())
+ {
+ return success;
+ }
+
+ LLTexLayerParamAlphaInfo *info = (LLTexLayerParamAlphaInfo *)getInfo();
+ gGL.flush();
+ if (info->mMultiplyBlend)
+ {
+ gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO); // Multiplication: approximates a min() function
+ }
+ else
+ {
+ gGL.setSceneBlendType(LLRender::BT_ADD); // Addition: approximates a max() function
+ }
+
+ if (!info->mStaticImageFileName.empty() && !mStaticImageInvalid)
+ {
+ if (mStaticImageTGA.isNull())
+ {
+ // Don't load the image file until we actually need it the first time. Like now.
+ mStaticImageTGA = LLTexLayerStaticImageList::getInstance()->getImageTGA(info->mStaticImageFileName);
+ // We now have something in one of our caches
+ LLTexLayerSet::sHasCaches |= mStaticImageTGA.notNull() ? TRUE : FALSE;
+
+ if (mStaticImageTGA.isNull())
+ {
+ llwarns << "Unable to load static file: " << info->mStaticImageFileName << llendl;
+ mStaticImageInvalid = TRUE; // don't try again.
+ return FALSE;
+ }
+ }
+
+ const S32 image_tga_width = mStaticImageTGA->getWidth();
+ const S32 image_tga_height = mStaticImageTGA->getHeight();
+ if (!mCachedProcessedTexture ||
+ (mCachedProcessedTexture->getWidth() != image_tga_width) ||
+ (mCachedProcessedTexture->getHeight() != image_tga_height) ||
+ (weight_changed))
+ {
+ mCachedEffectiveWeight = effective_weight;
+
+ if (!mCachedProcessedTexture)
+ {
+ llassert(gTextureManagerBridgep);
+ mCachedProcessedTexture = gTextureManagerBridgep->getLocalTexture(image_tga_width, image_tga_height, 1, FALSE);
+
+ // We now have something in one of our caches
+ LLTexLayerSet::sHasCaches |= mCachedProcessedTexture ? TRUE : FALSE;
+
+ mCachedProcessedTexture->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
+ }
+
+ // Applies domain and effective weight to data as it is decoded. Also resizes the raw image if needed.
+ mStaticImageRaw = NULL;
+ mStaticImageRaw = new LLImageRaw;
+ mStaticImageTGA->decodeAndProcess(mStaticImageRaw, info->mDomain, effective_weight);
+ mNeedsCreateTexture = TRUE;
+ lldebugs << "Built Cached Alpha: " << info->mStaticImageFileName << ": (" << mStaticImageRaw->getWidth() << ", " << mStaticImageRaw->getHeight() << ") " << "Domain: " << info->mDomain << " Weight: " << effective_weight << llendl;
+ }
+
+ if (mCachedProcessedTexture)
+ {
+ {
+ // Create the GL texture, and then hang onto it for future use.
+ if (mNeedsCreateTexture)
+ {
+ mCachedProcessedTexture->createGLTexture(0, mStaticImageRaw);
+ mNeedsCreateTexture = FALSE;
+ gGL.getTexUnit(0)->bind(mCachedProcessedTexture);
+ mCachedProcessedTexture->setAddressMode(LLTexUnit::TAM_CLAMP);
+ }
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ gGL.getTexUnit(0)->bind(mCachedProcessedTexture);
+ gl_rect_2d_simple_tex(width, height);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ stop_glerror();
+ }
+ }
+
+ // Don't keep the cache for other people's avatars
+ // (It's not really a "cache" in that case, but the logic is the same)
+ if (!mAvatarAppearance->isSelf())
+ {
+ mCachedProcessedTexture = NULL;
+ }
+ }
+ else
+ {
+ LLGLDisable no_alpha(GL_ALPHA_TEST);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f(0.f, 0.f, 0.f, effective_weight);
+ gl_rect_2d_simple(width, height);
+ }
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlphaInfo
+//-----------------------------------------------------------------------------
+LLTexLayerParamAlphaInfo::LLTexLayerParamAlphaInfo() :
+ mMultiplyBlend(FALSE),
+ mSkipIfZeroWeight(FALSE),
+ mDomain(0.f)
+{
+}
+
+BOOL LLTexLayerParamAlphaInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert(node->hasName("param") && node->getChildByName("param_alpha"));
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ LLXmlTreeNode* param_alpha_node = node->getChildByName("param_alpha");
+ if (!param_alpha_node)
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle tga_file_string = LLXmlTree::addAttributeString("tga_file");
+ if (param_alpha_node->getFastAttributeString(tga_file_string, mStaticImageFileName))
+ {
+ // Don't load the image file until it's actually needed.
+ }
+// else
+// {
+// llwarns << "<param_alpha> element is missing tga_file attribute." << llendl;
+// }
+
+ static LLStdStringHandle multiply_blend_string = LLXmlTree::addAttributeString("multiply_blend");
+ param_alpha_node->getFastAttributeBOOL(multiply_blend_string, mMultiplyBlend);
+
+ static LLStdStringHandle skip_if_zero_string = LLXmlTree::addAttributeString("skip_if_zero");
+ param_alpha_node->getFastAttributeBOOL(skip_if_zero_string, mSkipIfZeroWeight);
+
+ static LLStdStringHandle domain_string = LLXmlTree::addAttributeString("domain");
+ param_alpha_node->getFastAttributeF32(domain_string, mDomain);
+
+ return TRUE;
+}
+
+
+
+
+LLTexLayerParamColor::LLTexLayerParamColor(LLTexLayerInterface* layer) :
+ LLTexLayerParam(layer),
+ mAvgDistortionVec(1.f, 1.f, 1.f)
+{
+}
+
+LLTexLayerParamColor::LLTexLayerParamColor(LLAvatarAppearance *appearance) :
+ LLTexLayerParam(appearance),
+ mAvgDistortionVec(1.f, 1.f, 1.f)
+{
+}
+
+LLTexLayerParamColor::~LLTexLayerParamColor()
+{
+}
+
+/*virtual*/ LLViewerVisualParam* LLTexLayerParamColor::cloneParam(LLWearable* wearable) const
+{
+ LLTexLayerParamColor *new_param = new LLTexLayerParamColor(mTexLayer);
+ *new_param = *this;
+ return new_param;
+}
+
+LLColor4 LLTexLayerParamColor::getNetColor() const
+{
+ const LLTexLayerParamColorInfo *info = (LLTexLayerParamColorInfo *)getInfo();
+
+ llassert(info->mNumColors >= 1);
+
+ F32 effective_weight = (mAvatarAppearance && (mAvatarAppearance->getSex() & getSex())) ? mCurWeight : getDefaultWeight();
+
+ S32 index_last = info->mNumColors - 1;
+ F32 scaled_weight = effective_weight * index_last;
+ S32 index_start = (S32) scaled_weight;
+ S32 index_end = index_start + 1;
+ if (index_start == index_last)
+ {
+ return info->mColors[index_last];
+ }
+ else
+ {
+ F32 weight = scaled_weight - index_start;
+ const LLColor4 *start = &info->mColors[ index_start ];
+ const LLColor4 *end = &info->mColors[ index_end ];
+ return LLColor4((1.f - weight) * start->mV[VX] + weight * end->mV[VX],
+ (1.f - weight) * start->mV[VY] + weight * end->mV[VY],
+ (1.f - weight) * start->mV[VZ] + weight * end->mV[VZ],
+ (1.f - weight) * start->mV[VW] + weight * end->mV[VW]);
+ }
+}
+
+void LLTexLayerParamColor::setWeight(F32 weight, BOOL upload_bake)
+{
+ if (mIsAnimating)
+ {
+ return;
+ }
+
+ const LLTexLayerParamColorInfo *info = (LLTexLayerParamColorInfo *)getInfo();
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ F32 new_weight = llclamp(weight, min_weight, max_weight);
+ U8 cur_u8 = F32_to_U8(mCurWeight, min_weight, max_weight);
+ U8 new_u8 = F32_to_U8(new_weight, min_weight, max_weight);
+ if (cur_u8 != new_u8)
+ {
+ mCurWeight = new_weight;
+
+ if (info->mNumColors <= 0)
+ {
+ // This will happen when we set the default weight the first time.
+ return;
+ }
+
+ if ((mAvatarAppearance->getSex() & getSex()) && (mAvatarAppearance->isSelf() && !mIsDummy)) // only trigger a baked texture update if we're changing a wearable's visual param.
+ {
+ onGlobalColorChanged(upload_bake);
+ if (mTexLayer)
+ {
+ mAvatarAppearance->invalidateComposite(mTexLayer->getTexLayerSet(), upload_bake);
+ }
+ }
+
+// llinfos << "param " << mName << " = " << new_weight << llendl;
+ }
+}
+
+void LLTexLayerParamColor::setAnimationTarget(F32 target_value, BOOL upload_bake)
+{
+ // set value first then set interpolating flag to ignore further updates
+ mTargetWeight = target_value;
+ setWeight(target_value, upload_bake);
+ mIsAnimating = TRUE;
+ if (mNext)
+ {
+ mNext->setAnimationTarget(target_value, upload_bake);
+ }
+}
+
+void LLTexLayerParamColor::animate(F32 delta, BOOL upload_bake)
+{
+ if (mNext)
+ {
+ mNext->animate(delta, upload_bake);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamColorInfo
+//-----------------------------------------------------------------------------
+LLTexLayerParamColorInfo::LLTexLayerParamColorInfo() :
+ mOperation(LLTexLayerParamColor::OP_ADD),
+ mNumColors(0)
+{
+}
+
+BOOL LLTexLayerParamColorInfo::parseXml(LLXmlTreeNode *node)
+{
+ llassert(node->hasName("param") && node->getChildByName("param_color"));
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ LLXmlTreeNode* param_color_node = node->getChildByName("param_color");
+ if (!param_color_node)
+ {
+ return FALSE;
+ }
+
+ std::string op_string;
+ static LLStdStringHandle operation_string = LLXmlTree::addAttributeString("operation");
+ if (param_color_node->getFastAttributeString(operation_string, op_string))
+ {
+ LLStringUtil::toLower(op_string);
+ if (op_string == "add") mOperation = LLTexLayerParamColor::OP_ADD;
+ else if (op_string == "multiply") mOperation = LLTexLayerParamColor::OP_MULTIPLY;
+ else if (op_string == "blend") mOperation = LLTexLayerParamColor::OP_BLEND;
+ }
+
+ mNumColors = 0;
+
+ LLColor4U color4u;
+ for (LLXmlTreeNode* child = param_color_node->getChildByName("value");
+ child;
+ child = param_color_node->getNextNamedChild())
+ {
+ if ((mNumColors < MAX_COLOR_VALUES))
+ {
+ static LLStdStringHandle color_string = LLXmlTree::addAttributeString("color");
+ if (child->getFastAttributeColor4U(color_string, color4u))
+ {
+ mColors[ mNumColors ].setVec(color4u);
+ mNumColors++;
+ }
+ }
+ }
+ if (!mNumColors)
+ {
+ llwarns << "<param_color> is missing <value> sub-elements" << llendl;
+ return FALSE;
+ }
+
+ if ((mOperation == LLTexLayerParamColor::OP_BLEND) && (mNumColors != 1))
+ {
+ llwarns << "<param_color> with operation\"blend\" must have exactly one <value>" << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/indra/llappearance/lltexlayerparams.h b/indra/llappearance/lltexlayerparams.h
new file mode 100644
index 0000000000..b38d28d3eb
--- /dev/null
+++ b/indra/llappearance/lltexlayerparams.h
@@ -0,0 +1,219 @@
+/**
+ * @file lltexlayerparams.h
+ * @brief Texture layer parameters, used by lltexlayer.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXLAYERPARAMS_H
+#define LL_LLTEXLAYERPARAMS_H
+
+#include "llpointer.h"
+#include "v4color.h"
+#include "llviewervisualparam.h"
+
+class LLAvatarAppearance;
+class LLImageRaw;
+class LLImageTGA;
+class LLTexLayer;
+class LLTexLayerInterface;
+class LLGLTexture;
+class LLWearable;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerParam
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTexLayerParam : public LLViewerVisualParam
+{
+public:
+ LLTexLayerParam(LLTexLayerInterface *layer);
+ LLTexLayerParam(LLAvatarAppearance *appearance);
+ /*virtual*/ BOOL setInfo(LLViewerVisualParamInfo *info, BOOL add_to_appearance);
+ /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const = 0;
+
+protected:
+ LLTexLayerInterface* mTexLayer;
+ LLAvatarAppearance* mAvatarAppearance;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerParamAlpha
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL_ALIGN_PREFIX(16)
+class LLTexLayerParamAlpha : public LLTexLayerParam
+{
+public:
+ LLTexLayerParamAlpha( LLTexLayerInterface* layer );
+ LLTexLayerParamAlpha( LLAvatarAppearance* appearance );
+ /*virtual*/ ~LLTexLayerParamAlpha();
+
+ void* operator new(size_t size)
+ {
+ return ll_aligned_malloc_16(size);
+ }
+
+ void operator delete(void* ptr)
+ {
+ ll_aligned_free_16(ptr);
+ }
+
+ /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const;
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex avatar_sex ) {}
+ /*virtual*/ void setWeight(F32 weight, BOOL upload_bake);
+ /*virtual*/ void setAnimationTarget(F32 target_value, BOOL upload_bake);
+ /*virtual*/ void animate(F32 delta, BOOL upload_bake);
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 1.f; }
+ /*virtual*/ const LLVector4a& getAvgDistortion() { return mAvgDistortionVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 3.f; }
+ /*virtual*/ LLVector4a getVertexDistortion(S32 index, LLPolyMesh *poly_mesh) { return LLVector4a(1.f, 1.f, 1.f);}
+ /*virtual*/ const LLVector4a* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
+ /*virtual*/ const LLVector4a* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return NULL;};
+
+ // New functions
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ BOOL getSkip() const;
+ void deleteCaches();
+ BOOL getMultiplyBlend() const;
+
+private:
+ LLPointer<LLGLTexture> mCachedProcessedTexture;
+ LLPointer<LLImageTGA> mStaticImageTGA;
+ LLPointer<LLImageRaw> mStaticImageRaw;
+ BOOL mNeedsCreateTexture;
+ BOOL mStaticImageInvalid;
+ LL_ALIGN_16(LLVector4a mAvgDistortionVec);
+ F32 mCachedEffectiveWeight;
+
+public:
+ // Global list of instances for gathering statistics
+ static void dumpCacheByteCount();
+ static void getCacheByteCount( S32* gl_bytes );
+
+ typedef std::list< LLTexLayerParamAlpha* > param_alpha_ptr_list_t;
+ static param_alpha_ptr_list_t sInstances;
+} LL_ALIGN_POSTFIX(16);
+class LLTexLayerParamAlphaInfo : public LLViewerVisualParamInfo
+{
+ friend class LLTexLayerParamAlpha;
+public:
+ LLTexLayerParamAlphaInfo();
+ /*virtual*/ ~LLTexLayerParamAlphaInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+private:
+ std::string mStaticImageFileName;
+ BOOL mMultiplyBlend;
+ BOOL mSkipIfZeroWeight;
+ F32 mDomain;
+};
+//
+// LLTexLayerParamAlpha
+//-----------------------------------------------------------------------------
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLTexLayerParamColor
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+LL_ALIGN_PREFIX(16)
+class LLTexLayerParamColor : public LLTexLayerParam
+{
+public:
+ enum EColorOperation
+ {
+ OP_ADD = 0,
+ OP_MULTIPLY = 1,
+ OP_BLEND = 2,
+ OP_COUNT = 3 // Number of operations
+ };
+
+ LLTexLayerParamColor( LLTexLayerInterface* layer );
+ LLTexLayerParamColor( LLAvatarAppearance* appearance );
+
+ void* operator new(size_t size)
+ {
+ return ll_aligned_malloc_16(size);
+ }
+
+ void operator delete(void* ptr)
+ {
+ ll_aligned_free_16(ptr);
+ }
+
+ /* virtual */ ~LLTexLayerParamColor();
+
+ /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const;
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex avatar_sex ) {}
+ /*virtual*/ void setWeight(F32 weight, BOOL upload_bake);
+ /*virtual*/ void setAnimationTarget(F32 target_value, BOOL upload_bake);
+ /*virtual*/ void animate(F32 delta, BOOL upload_bake);
+
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 1.f; }
+ /*virtual*/ const LLVector4a& getAvgDistortion() { return mAvgDistortionVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 3.f; }
+ /*virtual*/ LLVector4a getVertexDistortion(S32 index, LLPolyMesh *poly_mesh) { return LLVector4a(1.f, 1.f, 1.f); }
+ /*virtual*/ const LLVector4a* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
+ /*virtual*/ const LLVector4a* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return NULL;};
+
+ // New functions
+ LLColor4 getNetColor() const;
+protected:
+ virtual void onGlobalColorChanged(bool upload_bake) {}
+private:
+ LL_ALIGN_16(LLVector4a mAvgDistortionVec);
+} LL_ALIGN_POSTFIX(16);
+
+class LLTexLayerParamColorInfo : public LLViewerVisualParamInfo
+{
+ friend class LLTexLayerParamColor;
+
+public:
+ LLTexLayerParamColorInfo();
+ virtual ~LLTexLayerParamColorInfo() {};
+ BOOL parseXml( LLXmlTreeNode* node );
+ LLTexLayerParamColor::EColorOperation getOperation() const { return mOperation; }
+private:
+ enum { MAX_COLOR_VALUES = 20 };
+ LLTexLayerParamColor::EColorOperation mOperation;
+ LLColor4 mColors[MAX_COLOR_VALUES];
+ S32 mNumColors;
+};
+
+typedef std::vector<LLTexLayerParamColor *> param_color_list_t;
+typedef std::vector<LLTexLayerParamAlpha *> param_alpha_list_t;
+typedef std::vector<LLTexLayerParamColorInfo *> param_color_info_list_t;
+typedef std::vector<LLTexLayerParamAlphaInfo *> param_alpha_info_list_t;
+
+#endif
diff --git a/indra/llappearance/lltexturemanagerbridge.cpp b/indra/llappearance/lltexturemanagerbridge.cpp
new file mode 100644
index 0000000000..33f2185e4f
--- /dev/null
+++ b/indra/llappearance/lltexturemanagerbridge.cpp
@@ -0,0 +1,32 @@
+ /**
+ * @file lltexturemanagerbridge.cpp
+ * @brief Defined a null texture manager bridge. Applications must provide their own bridge implementaton.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "lltexturemanagerbridge.h"
+
+// Define a null texture manager bridge. Applications must provide their own bridge implementaton.
+LLTextureManagerBridge* gTextureManagerBridgep = NULL;
+
+
diff --git a/indra/llappearance/lltexturemanagerbridge.h b/indra/llappearance/lltexturemanagerbridge.h
new file mode 100644
index 0000000000..4b814b522d
--- /dev/null
+++ b/indra/llappearance/lltexturemanagerbridge.h
@@ -0,0 +1,46 @@
+/**
+ * @file lltexturemanagerbridge.h
+ * @brief Bridge to an application-specific texture manager.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_TEXTUREMANAGERBRIDGE_H
+#define LL_TEXTUREMANAGERBRIDGE_H
+
+#include "llavatarappearancedefines.h"
+#include "llpointer.h"
+#include "llgltexture.h"
+
+// Abstract bridge interface
+class LLTextureManagerBridge
+{
+public:
+ virtual LLPointer<LLGLTexture> getLocalTexture(BOOL usemipmaps = TRUE, BOOL generate_gl_tex = TRUE) = 0;
+ virtual LLPointer<LLGLTexture> getLocalTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, BOOL generate_gl_tex = TRUE) = 0;
+ virtual LLGLTexture* getFetchedTexture(const LLUUID &image_id) = 0;
+};
+
+extern LLTextureManagerBridge* gTextureManagerBridgep;
+
+#endif // LL_TEXTUREMANAGERBRIDGE_H
+
diff --git a/indra/llappearance/llviewervisualparam.cpp b/indra/llappearance/llviewervisualparam.cpp
new file mode 100644
index 0000000000..cc81bcf118
--- /dev/null
+++ b/indra/llappearance/llviewervisualparam.cpp
@@ -0,0 +1,163 @@
+/**
+ * @file llviewervisualparam.cpp
+ * @brief Implementation of LLViewerVisualParam class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llviewervisualparam.h"
+#include "llxmltree.h"
+#include "llwearable.h"
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParamInfo()
+//-----------------------------------------------------------------------------
+LLViewerVisualParamInfo::LLViewerVisualParamInfo()
+ :
+ mWearableType( LLWearableType::WT_INVALID ),
+ mCrossWearable(FALSE),
+ mCamDist( 0.5f ),
+ mCamAngle( 0.f ),
+ mCamElevation( 0.f ),
+ mEditGroupDisplayOrder( 0 ),
+ mShowSimple(FALSE),
+ mSimpleMin(0.f),
+ mSimpleMax(100.f)
+{
+}
+
+LLViewerVisualParamInfo::~LLViewerVisualParamInfo()
+{
+}
+
+//-----------------------------------------------------------------------------
+// parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLViewerVisualParamInfo::parseXml(LLXmlTreeNode *node)
+{
+ llassert( node->hasName( "param" ) );
+
+ if (!LLVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ // VIEWER SPECIFIC PARAMS
+
+ std::string wearable;
+ static LLStdStringHandle wearable_string = LLXmlTree::addAttributeString("wearable");
+ if( node->getFastAttributeString( wearable_string, wearable) )
+ {
+ mWearableType = LLWearableType::typeNameToType( wearable );
+ }
+
+ static LLStdStringHandle edit_group_string = LLXmlTree::addAttributeString("edit_group");
+ if (!node->getFastAttributeString( edit_group_string, mEditGroup))
+ {
+ mEditGroup = "";
+ }
+
+ static LLStdStringHandle cross_wearable_string = LLXmlTree::addAttributeString("cross_wearable");
+ if (!node->getFastAttributeBOOL(cross_wearable_string, mCrossWearable))
+ {
+ mCrossWearable = FALSE;
+ }
+
+ // Optional camera offsets from the current joint center. Used for generating "hints" (thumbnails).
+ static LLStdStringHandle camera_distance_string = LLXmlTree::addAttributeString("camera_distance");
+ node->getFastAttributeF32( camera_distance_string, mCamDist );
+ static LLStdStringHandle camera_angle_string = LLXmlTree::addAttributeString("camera_angle");
+ node->getFastAttributeF32( camera_angle_string, mCamAngle ); // in degrees
+ static LLStdStringHandle camera_elevation_string = LLXmlTree::addAttributeString("camera_elevation");
+ node->getFastAttributeF32( camera_elevation_string, mCamElevation );
+
+ mCamAngle += 180;
+
+ static S32 params_loaded = 0;
+
+ // By default, parameters are displayed in the order in which they appear in the xml file.
+ // "edit_group_order" overriddes.
+ static LLStdStringHandle edit_group_order_string = LLXmlTree::addAttributeString("edit_group_order");
+ if( !node->getFastAttributeF32( edit_group_order_string, mEditGroupDisplayOrder ) )
+ {
+ mEditGroupDisplayOrder = (F32)params_loaded;
+ }
+
+ params_loaded++;
+
+ return TRUE;
+}
+
+/*virtual*/ void LLViewerVisualParamInfo::toStream(std::ostream &out)
+{
+ LLVisualParamInfo::toStream(out);
+
+ out << mWearableType << "\t";
+ out << mEditGroup << "\t";
+ out << mEditGroupDisplayOrder << "\t";
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParam()
+//-----------------------------------------------------------------------------
+LLViewerVisualParam::LLViewerVisualParam()
+{
+}
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+
+BOOL LLViewerVisualParam::setInfo(LLViewerVisualParamInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+ return TRUE;
+}
+
+/*
+//=============================================================================
+// These virtual functions should always be overridden,
+// but are included here for use as templates
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// parseData()
+//-----------------------------------------------------------------------------
+BOOL LLViewerVisualParam::parseData(LLXmlTreeNode *node)
+{
+ LLViewerVisualParamInfo* info = new LLViewerVisualParamInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ return FALSE;
+
+ return TRUE;
+}
+*/
diff --git a/indra/llappearance/llviewervisualparam.h b/indra/llappearance/llviewervisualparam.h
new file mode 100644
index 0000000000..2826e6c316
--- /dev/null
+++ b/indra/llappearance/llviewervisualparam.h
@@ -0,0 +1,110 @@
+/**
+ * @file llviewervisualparam.h
+ * @brief viewer side visual params (with data file parsing)
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLViewerVisualParam_H
+#define LL_LLViewerVisualParam_H
+
+#include "v3math.h"
+#include "llstring.h"
+#include "llvisualparam.h"
+
+class LLWearable;
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParamInfo
+//-----------------------------------------------------------------------------
+class LLViewerVisualParamInfo : public LLVisualParamInfo
+{
+ friend class LLViewerVisualParam;
+public:
+ LLViewerVisualParamInfo();
+ /*virtual*/ ~LLViewerVisualParamInfo();
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+ /*virtual*/ void toStream(std::ostream &out);
+
+protected:
+ S32 mWearableType;
+ BOOL mCrossWearable;
+ std::string mEditGroup;
+ F32 mCamDist;
+ F32 mCamAngle; // degrees
+ F32 mCamElevation;
+ F32 mEditGroupDisplayOrder;
+ BOOL mShowSimple; // show edit controls when in "simple ui" mode?
+ F32 mSimpleMin; // when in simple UI, apply this minimum, range 0.f to 100.f
+ F32 mSimpleMax; // when in simple UI, apply this maximum, range 0.f to 100.f
+};
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParam
+// VIRTUAL CLASS
+// a viewer side interface class for a generalized parametric modification of the avatar mesh
+//-----------------------------------------------------------------------------
+LL_ALIGN_PREFIX(16)
+class LLViewerVisualParam : public LLVisualParam
+{
+public:
+ LLViewerVisualParam();
+ /*virtual*/ ~LLViewerVisualParam(){};
+
+ // Special: These functions are overridden by child classes
+ LLViewerVisualParamInfo *getInfo() const { return (LLViewerVisualParamInfo*)mInfo; };
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLViewerVisualParamInfo *info);
+
+ virtual LLViewerVisualParam* cloneParam(LLWearable* wearable) const = 0;
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+
+ // New Virtual functions
+ virtual F32 getTotalDistortion() = 0;
+ virtual const LLVector4a& getAvgDistortion() = 0;
+ virtual F32 getMaxDistortion() = 0;
+ virtual LLVector4a getVertexDistortion(S32 index, LLPolyMesh *mesh) = 0;
+ virtual const LLVector4a* getFirstDistortion(U32 *index, LLPolyMesh **mesh) = 0;
+ virtual const LLVector4a* getNextDistortion(U32 *index, LLPolyMesh **mesh) = 0;
+
+ // interface methods
+ F32 getDisplayOrder() const { return getInfo()->mEditGroupDisplayOrder; }
+ S32 getWearableType() const { return getInfo()->mWearableType; }
+ const std::string& getEditGroup() const { return getInfo()->mEditGroup; }
+
+ F32 getCameraDistance() const { return getInfo()->mCamDist; }
+ F32 getCameraAngle() const { return getInfo()->mCamAngle; } // degrees
+ F32 getCameraElevation() const { return getInfo()->mCamElevation; }
+
+ BOOL getShowSimple() const { return getInfo()->mShowSimple; }
+ F32 getSimpleMin() const { return getInfo()->mSimpleMin; }
+ F32 getSimpleMax() const { return getInfo()->mSimpleMax; }
+
+ BOOL getCrossWearable() const { return getInfo()->mCrossWearable; }
+
+} LL_ALIGN_POSTFIX(16);
+
+#endif // LL_LLViewerVisualParam_H
diff --git a/indra/llappearance/llwearable.cpp b/indra/llappearance/llwearable.cpp
new file mode 100644
index 0000000000..4028c1dfad
--- /dev/null
+++ b/indra/llappearance/llwearable.cpp
@@ -0,0 +1,781 @@
+/**
+ * @file llwearable.cpp
+ * @brief LLWearable class implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llavatarappearance.h"
+#include "lllocaltextureobject.h"
+#include "lltexlayer.h"
+#include "lltexturemanagerbridge.h"
+#include "llvisualparam.h"
+#include "llavatarappearancedefines.h"
+#include "llwearable.h"
+
+using namespace LLAvatarAppearanceDefines;
+
+// static
+S32 LLWearable::sCurrentDefinitionVersion = 1;
+
+// Private local functions
+static std::string terse_F32_to_string(F32 f);
+
+// virtual
+LLWearable::~LLWearable()
+{
+}
+
+const std::string& LLWearable::getTypeLabel() const
+{
+ return LLWearableType::getTypeLabel(mType);
+}
+
+const std::string& LLWearable::getTypeName() const
+{
+ return LLWearableType::getTypeName(mType);
+}
+
+LLAssetType::EType LLWearable::getAssetType() const
+{
+ return LLWearableType::getAssetType(mType);
+}
+
+BOOL LLWearable::exportFile(LLFILE* fp) const
+{
+ llofstream ofs(fp);
+ return exportStream(ofs);
+}
+
+// virtual
+BOOL LLWearable::exportStream( std::ostream& output_stream ) const
+{
+ if (!output_stream.good()) return FALSE;
+
+ // header and version
+ output_stream << "LLWearable version " << mDefinitionVersion << "\n";
+ // name
+ output_stream << mName << "\n";
+ // description
+ output_stream << mDescription << "\n";
+
+ // permissions
+ if( !mPermissions.exportStream( output_stream ) )
+ {
+ return FALSE;
+ }
+
+ // sale info
+ if( !mSaleInfo.exportStream( output_stream ) )
+ {
+ return FALSE;
+ }
+
+ // wearable type
+ output_stream << "type " << (S32) getType() << "\n";
+
+ // parameters
+ output_stream << "parameters " << mVisualParamIndexMap.size() << "\n";
+
+ for (visual_param_index_map_t::const_iterator iter = mVisualParamIndexMap.begin();
+ iter != mVisualParamIndexMap.end();
+ ++iter)
+ {
+ S32 param_id = iter->first;
+ const LLVisualParam* param = iter->second;
+ F32 param_weight = param->getWeight();
+ output_stream << param_id << " " << terse_F32_to_string( param_weight ) << "\n";
+ }
+
+ // texture entries
+ output_stream << "textures " << mTEMap.size() << "\n";
+
+ for (te_map_t::const_iterator iter = mTEMap.begin(); iter != mTEMap.end(); ++iter)
+ {
+ S32 te = iter->first;
+ const LLUUID& image_id = iter->second->getID();
+ output_stream << te << " " << image_id << "\n";
+ }
+ return TRUE;
+}
+
+void LLWearable::createVisualParams(LLAvatarAppearance *avatarp)
+{
+ for (LLViewerVisualParam* param = (LLViewerVisualParam*) avatarp->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatarp->getNextVisualParam())
+ {
+ if (param->getWearableType() == mType)
+ {
+ LLVisualParam *clone_param = param->cloneParam(this);
+ clone_param->setParamLocation(LOC_UNKNOWN);
+ clone_param->setParamLocation(LOC_WEARABLE);
+ addVisualParam(clone_param);
+ }
+ }
+
+ // resync driver parameters to point to the newly cloned driven parameters
+ for (visual_param_index_map_t::iterator param_iter = mVisualParamIndexMap.begin();
+ param_iter != mVisualParamIndexMap.end();
+ ++param_iter)
+ {
+ LLVisualParam* param = param_iter->second;
+ LLVisualParam*(LLWearable::*wearable_function)(S32)const = &LLWearable::getVisualParam;
+ // need this line to disambiguate between versions of LLCharacter::getVisualParam()
+ LLVisualParam*(LLAvatarAppearance::*param_function)(S32)const = &LLAvatarAppearance::getVisualParam;
+ param->resetDrivenParams();
+ if(!param->linkDrivenParams(boost::bind(wearable_function,(LLWearable*)this, _1), false))
+ {
+ if( !param->linkDrivenParams(boost::bind(param_function,avatarp,_1 ), true))
+ {
+ llwarns << "could not link driven params for wearable " << getName() << " id: " << param->getID() << llendl;
+ continue;
+ }
+ }
+ }
+}
+
+void LLWearable::createLayers(S32 te, LLAvatarAppearance *avatarp)
+{
+ LLTexLayerSet *layer_set = NULL;
+ const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearanceDictionary::getInstance()->getTexture((ETextureIndex)te);
+ if (texture_dict->mIsUsedByBakedTexture)
+ {
+ const EBakedTextureIndex baked_index = texture_dict->mBakedTextureIndex;
+
+ layer_set = avatarp->getAvatarLayerSet(baked_index);
+ }
+
+ if (layer_set)
+ {
+ layer_set->cloneTemplates(mTEMap[te], (ETextureIndex)te, this);
+ }
+ else
+ {
+ llerrs << "could not find layerset for LTO in wearable!" << llendl;
+ }
+}
+
+LLWearable::EImportResult LLWearable::importFile(LLFILE* fp, LLAvatarAppearance* avatarp )
+{
+ llifstream ifs(fp);
+ return importStream(ifs, avatarp);
+}
+
+// virtual
+LLWearable::EImportResult LLWearable::importStream( std::istream& input_stream, LLAvatarAppearance* avatarp )
+{
+ // *NOTE: changing the type or size of this buffer will require
+ // changes in the fscanf() code below.
+ // We are using a local max buffer size here to avoid issues
+ // if MAX_STRING size changes.
+ const U32 PARSE_BUFFER_SIZE = 2048;
+ char buffer[PARSE_BUFFER_SIZE]; /* Flawfinder: ignore */
+ char uuid_buffer[37]; /* Flawfinder: ignore */
+
+ // This data is being generated on the viewer.
+ // Impose some sane limits on parameter and texture counts.
+ const S32 MAX_WEARABLE_ASSET_TEXTURES = 100;
+ const S32 MAX_WEARABLE_ASSET_PARAMETERS = 1000;
+
+ if(!avatarp)
+ {
+ return LLWearable::FAILURE;
+ }
+
+ // read header and version
+ if (!getNextPopulatedLine(input_stream, buffer, PARSE_BUFFER_SIZE))
+ {
+ llwarns << "Failed to read wearable asset input stream." << llendl;
+ return LLWearable::FAILURE;
+ }
+ if ( 1 != sscanf( /* Flawfinder: ignore */
+ buffer,
+ "LLWearable version %d\n",
+ &mDefinitionVersion ) )
+ {
+ return LLWearable::BAD_HEADER;
+ }
+
+ // Hack to allow wearables with definition version 24 to still load.
+ // This should only affect lindens and NDA'd testers who have saved wearables in 2.0
+ // the extra check for version == 24 can be removed before release, once internal testers
+ // have loaded these wearables again. See hack pt 2 at bottom of function to ensure that
+ // these wearables get re-saved with version definition 22.
+ if( mDefinitionVersion > LLWearable::sCurrentDefinitionVersion && mDefinitionVersion != 24 )
+ {
+ llwarns << "Wearable asset has newer version (" << mDefinitionVersion << ") than XML (" << LLWearable::sCurrentDefinitionVersion << ")" << llendl;
+ return LLWearable::FAILURE;
+ }
+
+ // name may be empty
+ if (!input_stream.good())
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading name" << llendl;
+ return LLWearable::FAILURE;
+ }
+ input_stream.getline(buffer, PARSE_BUFFER_SIZE);
+ mName = buffer;
+
+ // description may be empty
+ if (!input_stream.good())
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading description" << llendl;
+ return LLWearable::FAILURE;
+ }
+ input_stream.getline(buffer, PARSE_BUFFER_SIZE);
+ mDescription = buffer;
+
+ // permissions may have extra empty lines before the correct line
+ if (!getNextPopulatedLine(input_stream, buffer, PARSE_BUFFER_SIZE))
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading permissions" << llendl;
+ return LLWearable::FAILURE;
+ }
+ S32 perm_version = -1;
+ if ( 1 != sscanf( buffer, " permissions %d\n", &perm_version ) ||
+ perm_version != 0 )
+ {
+ llwarns << "Bad Wearable asset: missing valid permissions" << llendl;
+ return LLWearable::FAILURE;
+ }
+ if( !mPermissions.importStream( input_stream ) )
+ {
+ return LLWearable::FAILURE;
+ }
+
+ // sale info
+ if (!getNextPopulatedLine(input_stream, buffer, PARSE_BUFFER_SIZE))
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading sale info" << llendl;
+ return LLWearable::FAILURE;
+ }
+ S32 sale_info_version = -1;
+ if ( 1 != sscanf( buffer, " sale_info %d\n", &sale_info_version ) ||
+ sale_info_version != 0 )
+ {
+ llwarns << "Bad Wearable asset: missing valid sale_info" << llendl;
+ return LLWearable::FAILURE;
+ }
+ // Sale info used to contain next owner perm. It is now in the
+ // permissions. Thus, we read that out, and fix legacy
+ // objects. It's possible this op would fail, but it should pick
+ // up the vast majority of the tasks.
+ BOOL has_perm_mask = FALSE;
+ U32 perm_mask = 0;
+ if( !mSaleInfo.importStream(input_stream, has_perm_mask, perm_mask) )
+ {
+ return LLWearable::FAILURE;
+ }
+ if(has_perm_mask)
+ {
+ // fair use fix.
+ if(!(perm_mask & PERM_COPY))
+ {
+ perm_mask |= PERM_TRANSFER;
+ }
+ mPermissions.setMaskNext(perm_mask);
+ }
+
+ // wearable type
+ if (!getNextPopulatedLine(input_stream, buffer, PARSE_BUFFER_SIZE))
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading type" << llendl;
+ return LLWearable::FAILURE;
+ }
+ S32 type = -1;
+ if ( 1 != sscanf( buffer, "type %d\n", &type ) )
+ {
+ llwarns << "Bad Wearable asset: bad type" << llendl;
+ return LLWearable::FAILURE;
+ }
+ if( 0 <= type && type < LLWearableType::WT_COUNT )
+ {
+ setType((LLWearableType::EType)type, avatarp);
+ }
+ else
+ {
+ mType = LLWearableType::WT_COUNT;
+ llwarns << "Bad Wearable asset: bad type #" << type << llendl;
+ return LLWearable::FAILURE;
+ }
+
+ // parameters header
+ if (!getNextPopulatedLine(input_stream, buffer, PARSE_BUFFER_SIZE))
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading parameters header" << llendl;
+ return LLWearable::FAILURE;
+ }
+ S32 num_parameters = -1;
+ if ( 1 != sscanf( buffer, "parameters %d\n", &num_parameters ) )
+ {
+ llwarns << "Bad Wearable asset: missing parameters block" << llendl;
+ return LLWearable::FAILURE;
+ }
+ if ( num_parameters > MAX_WEARABLE_ASSET_PARAMETERS )
+ {
+ llwarns << "Bad Wearable asset: too many parameters, "
+ << num_parameters << llendl;
+ return LLWearable::FAILURE;
+ }
+ if( num_parameters != mVisualParamIndexMap.size() )
+ {
+ llwarns << "Wearable parameter mismatch. Reading in "
+ << num_parameters << " from file, but created "
+ << mVisualParamIndexMap.size()
+ << " from avatar parameters. type: "
+ << getType() << llendl;
+ }
+
+ // parameters
+ S32 i;
+ for( i = 0; i < num_parameters; i++ )
+ {
+ if (!getNextPopulatedLine(input_stream, buffer, PARSE_BUFFER_SIZE))
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading parameter #" << i << llendl;
+ return LLWearable::FAILURE;
+ }
+ S32 param_id = 0;
+ F32 param_weight = 0.f;
+ if ( 2 != sscanf( buffer, "%d %f\n", &param_id, &param_weight ) )
+ {
+ llwarns << "Bad Wearable asset: bad parameter, #" << i << llendl;
+ return LLWearable::FAILURE;
+ }
+ mSavedVisualParamMap[param_id] = param_weight;
+ }
+
+ // textures header
+ if (!getNextPopulatedLine(input_stream, buffer, PARSE_BUFFER_SIZE))
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading textures header" << i << llendl;
+ return LLWearable::FAILURE;
+ }
+ S32 num_textures = -1;
+ if ( 1 != sscanf( buffer, "textures %d\n", &num_textures) )
+ {
+ llwarns << "Bad Wearable asset: missing textures block" << llendl;
+ return LLWearable::FAILURE;
+ }
+ if ( num_textures > MAX_WEARABLE_ASSET_TEXTURES )
+ {
+ llwarns << "Bad Wearable asset: too many textures, "
+ << num_textures << llendl;
+ return LLWearable::FAILURE;
+ }
+
+ // textures
+ for( i = 0; i < num_textures; i++ )
+ {
+ if (!getNextPopulatedLine(input_stream, buffer, PARSE_BUFFER_SIZE))
+ {
+ llwarns << "Bad Wearable asset: early end of input stream "
+ << "while reading textures #" << i << llendl;
+ return LLWearable::FAILURE;
+ }
+ S32 te = 0;
+ if ( 2 != sscanf( /* Flawfinder: ignore */
+ buffer,
+ "%d %36s\n",
+ &te, uuid_buffer) )
+ {
+ llwarns << "Bad Wearable asset: bad texture, #" << i << llendl;
+ return LLWearable::FAILURE;
+ }
+
+ if( !LLUUID::validate( uuid_buffer ) )
+ {
+ llwarns << "Bad Wearable asset: bad texture uuid: "
+ << uuid_buffer << llendl;
+ return LLWearable::FAILURE;
+ }
+ LLUUID id = LLUUID(uuid_buffer);
+ LLGLTexture* image = gTextureManagerBridgep->getFetchedTexture( id );
+ if( mTEMap.find(te) != mTEMap.end() )
+ {
+ delete mTEMap[te];
+ }
+ if( mSavedTEMap.find(te) != mSavedTEMap.end() )
+ {
+ delete mSavedTEMap[te];
+ }
+
+ LLUUID textureid(uuid_buffer);
+ mTEMap[te] = new LLLocalTextureObject(image, textureid);
+ mSavedTEMap[te] = new LLLocalTextureObject(image, textureid);
+ createLayers(te, avatarp);
+ }
+
+ // copy all saved param values to working params
+ revertValues();
+
+ return LLWearable::SUCCESS;
+}
+
+BOOL LLWearable::getNextPopulatedLine(std::istream& input_stream, char* buffer, U32 buffer_size)
+{
+ if (!input_stream.good())
+ {
+ return FALSE;
+ }
+
+ do
+ {
+ input_stream.getline(buffer, buffer_size);
+ }
+ while (input_stream.good() && buffer[0]=='\0');
+
+ return (buffer[0] != '\0');
+}
+
+
+void LLWearable::setType(LLWearableType::EType type, LLAvatarAppearance *avatarp)
+{
+ mType = type;
+ createVisualParams(avatarp);
+}
+
+
+LLLocalTextureObject* LLWearable::getLocalTextureObject(S32 index)
+{
+ te_map_t::iterator iter = mTEMap.find(index);
+ if( iter != mTEMap.end() )
+ {
+ LLLocalTextureObject* lto = iter->second;
+ return lto;
+ }
+ return NULL;
+}
+
+const LLLocalTextureObject* LLWearable::getLocalTextureObject(S32 index) const
+{
+ te_map_t::const_iterator iter = mTEMap.find(index);
+ if( iter != mTEMap.end() )
+ {
+ const LLLocalTextureObject* lto = iter->second;
+ return lto;
+ }
+ return NULL;
+}
+
+std::vector<LLLocalTextureObject*> LLWearable::getLocalTextureListSeq()
+{
+ std::vector<LLLocalTextureObject*> result;
+
+ for(te_map_t::const_iterator iter = mTEMap.begin();
+ iter != mTEMap.end(); iter++)
+ {
+ LLLocalTextureObject* lto = iter->second;
+ result.push_back(lto);
+ }
+
+ return result;
+}
+
+void LLWearable::setLocalTextureObject(S32 index, LLLocalTextureObject &lto)
+{
+ if( mTEMap.find(index) != mTEMap.end() )
+ {
+ mTEMap.erase(index);
+ }
+ mTEMap[index] = new LLLocalTextureObject(lto);
+}
+
+void LLWearable::revertValues()
+{
+ // FIXME DRANO - this triggers changes to driven params on avatar, potentially clobbering baked appearance.
+
+ //update saved settings so wearable is no longer dirty
+ // non-driver params first
+ for (param_map_t::const_iterator iter = mSavedVisualParamMap.begin(); iter != mSavedVisualParamMap.end(); iter++)
+ {
+ S32 id = iter->first;
+ F32 value = iter->second;
+ LLVisualParam *param = getVisualParam(id);
+ if(param && !dynamic_cast<LLDriverParam*>(param) )
+ {
+ setVisualParamWeight(id, value, TRUE);
+ }
+ }
+
+ //then driver params
+ for (param_map_t::const_iterator iter = mSavedVisualParamMap.begin(); iter != mSavedVisualParamMap.end(); iter++)
+ {
+ S32 id = iter->first;
+ F32 value = iter->second;
+ LLVisualParam *param = getVisualParam(id);
+ if(param && dynamic_cast<LLDriverParam*>(param) )
+ {
+ setVisualParamWeight(id, value, TRUE);
+ }
+ }
+
+ // make sure that saved values are sane
+ for (param_map_t::const_iterator iter = mSavedVisualParamMap.begin(); iter != mSavedVisualParamMap.end(); iter++)
+ {
+ S32 id = iter->first;
+ LLVisualParam *param = getVisualParam(id);
+ if( param )
+ {
+ mSavedVisualParamMap[id] = param->getWeight();
+ }
+ }
+
+ syncImages(mSavedTEMap, mTEMap);
+}
+
+void LLWearable::saveValues()
+{
+ //update saved settings so wearable is no longer dirty
+ mSavedVisualParamMap.clear();
+ for (visual_param_index_map_t::const_iterator iter = mVisualParamIndexMap.begin(); iter != mVisualParamIndexMap.end(); ++iter)
+ {
+ S32 id = iter->first;
+ LLVisualParam *wearable_param = iter->second;
+ F32 value = wearable_param->getWeight();
+ mSavedVisualParamMap[id] = value;
+ }
+
+ // Deep copy of mTEMap (copies only those tes that are current, filling in defaults where needed)
+ syncImages(mTEMap, mSavedTEMap);
+}
+
+void LLWearable::syncImages(te_map_t &src, te_map_t &dst)
+{
+ // Deep copy of src (copies only those tes that are current, filling in defaults where needed)
+ for( S32 te = 0; te < TEX_NUM_INDICES; te++ )
+ {
+ if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex) te) == mType)
+ {
+ te_map_t::const_iterator iter = src.find(te);
+ LLUUID image_id;
+ LLGLTexture *image = NULL;
+ LLLocalTextureObject *lto = NULL;
+ if(iter != src.end())
+ {
+ // there's a Local Texture Object in the source image map. Use this to populate the values to store in the destination image map.
+ lto = iter->second;
+ image = lto->getImage();
+ image_id = lto->getID();
+ }
+ else
+ {
+ // there is no Local Texture Object in the source image map. Get defaults values for populating the destination image map.
+ image_id = getDefaultTextureImageID((ETextureIndex) te);
+ image = gTextureManagerBridgep->getFetchedTexture( image_id );
+ }
+
+ if( dst.find(te) != dst.end() )
+ {
+ // there's already an entry in the destination map for the texture. Just update its values.
+ dst[te]->setImage(image);
+ dst[te]->setID(image_id);
+ }
+ else
+ {
+ // no entry found in the destination map, we need to create a new Local Texture Object
+ dst[te] = new LLLocalTextureObject(image, image_id);
+ }
+
+ if( lto )
+ {
+ // If we pulled values from a Local Texture Object in the source map, make sure the proper flags are set in the new (or updated) entry in the destination map.
+ dst[te]->setBakedReady(lto->getBakedReady());
+ dst[te]->setDiscard(lto->getDiscard());
+ }
+ }
+ }
+}
+
+void LLWearable::destroyTextures()
+{
+ for( te_map_t::iterator iter = mTEMap.begin(); iter != mTEMap.end(); ++iter )
+ {
+ LLLocalTextureObject *lto = iter->second;
+ delete lto;
+ }
+ mTEMap.clear();
+ for( te_map_t::iterator iter = mSavedTEMap.begin(); iter != mSavedTEMap.end(); ++iter )
+ {
+ LLLocalTextureObject *lto = iter->second;
+ delete lto;
+ }
+ mSavedTEMap.clear();
+}
+
+void LLWearable::addVisualParam(LLVisualParam *param)
+{
+ if( mVisualParamIndexMap[param->getID()] )
+ {
+ delete mVisualParamIndexMap[param->getID()];
+ }
+ param->setIsDummy(FALSE);
+ param->setParamLocation(LOC_WEARABLE);
+ mVisualParamIndexMap[param->getID()] = param;
+ mSavedVisualParamMap[param->getID()] = param->getDefaultWeight();
+}
+
+
+void LLWearable::setVisualParamWeight(S32 param_index, F32 value, BOOL upload_bake)
+{
+ if( is_in_map(mVisualParamIndexMap, param_index ) )
+ {
+ LLVisualParam *wearable_param = mVisualParamIndexMap[param_index];
+ wearable_param->setWeight(value, upload_bake);
+ }
+ else
+ {
+ llerrs << "LLWearable::setVisualParam passed invalid parameter index: " << param_index << " for wearable type: " << this->getName() << llendl;
+ }
+}
+
+F32 LLWearable::getVisualParamWeight(S32 param_index) const
+{
+ if( is_in_map(mVisualParamIndexMap, param_index ) )
+ {
+ const LLVisualParam *wearable_param = mVisualParamIndexMap.find(param_index)->second;
+ return wearable_param->getWeight();
+ }
+ else
+ {
+ llwarns << "LLWerable::getVisualParam passed invalid parameter index: " << param_index << " for wearable type: " << this->getName() << llendl;
+ }
+ return (F32)-1.0;
+}
+
+LLVisualParam* LLWearable::getVisualParam(S32 index) const
+{
+ visual_param_index_map_t::const_iterator iter = mVisualParamIndexMap.find(index);
+ return (iter == mVisualParamIndexMap.end()) ? NULL : iter->second;
+}
+
+
+void LLWearable::getVisualParams(visual_param_vec_t &list)
+{
+ visual_param_index_map_t::iterator iter = mVisualParamIndexMap.begin();
+ visual_param_index_map_t::iterator end = mVisualParamIndexMap.end();
+
+ // add all visual params to the passed-in vector
+ for( ; iter != end; ++iter )
+ {
+ list.push_back(iter->second);
+ }
+}
+
+void LLWearable::animateParams(F32 delta, BOOL upload_bake)
+{
+ for(visual_param_index_map_t::iterator iter = mVisualParamIndexMap.begin();
+ iter != mVisualParamIndexMap.end();
+ ++iter)
+ {
+ LLVisualParam *param = (LLVisualParam*) iter->second;
+ param->animate(delta, upload_bake);
+ }
+}
+
+LLColor4 LLWearable::getClothesColor(S32 te) const
+{
+ LLColor4 color;
+ U32 param_name[3];
+ if( LLAvatarAppearance::teToColorParams( (LLAvatarAppearanceDefines::ETextureIndex)te, param_name ) )
+ {
+ for( U8 index = 0; index < 3; index++ )
+ {
+ color.mV[index] = getVisualParamWeight(param_name[index]);
+ }
+ }
+ return color;
+}
+
+void LLWearable::setClothesColor( S32 te, const LLColor4& new_color, BOOL upload_bake )
+{
+ U32 param_name[3];
+ if( LLAvatarAppearance::teToColorParams( (LLAvatarAppearanceDefines::ETextureIndex)te, param_name ) )
+ {
+ for( U8 index = 0; index < 3; index++ )
+ {
+ setVisualParamWeight(param_name[index], new_color.mV[index], upload_bake);
+ }
+ }
+}
+
+void LLWearable::writeToAvatar(LLAvatarAppearance* avatarp)
+{
+ if (!avatarp) return;
+
+ // Pull params
+ for( LLVisualParam* param = avatarp->getFirstVisualParam(); param; param = avatarp->getNextVisualParam() )
+ {
+ // cross-wearable parameters are not authoritative, as they are driven by a different wearable. So don't copy the values to the
+ // avatar object if cross wearable. Cross wearable params get their values from the avatar, they shouldn't write the other way.
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType) && (!((LLViewerVisualParam*)param)->getCrossWearable()) )
+ {
+ S32 param_id = param->getID();
+ F32 weight = getVisualParamWeight(param_id);
+
+ avatarp->setVisualParamWeight( param_id, weight, FALSE );
+ }
+ }
+}
+
+
+std::string terse_F32_to_string(F32 f)
+{
+ std::string r = llformat("%.2f", f);
+ S32 len = r.length();
+
+ // "1.20" -> "1.2"
+ // "24.00" -> "24."
+ while (len > 0 && ('0' == r[len - 1]))
+ {
+ r.erase(len-1, 1);
+ len--;
+ }
+ if ('.' == r[len - 1])
+ {
+ // "24." -> "24"
+ r.erase(len-1, 1);
+ }
+ else if (('-' == r[0]) && ('0' == r[1]))
+ {
+ // "-0.59" -> "-.59"
+ r.erase(1, 1);
+ }
+ else if ('0' == r[0])
+ {
+ // "0.59" -> ".59"
+ r.erase(0, 1);
+ }
+ return r;
+}
+
diff --git a/indra/llappearance/llwearable.h b/indra/llappearance/llwearable.h
new file mode 100644
index 0000000000..6f5a1e14e8
--- /dev/null
+++ b/indra/llappearance/llwearable.h
@@ -0,0 +1,142 @@
+/**
+ * @file llwearable.h
+ * @brief LLWearable class header file
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLWEARABLE_H
+#define LL_LLWEARABLE_H
+
+#include "llavatarappearancedefines.h"
+#include "llextendedstatus.h"
+#include "llpermissions.h"
+#include "llsaleinfo.h"
+#include "llwearabletype.h"
+#include "lllocaltextureobject.h"
+
+class LLMD5;
+class LLVisualParam;
+class LLTexGlobalColorInfo;
+class LLTexGlobalColor;
+class LLAvatarAppearance;
+
+// Abstract class.
+class LLWearable
+{
+ //--------------------------------------------------------------------
+ // Constructors and destructors
+ //--------------------------------------------------------------------
+public:
+ virtual ~LLWearable();
+
+ //--------------------------------------------------------------------
+ // Accessors
+ //--------------------------------------------------------------------
+public:
+ LLWearableType::EType getType() const { return mType; }
+ void setType(LLWearableType::EType type, LLAvatarAppearance *avatarp);
+ const std::string& getName() const { return mName; }
+ void setName(const std::string& name) { mName = name; }
+ const std::string& getDescription() const { return mDescription; }
+ void setDescription(const std::string& desc) { mDescription = desc; }
+ const LLPermissions& getPermissions() const { return mPermissions; }
+ void setPermissions(const LLPermissions& p) { mPermissions = p; }
+ const LLSaleInfo& getSaleInfo() const { return mSaleInfo; }
+ void setSaleInfo(const LLSaleInfo& info) { mSaleInfo = info; }
+ const std::string& getTypeLabel() const;
+ const std::string& getTypeName() const;
+ LLAssetType::EType getAssetType() const;
+ S32 getDefinitionVersion() const { return mDefinitionVersion; }
+ void setDefinitionVersion( S32 new_version ) { mDefinitionVersion = new_version; }
+ static S32 getCurrentDefinitionVersion() { return LLWearable::sCurrentDefinitionVersion; }
+
+public:
+ typedef std::vector<LLVisualParam*> visual_param_vec_t;
+
+ virtual void writeToAvatar(LLAvatarAppearance* avatarp);
+
+ enum EImportResult
+ {
+ FAILURE = 0,
+ SUCCESS,
+ BAD_HEADER
+ };
+ BOOL exportFile(LLFILE* file) const;
+ EImportResult importFile(LLFILE* file, LLAvatarAppearance* avatarp );
+ virtual BOOL exportStream( std::ostream& output_stream ) const;
+ virtual EImportResult importStream( std::istream& input_stream, LLAvatarAppearance* avatarp );
+
+ static void setCurrentDefinitionVersion( S32 version ) { LLWearable::sCurrentDefinitionVersion = version; }
+ virtual LLUUID getDefaultTextureImageID(LLAvatarAppearanceDefines::ETextureIndex index) const = 0;
+
+ LLLocalTextureObject* getLocalTextureObject(S32 index);
+ const LLLocalTextureObject* getLocalTextureObject(S32 index) const;
+ std::vector<LLLocalTextureObject*> getLocalTextureListSeq();
+
+ void setLocalTextureObject(S32 index, LLLocalTextureObject &lto);
+ void addVisualParam(LLVisualParam *param);
+ void setVisualParamWeight(S32 index, F32 value, BOOL upload_bake);
+ F32 getVisualParamWeight(S32 index) const;
+ LLVisualParam* getVisualParam(S32 index) const;
+ void getVisualParams(visual_param_vec_t &list);
+ void animateParams(F32 delta, BOOL upload_bake);
+
+ LLColor4 getClothesColor(S32 te) const;
+ void setClothesColor( S32 te, const LLColor4& new_color, BOOL upload_bake );
+
+ virtual void revertValues();
+ virtual void saveValues();
+
+ // Something happened that requires the wearable to be updated (e.g. worn/unworn).
+ virtual void setUpdated() const = 0;
+
+ // Update the baked texture hash.
+ virtual void addToBakedTextureHash(LLMD5& hash) const = 0;
+
+protected:
+ typedef std::map<S32, LLLocalTextureObject*> te_map_t;
+ void syncImages(te_map_t &src, te_map_t &dst);
+ void destroyTextures();
+ void createVisualParams(LLAvatarAppearance *avatarp);
+ void createLayers(S32 te, LLAvatarAppearance *avatarp);
+ BOOL getNextPopulatedLine(std::istream& input_stream, char* buffer, U32 buffer_size);
+
+ static S32 sCurrentDefinitionVersion; // Depends on the current state of the avatar_lad.xml.
+ S32 mDefinitionVersion; // Depends on the state of the avatar_lad.xml when this asset was created.
+ std::string mName;
+ std::string mDescription;
+ LLPermissions mPermissions;
+ LLSaleInfo mSaleInfo;
+ LLWearableType::EType mType;
+
+ typedef std::map<S32, F32> param_map_t;
+ param_map_t mSavedVisualParamMap; // last saved version of visual params
+
+ typedef std::map<S32, LLVisualParam *> visual_param_index_map_t;
+ visual_param_index_map_t mVisualParamIndexMap;
+
+ te_map_t mTEMap; // maps TE to LocalTextureObject
+ te_map_t mSavedTEMap; // last saved version of TEMap
+};
+
+#endif // LL_LLWEARABLE_H
diff --git a/indra/llappearance/llwearabledata.cpp b/indra/llappearance/llwearabledata.cpp
new file mode 100644
index 0000000000..68fdcca782
--- /dev/null
+++ b/indra/llappearance/llwearabledata.cpp
@@ -0,0 +1,360 @@
+/**
+ * @file llwearabledata.cpp
+ * @brief LLWearableData class implementation
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llwearabledata.h"
+
+#include "llavatarappearance.h"
+#include "llavatarappearancedefines.h"
+#include "lldriverparam.h"
+#include "llmd5.h"
+
+LLWearableData::LLWearableData() :
+ mAvatarAppearance(NULL)
+{
+}
+
+// virtual
+LLWearableData::~LLWearableData()
+{
+}
+
+using namespace LLAvatarAppearanceDefines;
+
+LLWearable* LLWearableData::getWearable(const LLWearableType::EType type, U32 index)
+{
+ wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type);
+ if (wearable_iter == mWearableDatas.end())
+ {
+ return NULL;
+ }
+ wearableentry_vec_t& wearable_vec = wearable_iter->second;
+ if (index>=wearable_vec.size())
+ {
+ return NULL;
+ }
+ else
+ {
+ return wearable_vec[index];
+ }
+}
+
+void LLWearableData::setWearable(const LLWearableType::EType type, U32 index, LLWearable *wearable)
+{
+ LLWearable *old_wearable = getWearable(type,index);
+ if (!old_wearable)
+ {
+ pushWearable(type,wearable);
+ return;
+ }
+
+ wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type);
+ if (wearable_iter == mWearableDatas.end())
+ {
+ llwarns << "invalid type, type " << type << " index " << index << llendl;
+ return;
+ }
+ wearableentry_vec_t& wearable_vec = wearable_iter->second;
+ if (index>=wearable_vec.size())
+ {
+ llwarns << "invalid index, type " << type << " index " << index << llendl;
+ }
+ else
+ {
+ wearable_vec[index] = wearable;
+ old_wearable->setUpdated();
+ const BOOL removed = FALSE;
+ wearableUpdated(wearable, removed);
+ }
+}
+
+U32 LLWearableData::pushWearable(const LLWearableType::EType type,
+ LLWearable *wearable,
+ bool trigger_updated /* = true */)
+{
+ if (wearable == NULL)
+ {
+ // no null wearables please!
+ llwarns << "Null wearable sent for type " << type << llendl;
+ return MAX_CLOTHING_PER_TYPE;
+ }
+ if (type < LLWearableType::WT_COUNT || mWearableDatas[type].size() < MAX_CLOTHING_PER_TYPE)
+ {
+ mWearableDatas[type].push_back(wearable);
+ if (trigger_updated)
+ {
+ const BOOL removed = FALSE;
+ wearableUpdated(wearable, removed);
+ }
+ return mWearableDatas[type].size()-1;
+ }
+ return MAX_CLOTHING_PER_TYPE;
+}
+
+// virtual
+void LLWearableData::wearableUpdated(LLWearable *wearable, BOOL removed)
+{
+ wearable->setUpdated();
+ // FIXME DRANO avoid updating params via wearables when rendering server-baked appearance.
+#if 0
+ if (mAvatarAppearance->isUsingServerBakes() && !mAvatarAppearance->isUsingLocalAppearance())
+ {
+ return;
+ }
+#endif
+ if (!removed)
+ {
+ pullCrossWearableValues(wearable->getType());
+ }
+}
+
+void LLWearableData::popWearable(LLWearable *wearable)
+{
+ if (wearable == NULL)
+ {
+ // nothing to do here. move along.
+ return;
+ }
+
+ U32 index = getWearableIndex(wearable);
+ const LLWearableType::EType type = wearable->getType();
+
+ if (index < MAX_CLOTHING_PER_TYPE && index < getWearableCount(type))
+ {
+ popWearable(type, index);
+ }
+}
+
+void LLWearableData::popWearable(const LLWearableType::EType type, U32 index)
+{
+ LLWearable *wearable = getWearable(type, index);
+ if (wearable)
+ {
+ mWearableDatas[type].erase(mWearableDatas[type].begin() + index);
+ const BOOL removed = TRUE;
+ wearableUpdated(wearable, removed);
+ }
+}
+
+void LLWearableData::clearWearableType(const LLWearableType::EType type)
+{
+ wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type);
+ if (wearable_iter == mWearableDatas.end())
+ {
+ return;
+ }
+ wearableentry_vec_t& wearable_vec = wearable_iter->second;
+ wearable_vec.clear();
+}
+
+bool LLWearableData::swapWearables(const LLWearableType::EType type, U32 index_a, U32 index_b)
+{
+ wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type);
+ if (wearable_iter == mWearableDatas.end())
+ {
+ return false;
+ }
+
+ wearableentry_vec_t& wearable_vec = wearable_iter->second;
+ if (0 > index_a || index_a >= wearable_vec.size()) return false;
+ if (0 > index_b || index_b >= wearable_vec.size()) return false;
+
+ LLWearable* wearable = wearable_vec[index_a];
+ wearable_vec[index_a] = wearable_vec[index_b];
+ wearable_vec[index_b] = wearable;
+ return true;
+}
+
+void LLWearableData::pullCrossWearableValues(const LLWearableType::EType type)
+{
+ llassert(mAvatarAppearance);
+ // scan through all of the avatar's visual parameters
+ for (LLViewerVisualParam* param = (LLViewerVisualParam*) mAvatarAppearance->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) mAvatarAppearance->getNextVisualParam())
+ {
+ if( param )
+ {
+ LLDriverParam *driver_param = dynamic_cast<LLDriverParam*>(param);
+ if(driver_param)
+ {
+ // parameter is a driver parameter, have it update its cross-driven params
+ driver_param->updateCrossDrivenParams(type);
+ }
+ }
+ }
+}
+
+
+U32 LLWearableData::getWearableIndex(const LLWearable *wearable) const
+{
+ if (wearable == NULL)
+ {
+ return MAX_CLOTHING_PER_TYPE;
+ }
+
+ const LLWearableType::EType type = wearable->getType();
+ wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type);
+ if (wearable_iter == mWearableDatas.end())
+ {
+ llwarns << "tried to get wearable index with an invalid type!" << llendl;
+ return MAX_CLOTHING_PER_TYPE;
+ }
+ const wearableentry_vec_t& wearable_vec = wearable_iter->second;
+ for(U32 index = 0; index < wearable_vec.size(); index++)
+ {
+ if (wearable_vec[index] == wearable)
+ {
+ return index;
+ }
+ }
+
+ return MAX_CLOTHING_PER_TYPE;
+}
+
+BOOL LLWearableData::isOnTop(LLWearable* wearable) const
+{
+ if (!wearable) return FALSE;
+ const LLWearableType::EType type = wearable->getType();
+ return ( getTopWearable(type) == wearable );
+}
+
+const LLWearable* LLWearableData::getWearable(const LLWearableType::EType type, U32 index) const
+{
+ wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type);
+ if (wearable_iter == mWearableDatas.end())
+ {
+ return NULL;
+ }
+ const wearableentry_vec_t& wearable_vec = wearable_iter->second;
+ if (index>=wearable_vec.size())
+ {
+ return NULL;
+ }
+ else
+ {
+ return wearable_vec[index];
+ }
+}
+
+LLWearable* LLWearableData::getTopWearable(const LLWearableType::EType type)
+{
+ U32 count = getWearableCount(type);
+ if ( count == 0)
+ {
+ return NULL;
+ }
+
+ return getWearable(type, count-1);
+}
+
+const LLWearable* LLWearableData::getTopWearable(const LLWearableType::EType type) const
+{
+ U32 count = getWearableCount(type);
+ if ( count == 0)
+ {
+ return NULL;
+ }
+
+ return getWearable(type, count-1);
+}
+
+LLWearable* LLWearableData::getBottomWearable(const LLWearableType::EType type)
+{
+ if (getWearableCount(type) == 0)
+ {
+ return NULL;
+ }
+
+ return getWearable(type, 0);
+}
+
+const LLWearable* LLWearableData::getBottomWearable(const LLWearableType::EType type) const
+{
+ if (getWearableCount(type) == 0)
+ {
+ return NULL;
+ }
+
+ return getWearable(type, 0);
+}
+
+U32 LLWearableData::getWearableCount(const LLWearableType::EType type) const
+{
+ wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type);
+ if (wearable_iter == mWearableDatas.end())
+ {
+ return 0;
+ }
+ const wearableentry_vec_t& wearable_vec = wearable_iter->second;
+ return wearable_vec.size();
+}
+
+U32 LLWearableData::getWearableCount(const U32 tex_index) const
+{
+ const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType((LLAvatarAppearanceDefines::ETextureIndex)tex_index);
+ return getWearableCount(wearable_type);
+}
+
+LLUUID LLWearableData::computeBakedTextureHash(LLAvatarAppearanceDefines::EBakedTextureIndex baked_index,
+ BOOL generate_valid_hash) // Set to false if you want to upload the baked texture w/o putting it in the cache
+{
+ LLUUID hash_id;
+ bool hash_computed = false;
+ LLMD5 hash;
+ const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture(baked_index);
+
+ for (U8 i=0; i < baked_dict->mWearables.size(); i++)
+ {
+ const LLWearableType::EType baked_type = baked_dict->mWearables[i];
+ const U32 num_wearables = getWearableCount(baked_type);
+ for (U32 index = 0; index < num_wearables; ++index)
+ {
+ const LLWearable* wearable = getWearable(baked_type,index);
+ if (wearable)
+ {
+ wearable->addToBakedTextureHash(hash);
+ hash_computed = true;
+ }
+ }
+ }
+ if (hash_computed)
+ {
+ hash.update((const unsigned char*)baked_dict->mWearablesHashID.mData, UUID_BYTES);
+
+ if (!generate_valid_hash)
+ {
+ invalidateBakedTextureHash(hash);
+ }
+ hash.finalize();
+ hash.raw_digest(hash_id.mData);
+ }
+
+ return hash_id;
+}
+
+
diff --git a/indra/llappearance/llwearabledata.h b/indra/llappearance/llwearabledata.h
new file mode 100644
index 0000000000..3e92f2ead8
--- /dev/null
+++ b/indra/llappearance/llwearabledata.h
@@ -0,0 +1,109 @@
+/**
+ * @file llwearabledata.h
+ * @brief LLWearableData class header file
+ *
+ * $LicenseInfo:firstyear=20012license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_WEARABLEDATA_H
+#define LL_WEARABLEDATA_H
+
+#include "llavatarappearancedefines.h"
+#include "llwearable.h"
+#include "llerror.h"
+
+class LLAvatarAppearance;
+
+class LLWearableData
+{
+ // *TODO: Figure out why this is causing compile error.
+ //LOG_CLASS(LLWearableData);
+
+ //--------------------------------------------------------------------
+ // Constructors / destructors / Initializers
+ //--------------------------------------------------------------------
+public:
+ LLWearableData();
+ virtual ~LLWearableData();
+
+ void setAvatarAppearance(LLAvatarAppearance* appearance) { mAvatarAppearance = appearance; }
+
+protected:
+ //--------------------------------------------------------------------
+ // Accessors
+ //--------------------------------------------------------------------
+public:
+ LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/);
+ const LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/) const;
+ LLWearable* getTopWearable(const LLWearableType::EType type);
+ const LLWearable* getTopWearable(const LLWearableType::EType type) const;
+ LLWearable* getBottomWearable(const LLWearableType::EType type);
+ const LLWearable* getBottomWearable(const LLWearableType::EType type) const;
+ U32 getWearableCount(const LLWearableType::EType type) const;
+ U32 getWearableCount(const U32 tex_index) const;
+ U32 getWearableIndex(const LLWearable *wearable) const;
+
+ BOOL isOnTop(LLWearable* wearable) const;
+
+ static const U32 MAX_CLOTHING_PER_TYPE = 5;
+
+ //--------------------------------------------------------------------
+ // Setters
+ //--------------------------------------------------------------------
+protected:
+ // Low-level data structure setter - public access is via setWearableItem, etc.
+ void setWearable(const LLWearableType::EType type, U32 index, LLWearable *wearable);
+ U32 pushWearable(const LLWearableType::EType type, LLWearable *wearable,
+ bool trigger_updated = true);
+ virtual void wearableUpdated(LLWearable *wearable, BOOL removed);
+ void popWearable(LLWearable *wearable);
+ void popWearable(const LLWearableType::EType type, U32 index);
+ void clearWearableType(const LLWearableType::EType type);
+ bool swapWearables(const LLWearableType::EType type, U32 index_a, U32 index_b);
+
+private:
+ void pullCrossWearableValues(const LLWearableType::EType type);
+
+ //--------------------------------------------------------------------
+ // Server Communication
+ //--------------------------------------------------------------------
+public:
+ LLUUID computeBakedTextureHash(LLAvatarAppearanceDefines::EBakedTextureIndex baked_index,
+ BOOL generate_valid_hash = TRUE);
+protected:
+ virtual void invalidateBakedTextureHash(LLMD5& hash) const {}
+
+ //--------------------------------------------------------------------
+ // Member variables
+ //--------------------------------------------------------------------
+protected:
+ LLAvatarAppearance* mAvatarAppearance;
+ typedef std::vector<LLWearable*> wearableentry_vec_t; // all wearables of a certain type (EG all shirts)
+ typedef std::map<LLWearableType::EType, wearableentry_vec_t> wearableentry_map_t; // wearable "categories" arranged by wearable type
+ wearableentry_map_t mWearableDatas;
+
+};
+
+
+
+#endif // LL_WEARABLEDATA_H
+
diff --git a/indra/llappearance/llwearabletype.cpp b/indra/llappearance/llwearabletype.cpp
new file mode 100644
index 0000000000..618e2a1941
--- /dev/null
+++ b/indra/llappearance/llwearabletype.cpp
@@ -0,0 +1,171 @@
+/**
+ * @file llwearabletype.cpp
+ * @brief LLWearableType class implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llwearabletype.h"
+#include "llinventorytype.h"
+
+static LLTranslationBridge* sTrans = NULL;
+
+// static
+void LLWearableType::initClass(LLTranslationBridge* trans)
+{
+ sTrans = trans;
+}
+
+void LLWearableType::cleanupClass()
+{
+ delete sTrans;
+}
+
+struct WearableEntry : public LLDictionaryEntry
+{
+ WearableEntry(const std::string &name,
+ const std::string& default_new_name,
+ LLAssetType::EType assetType,
+ LLInventoryType::EIconName iconName,
+ BOOL disable_camera_switch = FALSE,
+ BOOL allow_multiwear = TRUE) :
+ LLDictionaryEntry(name),
+ mAssetType(assetType),
+ mDefaultNewName(default_new_name),
+ mLabel(sTrans->getString(name)),
+ mIconName(iconName),
+ mDisableCameraSwitch(disable_camera_switch),
+ mAllowMultiwear(allow_multiwear)
+ {
+
+ }
+ const LLAssetType::EType mAssetType;
+ const std::string mLabel;
+ const std::string mDefaultNewName; //keep mLabel for backward compatibility
+ LLInventoryType::EIconName mIconName;
+ BOOL mDisableCameraSwitch;
+ BOOL mAllowMultiwear;
+};
+
+class LLWearableDictionary : public LLSingleton<LLWearableDictionary>,
+ public LLDictionary<LLWearableType::EType, WearableEntry>
+{
+public:
+ LLWearableDictionary();
+};
+
+LLWearableDictionary::LLWearableDictionary()
+{
+ addEntry(LLWearableType::WT_SHAPE, new WearableEntry("shape", "New Shape", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_SHAPE, FALSE, FALSE));
+ addEntry(LLWearableType::WT_SKIN, new WearableEntry("skin", "New Skin", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_SKIN, FALSE, FALSE));
+ addEntry(LLWearableType::WT_HAIR, new WearableEntry("hair", "New Hair", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_HAIR, FALSE, FALSE));
+ addEntry(LLWearableType::WT_EYES, new WearableEntry("eyes", "New Eyes", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_EYES, FALSE, FALSE));
+ addEntry(LLWearableType::WT_SHIRT, new WearableEntry("shirt", "New Shirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SHIRT, FALSE, TRUE));
+ addEntry(LLWearableType::WT_PANTS, new WearableEntry("pants", "New Pants", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PANTS, FALSE, TRUE));
+ addEntry(LLWearableType::WT_SHOES, new WearableEntry("shoes", "New Shoes", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SHOES, FALSE, TRUE));
+ addEntry(LLWearableType::WT_SOCKS, new WearableEntry("socks", "New Socks", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SOCKS, FALSE, TRUE));
+ addEntry(LLWearableType::WT_JACKET, new WearableEntry("jacket", "New Jacket", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_JACKET, FALSE, TRUE));
+ addEntry(LLWearableType::WT_GLOVES, new WearableEntry("gloves", "New Gloves", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_GLOVES, FALSE, TRUE));
+ addEntry(LLWearableType::WT_UNDERSHIRT, new WearableEntry("undershirt", "New Undershirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNDERSHIRT, FALSE, TRUE));
+ addEntry(LLWearableType::WT_UNDERPANTS, new WearableEntry("underpants", "New Underpants", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNDERPANTS, FALSE, TRUE));
+ addEntry(LLWearableType::WT_SKIRT, new WearableEntry("skirt", "New Skirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SKIRT, FALSE, TRUE));
+ addEntry(LLWearableType::WT_ALPHA, new WearableEntry("alpha", "New Alpha", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_ALPHA, FALSE, TRUE));
+ addEntry(LLWearableType::WT_TATTOO, new WearableEntry("tattoo", "New Tattoo", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_TATTOO, FALSE, TRUE));
+
+ addEntry(LLWearableType::WT_PHYSICS, new WearableEntry("physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, TRUE));
+
+ addEntry(LLWearableType::WT_INVALID, new WearableEntry("invalid", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_NONE, FALSE, FALSE));
+ addEntry(LLWearableType::WT_NONE, new WearableEntry("none", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_NONE, FALSE, FALSE));
+}
+
+// static
+LLWearableType::EType LLWearableType::typeNameToType(const std::string& type_name)
+{
+ const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
+ const LLWearableType::EType wearable = dict->lookup(type_name);
+ return wearable;
+}
+
+// static
+const std::string& LLWearableType::getTypeName(LLWearableType::EType type)
+{
+ const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
+ const WearableEntry *entry = dict->lookup(type);
+ if (!entry) return getTypeName(WT_INVALID);
+ return entry->mName;
+}
+
+//static
+const std::string& LLWearableType::getTypeDefaultNewName(LLWearableType::EType type)
+{
+ const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
+ const WearableEntry *entry = dict->lookup(type);
+ if (!entry) return getTypeDefaultNewName(WT_INVALID);
+ return entry->mDefaultNewName;
+}
+
+// static
+const std::string& LLWearableType::getTypeLabel(LLWearableType::EType type)
+{
+ const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
+ const WearableEntry *entry = dict->lookup(type);
+ if (!entry) return getTypeLabel(WT_INVALID);
+ return entry->mLabel;
+}
+
+// static
+LLAssetType::EType LLWearableType::getAssetType(LLWearableType::EType type)
+{
+ const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
+ const WearableEntry *entry = dict->lookup(type);
+ if (!entry) return getAssetType(WT_INVALID);
+ return entry->mAssetType;
+}
+
+// static
+LLInventoryType::EIconName LLWearableType::getIconName(LLWearableType::EType type)
+{
+ const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
+ const WearableEntry *entry = dict->lookup(type);
+ if (!entry) return getIconName(WT_INVALID);
+ return entry->mIconName;
+}
+
+// static
+BOOL LLWearableType::getDisableCameraSwitch(LLWearableType::EType type)
+{
+ const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
+ const WearableEntry *entry = dict->lookup(type);
+ if (!entry) return FALSE;
+ return entry->mDisableCameraSwitch;
+}
+
+// static
+BOOL LLWearableType::getAllowMultiwear(LLWearableType::EType type)
+{
+ const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
+ const WearableEntry *entry = dict->lookup(type);
+ if (!entry) return FALSE;
+ return entry->mAllowMultiwear;
+}
+
diff --git a/indra/llappearance/llwearabletype.h b/indra/llappearance/llwearabletype.h
new file mode 100644
index 0000000000..e51e6731d3
--- /dev/null
+++ b/indra/llappearance/llwearabletype.h
@@ -0,0 +1,86 @@
+/**
+ * @file llwearabletype.h
+ * @brief LLWearableType class header file
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLWEARABLETYPE_H
+#define LL_LLWEARABLETYPE_H
+
+#include "llassettype.h"
+#include "lldictionary.h"
+#include "llinventorytype.h"
+#include "llsingleton.h"
+
+class LLTranslationBridge
+{
+public:
+ virtual std::string getString(const std::string &xml_desc) = 0;
+};
+
+
+class LLWearableType
+{
+public:
+ enum EType
+ {
+ WT_SHAPE = 0,
+ WT_SKIN = 1,
+ WT_HAIR = 2,
+ WT_EYES = 3,
+ WT_SHIRT = 4,
+ WT_PANTS = 5,
+ WT_SHOES = 6,
+ WT_SOCKS = 7,
+ WT_JACKET = 8,
+ WT_GLOVES = 9,
+ WT_UNDERSHIRT = 10,
+ WT_UNDERPANTS = 11,
+ WT_SKIRT = 12,
+ WT_ALPHA = 13,
+ WT_TATTOO = 14,
+ WT_PHYSICS = 15,
+ WT_COUNT = 16,
+
+ WT_INVALID = 255,
+ WT_NONE = -1,
+ };
+
+ static void initClass(LLTranslationBridge* trans); // initializes static members
+ static void cleanupClass(); // initializes static members
+
+ static const std::string& getTypeName(EType type);
+ static const std::string& getTypeDefaultNewName(EType type);
+ static const std::string& getTypeLabel(EType type);
+ static LLAssetType::EType getAssetType(EType type);
+ static EType typeNameToType(const std::string& type_name);
+ static LLInventoryType::EIconName getIconName(EType type);
+ static BOOL getDisableCameraSwitch(EType type);
+ static BOOL getAllowMultiwear(EType type);
+
+protected:
+ LLWearableType() {}
+ ~LLWearableType() {}
+};
+
+#endif // LL_LLWEARABLETYPE_H