summaryrefslogtreecommitdiff
path: root/indra/llappearance
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llappearance')
-rw-r--r--indra/llappearance/CMakeLists.txt1
-rw-r--r--indra/llappearance/llavatarappearance.cpp66
-rw-r--r--indra/llappearance/llavatarappearance.h10
-rw-r--r--indra/llappearance/lljointdata.h66
-rw-r--r--indra/llappearance/lltexlayer.cpp29
5 files changed, 156 insertions, 16 deletions
diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt
index f3f822f61c..b382e21b3c 100644
--- a/indra/llappearance/CMakeLists.txt
+++ b/indra/llappearance/CMakeLists.txt
@@ -14,6 +14,7 @@ set(llappearance_SOURCE_FILES
llavatarjoint.cpp
llavatarjointmesh.cpp
lldriverparam.cpp
+ lljointdata.h
lllocaltextureobject.cpp
llpolyskeletaldistortion.cpp
llpolymesh.cpp
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index 3d66809ed6..dab18c240d 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -29,16 +29,17 @@
#include "llavatarappearance.h"
#include "llavatarappearancedefines.h"
#include "llavatarjointmesh.h"
+#include "lljointdata.h"
#include "llstl.h"
#include "lldir.h"
#include "llpolymorph.h"
#include "llpolymesh.h"
#include "llpolyskeletaldistortion.h"
-#include "llstl.h"
#include "lltexglobalcolor.h"
#include "llwearabledata.h"
#include "boost/bind.hpp"
#include "boost/tokenizer.hpp"
+#include "v4math.h"
using namespace LLAvatarAppearanceDefines;
@@ -71,11 +72,13 @@ public:
mChildren.clear();
}
bool parseXml(LLXmlTreeNode* node);
+ glm::mat4 getJointMatrix();
private:
std::string mName;
std::string mSupport;
std::string mAliases;
+ std::string mGroup;
bool mIsJoint;
LLVector3 mPos;
LLVector3 mEnd;
@@ -106,10 +109,16 @@ public:
S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
private:
+ typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
+ static void getJointMatricesAndHierarhy(
+ LLAvatarBoneInfo* bone_info,
+ LLJointData& data,
+ const glm::mat4& parent_mat);
+
+private:
S32 mNumBones;
S32 mNumCollisionVolumes;
LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
- typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
bone_info_list_t mBoneInfoList;
};
@@ -1598,6 +1607,15 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
mSupport = "base";
}
+ // Skeleton has 133 bones, but shader only allows 110 (LL_MAX_JOINTS_PER_MESH_OBJECT)
+ // Groups can be used by importer to cut out unused groups of joints
+ static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
+ if (!node->getFastAttributeString(group_string, mGroup))
+ {
+ LL_WARNS() << "Bone without group " << mName << LL_ENDL;
+ mGroup = "global";
+ }
+
if (mIsJoint)
{
static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
@@ -1623,6 +1641,21 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
return true;
}
+
+glm::mat4 LLAvatarBoneInfo::getJointMatrix()
+{
+ glm::mat4 mat(1.0f);
+ // 1. Scaling
+ mat = glm::scale(mat, glm::vec3(mScale[0], mScale[1], mScale[2]));
+ // 2. Rotation (Euler angles rad)
+ mat = glm::rotate(mat, mRot[0], glm::vec3(1, 0, 0));
+ mat = glm::rotate(mat, mRot[1], glm::vec3(0, 1, 0));
+ mat = glm::rotate(mat, mRot[2], glm::vec3(0, 0, 1));
+ // 3. Position
+ mat = glm::translate(mat, glm::vec3(mPos[0], mPos[1], mPos[2]));
+ return mat;
+}
+
//-----------------------------------------------------------------------------
// LLAvatarSkeletonInfo::parseXml()
//-----------------------------------------------------------------------------
@@ -1653,6 +1686,25 @@ bool LLAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
return true;
}
+void LLAvatarSkeletonInfo::getJointMatricesAndHierarhy(
+ LLAvatarBoneInfo* bone_info,
+ LLJointData& data,
+ const glm::mat4& parent_mat)
+{
+ data.mName = bone_info->mName;
+ data.mJointMatrix = bone_info->getJointMatrix();
+ data.mScale = glm::vec3(bone_info->mScale[0], bone_info->mScale[1], bone_info->mScale[2]);
+ data.mRotation = bone_info->mRot;
+ data.mRestMatrix = parent_mat * data.mJointMatrix;
+ data.mIsJoint = bone_info->mIsJoint;
+ data.mGroup = bone_info->mGroup;
+ for (LLAvatarBoneInfo* child_info : bone_info->mChildren)
+ {
+ LLJointData& child_data = data.mChildren.emplace_back();
+ getJointMatricesAndHierarhy(child_info, child_data, data.mRestMatrix);
+ }
+}
+
//Make aliases for joint and push to map.
void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
{
@@ -1714,6 +1766,16 @@ const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases
return mJointAliasMap;
}
+void LLAvatarAppearance::getJointMatricesAndHierarhy(std::vector<LLJointData> &data) const
+{
+ glm::mat4 identity(1.f);
+ for (LLAvatarBoneInfo* bone_info : sAvatarSkeletonInfo->mBoneInfoList)
+ {
+ LLJointData& child_data = data.emplace_back();
+ LLAvatarSkeletonInfo::getJointMatricesAndHierarhy(bone_info, child_data, identity);
+ }
+}
+
//-----------------------------------------------------------------------------
// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h
index 13e504e639..84cb42056a 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -34,6 +34,7 @@
#include "lltexlayer.h"
#include "llviewervisualparam.h"
#include "llxmltree.h"
+#include "v4math.h"
class LLTexLayerSet;
class LLTexGlobalColor;
@@ -41,6 +42,7 @@ class LLTexGlobalColorInfo;
class LLWearableData;
class LLAvatarBoneInfo;
class LLAvatarSkeletonInfo;
+class LLJointData;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// LLAvatarAppearance
@@ -138,7 +140,7 @@ public:
LLVector3 mHeadOffset{}; // current head position
LLAvatarJoint* mRoot{ nullptr };
- typedef std::map<std::string, LLJoint*> joint_map_t;
+ typedef std::map<std::string, LLJoint*, std::less<>> joint_map_t;
joint_map_t mJointMap;
typedef std::map<std::string, LLVector3> joint_state_map_t;
@@ -151,9 +153,11 @@ public:
public:
typedef std::vector<LLAvatarJoint*> avatar_joint_list_t;
const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
- typedef std::map<std::string, std::string> joint_alias_map_t;
+ typedef std::map<std::string, std::string, std::less<>> joint_alias_map_t;
const joint_alias_map_t& getJointAliases();
-
+ typedef std::map<std::string, std::string> joint_parent_map_t; // matrix plus parent
+ typedef std::map<std::string, glm::mat4> joint_rest_map_t;
+ void getJointMatricesAndHierarhy(std::vector<LLJointData> &data) const;
protected:
static bool parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree);
diff --git a/indra/llappearance/lljointdata.h b/indra/llappearance/lljointdata.h
new file mode 100644
index 0000000000..2fc26198ee
--- /dev/null
+++ b/indra/llappearance/lljointdata.h
@@ -0,0 +1,66 @@
+/**
+ * @file lljointdata.h
+ * @brief LLJointData class for holding individual joint data and skeleton
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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_LLJOINTDATA_H
+#define LL_LLJOINTDATA_H
+
+#include "v4math.h"
+
+// may be just move LLAvatarBoneInfo
+class LLJointData
+{
+public:
+ std::string mName;
+ std::string mGroup;
+ glm::mat4 mJointMatrix;
+ glm::mat4 mRestMatrix;
+ glm::vec3 mScale;
+ LLVector3 mRotation;
+
+ typedef std::vector<LLJointData> bones_t;
+ bones_t mChildren;
+
+ bool mIsJoint; // if not, collision_volume
+ enum SupportCategory
+ {
+ SUPPORT_BASE,
+ SUPPORT_EXTENDED
+ };
+ SupportCategory mSupport;
+ void setSupport(const std::string& support)
+ {
+ if (support == "extended")
+ {
+ mSupport = SUPPORT_EXTENDED;
+ }
+ else
+ {
+ mSupport = SUPPORT_BASE;
+ }
+ }
+};
+
+#endif //LL_LLJOINTDATA_H
diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp
index e368ace35e..f72f47723f 100644
--- a/indra/llappearance/lltexlayer.cpp
+++ b/indra/llappearance/lltexlayer.cpp
@@ -1293,7 +1293,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
{
if (!force_render && !hasMorph())
{
- LL_DEBUGS() << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
+ LL_DEBUGS("Morph") << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
return;
}
LL_PROFILE_ZONE_SCOPED;
@@ -1325,7 +1325,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
success &= param->render( x, y, width, height );
if (!success && !force_render)
{
- LL_DEBUGS() << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
+ LL_DEBUGS("Morph") << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
return;
}
}
@@ -1365,7 +1365,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
}
else
{
- LL_WARNS() << "Skipping rendering of " << getInfo()->mStaticImageFileName
+ LL_WARNS("Morph") << "Skipping rendering of " << getInfo()->mStaticImageFileName
<< "; expected 1 or 4 components." << LL_ENDL;
}
}
@@ -1404,8 +1404,8 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
// We can get bad morph masks during login, on minimize, and occasional gl errors.
// We should only be doing this when we believe something has changed with respect to the user's appearance.
{
- LL_DEBUGS("Avatar") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
- // clear out a slot if we have filled our cache
+ LL_DEBUGS("Morph") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
+ // 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)
{
@@ -1446,13 +1446,20 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
#if GL_VERSION_1_1
glGetTexImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
#endif
-
- U8* alpha_cursor = alpha_data;
- U8* pixel = temp;
- for (int i = 0; i < pixels; i++)
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ LL_INFOS("Morph") << "GL Error while reading back morph texture. Error code: " << error << LL_ENDL;
+ }
+ else
{
- *alpha_cursor++ = pixel[3];
- pixel += 4;
+ U8* alpha_cursor = alpha_data;
+ U8* pixel = temp;
+ for (int i = 0; i < pixels; i++)
+ {
+ *alpha_cursor++ = pixel[3];
+ pixel += 4;
+ }
}
gGL.getTexUnit(0)->disable();