summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorErik Kundiman <erik@megapahit.org>2025-07-03 21:26:14 +0800
committerErik Kundiman <erik@megapahit.org>2025-07-03 21:26:14 +0800
commit2df6ca5bc3e7931fcdddc57618a6d08547ba411b (patch)
treea126ef5814878704d137aa81c2851414e5d27a40 /indra
parent9dee269eacd32c48627d3fcc243104a56f021bbc (diff)
parent900516a449c618c87517d295db7f76662e555d00 (diff)
Merge tag 'Second_Life_Project#900516a4-glTF_Mesh_Import' into gltf_mesh_import
Diffstat (limited to 'indra')
-rw-r--r--indra/llprimitive/llmodelloader.cpp37
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/settings.xml2
-rw-r--r--indra/newview/gltf/llgltfloader.cpp277
-rw-r--r--indra/newview/gltf/llgltfloader.h8
-rw-r--r--indra/newview/llappviewer.cpp21
-rw-r--r--indra/newview/llappviewer.h1
-rw-r--r--indra/newview/llviewermenu.cpp16
-rw-r--r--indra/newview/skins/default/xui/en/floater_model_preview.xml9
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml10
10 files changed, 229 insertions, 154 deletions
diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index 55f1338dab..7fc4aa2bdf 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -149,7 +149,42 @@ LLModelLoader::~LLModelLoader()
void LLModelLoader::run()
{
mWarningsArray.clear();
- doLoadModel();
+ try
+ {
+ doLoadModel();
+ }
+ // Model loader isn't mission critical, so we just log all exceptions
+ catch (const LLException& e)
+ {
+ LL_WARNS("THREAD") << "LLException in model loader: " << e.what() << "" << LL_ENDL;
+ LLSD args;
+ args["Message"] = "UnknownException";
+ args["FILENAME"] = mFilename;
+ args["EXCEPTION"] = e.what();
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL;
+ LLSD args;
+ args["Message"] = "UnknownException";
+ args["FILENAME"] = mFilename;
+ args["EXCEPTION"] = e.what();
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ }
+ catch (...)
+ {
+ LOG_UNHANDLED_EXCEPTION("LLModelLoader");
+ LLSD args;
+ args["Message"] = "UnknownException";
+ args["FILENAME"] = mFilename;
+ args["EXCEPTION"] = "Unknown exception";
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ }
+
// todo: we are inside of a thread, push this into main thread worker,
// not into doOnIdleOneTime that laks tread safety
doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 6329380f96..0ee843cc60 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-7.1.15
+7.2.0
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 33c5474d0d..12f141aa38 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -27,7 +27,7 @@
<key>ImporterModelLimit</key>
<map>
<key>Comment</key>
- <string>Limits amount of importer generated models for dae files</string>
+ <string>Limits amount of importer generated (when over 8 faces) models for dae and gltf files</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp
index bd3c1a3833..dd9e0d9b3a 100644
--- a/indra/newview/gltf/llgltfloader.cpp
+++ b/indra/newview/gltf/llgltfloader.cpp
@@ -124,7 +124,32 @@ bool LLGLTFLoader::OpenFile(const std::string &filename)
std::string filename_lc(filename);
LLStringUtil::toLower(filename_lc);
- mGltfLoaded = mGLTFAsset.load(filename, false);
+ try
+ {
+ mGltfLoaded = mGLTFAsset.load(filename, false);
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL;
+ LLSD args;
+ args["Message"] = "ParsingErrorException";
+ args["FILENAME"] = filename;
+ args["EXCEPTION"] = e.what();
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ return false;
+ }
+ catch (...)
+ {
+ LOG_UNHANDLED_EXCEPTION("LLGLTFLoader");
+ LLSD args;
+ args["Message"] = "ParsingErrorException";
+ args["FILENAME"] = filename;
+ args["EXCEPTION"] = "Unknown exception";
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ return false;
+ }
if (!mGltfLoaded)
{
@@ -175,6 +200,16 @@ void LLGLTFLoader::addModelToScene(
U32 face_limit = (submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES;
if (face_limit < volume_faces)
{
+ LL_WARNS("GLTF_IMPORT") << "Model contains " << volume_faces
+ << " faces, exceeding the limit of " << face_limit << LL_ENDL;
+
+ LLSD args;
+ args["Message"] = "ModelTooManySubmodels";
+ args["MODEL_NAME"] = pModel->mLabel;
+ args["SUBMODEL_COUNT"] = static_cast<S32>(llfloor((F32)volume_faces / LL_SCULPT_MESH_MAX_FACES));
+ args["SUBMODEL_LIMIT"] = static_cast<S32>(submodel_limit);
+ mWarningsArray.append(args);
+
pModel->setNumVolumeFaces(face_limit);
}
@@ -284,7 +319,9 @@ bool LLGLTFLoader::parseMeshes()
// Track how many times each mesh name has been used
std::map<std::string, S32> mesh_name_counts;
- U32 submodel_limit = mGLTFAsset.mNodes.size() > 0 ? mGeneratedModelLimit / (U32)mGLTFAsset.mNodes.size() : 0;
+
+ // For now use mesh count, but might be better to do 'mNodes.size() - joints count'.
+ U32 submodel_limit = mGLTFAsset.mMeshes.size() > 0 ? mGeneratedModelLimit / (U32)mGLTFAsset.mMeshes.size() : 0;
// Check if we have scenes defined
if (!mGLTFAsset.mScenes.empty())
@@ -318,20 +355,7 @@ bool LLGLTFLoader::parseMeshes()
return false;
}
- // Check total model count against limit
- U32 total_models = static_cast<U32>(mModelList.size());
- if (total_models > mGeneratedModelLimit)
- {
- LL_WARNS("GLTF_IMPORT") << "Model contains " << total_models
- << " mesh parts, exceeding the limit of " << mGeneratedModelLimit << LL_ENDL;
-
- LLSD args;
- args["Message"] = "TooManyMeshParts";
- args["PART_COUNT"] = static_cast<S32>(total_models);
- args["LIMIT"] = static_cast<S32>(mGeneratedModelLimit);
- mWarningsArray.append(args);
- return false;
- }
+ checkGlobalJointUsage();
return true;
}
@@ -478,7 +502,7 @@ void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S3
}
}
-bool LLGLTFLoader::addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx) const
+bool LLGLTFLoader::addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx)
{
const std::string& legal_name = mJointNames[gltf_skin_idx][gltf_joint_idx];
if (legal_name.empty())
@@ -493,6 +517,9 @@ bool LLGLTFLoader::addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_
skin_info.mInvBindMatrix.push_back(mInverseBindMatrices[gltf_skin_idx][gltf_joint_idx]);
skin_info.mAlternateBindMatrix.push_back(mAlternateBindMatrices[gltf_skin_idx][gltf_joint_idx]);
+ // Track joint usage for this skin, for the sake of unused joints detection
+ mJointUsage[gltf_skin_idx][gltf_joint_idx]++;
+
return true;
}
@@ -550,11 +577,11 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx];
size_t jointCnt = gltf_skin.mJoints.size();
- gltf_joint_index_use.resize(jointCnt);
+ gltf_joint_index_use.resize(jointCnt, 0);
for (size_t i = 0; i < jointCnt; ++i)
{
- if (mJointNames[i].empty())
+ if (mJointNames[skinIdx][i].empty())
{
// This might need to hold a substitute index
gltf_joint_index_use[i] = -1; // mark as unsupported
@@ -885,6 +912,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
std::vector<U16> indices_16;
std::vector<S64> vertices_remap; // should it be a point map?
vertices_remap.resize(faceVertices.size(), -1);
+ S32 created_faces = 0;
std::vector<LLVolumeFace::VertexData> face_verts;
min = glm::vec3(FLT_MAX);
max = glm::vec3(-FLT_MAX);
@@ -933,6 +961,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0);
pModel->getVolumeFaces().push_back(face);
pModel->getMaterialList().push_back(materialName);
+ created_faces++;
std::fill(vertices_remap.begin(), vertices_remap.end(), -1);
indices_16.clear();
@@ -950,7 +979,17 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0);
pModel->getVolumeFaces().push_back(face);
pModel->getMaterialList().push_back(materialName);
+ created_faces++;
}
+
+ LL_INFOS("GLTF_IMPORT") << "Primitive " << pModel->mLabel
+ << " is over vertices limit, it was split into " << created_faces
+ << " faces" << LL_ENDL;
+ LLSD args;
+ args["Message"] = "ModelSplitPrimitive";
+ args["MODEL_NAME"] = pModel->mLabel;
+ args["FACE_COUNT"] = created_faces;
+ mWarningsArray.append(args);
}
else
{
@@ -996,7 +1035,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
S32 replacement_index = 0;
std::vector<S32> gltfindex_to_joitindex_map;
size_t jointCnt = gltf_skin.mJoints.size();
- gltfindex_to_joitindex_map.resize(jointCnt);
+ gltfindex_to_joitindex_map.resize(jointCnt, -1);
if (valid_joints_count > LL_MAX_JOINTS_PER_MESH_OBJECT)
{
@@ -1094,7 +1133,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
<< " Count: " << (S32)skin_info.mInvBindMatrix.size()
<< " Limit:" << (S32)LL_MAX_JOINTS_PER_MESH_OBJECT << LL_ENDL;
LLSD args;
- args["Message"] = "ModelTooManyJoint";
+ args["Message"] = "ModelTooManyJoints";
args["MODEL_NAME"] = pModel->mLabel;
args["JOINT_COUNT"] = (S32)skin_info.mInvBindMatrix.size();
args["MAX"] = (S32)LL_MAX_JOINTS_PER_MESH_OBJECT;
@@ -1135,6 +1174,7 @@ void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx)
mInverseBindMatrices.resize(skin_idx + 1);
mAlternateBindMatrices.resize(skin_idx + 1);
mJointNames.resize(skin_idx + 1);
+ mJointUsage.resize(skin_idx + 1);
mValidJointsCount.resize(skin_idx + 1, 0);
}
@@ -1173,20 +1213,6 @@ void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx)
}
}
- if (mValidJointsCount[skin_idx] > LL_MAX_JOINTS_PER_MESH_OBJECT)
- {
- LL_WARNS("GLTF_IMPORT") << "Too many jonts, will strip unused joints"
- << " Count: " << mValidJointsCount[skin_idx]
- << " Limit:" << (S32)LL_MAX_JOINTS_PER_MESH_OBJECT << LL_ENDL;
-
- LLSD args;
- args["Message"] = "SkinJointsOverLimit";
- args["SKIN_INDEX"] = (S32)skin_idx;
- args["JOINT_COUNT"] = mValidJointsCount[skin_idx];
- args["MAX"] = (S32)LL_MAX_JOINTS_PER_MESH_OBJECT;
- mWarningsArray.append(args);
- }
-
// Go over viewer joints and build overrides
glm::mat4 ident(1.0);
for (auto &viewer_data : mViewerJointData)
@@ -1210,6 +1236,7 @@ void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx)
{
mJointNames[skin_idx].emplace_back();
}
+ mJointUsage[skin_idx].push_back(0);
// Compute bind matrices
@@ -1268,6 +1295,21 @@ void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx)
mJointsFromNode.push_front(legal_name);
}
}
+
+ S32 valid_joints = mValidJointsCount[skin_idx];
+ if (valid_joints < joint_count)
+ {
+ LL_WARNS("GLTF_IMPORT") << "Skin " << skin_idx
+ << " defines " << joint_count
+ << " joints, but only " << valid_joints
+ << " were recognized and are compatible." << LL_ENDL;
+ LLSD args;
+ args["Message"] = "SkinUsupportedJoints";
+ args["SKIN_INDEX"] = skin_idx;
+ args["JOINT_COUNT"] = joint_count;
+ args["LEGAL_COUNT"] = valid_joints;
+ mWarningsArray.append(args);
+ }
}
void LLGLTFLoader::populateJointGroups()
@@ -1279,109 +1321,6 @@ void LLGLTFLoader::populateJointGroups()
}
}
-
-S32 LLGLTFLoader::findClosestValidJoint(S32 source_joint, const LL::GLTF::Skin& gltf_skin) const
-{
- S32 source_joint_node = gltf_skin.mJoints[source_joint];
- S32 root_node = source_joint_node;
- S32 found_node = source_joint_node;
- S32 size = (S32)gltf_skin.mJoints.size();
- do
- {
- root_node = found_node;
- for (S32 i = 0; i < size; i++)
- {
- S32 joint = gltf_skin.mJoints[i];
- const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint];
- std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_node);
- if (it != jointNode.mChildren.end())
- {
- // Found node's parent
- found_node = joint;
- if (mJointMap.find(jointNode.mName) != mJointMap.end())
- {
- return i;
- }
- break;
- }
- }
- } while (root_node != found_node);
-
- return -1;
-}
-
-S32 LLGLTFLoader::findValidRootJointNode(S32 source_joint_node, const LL::GLTF::Skin& gltf_skin) const
-{
- S32 root_node = 0;
- S32 found_node = source_joint_node;
- S32 size = (S32)gltf_skin.mJoints.size();
- do
- {
- root_node = found_node;
- for (S32 i = 0; i < size; i++)
- {
- S32 joint = gltf_skin.mJoints[i];
- const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint];
-
- if (mJointMap.find(jointNode.mName) != mJointMap.end())
- {
- std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_node);
- if (it != jointNode.mChildren.end())
- {
- // Found node's parent
- found_node = joint;
- break;
- }
- }
- }
- } while (root_node != found_node);
-
- return root_node;
-}
-
-S32 LLGLTFLoader::findGLTFRootJointNode(const LL::GLTF::Skin& gltf_skin) const
-{
- S32 root_node = 0;
- S32 found_node = 0;
- S32 size = (S32)gltf_skin.mJoints.size();
- do
- {
- root_node = found_node;
- for (S32 i = 0; i < size; i++)
- {
- S32 joint = gltf_skin.mJoints[i];
- const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint];
- std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_node);
- if (it != jointNode.mChildren.end())
- {
- // Found node's parent
- found_node = joint;
- break;
- }
- }
- } while (root_node != found_node);
-
- LL_INFOS("GLTF_DEBUG") << "mJointList name: ";
- const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[root_node];
- LL_CONT << jointNode.mName << " index: " << root_node << LL_ENDL;
- return root_node;
-}
-
-S32 LLGLTFLoader::findParentNode(S32 node) const
-{
- S32 size = (S32)mGLTFAsset.mNodes.size();
- for (S32 i = 0; i < size; i++)
- {
- const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[i];
- std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), node);
- if (it != jointNode.mChildren.end())
- {
- return i;
- }
- }
- return -1;
-}
-
void LLGLTFLoader::buildJointGroup(LLJointData& viewer_data, const std::string &parent_group)
{
JointGroups& jount_group_data = mJointGroups[viewer_data.mName];
@@ -1436,11 +1375,22 @@ void LLGLTFLoader::buildOverrideMatrix(LLJointData& viewer_data, joints_data_map
node.mOverrideMatrix = glm::recompose(glm::vec3(1, 1, 1), glm::identity<glm::quat>(), translation_override, glm::vec3(0, 0, 0), glm::vec4(0, 0, 0, 1));
- glm::mat4 override_joint = node.mOverrideMatrix;
- override_joint = glm::scale(override_joint, viewer_data.mScale);
+ glm::mat4 overriden_joint = node.mOverrideMatrix;
+ // This is incomplete or even wrong.
+ // Viewer allows overrides, which are base joint with applied translation override.
+ // So we should be taking viewer joint matrix and replacing translation part with an override.
+ // Or should rebuild the matrix from viewer_data.scale, viewer_data.rotation, translation_override parts.
+ overriden_joint = glm::scale(overriden_joint, viewer_data.mScale);
- rest = parent_rest * override_joint;
- node.mOverrideRestMatrix = rest;
+ rest = parent_rest * overriden_joint;
+ if (viewer_data.mIsJoint)
+ {
+ node.mOverrideRestMatrix = rest;
+ }
+ else
+ {
+ node.mOverrideRestMatrix = parent_support_rest * overriden_joint;
+ }
}
else
{
@@ -1604,6 +1554,47 @@ void LLGLTFLoader::checkForXYrotation(const LL::GLTF::Skin& gltf_skin)
}
}
+void LLGLTFLoader::checkGlobalJointUsage()
+{
+ // Check if some joints remained unused
+ for (S32 skin_idx = 0; skin_idx < (S32)mGLTFAsset.mSkins.size(); ++skin_idx)
+ {
+ const LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skin_idx];
+ S32 joint_count = (S32)gltf_skin.mJoints.size();
+ S32 used_joints = 0;
+ for (S32 i = 0; i < joint_count; ++i)
+ {
+ S32 joint = gltf_skin.mJoints[i];
+ if (mJointUsage[skin_idx][i] == 0)
+ {
+ // Joint is unused, log it
+ LL_INFOS("GLTF_DEBUG") << "Joint " << mJointNames[skin_idx][i]
+ << " in skin " << skin_idx << " is unused." << LL_ENDL;
+ }
+ else
+ {
+ used_joints++;
+ }
+ }
+
+ S32 valid_joints = mValidJointsCount[skin_idx];
+ if (valid_joints > used_joints)
+ {
+ S32 unsed_joints = valid_joints - used_joints;
+ LL_INFOS("GLTF_IMPORT") << "Skin " << skin_idx
+ << " declares " << valid_joints
+ << " valid joints, of them " << unsed_joints
+ << " remained unused" << LL_ENDL;
+ LLSD args;
+ args["Message"] = "SkinUnusedJoints";
+ args["SKIN_INDEX"] = (S32)skin_idx;
+ args["JOINT_COUNT"] = valid_joints;
+ args["USED_COUNT"] = used_joints;
+ mWarningsArray.append(args);
+ }
+ }
+}
+
std::string LLGLTFLoader::extractTextureToTempFile(S32 textureIndex, const std::string& texture_type)
{
if (textureIndex < 0 || textureIndex >= mGLTFAsset.mTextures.size())
@@ -1705,6 +1696,8 @@ void LLGLTFLoader::notifyUnsupportedExtension(bool unsupported)
}
args["EXT"] = ext;
mWarningsArray.append(args);
+
+ LL_WARNS("GLTF_IMPORT") << "Model uses unsupported extension: " << ext << LL_ENDL;
}
}
diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h
index b53258ab8b..10da5c8651 100644
--- a/indra/newview/gltf/llgltfloader.h
+++ b/indra/newview/gltf/llgltfloader.h
@@ -116,6 +116,7 @@ protected:
bind_matrices_t mInverseBindMatrices;
bind_matrices_t mAlternateBindMatrices;
joint_names_t mJointNames; // empty string when no legal name for a given idx
+ std::vector<std::vector<S32>> mJointUsage; // detect and warn about unsed joints
// what group a joint belongs to.
// For purpose of stripping unused groups when joints are over limit.
@@ -134,15 +135,11 @@ private:
bool parseMeshes();
void computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const;
void processNodeHierarchy(S32 node_idx, std::map<std::string, S32>& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params);
- bool addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx) const;
+ bool addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx);
bool populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh &mesh, const LL::GLTF::Node &node, material_map& mats, S32 instance_count);
void populateJointsFromSkin(S32 skin_idx);
void populateJointGroups();
void addModelToScene(LLModel* pModel, U32 submodel_limit, const LLMatrix4& transformation, const LLVolumeParams& volume_params, const material_map& mats);
- S32 findClosestValidJoint(S32 source_joint, const LL::GLTF::Skin& gltf_skin) const;
- S32 findValidRootJointNode(S32 source_joint_node, const LL::GLTF::Skin& gltf_skin) const;
- S32 findGLTFRootJointNode(const LL::GLTF::Skin& gltf_skin) const; // if there are multiple roots, gltf stores them under one commor joint
- S32 findParentNode(S32 node) const;
void buildJointGroup(LLJointData& viewer_data, const std::string& parent_group);
void buildOverrideMatrix(LLJointData& data, joints_data_map_t &gltf_nodes, joints_name_to_node_map_t &names_to_nodes, glm::mat4& parent_rest, glm::mat4& support_rest) const;
glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const;
@@ -150,6 +147,7 @@ private:
glm::mat4 computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const;
bool checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx);
void checkForXYrotation(const LL::GLTF::Skin& gltf_skin);
+ void checkGlobalJointUsage();
std::string extractTextureToTempFile(S32 textureIndex, const std::string& texture_type);
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 0f9ee93d56..dc1a7d1dd1 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -5733,6 +5733,27 @@ void LLAppViewer::forceErrorThreadCrash()
thread->start();
}
+void LLAppViewer::forceExceptionThreadCrash()
+{
+ class LLCrashTestThread : public LLThread
+ {
+ public:
+
+ LLCrashTestThread() : LLThread("Crash logging test thread")
+ {
+ }
+
+ void run()
+ {
+ throw std::exception();
+ }
+ };
+
+ LL_WARNS() << "This is a deliberate exception in a thread" << LL_ENDL;
+ LLCrashTestThread* thread = new LLCrashTestThread();
+ thread->start();
+}
+
void LLAppViewer::initMainloopTimeout(std::string_view state, F32 secs)
{
if (!mMainloopTimeout)
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index b4b8e5bac3..bafe952659 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -175,6 +175,7 @@ public:
virtual void forceErrorCoroprocedureCrash();
virtual void forceErrorWorkQueueCrash();
virtual void forceErrorThreadCrash();
+ virtual void forceExceptionThreadCrash();
// The list is found in app_settings/settings_files.xml
// but since they are used explicitly in code,
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index c3bb8ef4c4..e04054aaa4 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -295,6 +295,7 @@ void force_error_coroutine_crash();
void force_error_coroprocedure_crash();
void force_error_work_queue_crash();
void force_error_thread_crash();
+void force_exception_thread_crash();
void handle_force_delete();
void print_object_info();
@@ -2676,6 +2677,15 @@ class LLAdvancedForceErrorThreadCrash : public view_listener_t
}
};
+class LLAdvancedForceExceptionThreadCrash : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ force_exception_thread_crash();
+ return true;
+ }
+};
+
class LLAdvancedForceErrorDisconnectViewer : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -8814,6 +8824,11 @@ void force_error_thread_crash()
LLAppViewer::instance()->forceErrorThreadCrash();
}
+void force_exception_thread_crash()
+{
+ LLAppViewer::instance()->forceExceptionThreadCrash();
+}
+
class LLToolsUseSelectionForGrid : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -10019,6 +10034,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedForceErrorCoroprocedureCrash(), "Advanced.ForceErrorCoroprocedureCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash");
+ view_listener_t::addMenu(new LLAdvancedForceExceptionThreadCrash(), "Advanced.ForceExceptionThreadCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer");
// Advanced (toplevel)
diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml
index f8543ba1aa..4141191038 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -61,6 +61,7 @@
<string name="ParsingErrorNoRoot">Document has no root</string>
<string name="ParsingErrorNoScene">Document has no visual_scene</string>
<string name="ParsingErrorPositionInvalidModel">Unable to process mesh without position data. Invalid model.</string>
+ <string name="UnknownException">Importer crashed while processing [FILENAME], if you encounter this and file is valid, please report the issue to Second Life Support. Exception: [EXCEPTION].</string>
<!-- GLTF specific messages -->
<string name="NoScenesFound">No scenes defined in GLTF file</string>
@@ -71,12 +72,16 @@
<string name="TextureFound">Found texture: [TEXTURE_NAME] for material: [MATERIAL_NAME]</string>
<string name="IgnoredExtension">Model uses unsupported extension: [EXT], related material properties are ignored</string>
<string name="UnsupportedExtension">Unable to load model, unsupported extension: [EXT]</string>
- <string name="TooManyMeshParts">Model contains [PART_COUNT] mesh parts. Maximum allowed: [LIMIT]</string>
<string name="FailedToCreateTempFile">Failed to create temporary file for embedded [TEXTURE_TYPE] texture [TEXTURE_INDEX]: [TEMP_FILE]</string>
<string name="SkinJointsOverLimit">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, maximum is: [MAX]. Unused joints will be stripped on per model basis.</string>
- <string name="ModelTooManyJoint">Model [MODEL_NAME] uses [JOINT_COUNT], maximum: [MAX], upload might fail</string>
+ <string name="SkinUsupportedJoints">Skin [SKIN_INDEX] defines [JOINT_COUNT] joints, but only [LEGAL_COUNT] were recognized and are compatible</string>
+ <string name="SkinUnusedJoints">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, of them only [USED_COUNT] were used</string>
+ <string name="ModelTooManyJoints">Model [MODEL_NAME] uses [JOINT_COUNT], maximum: [MAX], upload might fail</string>
+ <string name="ModelSplitPrimitive">Too many vertices in primitive [MODEL_NAME], it was split into [FACE_COUNT] faces</string>
+ <string name="ModelTooManySubmodels">Model [MODEL_NAME] contains [SUBMODEL_COUNT] generated mesh parts, parts were trimmed to [SUBMODEL_LIMIT]</string>
<string name="ParsingErrorMissingBuffer">Buffer is either missing or empty [BUFFER_NAME].</string>
<string name="ParsingErrorMissingBufferBin">Buffer is either missing or empty. Check presence of [BUFFER_URI] file.</string>
+ <string name="ParsingErrorException">Parser failed to process [FILENAME], file might be corrupt, incomplete or protected from reading. Exception: [EXCEPTION].</string>
<panel
follows="top|left"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 1ec59bf2eb..6c7511796b 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2901,12 +2901,18 @@ function="World.EnvPreset"
function="Advanced.ForceErrorWorkQueueCrash" />
</menu_item_call>
<menu_item_call
- label="Force a Crash in a Thread"
- name="Force a Crash in a Thread">
+ label="Force an LLError Crash in a Thread"
+ name="Force an LLError Crash in a Thread">
<menu_item_call.on_click
function="Advanced.ForceErrorThreadCrash" />
</menu_item_call>
<menu_item_call
+ label="Force an Exception Crash in a Thread"
+ name="Force an Exception Crash in a Thread">
+ <menu_item_call.on_click
+ function="Advanced.ForceExceptionThreadCrash" />
+ </menu_item_call>
+ <menu_item_call
label="Force Disconnect Viewer"
name="Force Disconnect Viewer">
<menu_item_call.on_click