diff options
author | Ima Mechanique <ima.mechanique@secondlife.com> | 2013-06-26 17:39:23 +0100 |
---|---|---|
committer | Ima Mechanique <ima.mechanique@secondlife.com> | 2013-06-26 17:39:23 +0100 |
commit | b615858d6c41c29b3b610ec0dba09f30b1f8b3a8 (patch) | |
tree | 2b193625c9ed4a0ca36e5b5f99f1cc765e979d74 /indra/llappearance | |
parent | e63d92ab504d74399cae2a9cc7625ef3ec330c38 (diff) | |
parent | 2655c7a17ae38a073dcf8f05b0127b68edc34c95 (diff) |
Merging last four months of development
Diffstat (limited to 'indra/llappearance')
36 files changed, 13313 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..3bb759d458 --- /dev/null +++ b/indra/llappearance/llavatarappearance.cpp @@ -0,0 +1,1953 @@ +/** + * @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(AVATAR_HOVER); + + 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; + + // Certain configurations of avatars can force the overall height (with offset) to go negative. + // Enforce a constraint to make sure we don't go below 0.1 meters. + // Camera positioning and other things start to break down when your avatar is "walking" while being fully underground + if (new_body_size.mV[VZ] + mAvatarOffset.mV[VZ] < 0.1f) + { + mAvatarOffset.mV[VZ] = -(new_body_size.mV[VZ] - 0.11f); // avoid floating point rounding making the above check continue to fail. + + llassert(new_body_size.mV[VZ] + mAvatarOffset.mV[VZ] >= 0.1f); + + if (mWearableData && isSelf()) + { + LLWearable* shape = mWearableData->getWearable(LLWearableType::WT_SHAPE, 0); + if (shape) + { + shape->setVisualParamWeight(AVATAR_HOVER, mAvatarOffset.mV[VZ], false); + } + } + } + + 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 ¤t_volume_num, S32 ¤t_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..8a1d2c4707 --- /dev/null +++ b/indra/llappearance/llavatarappearancedefines.h @@ -0,0 +1,231 @@ +/** + * @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; + +static const U32 AVATAR_HOVER = 11001; + +//-------------------------------------------------------------------- +// 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 == ¤t_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(¤t_joint->getParent()->getWorldMatrix(), NULL)); +// llinfos << "joint " << joint_count << current_joint->getParent()->getName() << llendl; +// joint_count++; + mMesh->mJointRenderData.put(new LLJointRenderData(¤t_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..93c2f15a53 --- /dev/null +++ b/indra/llappearance/llpolymorph.cpp @@ -0,0 +1,802 @@ +/** + * @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]; + + // guard against degenerate input data before we create NaNs below! + // + norm.normalize3fast(); + normals[vert_index_mesh] = norm; + + // calculate new binormals + LLVector4a binorm = mMorphData->mBinormals[vert_index_morph]; + + // guard against degenerate input data before we create NaNs below! + // + if (!binorm.isFinite3() || (binorm.dot3(binorm).getF32() <= F_APPROXIMATELY_ZERO)) + { + binorm.set(1,0,0,1); + } + + 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..f951a982e5 --- /dev/null +++ b/indra/llappearance/lltexlayer.cpp @@ -0,0 +1,2019 @@ +/** + * @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 "../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 ¶m_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*)¶m_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*)¶m_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 ¶m_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..6aae9a8cc1 --- /dev/null +++ b/indra/llappearance/lltexlayerparams.cpp @@ -0,0 +1,570 @@ +/** + * @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 "../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..d86a460511 --- /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.exportLegacyStream( output_stream ) ) + { + return FALSE; + } + + // sale info + if( !mSaleInfo.exportLegacyStream( 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.importLegacyStream( 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.importLegacyStream(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", ¶m_id, ¶m_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 <o) +{ + 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 <o); + 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..03bd179f25 --- /dev/null +++ b/indra/llappearance/llwearabledata.h @@ -0,0 +1,109 @@ +/** + * @file llwearabledata.h + * @brief LLWearableData class header file + * + * $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_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 |