summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan "Geenz" Goodman <geenz@geenzo.com>2025-06-27 21:28:58 -0400
committerGitHub <noreply@github.com>2025-06-27 21:28:58 -0400
commitb0c951ffe348f478f27a85720cc7aeffea32fe73 (patch)
tree9e473b5ecbd873062b29d91ee94cc06453e12dd7
parentf48fe44684a535ed2eefc64c134551ce72e9ecf4 (diff)
Revert "Merge develop into glTF mesh import"
-rw-r--r--.github/workflows/build.yaml5
-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/llcharacter/llbvhloader.cpp6
-rw-r--r--indra/llcharacter/llbvhloader.h2
-rw-r--r--indra/llcharacter/llcharacter.cpp7
-rw-r--r--indra/llcharacter/llcharacter.h2
-rw-r--r--indra/llprimitive/CMakeLists.txt2
-rw-r--r--indra/llprimitive/lldaeloader.cpp2
-rw-r--r--indra/llprimitive/lldaeloader.h26
-rw-r--r--indra/llprimitive/llgltfloader.cpp404
-rw-r--r--indra/llprimitive/llgltfloader.h206
-rw-r--r--indra/llprimitive/llmodel.cpp182
-rw-r--r--indra/llprimitive/llmodel.h1
-rw-r--r--indra/llprimitive/llmodelloader.cpp54
-rw-r--r--indra/llprimitive/llmodelloader.h9
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/gltf/asset.cpp198
-rw-r--r--indra/newview/gltf/asset.h13
-rw-r--r--indra/newview/gltf/buffer_util.h11
-rw-r--r--indra/newview/gltf/llgltfloader.cpp1710
-rw-r--r--indra/newview/gltf/llgltfloader.h203
-rw-r--r--indra/newview/gltfscenemanager.cpp2
-rw-r--r--indra/newview/llfilepicker.cpp6
-rw-r--r--indra/newview/llfloaterbvhpreview.cpp4
-rw-r--r--indra/newview/llfloaterbvhpreview.h2
-rw-r--r--indra/newview/llfloatermodelpreview.cpp7
-rw-r--r--indra/newview/llmaterialeditor.cpp3
-rw-r--r--indra/newview/llmodelpreview.cpp52
-rw-r--r--indra/newview/llskinningutil.cpp9
-rw-r--r--indra/newview/llvoavatar.cpp8
-rw-r--r--indra/newview/llvoavatar.h2
-rw-r--r--indra/newview/llvoavatarself.cpp6
-rw-r--r--indra/newview/llvoavatarself.h2
-rw-r--r--indra/newview/skins/default/xui/en/floater_model_preview.xml20
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml5
38 files changed, 772 insertions, 2544 deletions
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 4bf2af644a..198785d39b 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -218,10 +218,8 @@ jobs:
prefix=${ba[0]}
if [ "$prefix" == "project" ]; then
IFS='_' read -ra prj <<< "${ba[1]}"
- prj_str="${prj[*]}"
# uppercase first letter of each word
- capitalized=$(echo "$prj_str" | awk '{for (i=1; i<=NF; i++) $i = toupper(substr($i,1,1)) substr($i,2); print}')
- export viewer_channel="Second Life Project $capitalized"
+ export viewer_channel="Second Life Project ${prj[*]^}"
elif [[ "$prefix" == "release" || "$prefix" == "main" ]];
then
export viewer_channel="Second Life Release"
@@ -457,6 +455,7 @@ jobs:
prerelease: true
generate_release_notes: true
target_commitish: ${{ github.sha }}
+ previous_tag: release
append_body: true
fail_on_unmatched_files: true
files: |
diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt
index 6744c8d8a4..c3be8bc78e 100644
--- a/indra/llappearance/CMakeLists.txt
+++ b/indra/llappearance/CMakeLists.txt
@@ -14,7 +14,6 @@ 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 dab18c240d..3d66809ed6 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -29,17 +29,16 @@
#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;
@@ -72,13 +71,11 @@ 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;
@@ -109,16 +106,10 @@ 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;
};
@@ -1607,15 +1598,6 @@ 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");
@@ -1641,21 +1623,6 @@ 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()
//-----------------------------------------------------------------------------
@@ -1686,25 +1653,6 @@ 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)
{
@@ -1766,16 +1714,6 @@ 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 2748da9a1d..99bc5eeef5 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -34,7 +34,6 @@
#include "lltexlayer.h"
#include "llviewervisualparam.h"
#include "llxmltree.h"
-#include "v4math.h"
class LLTexLayerSet;
class LLTexGlobalColor;
@@ -42,7 +41,6 @@ class LLTexGlobalColorInfo;
class LLWearableData;
class LLAvatarBoneInfo;
class LLAvatarSkeletonInfo;
-class LLJointData;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// LLAvatarAppearance
@@ -140,7 +138,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;
@@ -153,11 +151,9 @@ 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
deleted file mode 100644
index 2fc26198ee..0000000000
--- a/indra/llappearance/lljointdata.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * @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/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp
index 9dace08e6f..581e9f62d5 100644
--- a/indra/llcharacter/llbvhloader.cpp
+++ b/indra/llcharacter/llbvhloader.cpp
@@ -131,7 +131,7 @@ LLQuaternion::Order bvhStringToOrder( char *str )
// LLBVHLoader()
//-----------------------------------------------------------------------------
-LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string>& joint_alias_map )
+LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string, std::less<>>& joint_alias_map )
{
reset();
errorLine = 0;
@@ -156,9 +156,9 @@ LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &error
}
// Recognize all names we've been told are legal.
- for (std::map<std::string, std::string>::value_type& alias_pair : joint_alias_map)
+ for (const auto& [alias, joint] : joint_alias_map)
{
- makeTranslation( alias_pair.first , alias_pair.second );
+ makeTranslation(alias, joint);
}
char error_text[128]; /* Flawfinder: ignore */
diff --git a/indra/llcharacter/llbvhloader.h b/indra/llcharacter/llbvhloader.h
index de31f76dd3..ae2e347ba1 100644
--- a/indra/llcharacter/llbvhloader.h
+++ b/indra/llcharacter/llbvhloader.h
@@ -227,7 +227,7 @@ class LLBVHLoader
friend class LLKeyframeMotion;
public:
// Constructor
- LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string>& joint_alias_map );
+ LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string, std::less<>>& joint_alias_map );
~LLBVHLoader();
/*
diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp
index ecbcdb3bf5..8efcd9dd29 100644
--- a/indra/llcharacter/llcharacter.cpp
+++ b/indra/llcharacter/llcharacter.cpp
@@ -77,12 +77,11 @@ LLCharacter::~LLCharacter()
//-----------------------------------------------------------------------------
// getJoint()
//-----------------------------------------------------------------------------
-LLJoint *LLCharacter::getJoint( const std::string &name )
+LLJoint* LLCharacter::getJoint(std::string_view name)
{
- LLJoint* joint = NULL;
+ LLJoint* joint = nullptr;
- LLJoint *root = getRootJoint();
- if (root)
+ if (LLJoint* root = getRootJoint())
{
joint = root->findJoint(name);
}
diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
index 6143ec8cd1..0046c5da7e 100644
--- a/indra/llcharacter/llcharacter.h
+++ b/indra/llcharacter/llcharacter.h
@@ -76,7 +76,7 @@ public:
// get the specified joint
// default implementation does recursive search,
// subclasses may optimize/cache results.
- virtual LLJoint *getJoint( const std::string &name );
+ virtual LLJoint* getJoint(std::string_view name);
// get the position of the character
virtual LLVector3 getCharacterPosition() = 0;
diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt
index e13f0bbd96..3d8e02cb16 100644
--- a/indra/llprimitive/CMakeLists.txt
+++ b/indra/llprimitive/CMakeLists.txt
@@ -12,6 +12,7 @@ include(TinyGLTF)
set(llprimitive_SOURCE_FILES
lldaeloader.cpp
+ llgltfloader.cpp
llgltfmaterial.cpp
llmaterialid.cpp
llmaterial.cpp
@@ -31,6 +32,7 @@ set(llprimitive_SOURCE_FILES
set(llprimitive_HEADER_FILES
CMakeLists.txt
lldaeloader.h
+ llgltfloader.h
llgltfmaterial.h
llgltfmaterial_templates.h
legacy_object_types.h
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 0759447902..eadb052b38 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -880,7 +880,7 @@ LLDAELoader::LLDAELoader(
void* opaque_userdata,
JointTransformMap& jointTransformMap,
JointNameSet& jointsFromNodes,
- std::map<std::string, std::string>& jointAliasMap,
+ std::map<std::string, std::string, std::less<>>& jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit,
bool preprocess)
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index 4531e03474..dc20feca52 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -47,19 +47,19 @@ public:
dae_model_map mModelsMap;
LLDAELoader(
- std::string filename,
- S32 lod,
- LLModelLoader::load_callback_t load_cb,
- LLModelLoader::joint_lookup_func_t joint_lookup_func,
- LLModelLoader::texture_load_func_t texture_load_func,
- LLModelLoader::state_callback_t state_cb,
- void* opaque_userdata,
- JointTransformMap& jointTransformMap,
- JointNameSet& jointsFromNodes,
- std::map<std::string, std::string>& jointAliasMap,
- U32 maxJointsPerMesh,
- U32 modelLimit,
- bool preprocess);
+ std::string filename,
+ S32 lod,
+ LLModelLoader::load_callback_t load_cb,
+ LLModelLoader::joint_lookup_func_t joint_lookup_func,
+ LLModelLoader::texture_load_func_t texture_load_func,
+ LLModelLoader::state_callback_t state_cb,
+ void* opaque_userdata,
+ JointTransformMap& jointTransformMap,
+ JointNameSet& jointsFromNodes,
+ std::map<std::string, std::string, std::less<>>& jointAliasMap,
+ U32 maxJointsPerMesh,
+ U32 modelLimit,
+ bool preprocess);
virtual ~LLDAELoader() ;
virtual bool OpenFile(const std::string& filename);
diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp
new file mode 100644
index 0000000000..724b1a88b2
--- /dev/null
+++ b/indra/llprimitive/llgltfloader.cpp
@@ -0,0 +1,404 @@
+/**
+ * @file LLGLTFLoader.cpp
+ * @brief LLGLTFLoader class implementation
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, 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 "llgltfloader.h"
+
+// Import & define single-header gltf import/export lib
+#define TINYGLTF_IMPLEMENTATION
+#define TINYGLTF_USE_CPP14 // default is C++ 11
+
+// tinygltf by default loads image files using STB
+#define STB_IMAGE_IMPLEMENTATION
+// to use our own image loading:
+// 1. replace this definition with TINYGLTF_NO_STB_IMAGE
+// 2. provide image loader callback with TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)
+
+// tinygltf saves image files using STB
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+// similarly, can override with TINYGLTF_NO_STB_IMAGE_WRITE and TinyGLTF::SetImageWriter(fxn, data)
+
+// Additionally, disable inclusion of STB header files entirely with
+// TINYGLTF_NO_INCLUDE_STB_IMAGE
+// TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
+#include "tinygltf/tiny_gltf.h"
+
+
+// TODO: includes inherited from dae loader. Validate / prune
+
+#include "llsdserialize.h"
+#include "lljoint.h"
+
+#include "llmatrix4a.h"
+
+#include <boost/regex.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
+static const std::string lod_suffix[LLModel::NUM_LODS] =
+{
+ "_LOD0",
+ "_LOD1",
+ "_LOD2",
+ "",
+ "_PHYS",
+};
+
+
+LLGLTFLoader::LLGLTFLoader(std::string filename,
+ S32 lod,
+ LLModelLoader::load_callback_t load_cb,
+ LLModelLoader::joint_lookup_func_t joint_lookup_func,
+ LLModelLoader::texture_load_func_t texture_load_func,
+ LLModelLoader::state_callback_t state_cb,
+ void * opaque_userdata,
+ JointTransformMap & jointTransformMap,
+ JointNameSet & jointsFromNodes,
+ std::map<std::string, std::string, std::less<>> & jointAliasMap,
+ U32 maxJointsPerMesh,
+ U32 modelLimit) //,
+ //bool preprocess)
+ : LLModelLoader( filename,
+ lod,
+ load_cb,
+ joint_lookup_func,
+ texture_load_func,
+ state_cb,
+ opaque_userdata,
+ jointTransformMap,
+ jointsFromNodes,
+ jointAliasMap,
+ maxJointsPerMesh ),
+ //mPreprocessGLTF(preprocess),
+ mMeshesLoaded(false),
+ mMaterialsLoaded(false)
+{
+}
+
+LLGLTFLoader::~LLGLTFLoader() {}
+
+bool LLGLTFLoader::OpenFile(const std::string &filename)
+{
+ tinygltf::TinyGLTF loader;
+ std::string error_msg;
+ std::string warn_msg;
+ std::string filename_lc(filename);
+ LLStringUtil::toLower(filename_lc);
+
+ // Load a tinygltf model fom a file. Assumes that the input filename has already been
+ // been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish.
+ if (std::string::npos == filename_lc.rfind(".gltf"))
+ { // file is binary
+ mGltfLoaded = loader.LoadBinaryFromFile(&mGltfModel, &error_msg, &warn_msg, filename);
+ }
+ else
+ { // file is ascii
+ mGltfLoaded = loader.LoadASCIIFromFile(&mGltfModel, &error_msg, &warn_msg, filename);
+ }
+
+ if (!mGltfLoaded)
+ {
+ if (!warn_msg.empty())
+ LL_WARNS("GLTF_IMPORT") << "gltf load warning: " << warn_msg.c_str() << LL_ENDL;
+ if (!error_msg.empty())
+ LL_WARNS("GLTF_IMPORT") << "gltf load error: " << error_msg.c_str() << LL_ENDL;
+ return false;
+ }
+
+ mMeshesLoaded = parseMeshes();
+ if (mMeshesLoaded) uploadMeshes();
+
+ mMaterialsLoaded = parseMaterials();
+ if (mMaterialsLoaded) uploadMaterials();
+
+ return (mMeshesLoaded || mMaterialsLoaded);
+}
+
+bool LLGLTFLoader::parseMeshes()
+{
+ if (!mGltfLoaded) return false;
+
+ // 2022-04 DJH Volume params from dae example. TODO understand PCODE
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+
+ for (tinygltf::Mesh mesh : mGltfModel.meshes)
+ {
+ LLModel *pModel = new LLModel(volume_params, 0.f);
+
+ if (populateModelFromMesh(pModel, mesh) &&
+ (LLModel::NO_ERRORS == pModel->getStatus()) &&
+ validate_model(pModel))
+ {
+ mModelList.push_back(pModel);
+ }
+ else
+ {
+ setLoadState(ERROR_MODEL + pModel->getStatus());
+ delete(pModel);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh)
+{
+ pModel->mLabel = mesh.name;
+ int pos_idx;
+ tinygltf::Accessor indices_a, positions_a, normals_a, uv0_a, color0_a;
+
+ auto prims = mesh.primitives;
+ for (auto prim : prims)
+ {
+ if (prim.indices >= 0) indices_a = mGltfModel.accessors[prim.indices];
+
+ pos_idx = (prim.attributes.count("POSITION") > 0) ? prim.attributes.at("POSITION") : -1;
+ if (pos_idx >= 0)
+ {
+ positions_a = mGltfModel.accessors[pos_idx];
+ if (TINYGLTF_COMPONENT_TYPE_FLOAT != positions_a.componentType)
+ continue;
+ auto positions_bv = mGltfModel.bufferViews[positions_a.bufferView];
+ auto positions_buf = mGltfModel.buffers[positions_bv.buffer];
+ //auto type = positions_vb.
+ //if (positions_buf.name
+ }
+
+#if 0
+ int norm_idx, tan_idx, uv0_idx, uv1_idx, color0_idx, color1_idx;
+ norm_idx = (prim.attributes.count("NORMAL") > 0) ? prim.attributes.at("NORMAL") : -1;
+ tan_idx = (prim.attributes.count("TANGENT") > 0) ? prim.attributes.at("TANGENT") : -1;
+ uv0_idx = (prim.attributes.count("TEXCOORDS_0") > 0) ? prim.attributes.at("TEXCOORDS_0") : -1;
+ uv1_idx = (prim.attributes.count("TEXCOORDS_1") > 0) ? prim.attributes.at("TEXCOORDS_1") : -1;
+ color0_idx = (prim.attributes.count("COLOR_0") > 0) ? prim.attributes.at("COLOR_0") : -1;
+ color1_idx = (prim.attributes.count("COLOR_1") > 0) ? prim.attributes.at("COLOR_1") : -1;
+#endif
+
+ if (prim.mode == TINYGLTF_MODE_TRIANGLES)
+ {
+ //auto pos = mesh. TODO resume here DJH 2022-04
+ }
+ }
+
+ //pModel->addFace()
+ return false;
+}
+
+bool LLGLTFLoader::parseMaterials()
+{
+ if (!mGltfLoaded) return false;
+
+ // fill local texture data structures
+ mSamplers.clear();
+ for (auto in_sampler : mGltfModel.samplers)
+ {
+ gltf_sampler sampler;
+ sampler.magFilter = in_sampler.magFilter > 0 ? in_sampler.magFilter : GL_LINEAR;
+ sampler.minFilter = in_sampler.minFilter > 0 ? in_sampler.minFilter : GL_LINEAR;;
+ sampler.wrapS = in_sampler.wrapS;
+ sampler.wrapT = in_sampler.wrapT;
+ sampler.name = in_sampler.name; // unused
+ mSamplers.push_back(sampler);
+ }
+
+ mImages.clear();
+ for (auto in_image : mGltfModel.images)
+ {
+ gltf_image image;
+ image.numChannels = in_image.component;
+ image.bytesPerChannel = in_image.bits >> 3; // Convert bits to bytes
+ image.pixelType = in_image.pixel_type; // Maps exactly, i.e. TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE == GL_UNSIGNED_BYTE, etc
+ image.size = static_cast<U32>(in_image.image.size());
+ image.height = in_image.height;
+ image.width = in_image.width;
+ image.data = in_image.image.data();
+
+ if (in_image.as_is)
+ {
+ LL_WARNS("GLTF_IMPORT") << "Unsupported image encoding" << LL_ENDL;
+ return false;
+ }
+
+ if (image.size != image.height * image.width * image.numChannels * image.bytesPerChannel)
+ {
+ LL_WARNS("GLTF_IMPORT") << "Image size error" << LL_ENDL;
+ return false;
+ }
+
+ mImages.push_back(image);
+ }
+
+ mTextures.clear();
+ for (auto in_tex : mGltfModel.textures)
+ {
+ gltf_texture tex;
+ tex.imageIdx = in_tex.source;
+ tex.samplerIdx = in_tex.sampler;
+ tex.imageUuid.setNull();
+
+ if (tex.imageIdx >= mImages.size() || tex.samplerIdx >= mSamplers.size())
+ {
+ LL_WARNS("GLTF_IMPORT") << "Texture sampler/image index error" << LL_ENDL;
+ return false;
+ }
+
+ mTextures.push_back(tex);
+ }
+
+ // parse each material
+ for (tinygltf::Material gltf_material : mGltfModel.materials)
+ {
+ gltf_render_material mat;
+ mat.name = gltf_material.name;
+
+ tinygltf::PbrMetallicRoughness& pbr = gltf_material.pbrMetallicRoughness;
+ mat.hasPBR = true; // Always true, for now
+
+ mat.baseColor.set(pbr.baseColorFactor.data());
+ mat.hasBaseTex = pbr.baseColorTexture.index >= 0;
+ mat.baseColorTexIdx = pbr.baseColorTexture.index;
+ mat.baseColorTexCoords = pbr.baseColorTexture.texCoord;
+
+ mat.metalness = pbr.metallicFactor;
+ mat.roughness = pbr.roughnessFactor;
+ mat.hasMRTex = pbr.metallicRoughnessTexture.index >= 0;
+ mat.metalRoughTexIdx = pbr.metallicRoughnessTexture.index;
+ mat.metalRoughTexCoords = pbr.metallicRoughnessTexture.texCoord;
+
+ mat.normalScale = gltf_material.normalTexture.scale;
+ mat.hasNormalTex = gltf_material.normalTexture.index >= 0;
+ mat.normalTexIdx = gltf_material.normalTexture.index;
+ mat.normalTexCoords = gltf_material.normalTexture.texCoord;
+
+ mat.occlusionScale = gltf_material.occlusionTexture.strength;
+ mat.hasOcclusionTex = gltf_material.occlusionTexture.index >= 0;
+ mat.occlusionTexIdx = gltf_material.occlusionTexture.index;
+ mat.occlusionTexCoords = gltf_material.occlusionTexture.texCoord;
+
+ mat.emissiveColor.set(gltf_material.emissiveFactor.data());
+ mat.hasEmissiveTex = gltf_material.emissiveTexture.index >= 0;
+ mat.emissiveTexIdx = gltf_material.emissiveTexture.index;
+ mat.emissiveTexCoords = gltf_material.emissiveTexture.texCoord;
+
+ mat.alphaMode = gltf_material.alphaMode;
+ mat.alphaMask = gltf_material.alphaCutoff;
+
+ if ((mat.hasNormalTex && (mat.normalTexIdx >= mTextures.size())) ||
+ (mat.hasOcclusionTex && (mat.occlusionTexIdx >= mTextures.size())) ||
+ (mat.hasEmissiveTex && (mat.emissiveTexIdx >= mTextures.size())) ||
+ (mat.hasBaseTex && (mat.baseColorTexIdx >= mTextures.size())) ||
+ (mat.hasMRTex && (mat.metalRoughTexIdx >= mTextures.size())))
+ {
+ LL_WARNS("GLTF_IMPORT") << "Texture resource index error" << LL_ENDL;
+ return false;
+ }
+
+ if ((mat.hasNormalTex && (mat.normalTexCoords > 2)) || // mesh can have up to 3 sets of UV
+ (mat.hasOcclusionTex && (mat.occlusionTexCoords > 2)) ||
+ (mat.hasEmissiveTex && (mat.emissiveTexCoords > 2)) ||
+ (mat.hasBaseTex && (mat.baseColorTexCoords > 2)) ||
+ (mat.hasMRTex && (mat.metalRoughTexCoords > 2)))
+ {
+ LL_WARNS("GLTF_IMPORT") << "Image texcoord index error" << LL_ENDL;
+ return false;
+ }
+
+ mMaterials.push_back(mat);
+ }
+
+ return true;
+}
+
+// TODO: convert raw vertex buffers to UUIDs
+void LLGLTFLoader::uploadMeshes()
+{
+ llassert(0);
+}
+
+// convert raw image buffers to texture UUIDs & assemble into a render material
+void LLGLTFLoader::uploadMaterials()
+{
+ for (gltf_render_material mat : mMaterials) // Initially 1 material per gltf file, but design for multiple
+ {
+ if (mat.hasBaseTex)
+ {
+ gltf_texture& gtex = mTextures[mat.baseColorTexIdx];
+ if (gtex.imageUuid.isNull())
+ {
+ gtex.imageUuid = imageBufferToTextureUUID(gtex);
+ }
+ }
+
+ if (mat.hasMRTex)
+ {
+ gltf_texture& gtex = mTextures[mat.metalRoughTexIdx];
+ if (gtex.imageUuid.isNull())
+ {
+ gtex.imageUuid = imageBufferToTextureUUID(gtex);
+ }
+ }
+
+ if (mat.hasNormalTex)
+ {
+ gltf_texture& gtex = mTextures[mat.normalTexIdx];
+ if (gtex.imageUuid.isNull())
+ {
+ gtex.imageUuid = imageBufferToTextureUUID(gtex);
+ }
+ }
+
+ if (mat.hasOcclusionTex)
+ {
+ gltf_texture& gtex = mTextures[mat.occlusionTexIdx];
+ if (gtex.imageUuid.isNull())
+ {
+ gtex.imageUuid = imageBufferToTextureUUID(gtex);
+ }
+ }
+
+ if (mat.hasEmissiveTex)
+ {
+ gltf_texture& gtex = mTextures[mat.emissiveTexIdx];
+ if (gtex.imageUuid.isNull())
+ {
+ gtex.imageUuid = imageBufferToTextureUUID(gtex);
+ }
+ }
+ }
+}
+
+LLUUID LLGLTFLoader::imageBufferToTextureUUID(const gltf_texture& tex)
+{
+ //gltf_image& image = mImages[tex.imageIdx];
+ //gltf_sampler& sampler = mSamplers[tex.samplerIdx];
+
+ // fill an LLSD container with image+sampler data
+
+ // upload texture
+
+ // retrieve UUID
+
+ return LLUUID::null;
+}
diff --git a/indra/llprimitive/llgltfloader.h b/indra/llprimitive/llgltfloader.h
new file mode 100644
index 0000000000..848a07c1e4
--- /dev/null
+++ b/indra/llprimitive/llgltfloader.h
@@ -0,0 +1,206 @@
+/**
+ * @file LLGLTFLoader.h
+ * @brief LLGLTFLoader class definition
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, 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_LLGLTFLoader_H
+#define LL_LLGLTFLoader_H
+
+#include "tinygltf/tiny_gltf.h"
+
+#include "llglheaders.h"
+#include "llmodelloader.h"
+
+// gltf_* structs are temporary, used to organize the subset of data that eventually goes into the material LLSD
+
+class gltf_sampler
+{
+public:
+ // Uses GL enums
+ S32 minFilter; // GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR or GL_LINEAR_MIPMAP_LINEAR
+ S32 magFilter; // GL_NEAREST or GL_LINEAR
+ S32 wrapS; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT
+ S32 wrapT; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT
+ //S32 wrapR; // Found in some sample files, but not part of glTF 2.0 spec. Ignored.
+ std::string name; // optional, currently unused
+ // extensions and extras are sampler optional fields that we don't support - at least initially
+};
+
+class gltf_image
+{
+public:// Note that glTF images are defined with row 0 at the top (opposite of OpenGL)
+ U8* data; // ptr to decoded image data
+ U32 size; // in bytes, regardless of channel width
+ U32 width;
+ U32 height;
+ U32 numChannels; // range 1..4
+ U32 bytesPerChannel; // converted from gltf "bits", expects only 8, 16 or 32 as input
+ U32 pixelType; // one of (TINYGLTF_COMPONENT_TYPE)_UNSIGNED_BYTE, _UNSIGNED_SHORT, _UNSIGNED_INT, or _FLOAT
+};
+
+class gltf_texture
+{
+public:
+ U32 imageIdx;
+ U32 samplerIdx;
+ LLUUID imageUuid = LLUUID::null;
+};
+
+class gltf_render_material
+{
+public:
+ std::string name;
+
+ // scalar values
+ LLColor4 baseColor; // linear encoding. Multiplied with vertex color, if present.
+ double metalness;
+ double roughness;
+ double normalScale; // scale applies only to X,Y components of normal
+ double occlusionScale; // strength multiplier for occlusion
+ LLColor4 emissiveColor; // emissive mulitiplier, assumed linear encoding (spec 2.0 is silent)
+ std::string alphaMode; // "OPAQUE", "MASK" or "BLEND"
+ double alphaMask; // alpha cut-off
+
+ // textures
+ U32 baseColorTexIdx; // always sRGB encoded
+ U32 metalRoughTexIdx; // always linear, roughness in G channel, metalness in B channel
+ U32 normalTexIdx; // linear, valid range R[0-1], G[0-1], B[0.5-1]. Normal = texel * 2 - vec3(1.0)
+ U32 occlusionTexIdx; // linear, occlusion in R channel, 0 meaning fully occluded, 1 meaning not occluded
+ U32 emissiveTexIdx; // always stored as sRGB, in nits (candela / meter^2)
+
+ // texture coordinates
+ U32 baseColorTexCoords;
+ U32 metalRoughTexCoords;
+ U32 normalTexCoords;
+ U32 occlusionTexCoords;
+ U32 emissiveTexCoords;
+
+ // TODO: Add traditional (diffuse, normal, specular) UUIDs here, or add this struct to LL_TextureEntry??
+
+ bool hasPBR;
+ bool hasBaseTex, hasMRTex, hasNormalTex, hasOcclusionTex, hasEmissiveTex;
+
+ // This field is populated after upload
+ LLUUID material_uuid = LLUUID::null;
+
+};
+
+class gltf_mesh
+{
+public:
+ std::string name;
+
+ // TODO add mesh import DJH 2022-04
+
+};
+
+class LLGLTFLoader : public LLModelLoader
+{
+ public:
+ typedef std::map<std::string, LLImportMaterial> material_map;
+
+ LLGLTFLoader(std::string filename,
+ S32 lod,
+ LLModelLoader::load_callback_t load_cb,
+ LLModelLoader::joint_lookup_func_t joint_lookup_func,
+ LLModelLoader::texture_load_func_t texture_load_func,
+ LLModelLoader::state_callback_t state_cb,
+ void * opaque_userdata,
+ JointTransformMap & jointTransformMap,
+ JointNameSet & jointsFromNodes,
+ std::map<std::string, std::string,std::less<>> &jointAliasMap,
+ U32 maxJointsPerMesh,
+ U32 modelLimit); //,
+ //bool preprocess );
+ virtual ~LLGLTFLoader();
+
+ virtual bool OpenFile(const std::string &filename);
+
+protected:
+ tinygltf::Model mGltfModel;
+ bool mGltfLoaded;
+ bool mMeshesLoaded;
+ bool mMaterialsLoaded;
+
+ std::vector<gltf_mesh> mMeshes;
+ std::vector<gltf_render_material> mMaterials;
+
+ std::vector<gltf_texture> mTextures;
+ std::vector<gltf_image> mImages;
+ std::vector<gltf_sampler> mSamplers;
+
+private:
+ bool parseMeshes();
+ void uploadMeshes();
+ bool parseMaterials();
+ void uploadMaterials();
+ bool populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh);
+ LLUUID imageBufferToTextureUUID(const gltf_texture& tex);
+
+ // bool mPreprocessGLTF;
+
+ /* Below inherited from dae loader - unknown if/how useful here
+
+ void processElement(gltfElement *element, bool &badElement, GLTF *gltf);
+ void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin);
+
+ material_map getMaterials(LLModel *model, gltfInstance_geometry *instance_geo, GLTF *gltf);
+ LLImportMaterial profileToMaterial(gltfProfile_COMMON *material, GLTF *gltf);
+ LLColor4 getGltfColor(gltfElement *element);
+
+ gltfElement *getChildFromElement(gltfElement *pElement, std::string const &name);
+
+ bool isNodeAJoint(gltfNode *pNode);
+ void processJointNode(gltfNode *pNode, std::map<std::string, LLMatrix4> &jointTransforms);
+ void extractTranslation(gltfTranslate *pTranslate, LLMatrix4 &transform);
+ void extractTranslationViaElement(gltfElement *pTranslateElement, LLMatrix4 &transform);
+ void extractTranslationViaSID(gltfElement *pElement, LLMatrix4 &transform);
+ void buildJointToNodeMappingFromScene(gltfElement *pRoot);
+ void processJointToNodeMapping(gltfNode *pNode);
+ void processChildJoints(gltfNode *pParentNode);
+
+ bool verifyCount(int expected, int result);
+
+ // Verify that a controller matches vertex counts
+ bool verifyController(gltfController *pController);
+
+ static bool addVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh, LLSD &log_msg);
+ static bool createVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh);
+
+ static LLModel *loadModelFromGltfMesh(gltfMesh *mesh);
+
+ // Loads a mesh breaking it into one or more models as necessary
+ // to get around volume face limitations while retaining >8 materials
+ //
+ bool loadModelsFromGltfMesh(gltfMesh *mesh, std::vector<LLModel *> &models_out, U32 submodel_limit);
+
+ static std::string getElementLabel(gltfElement *element);
+ static size_t getSuffixPosition(std::string label);
+ static std::string getLodlessLabel(gltfElement *element);
+
+ static std::string preprocessGLTF(std::string filename);
+ */
+
+};
+#endif // LL_LLGLTFLLOADER_H
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 3d31cfbb7f..4e3e49ec9f 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -334,162 +334,6 @@ void LLModel::normalizeVolumeFaces()
}
}
-void LLModel::normalizeVolumeFacesAndWeights()
-{
- if (!mVolumeFaces.empty())
- {
- LLVector4a min, max;
-
- // For all of the volume faces
- // in the model, loop over
- // them and see what the extents
- // of the volume along each axis.
- min = mVolumeFaces[0].mExtents[0];
- max = mVolumeFaces[0].mExtents[1];
-
- for (U32 i = 1; i < mVolumeFaces.size(); ++i)
- {
- LLVolumeFace& face = mVolumeFaces[i];
-
- update_min_max(min, max, face.mExtents[0]);
- update_min_max(min, max, face.mExtents[1]);
-
- if (face.mTexCoords)
- {
- LLVector2& min_tc = face.mTexCoordExtents[0];
- LLVector2& max_tc = face.mTexCoordExtents[1];
-
- min_tc = face.mTexCoords[0];
- max_tc = face.mTexCoords[0];
-
- for (S32 j = 1; j < face.mNumVertices; ++j)
- {
- update_min_max(min_tc, max_tc, face.mTexCoords[j]);
- }
- }
- else
- {
- face.mTexCoordExtents[0].set(0, 0);
- face.mTexCoordExtents[1].set(1, 1);
- }
- }
-
- // Now that we have the extents of the model
- // we can compute the offset needed to center
- // the model at the origin.
-
- // Compute center of the model
- // and make it negative to get translation
- // needed to center at origin.
- LLVector4a trans;
- trans.setAdd(min, max);
- trans.mul(-0.5f);
-
- // Compute the total size along all
- // axes of the model.
- LLVector4a size;
- size.setSub(max, min);
-
- // Prevent division by zero.
- F32 x = size[0];
- F32 y = size[1];
- F32 z = size[2];
- F32 w = size[3];
- if (fabs(x) < F_APPROXIMATELY_ZERO)
- {
- x = 1.0;
- }
- if (fabs(y) < F_APPROXIMATELY_ZERO)
- {
- y = 1.0;
- }
- if (fabs(z) < F_APPROXIMATELY_ZERO)
- {
- z = 1.0;
- }
- size.set(x, y, z, w);
-
- // Compute scale as reciprocal of size
- LLVector4a scale;
- scale.splat(1.f);
- scale.div(size);
-
- LLVector4a inv_scale(1.f);
- inv_scale.div(scale);
-
- for (U32 i = 0; i < mVolumeFaces.size(); ++i)
- {
- LLVolumeFace& face = mVolumeFaces[i];
-
- // We shrink the extents so
- // that they fall within
- // the unit cube.
- // VFExtents change
- face.mExtents[0].add(trans);
- face.mExtents[0].mul(scale);
-
- face.mExtents[1].add(trans);
- face.mExtents[1].mul(scale);
-
- // For all the positions, we scale
- // the positions to fit within the unit cube.
- LLVector4a* pos = (LLVector4a*)face.mPositions;
- LLVector4a* norm = (LLVector4a*)face.mNormals;
- LLVector4a* t = (LLVector4a*)face.mTangents;
-
- for (S32 j = 0; j < face.mNumVertices; ++j)
- {
- pos[j].add(trans);
- pos[j].mul(scale);
- if (norm && !norm[j].equals3(LLVector4a::getZero()))
- {
- norm[j].mul(inv_scale);
- norm[j].normalize3();
- }
-
- if (t)
- {
- F32 w = t[j].getF32ptr()[3];
- t[j].mul(inv_scale);
- t[j].normalize3();
- t[j].getF32ptr()[3] = w;
- }
- }
- }
-
- weight_map old_weights = mSkinWeights;
- mSkinWeights.clear();
- mPosition.clear();
-
- for (auto& weights : old_weights)
- {
- LLVector4a pos(weights.first.mV[VX], weights.first.mV[VY], weights.first.mV[VZ]);
- pos.add(trans);
- pos.mul(scale);
- LLVector3 scaled_pos(pos.getF32ptr());
- mPosition.push_back(scaled_pos);
- mSkinWeights[scaled_pos] = weights.second;
- }
-
- // mNormalizedScale is the scale at which
- // we would need to multiply the model
- // by to get the original size of the
- // model instead of the normalized size.
- LLVector4a normalized_scale;
- normalized_scale.splat(1.f);
- normalized_scale.div(scale);
- mNormalizedScale.set(normalized_scale.getF32ptr());
- mNormalizedTranslation.set(trans.getF32ptr());
- mNormalizedTranslation *= -1.f;
-
- // remember normalized scale so original dimensions can be recovered for mesh processing (i.e. tangent generation)
- for (auto& face : mVolumeFaces)
- {
- face.mNormalizedScale = mNormalizedScale;
- }
- }
-}
-
void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const
{
scale_out = mNormalizedScale;
@@ -1717,21 +1561,11 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
{
ret["joint_names"][i] = mJointNames[i];
- // For model to work at all there must be a matching bind matrix,
- // so supply an indentity one if it isn't true
- // Note: can build an actual bind matrix from joints
- const LLMatrix4a& inv_bind = mInvBindMatrix.size() > i ? mInvBindMatrix[i] : LLMatrix4a::identity();
- if (i >= mInvBindMatrix.size())
- {
- LL_WARNS("MESHSKININFO") << "Joint index " << i << " (" << mJointNames[i] << ") exceeds inverse bind matrix size "
- << mInvBindMatrix.size() << LL_ENDL;
- }
-
for (U32 j = 0; j < 4; j++)
{
for (U32 k = 0; k < 4; k++)
{
- ret["inverse_bind_matrix"][i][j * 4 + k] = inv_bind.mMatrix[j][k];
+ ret["inverse_bind_matrix"][i][j*4+k] = mInvBindMatrix[i].mMatrix[j][k];
}
}
}
@@ -1744,25 +1578,15 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
}
}
- // optional 'joint overrides'
- if (include_joints && mAlternateBindMatrix.size() > 0)
+ if ( include_joints && mAlternateBindMatrix.size() > 0 )
{
for (U32 i = 0; i < mJointNames.size(); ++i)
{
- // If there is not enough to match mJointNames,
- // either supply no alternate matrixes at all or supply
- // replacements
- const LLMatrix4a& alt_bind = mAlternateBindMatrix.size() > i ? mAlternateBindMatrix[i] : LLMatrix4a::identity();
- if (i >= mAlternateBindMatrix.size())
- {
- LL_WARNS("MESHSKININFO") << "Joint index " << i << " (" << mJointNames[i] << ") exceeds alternate bind matrix size "
- << mAlternateBindMatrix.size() << LL_ENDL;
- }
for (U32 j = 0; j < 4; j++)
{
for (U32 k = 0; k < 4; k++)
{
- ret["alt_inverse_bind_matrix"][i][j * 4 + k] = alt_bind.mMatrix[j][k];
+ ret["alt_inverse_bind_matrix"][i][j*4+k] = mAlternateBindMatrix[i].mMatrix[j][k];
}
}
}
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index 5c6d0a55d2..fe28926720 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -202,7 +202,6 @@ public:
void sortVolumeFacesByMaterialName();
void normalizeVolumeFaces();
- void normalizeVolumeFacesAndWeights();
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
void remapVolumeFaces();
void optimizeVolumeFaces();
diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index f97ac16a83..7facd53a72 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -150,8 +150,6 @@ void LLModelLoader::run()
{
mWarningsArray.clear();
doLoadModel();
- // 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));
}
@@ -468,58 +466,6 @@ bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::
return true;
}
-void LLModelLoader::dumpDebugData()
-{
- std::string log_file = mFilename + "_importer.txt";
- LLStringUtil::toLower(log_file);
- llofstream file;
- file.open(log_file.c_str());
- if (!file)
- {
- LL_WARNS() << "dumpDebugData failed to open file " << log_file << LL_ENDL;
- return;
- }
- file << "Importing: " << mFilename << "\n";
-
- std::map<std::string, LLMatrix4a> inv_bind;
- std::map<std::string, LLMatrix4a> alt_bind;
- for (LLPointer<LLModel>& mdl : mModelList)
- {
-
- file << "Model name: " << mdl->mLabel << "\n";
- const LLMeshSkinInfo& skin_info = mdl->mSkinInfo;
- file << "Shape Bind matrix: " << skin_info.mBindShapeMatrix << "\n";
- file << "Skin Weights count: " << (S32)mdl->mSkinWeights.size() << "\n";
-
- // some objects might have individual bind matrices,
- // but for now it isn't accounted for
- size_t joint_count = skin_info.mJointNames.size();
- for (size_t i = 0; i< joint_count;i++)
- {
- const std::string& joint = skin_info.mJointNames[i];
- if (skin_info.mInvBindMatrix.size() > i)
- {
- inv_bind[joint] = skin_info.mInvBindMatrix[i];
- }
- if (skin_info.mAlternateBindMatrix.size() > i)
- {
- alt_bind[joint] = skin_info.mAlternateBindMatrix[i];
- }
- }
- }
-
- file << "Inv Bind matrices.\n";
- for (auto& bind : inv_bind)
- {
- file << "Joint: " << bind.first << " Matrix: " << bind.second << "\n";
- }
-
- file << "Alt Bind matrices.\n";
- for (auto& bind : alt_bind)
- {
- file << "Joint: " << bind.first << " Matrix: " << bind.second << "\n";
- }
-}
//called in the main thread
void LLModelLoader::loadTextures()
diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h
index 7c808dcae0..aece922111 100644
--- a/indra/llprimitive/llmodelloader.h
+++ b/indra/llprimitive/llmodelloader.h
@@ -36,7 +36,7 @@ class LLJoint;
typedef std::map<std::string, LLMatrix4> JointTransformMap;
typedef std::map<std::string, LLMatrix4>::iterator JointTransformMapIt;
-typedef std::map<std::string, std::string> JointMap;
+typedef std::map<std::string, std::string, std::less<>> JointMap;
typedef std::deque<std::string> JointNameSet;
const S32 SLM_SUPPORTED_VERSION = 3;
@@ -111,7 +111,6 @@ public:
bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info)
model_list mModelList;
- // The scene is pretty much what ends up getting loaded for upload. Basically assign things to this guy if you want something uploaded.
scene mScene;
typedef std::queue<LLPointer<LLModel> > model_queue;
@@ -120,14 +119,9 @@ public:
model_queue mPhysicsQ;
//map of avatar joints as named in COLLADA assets to internal joint names
- // Do not use this for anything other than looking up the name of a joint. This is populated elsewhere.
JointMap mJointMap;
-
- // The joint list is what you want to use to actually setup the specific joint transformations.
JointTransformMap& mJointList;
JointNameSet& mJointsFromNode;
-
-
U32 mMaxJointsPerMesh;
LLModelLoader(
@@ -198,7 +192,6 @@ public:
const LLSD logOut() const { return mWarningsArray; }
void clearLog() { mWarningsArray.clear(); }
- void dumpDebugData();
protected:
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index dee3d5ed59..98151e2f4d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -76,7 +76,6 @@ set(viewer_SOURCE_FILES
gltf/accessor.cpp
gltf/primitive.cpp
gltf/animation.cpp
- gltf/llgltfloader.cpp
groupchatlistener.cpp
llaccountingcostmanager.cpp
llaisapi.cpp
@@ -747,7 +746,6 @@ set(viewer_HEADER_FILES
gltf/buffer_util.h
gltf/primitive.h
gltf/animation.h
- gltf/llgltfloader.h
llaccountingcost.h
llaccountingcostmanager.h
llaisapi.h
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index 8c9f77686a..c210b9c61d 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -50,10 +50,6 @@ namespace LL
"KHR_texture_transform"
};
- static std::unordered_set<std::string> ExtensionsIgnored = {
- "KHR_materials_pbrSpecularGlossiness"
- };
-
Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
{
if (alpha_mode == "OPAQUE")
@@ -476,14 +472,11 @@ void Asset::update()
for (auto& image : mImages)
{
- if (image.mLoadIntoTexturePipe)
- {
- if (image.mTexture.notNull())
- { // HACK - force texture to be loaded full rez
- // TODO: calculate actual vsize
- image.mTexture->addTextureStats(2048.f * 2048.f);
- image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
- }
+ if (image.mTexture.notNull())
+ { // HACK - force texture to be loaded full rez
+ // TODO: calculate actual vsize
+ image.mTexture->addTextureStats(2048.f * 2048.f);
+ image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
}
}
}
@@ -493,23 +486,18 @@ void Asset::update()
bool Asset::prep()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
- // check required extensions
+ // check required extensions and fail if not supported
+ bool unsupported = false;
for (auto& extension : mExtensionsRequired)
{
if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())
{
- if (ExtensionsIgnored.find(extension) == ExtensionsIgnored.end())
- {
- LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
- mUnsupportedExtensions.push_back(extension);
- }
- else
- {
- mIgnoredExtensions.push_back(extension);
- }
+ LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
+ unsupported = true;
}
}
- if (mUnsupportedExtensions.size() > 0)
+
+ if (unsupported)
{
return false;
}
@@ -525,7 +513,7 @@ bool Asset::prep()
for (auto& image : mImages)
{
- if (!image.prep(*this, mLoadIntoVRAM))
+ if (!image.prep(*this))
{
return false;
}
@@ -554,106 +542,102 @@ bool Asset::prep()
return false;
}
}
- if (mLoadIntoVRAM)
- {
- // prepare vertex buffers
- // material count is number of materials + 1 for default material
- U32 mat_count = (U32) mMaterials.size() + 1;
+ // prepare vertex buffers
+
+ // material count is number of materials + 1 for default material
+ U32 mat_count = (U32) mMaterials.size() + 1;
- if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
- { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
- gDebugProgram.bind();
+ if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
+ { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
+ gDebugProgram.bind();
+ }
+
+ for (S32 double_sided = 0; double_sided < 2; ++double_sided)
+ {
+ RenderData& rd = mRenderData[double_sided];
+ for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
+ {
+ rd.mBatches[i].resize(mat_count);
}
- for (S32 double_sided = 0; double_sided < 2; ++double_sided)
+ // for each material
+ for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
{
- RenderData& rd = mRenderData[double_sided];
- for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
+ // for each shader variant
+ U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
+ U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
+
+ S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
+ if (ds_mat != double_sided)
{
- rd.mBatches[i].resize(mat_count);
+ continue;
}
- // for each material
- for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
+ for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
{
- // for each shader variant
- U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
- U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
-
- S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
- if (ds_mat != double_sided)
+ U32 attribute_mask = 0;
+ // for each mesh
+ for (auto& mesh : mMeshes)
{
- continue;
- }
-
- for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
- {
- U32 attribute_mask = 0;
- // for each mesh
- for (auto& mesh : mMeshes)
+ // for each primitive
+ for (auto& primitive : mesh.mPrimitives)
{
- // for each primitive
- for (auto& primitive : mesh.mPrimitives)
+ if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
{
- if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
- {
- // accumulate vertex and index counts
- primitive.mVertexOffset = vertex_count[variant];
- primitive.mIndexOffset = index_count[variant];
+ // accumulate vertex and index counts
+ primitive.mVertexOffset = vertex_count[variant];
+ primitive.mIndexOffset = index_count[variant];
- vertex_count[variant] += primitive.getVertexCount();
- index_count[variant] += primitive.getIndexCount();
+ vertex_count[variant] += primitive.getVertexCount();
+ index_count[variant] += primitive.getIndexCount();
- // all primitives of a given variant and material should all have the same attribute mask
- llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
- attribute_mask |= primitive.mAttributeMask;
- }
+ // all primitives of a given variant and material should all have the same attribute mask
+ llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
+ attribute_mask |= primitive.mAttributeMask;
}
}
+ }
- // allocate vertex buffer and pack it
- if (vertex_count[variant] > 0)
- {
- U32 mat_idx = mat_id + 1;
- #if 0
- LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
+ // allocate vertex buffer and pack it
+ if (vertex_count[variant] > 0)
+ {
+ U32 mat_idx = mat_id + 1;
+ LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
- rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
- vb->allocateBuffer(vertex_count[variant],
- index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
- vb->setBuffer();
+ rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
+ vb->allocateBuffer(vertex_count[variant],
+ index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
+ vb->setBuffer();
- for (auto& mesh : mMeshes)
+ for (auto& mesh : mMeshes)
+ {
+ for (auto& primitive : mesh.mPrimitives)
{
- for (auto& primitive : mesh.mPrimitives)
+ if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
{
- if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
- {
- primitive.upload(vb);
- }
+ primitive.upload(vb);
}
}
+ }
- vb->unmapBuffer();
+ vb->unmapBuffer();
- vb->unbind();
- #endif
- }
+ vb->unbind();
}
}
}
+ }
- // sanity check that all primitives have a vertex buffer
- for (auto& mesh : mMeshes)
+ // sanity check that all primitives have a vertex buffer
+ for (auto& mesh : mMeshes)
+ {
+ for (auto& primitive : mesh.mPrimitives)
{
- for (auto& primitive : mesh.mPrimitives)
- {
- //llassert(primitive.mVertexBuffer.notNull());
- }
+ llassert(primitive.mVertexBuffer.notNull());
}
}
- #if 0
+
// build render batches
for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)
{
@@ -680,7 +664,6 @@ bool Asset::prep()
}
}
}
- #endif
return true;
}
@@ -689,10 +672,9 @@ Asset::Asset(const Value& src)
*this = src;
}
-bool Asset::load(std::string_view filename, bool loadIntoVRAM)
+bool Asset::load(std::string_view filename)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
- mLoadIntoVRAM = loadIntoVRAM;
mFilename = filename;
std::string ext = gDirUtilp->getExtension(mFilename);
@@ -710,7 +692,7 @@ bool Asset::load(std::string_view filename, bool loadIntoVRAM)
}
else if (ext == "glb")
{
- return loadBinary(str, mLoadIntoVRAM);
+ return loadBinary(str);
}
else
{
@@ -727,9 +709,8 @@ bool Asset::load(std::string_view filename, bool loadIntoVRAM)
return false;
}
-bool Asset::loadBinary(const std::string& data, bool loadIntoVRAM)
+bool Asset::loadBinary(const std::string& data)
{
- mLoadIntoVRAM = loadIntoVRAM;
// load from binary gltf
const U8* ptr = (const U8*)data.data();
const U8* end = ptr + data.size();
@@ -954,9 +935,8 @@ void Asset::eraseBufferView(S32 bufferView)
LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
-bool Image::prep(Asset& asset, bool loadIntoVRAM)
+bool Image::prep(Asset& asset)
{
- mLoadIntoTexturePipe = loadIntoVRAM;
LLUUID id;
if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
{ // loaded from an asset, fetch the texture from the asset system
@@ -971,12 +951,12 @@ bool Image::prep(Asset& asset, bool loadIntoVRAM)
{ // embedded in a buffer, load the texture from the buffer
BufferView& bufferView = asset.mBufferViews[mBufferView];
Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
- if (mLoadIntoTexturePipe)
- {
- U8* data = buffer.mData.data() + bufferView.mByteOffset;
- mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
- }
- else if (mTexture.isNull() && mLoadIntoTexturePipe)
+
+ U8* data = buffer.mData.data() + bufferView.mByteOffset;
+
+ mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
+
+ if (mTexture.isNull())
{
LL_WARNS("GLTF") << "Failed to load image from buffer:" << LL_ENDL;
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
@@ -991,12 +971,12 @@ bool Image::prep(Asset& asset, bool loadIntoVRAM)
std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri;
LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file);
- if (tracking_id.notNull() && mLoadIntoTexturePipe)
+ if (tracking_id.notNull())
{
LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
mTexture = LLViewerTextureManager::getFetchedTexture(world_id);
}
- else if (mLoadIntoTexturePipe)
+ else
{
LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
@@ -1011,7 +991,7 @@ bool Image::prep(Asset& asset, bool loadIntoVRAM)
return false;
}
- if (!asset.mFilename.empty() && mLoadIntoTexturePipe)
+ if (!asset.mFilename.empty())
{ // local preview, boost image so it doesn't discard and force to save raw image in case we save out or upload
mTexture->setBoostLevel(LLViewerTexture::BOOST_PREVIEW);
mTexture->forceToSaveRawImage(0, F32_MAX);
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index b9554d753c..27821659db 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -286,7 +286,6 @@ namespace LL
void serialize(boost::json::object& dst) const;
};
- // Image is for images that we want to load for the given asset. This acts as an interface into the viewer's texture pipe.
class Image
{
public:
@@ -302,8 +301,6 @@ namespace LL
S32 mBits = -1;
S32 mPixelType = -1;
- bool mLoadIntoTexturePipe = false;
-
LLPointer<LLViewerFetchedTexture> mTexture;
const Image& operator=(const Value& src);
@@ -319,7 +316,7 @@ namespace LL
// preserve only uri and name
void clearData(Asset& asset);
- bool prep(Asset& asset, bool loadIntoVRAM);
+ bool prep(Asset& asset);
};
// Render Batch -- vertex buffer and list of primitives to render using
@@ -394,10 +391,6 @@ namespace LL
// UBO for storing material data
U32 mMaterialsUBO = 0;
- bool mLoadIntoVRAM = false;
-
- std::vector<std::string> mUnsupportedExtensions;
- std::vector<std::string> mIgnoredExtensions;
// prepare for first time use
bool prep();
@@ -435,12 +428,12 @@ namespace LL
// accepts .gltf and .glb files
// Any existing data will be lost
// returns result of prep() on success
- bool load(std::string_view filename, bool loadIntoVRAM);
+ bool load(std::string_view filename);
// load .glb contents from memory
// data - binary contents of .glb file
// returns result of prep() on success
- bool loadBinary(const std::string& data, bool loadIntoVRAM);
+ bool loadBinary(const std::string& data);
const Asset& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
index be36c5e90b..ef9bba8128 100644
--- a/indra/newview/gltf/buffer_util.h
+++ b/indra/newview/gltf/buffer_util.h
@@ -159,12 +159,6 @@ namespace LL
}
template<>
- inline void copyVec3<F32, LLColor4U>(F32* src, LLColor4U& dst)
- {
- dst.set((U8)(src[0] * 255.f), (U8)(src[1] * 255.f), (U8)(src[2] * 255.f), 255);
- }
-
- template<>
inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
{
dst.set((U8)(src[0]), (U8)(src[1]), (U8)(src[2]), 255);
@@ -375,11 +369,6 @@ namespace LL
template<class T>
inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
{
- if (accessor.mBufferView == INVALID_INDEX)
- {
- LL_WARNS("GLTF") << "Invalid buffer" << LL_ENDL;
- return;
- }
const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp
deleted file mode 100644
index 7ae255e054..0000000000
--- a/indra/newview/gltf/llgltfloader.cpp
+++ /dev/null
@@ -1,1710 +0,0 @@
-/**
- * @file LLGLTFLoader.cpp
- * @brief LLGLTFLoader class implementation
- *
- * $LicenseInfo:firstyear=2022&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2022, 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 "llgltfloader.h"
-#include "meshoptimizer.h"
-#include <glm/gtc/packing.hpp>
-
-// Import & define single-header gltf import/export lib
-#define TINYGLTF_IMPLEMENTATION
-#define TINYGLTF_USE_CPP14 // default is C++ 11
-
-// tinygltf by default loads image files using STB
-#define STB_IMAGE_IMPLEMENTATION
-// to use our own image loading:
-// 1. replace this definition with TINYGLTF_NO_STB_IMAGE
-// 2. provide image loader callback with TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)
-
-// tinygltf saves image files using STB
-#define STB_IMAGE_WRITE_IMPLEMENTATION
-// similarly, can override with TINYGLTF_NO_STB_IMAGE_WRITE and TinyGLTF::SetImageWriter(fxn, data)
-
-// Additionally, disable inclusion of STB header files entirely with
-// TINYGLTF_NO_INCLUDE_STB_IMAGE
-// TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
-#include "tinygltf/tiny_gltf.h"
-
-
-// TODO: includes inherited from dae loader. Validate / prune
-
-#include "llsdserialize.h"
-#include "lljoint.h"
-#include "llbase64.h"
-#include "lldir.h"
-
-#include "llmatrix4a.h"
-
-#include <boost/regex.hpp>
-#include <boost/algorithm/string/replace.hpp>
-#include <fstream>
-
-static const std::string lod_suffix[LLModel::NUM_LODS] =
-{
- "_LOD0",
- "_LOD1",
- "_LOD2",
- "",
- "_PHYS",
-};
-
-// Premade rotation matrix, GLTF is Y-up while SL is Z-up
-static const glm::mat4 coord_system_rotation(
- 1.f, 0.f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 0.f, -1.f, 0.f, 0.f,
- 0.f, 0.f, 0.f, 1.f
-);
-
-
-static const glm::mat4 coord_system_rotationxy(
- 0.f, 1.f, 0.f, 0.f,
- -1.f, 0.f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 0.f, 0.f, 0.f, 1.f
-);
-
-LLGLTFLoader::LLGLTFLoader(std::string filename,
- S32 lod,
- LLModelLoader::load_callback_t load_cb,
- LLModelLoader::joint_lookup_func_t joint_lookup_func,
- LLModelLoader::texture_load_func_t texture_load_func,
- LLModelLoader::state_callback_t state_cb,
- void * opaque_userdata,
- JointTransformMap & jointTransformMap,
- JointNameSet & jointsFromNodes,
- std::map<std::string, std::string> &jointAliasMap,
- U32 maxJointsPerMesh,
- U32 modelLimit,
- std::vector<LLJointData> viewer_skeleton) //,
- //bool preprocess)
- : LLModelLoader( filename,
- lod,
- load_cb,
- joint_lookup_func,
- texture_load_func,
- state_cb,
- opaque_userdata,
- jointTransformMap,
- jointsFromNodes,
- jointAliasMap,
- maxJointsPerMesh )
- , mGeneratedModelLimit(modelLimit)
- , mViewerJointData(viewer_skeleton)
-{
-}
-
-LLGLTFLoader::~LLGLTFLoader() {}
-
-bool LLGLTFLoader::OpenFile(const std::string &filename)
-{
- tinygltf::TinyGLTF loader;
- std::string filename_lc(filename);
- LLStringUtil::toLower(filename_lc);
-
- mGltfLoaded = mGLTFAsset.load(filename, false);
-
- if (!mGltfLoaded)
- {
- notifyUnsupportedExtension(true);
-
- for (const auto& buffer : mGLTFAsset.mBuffers)
- {
- if (buffer.mByteLength > 0 && buffer.mData.empty())
- {
- bool bin_file = buffer.mUri.ends_with(".bin");
- LLSD args;
- args["Message"] = bin_file ? "ParsingErrorMissingBufferBin" : "ParsingErrorMissingBuffer";
- args["BUFFER_NAME"] = buffer.mName;
- args["BUFFER_URI"] = buffer.mUri;
- mWarningsArray.append(args);
- }
- }
- setLoadState(ERROR_PARSING);
- return false;
- }
-
- notifyUnsupportedExtension(false);
-
- bool meshesLoaded = parseMeshes();
-
- setLoadState(DONE);
-
- return meshesLoaded;
-}
-
-void LLGLTFLoader::addModelToScene(
- LLModel* pModel,
- U32 submodel_limit,
- const LLMatrix4& transformation,
- const LLVolumeParams& volume_params,
- const material_map& mats)
-{
- U32 volume_faces = pModel->getNumVolumeFaces();
-
- // Side-steps all manner of issues when splitting models
- // and matching lower LOD materials to base models
- //
- pModel->sortVolumeFacesByMaterialName();
-
- int submodelID = 0;
-
- // remove all faces that definitely won't fit into one model and submodel limit
- U32 face_limit = (submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES;
- if (face_limit < volume_faces)
- {
- pModel->setNumVolumeFaces(face_limit);
- }
-
- LLVolume::face_list_t remainder;
- std::vector<LLModel*> ready_models;
- LLModel* current_model = pModel;
- do
- {
- current_model->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder);
-
- volume_faces = static_cast<U32>(remainder.size());
-
- // Don't add to scene yet because weights and materials aren't ready.
- // Just save it
- ready_models.push_back(current_model);
-
- // If we have left-over volume faces, create another model
- // to absorb them.
- if (volume_faces)
- {
- LLModel* next = new LLModel(volume_params, 0.f);
- next->ClearFacesAndMaterials();
- next->mSubmodelID = ++submodelID;
- next->mLabel = pModel->mLabel + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod];
- next->getVolumeFaces() = remainder;
- next->mNormalizedScale = current_model->mNormalizedScale;
- next->mNormalizedTranslation = current_model->mNormalizedTranslation;
- next->mSkinWeights = current_model->mSkinWeights;
- next->mPosition = current_model->mPosition;
-
- const LLMeshSkinInfo& current_skin_info = current_model->mSkinInfo;
- LLMeshSkinInfo& next_skin_info = next->mSkinInfo;
- next_skin_info.mJointNames = current_skin_info.mJointNames;
- next_skin_info.mJointNums = current_skin_info.mJointNums;
- next_skin_info.mBindShapeMatrix = current_skin_info.mBindShapeMatrix;
- next_skin_info.mInvBindMatrix = current_skin_info.mInvBindMatrix;
- next_skin_info.mAlternateBindMatrix = current_skin_info.mAlternateBindMatrix;
- next_skin_info.mPelvisOffset = current_skin_info.mPelvisOffset;
-
-
- if (current_model->mMaterialList.size() > LL_SCULPT_MESH_MAX_FACES)
- {
- next->mMaterialList.assign(current_model->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, current_model->mMaterialList.end());
- current_model->mMaterialList.resize(LL_SCULPT_MESH_MAX_FACES);
- }
-
- current_model = next;
- }
-
- remainder.clear();
-
- } while (volume_faces);
-
- for (auto model : ready_models)
- {
- // remove unused/redundant vertices
- model->remapVolumeFaces();
-
- mModelList.push_back(model);
-
- std::map<std::string, LLImportMaterial> materials;
- for (U32 i = 0; i < (U32)model->mMaterialList.size(); ++i)
- {
- material_map::const_iterator found = mats.find(model->mMaterialList[i]);
- if (found != mats.end())
- {
- materials[model->mMaterialList[i]] = found->second;
- }
- else
- {
- materials[model->mMaterialList[i]] = LLImportMaterial();
- }
- }
- mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials));
- stretch_extents(model, transformation);
- }
-}
-
-bool LLGLTFLoader::parseMeshes()
-{
- if (!mGltfLoaded) return false;
-
- // 2022-04 DJH Volume params from dae example. TODO understand PCODE
- LLVolumeParams volume_params;
- volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
-
- mTransform.setIdentity();
-
- for (auto& node : mGLTFAsset.mNodes)
- {
- // Make node matrix valid for correct transformation
- node.makeMatrixValid();
- }
-
- if (mGLTFAsset.mSkins.size() > 0)
- {
- checkForXYrotation(mGLTFAsset.mSkins[0]);
- populateJointGroups();
- }
-
- // Populate the joints from skins first.
- // There's not many skins - and you can pretty easily iterate through the nodes from that.
- for (S32 i = 0; i < mGLTFAsset.mSkins.size(); i++)
- {
- populateJointsFromSkin(i);
- }
-
- // 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;
-
- // Check if we have scenes defined
- if (!mGLTFAsset.mScenes.empty())
- {
- // Process the default scene (or first scene if no default)
- S32 scene_idx = mGLTFAsset.mScene >= 0 ? mGLTFAsset.mScene : 0;
-
- if (scene_idx < mGLTFAsset.mScenes.size())
- {
- const LL::GLTF::Scene& scene = mGLTFAsset.mScenes[scene_idx];
-
- LL_INFOS("GLTF_IMPORT") << "Processing scene " << scene_idx << " with " << scene.mNodes.size() << " root nodes" << LL_ENDL;
-
- // Process all root nodes defined in the scene
- for (S32 root_idx : scene.mNodes)
- {
- if (root_idx >= 0 && root_idx < static_cast<S32>(mGLTFAsset.mNodes.size()))
- {
- processNodeHierarchy(root_idx, mesh_name_counts, submodel_limit, volume_params);
- }
- }
- }
- }
- else
- {
- LL_WARNS("GLTF_IMPORT") << "No scenes defined in GLTF file" << LL_ENDL;
-
- LLSD args;
- args["Message"] = "NoScenesFound";
- mWarningsArray.append(args);
- 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;
- }
-
- return true;
-}
-
-void LLGLTFLoader::processNodeHierarchy(S32 node_idx, std::map<std::string, S32>& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params)
-{
- if (node_idx < 0 || node_idx >= static_cast<S32>(mGLTFAsset.mNodes.size()))
- return;
-
- auto& node = mGLTFAsset.mNodes[node_idx];
-
- LL_INFOS("GLTF_IMPORT") << "Processing node " << node_idx << " (" << node.mName << ")"
- << " - has mesh: " << (node.mMesh >= 0 ? "yes" : "no")
- << " - children: " << node.mChildren.size() << LL_ENDL;
-
- // Process this node's mesh if it has one
- if (node.mMesh >= 0 && node.mMesh < mGLTFAsset.mMeshes.size())
- {
- LLMatrix4 transformation;
- material_map mats;
-
- LLModel* pModel = new LLModel(volume_params, 0.f);
- auto& mesh = mGLTFAsset.mMeshes[node.mMesh];
-
- // Get base mesh name and track usage
- std::string base_name = mesh.mName;
- if (base_name.empty())
- {
- base_name = "mesh_" + std::to_string(node.mMesh);
- }
-
- S32 instance_count = mesh_name_counts[base_name]++;
-
- if (populateModelFromMesh(pModel, mesh, node, mats, instance_count) &&
- (LLModel::NO_ERRORS == pModel->getStatus()) &&
- validate_model(pModel))
- {
- mTransform.setIdentity();
- transformation = mTransform;
-
- // adjust the transformation to compensate for mesh normalization
- LLVector3 mesh_scale_vector;
- LLVector3 mesh_translation_vector;
- pModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
-
- LLMatrix4 mesh_translation;
- mesh_translation.setTranslation(mesh_translation_vector);
- mesh_translation *= transformation;
- transformation = mesh_translation;
-
- LLMatrix4 mesh_scale;
- mesh_scale.initScale(mesh_scale_vector);
- mesh_scale *= transformation;
- transformation = mesh_scale;
-
- if (node.mSkin >= 0)
- {
- // "Bind Shape Matrix" is supposed to transform the geometry of the skinned mesh
- // into the coordinate space of the joints.
- // In GLTF, this matrix is omitted, and it is assumed that this transform is either
- // premultiplied with the mesh data, or postmultiplied to the inverse bind matrices.
- //
- // TODO: There appears to be missing rotation when joints rotate the model
- // or inverted bind matrices are missing inherited rotation
- // (based of values the 'bento shoes' mesh might be missing 90 degrees horizontaly
- // prior to skinning)
-
- pModel->mSkinInfo.mBindShapeMatrix.loadu(mesh_scale);
- LL_INFOS("GLTF_DEBUG") << "Model: " << pModel->mLabel << " mBindShapeMatrix: " << pModel->mSkinInfo.mBindShapeMatrix << LL_ENDL;
- }
-
- if (transformation.determinant() < 0)
- { // negative scales are not supported
- LL_INFOS("GLTF_IMPORT") << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: "
- << pModel->mLabel << LL_ENDL;
- LLSD args;
- args["Message"] = "NegativeScaleNormTrans";
- args["LABEL"] = pModel->mLabel;
- mWarningsArray.append(args);
- }
-
- addModelToScene(pModel, submodel_limit, transformation, volume_params, mats);
- mats.clear();
- }
- else
- {
- setLoadState(ERROR_MODEL + pModel->getStatus());
- delete pModel;
- return;
- }
- }
- else if (node.mMesh >= 0)
- {
- // Log invalid mesh reference
- LL_WARNS("GLTF_IMPORT") << "Node " << node_idx << " (" << node.mName
- << ") references invalid mesh " << node.mMesh
- << " (total meshes: " << mGLTFAsset.mMeshes.size() << ")" << LL_ENDL;
-
- LLSD args;
- args["Message"] = "InvalidMeshReference";
- args["NODE_NAME"] = node.mName;
- args["MESH_INDEX"] = node.mMesh;
- args["TOTAL_MESHES"] = static_cast<S32>(mGLTFAsset.mMeshes.size());
- mWarningsArray.append(args);
- }
-
- // Process all children recursively
- for (S32 child_idx : node.mChildren)
- {
- processNodeHierarchy(child_idx, mesh_name_counts, submodel_limit, volume_params);
- }
-}
-
-void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const
-{
- if (node_index < 0 || node_index >= static_cast<S32>(asset.mNodes.size()))
- {
- combined_transform = glm::mat4(1.0f);
- return;
- }
-
- const auto& node = asset.mNodes[node_index];
-
- // Ensure the node's matrix is valid
- const_cast<LL::GLTF::Node&>(node).makeMatrixValid();
-
- // Start with this node's transform
- combined_transform = node.mMatrix;
-
- // Find and apply parent transform if it exists
- for (size_t i = 0; i < asset.mNodes.size(); ++i)
- {
- const auto& potential_parent = asset.mNodes[i];
- auto it = std::find(potential_parent.mChildren.begin(), potential_parent.mChildren.end(), node_index);
-
- if (it != potential_parent.mChildren.end())
- {
- // Found parent - recursively get its combined transform and apply it
- glm::mat4 parent_transform;
- computeCombinedNodeTransform(asset, static_cast<S32>(i), parent_transform);
- combined_transform = parent_transform * combined_transform;
- return; // Early exit - a node can only have one parent
- }
- }
-}
-
-bool LLGLTFLoader::addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx) const
-{
- const std::string& legal_name = mJointNames[gltf_skin_idx][gltf_joint_idx];
- if (legal_name.empty())
- {
- llassert(false); // should have been stopped by gltf_joint_index_use[i] == -1
- return false;
- }
- skin_info.mJointNames.push_back(legal_name);
- skin_info.mJointNums.push_back(-1);
-
- // In scope of same skin multiple meshes reuse same bind matrices
- skin_info.mInvBindMatrix.push_back(mInverseBindMatrices[gltf_skin_idx][gltf_joint_idx]);
- skin_info.mAlternateBindMatrix.push_back(mAlternateBindMatrices[gltf_skin_idx][gltf_joint_idx]);
-
- return true;
-}
-
-bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats, S32 instance_count)
-{
- // Set the requested label for the floater display and uploading
- pModel->mRequestedLabel = gDirUtilp->getBaseFileName(mFilename, true);
-
- // Create unique model name
- std::string base_name = mesh.mName;
- if (base_name.empty())
- {
- S32 mesh_index = static_cast<S32>(&mesh - &mGLTFAsset.mMeshes[0]);
- base_name = "mesh_" + std::to_string(mesh_index);
- }
-
- LL_INFOS("GLTF_DEBUG") << "Processing model " << base_name << LL_ENDL;
-
- if (instance_count > 0)
- {
- pModel->mLabel = base_name + "_copy_" + std::to_string(instance_count);
- }
- else
- {
- pModel->mLabel = base_name;
- }
-
- pModel->ClearFacesAndMaterials();
-
- S32 skinIdx = nodeno.mSkin;
-
- // Compute final combined transform matrix (hierarchy + coordinate rotation)
- S32 node_index = static_cast<S32>(&nodeno - &mGLTFAsset.mNodes[0]);
- glm::mat4 hierarchy_transform;
- computeCombinedNodeTransform(mGLTFAsset, node_index, hierarchy_transform);
-
- // Combine transforms: coordinate rotation applied to hierarchy transform
- glm::mat4 final_transform = coord_system_rotation * hierarchy_transform;
- if (mApplyXYRotation)
- {
- final_transform = coord_system_rotationxy * final_transform;
- }
-
- // Check if we have a negative scale (flipped coordinate system)
- bool hasNegativeScale = glm::determinant(final_transform) < 0.0f;
-
- // Pre-compute normal transform matrix (transpose of inverse of upper-left 3x3)
- const glm::mat3 normal_transform = glm::transpose(glm::inverse(glm::mat3(final_transform)));
-
- // Mark unsuported joints with '-1' so that they won't get added into weights
- // GLTF maps all joints onto all meshes. Gather use count per mesh to cut unused ones.
- std::vector<S32> gltf_joint_index_use;
- if (skinIdx >= 0 && mGLTFAsset.mSkins.size() > skinIdx)
- {
- LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx];
-
- size_t jointCnt = gltf_skin.mJoints.size();
- gltf_joint_index_use.resize(jointCnt);
-
- for (size_t i = 0; i < jointCnt; ++i)
- {
- if (mJointNames[i].empty())
- {
- // This might need to hold a substitute index
- gltf_joint_index_use[i] = -1; // mark as unsupported
- }
- }
- }
-
- for (size_t prim_idx = 0; prim_idx < mesh.mPrimitives.size(); ++prim_idx)
- {
- const LL::GLTF::Primitive& prim = mesh.mPrimitives[prim_idx];
-
- // So primitives already have all of the data we need for a given face in SL land.
- // Primitives may only ever have a single material assigned to them - as the relation is 1:1 in terms of intended draw call
- // count. Just go ahead and populate faces direct from the GLTF primitives here. -Geenz 2025-04-07
- LLVolumeFace face;
- std::vector<GLTFVertex> vertices;
-
- LLImportMaterial impMat;
- impMat.mDiffuseColor = LLColor4::white; // Default color
-
- // Process material if available
- if (prim.mMaterial >= 0 && prim.mMaterial < mGLTFAsset.mMaterials.size())
- {
- LL::GLTF::Material* material = &mGLTFAsset.mMaterials[prim.mMaterial];
-
- // Set diffuse color from base color factor
- impMat.mDiffuseColor = LLColor4(
- material->mPbrMetallicRoughness.mBaseColorFactor[0],
- material->mPbrMetallicRoughness.mBaseColorFactor[1],
- material->mPbrMetallicRoughness.mBaseColorFactor[2],
- material->mPbrMetallicRoughness.mBaseColorFactor[3]
- );
-
- // Process base color texture if it exists
- if (material->mPbrMetallicRoughness.mBaseColorTexture.mIndex >= 0)
- {
- S32 texIndex = material->mPbrMetallicRoughness.mBaseColorTexture.mIndex;
- if (texIndex < mGLTFAsset.mTextures.size())
- {
- S32 sourceIndex = mGLTFAsset.mTextures[texIndex].mSource;
- if (sourceIndex >= 0 && sourceIndex < mGLTFAsset.mImages.size())
- {
- LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex];
-
- // Use URI as texture file name
- if (!image.mUri.empty())
- {
- // URI might be a remote URL or a local path
- std::string filename = image.mUri;
-
- // Extract just the filename from the URI
- size_t pos = filename.find_last_of("/\\");
- if (pos != std::string::npos)
- {
- filename = filename.substr(pos + 1);
- }
-
- // Store the texture filename
- impMat.mDiffuseMapFilename = filename;
- impMat.mDiffuseMapLabel = material->mName.empty() ? filename : material->mName;
-
- LL_INFOS("GLTF_IMPORT") << "Found texture: " << impMat.mDiffuseMapFilename
- << " for material: " << material->mName << LL_ENDL;
-
- LLSD args;
- args["Message"] = "TextureFound";
- args["TEXTURE_NAME"] = impMat.mDiffuseMapFilename;
- args["MATERIAL_NAME"] = material->mName;
- mWarningsArray.append(args);
-
- // If the image has a texture loaded already, use it
- if (image.mTexture.notNull())
- {
- impMat.setDiffuseMap(image.mTexture->getID());
- LL_INFOS("GLTF_IMPORT") << "Using existing texture ID: " << image.mTexture->getID().asString() << LL_ENDL;
- }
- else
- {
- // Texture will be loaded later through the callback system
- LL_INFOS("GLTF_IMPORT") << "Texture needs loading: " << impMat.mDiffuseMapFilename << LL_ENDL;
- }
- }
- else if (image.mTexture.notNull())
- {
- // No URI but we have a texture, use it directly
- impMat.setDiffuseMap(image.mTexture->getID());
- LL_INFOS("GLTF_IMPORT") << "Using existing texture ID without URI: " << image.mTexture->getID().asString() << LL_ENDL;
- }
- else if (image.mBufferView >= 0)
- {
- // For embedded textures (no URI but has buffer data)
- std::string temp_filename = extractTextureToTempFile(texIndex, "base_color");
- if (!temp_filename.empty())
- {
- impMat.mDiffuseMapFilename = temp_filename;
- impMat.mDiffuseMapLabel = material->mName.empty() ? temp_filename : material->mName;
- }
- }
- }
- }
- }
- }
-
- if (prim.getIndexCount() % 3 != 0)
- {
- LL_WARNS("GLTF_IMPORT") << "Mesh '" << mesh.mName << "' primitive " << prim_idx
- << ": Invalid index count " << prim.getIndexCount()
- << " (not divisible by 3). GLTF files must contain triangulated geometry." << LL_ENDL;
-
- LLSD args;
- args["Message"] = "InvalidGeometryNonTriangulated";
- args["MESH_NAME"] = mesh.mName;
- args["PRIMITIVE_INDEX"] = static_cast<S32>(prim_idx);
- args["INDEX_COUNT"] = static_cast<S32>(prim.getIndexCount());
- mWarningsArray.append(args);
- return false; // Skip this primitive
- }
-
- // Apply the global scale and center offset to all vertices
- for (U32 i = 0; i < prim.getVertexCount(); i++)
- {
- // Use pre-computed final_transform
- glm::vec4 pos(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2], 1.0f);
- glm::vec4 transformed_pos = final_transform * pos;
-
- GLTFVertex vert;
- vert.position = glm::vec3(transformed_pos);
-
- if (!prim.mNormals.empty())
- {
- // Use pre-computed normal_transform
- glm::vec3 normal_vec(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]);
- vert.normal = glm::normalize(normal_transform * normal_vec);
- }
- else
- {
- // Use default normal (pointing up in model space)
- vert.normal = glm::normalize(normal_transform * glm::vec3(0.0f, 0.0f, 1.0f));
- LL_DEBUGS("GLTF_IMPORT") << "No normals found for primitive, using default normal." << LL_ENDL;
- }
-
- vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], -prim.mTexCoords0[i][1]);
-
- if (skinIdx >= 0)
- {
- vert.weights = glm::vec4(prim.mWeights[i]);
-
- auto accessorIdx = prim.mAttributes.at("JOINTS_0");
- LL::GLTF::Accessor::ComponentType componentType = LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE;
- if (accessorIdx >= 0)
- {
- auto accessor = mGLTFAsset.mAccessors[accessorIdx];
- componentType = accessor.mComponentType;
- }
-
- // The GLTF spec allows for either an unsigned byte for joint indices, or an unsigned short.
- // Detect and unpack accordingly.
- if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE)
- {
- auto ujoint = glm::unpackUint4x8((U32)(prim.mJoints[i] & 0xFFFFFFFF));
- vert.joints = glm::u16vec4(ujoint.x, ujoint.y, ujoint.z, ujoint.w);
- }
- else if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_SHORT)
- {
- vert.joints = glm::unpackUint4x16(prim.mJoints[i]);
- }
- else
- {
- vert.joints = glm::zero<glm::u16vec4>();
- vert.weights = glm::zero<glm::vec4>();
- }
- }
- vertices.push_back(vert);
- }
-
- // Check for empty vertex array before processing
- if (vertices.empty())
- {
- LL_WARNS("GLTF_IMPORT") << "Empty vertex array for primitive " << prim_idx << " in model " << mesh.mName << LL_ENDL;
- LLSD args;
- args["Message"] = "EmptyVertexArray";
- args["MESH_NAME"] = mesh.mName;
- args["PRIMITIVE_INDEX"] = static_cast<S32>(prim_idx);
- args["INDEX_COUNT"] = static_cast<S32>(prim.getIndexCount());
- mWarningsArray.append(args);
- return false; // Skip this primitive
- }
-
- std::vector<LLVolumeFace::VertexData> faceVertices;
- glm::vec3 min = glm::vec3(FLT_MAX);
- glm::vec3 max = glm::vec3(-FLT_MAX);
-
- for (U32 i = 0; i < vertices.size(); i++)
- {
- LLVolumeFace::VertexData vert;
-
- // Update min/max bounds
- if (i == 0)
- {
- min = max = vertices[i].position;
- }
- else
- {
- min.x = std::min(min.x, vertices[i].position.x);
- min.y = std::min(min.y, vertices[i].position.y);
- min.z = std::min(min.z, vertices[i].position.z);
- max.x = std::max(max.x, vertices[i].position.x);
- max.y = std::max(max.y, vertices[i].position.y);
- max.z = std::max(max.z, vertices[i].position.z);
- }
-
- LLVector4a position = LLVector4a(vertices[i].position.x, vertices[i].position.y, vertices[i].position.z);
- LLVector4a normal = LLVector4a(vertices[i].normal.x, vertices[i].normal.y, vertices[i].normal.z);
- vert.setPosition(position);
- vert.setNormal(normal);
- vert.mTexCoord = LLVector2(vertices[i].uv0.x, vertices[i].uv0.y);
- faceVertices.push_back(vert);
-
- if (skinIdx >= 0)
- {
- // create list of weights that influence this vertex
- LLModel::weight_list weight_list;
-
- // Drop joints that viewer doesn't support (negative in gltf_joint_index_use_count)
- // don't reindex them yet, more indexes will be removed
- // Also drop joints that have no weight. GLTF stores 4 per vertex, so there might be
- // 'empty' ones
- if (gltf_joint_index_use[vertices[i].joints.x] >= 0
- && vertices[i].weights.x > 0.f)
- {
- weight_list.push_back(LLModel::JointWeight(vertices[i].joints.x, vertices[i].weights.x));
- gltf_joint_index_use[vertices[i].joints.x]++;
- }
- if (gltf_joint_index_use[vertices[i].joints.y] >= 0
- && vertices[i].weights.y > 0.f)
- {
- weight_list.push_back(LLModel::JointWeight(vertices[i].joints.y, vertices[i].weights.y));
- gltf_joint_index_use[vertices[i].joints.y]++;
- }
- if (gltf_joint_index_use[vertices[i].joints.z] >= 0
- && vertices[i].weights.z > 0.f)
- {
- weight_list.push_back(LLModel::JointWeight(vertices[i].joints.z, vertices[i].weights.z));
- gltf_joint_index_use[vertices[i].joints.z]++;
- }
- if (gltf_joint_index_use[vertices[i].joints.w] >= 0
- && vertices[i].weights.w > 0.f)
- {
- weight_list.push_back(LLModel::JointWeight(vertices[i].joints.w, vertices[i].weights.w));
- gltf_joint_index_use[vertices[i].joints.w]++;
- }
-
- std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater());
-
- std::vector<LLModel::JointWeight> wght;
- F32 total = 0.f;
-
- for (U32 j = 0; j < llmin((U32)4, (U32)weight_list.size()); ++j)
- {
- // take up to 4 most significant weights
- // Ported from the DAE loader - however, GLTF right now only supports up to four weights per vertex.
- wght.push_back(weight_list[j]);
- total += weight_list[j].mWeight;
- }
-
- if (total != 0.f)
- {
- F32 scale = 1.f / total;
- if (scale != 1.f)
- { // normalize weights
- for (U32 j = 0; j < wght.size(); ++j)
- {
- wght[j].mWeight *= scale;
- }
- }
- }
-
- if (wght.size() > 0)
- {
- pModel->mSkinWeights[LLVector3(vertices[i].position)] = wght;
- }
- }
- }
-
- // Create a unique material name for this primitive
- std::string materialName;
- if (prim.mMaterial >= 0 && prim.mMaterial < mGLTFAsset.mMaterials.size())
- {
- LL::GLTF::Material* material = &mGLTFAsset.mMaterials[prim.mMaterial];
- materialName = material->mName;
-
- if (materialName.empty())
- {
- materialName = "mat" + std::to_string(prim.mMaterial);
- }
- }
- else
- {
- materialName = "mat_default" + std::to_string(pModel->getNumVolumeFaces() - 1);
- }
- mats[materialName] = impMat;
-
- // Indices handling
- if (faceVertices.size() >= USHRT_MAX)
- {
- // Will have to remap 32 bit indices into 16 bit indices
- // For the sake of simplicity build vector of 32 bit indices first
- std::vector<U32> indices_32;
- for (U32 i = 0; i < prim.getIndexCount(); i += 3)
- {
- // When processing indices, flip winding order if needed
- if (hasNegativeScale)
- {
- // Flip winding order for negative scale
- indices_32.push_back(prim.mIndexArray[i]);
- indices_32.push_back(prim.mIndexArray[i + 2]); // Swap these two
- indices_32.push_back(prim.mIndexArray[i + 1]);
- }
- else
- {
- indices_32.push_back(prim.mIndexArray[i]);
- indices_32.push_back(prim.mIndexArray[i + 1]);
- indices_32.push_back(prim.mIndexArray[i + 2]);
- }
- }
-
- // remap 32 bit into multiple 16 bit ones
- std::vector<U16> indices_16;
- std::vector<S64> vertices_remap; // should it be a point map?
- vertices_remap.resize(faceVertices.size(), -1);
- std::vector<LLVolumeFace::VertexData> face_verts;
- min = glm::vec3(FLT_MAX);
- max = glm::vec3(-FLT_MAX);
- for (size_t idx = 0; idx < indices_32.size(); idx++)
- {
- size_t vert_index = indices_32[idx];
- if (vertices_remap[vert_index] == -1)
- {
- // First encounter, add it
- size_t new_vert_idx = face_verts.size();
- vertices_remap[vert_index] = (S64)new_vert_idx;
- face_verts.push_back(faceVertices[vert_index]);
- vert_index = new_vert_idx;
-
- // Update min/max bounds
- const LLVector4a& vec = face_verts[new_vert_idx].getPosition();
- if (new_vert_idx == 0)
- {
- min.x = vec[0];
- min.y = vec[1];
- min.z = vec[2];
- max = min;
- }
- else
- {
- min.x = std::min(min.x, vec[0]);
- min.y = std::min(min.y, vec[1]);
- min.z = std::min(min.z, vec[2]);
- max.x = std::max(max.x, vec[0]);
- max.y = std::max(max.y, vec[1]);
- max.z = std::max(max.z, vec[2]);
- }
- }
- else
- {
- // already in vector, get position
- vert_index = (size_t)vertices_remap[vert_index];
- }
- indices_16.push_back((U16)vert_index);
-
- if (indices_16.size() % 3 == 0 && face_verts.size() >= 65532)
- {
- LLVolumeFace face;
- face.fillFromLegacyData(face_verts, indices_16);
- face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0);
- face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0);
- pModel->getVolumeFaces().push_back(face);
- pModel->getMaterialList().push_back(materialName);
-
- std::fill(vertices_remap.begin(), vertices_remap.end(), -1);
- indices_16.clear();
- face_verts.clear();
-
- min = glm::vec3(FLT_MAX);
- max = glm::vec3(-FLT_MAX);
- }
- }
- if (indices_16.size() > 0 && face_verts.size() > 0)
- {
- LLVolumeFace face;
- face.fillFromLegacyData(face_verts, indices_16);
- face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0);
- face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0);
- pModel->getVolumeFaces().push_back(face);
- pModel->getMaterialList().push_back(materialName);
- }
- }
- else
- {
- // can use indices directly
- std::vector<U16> indices;
- for (U32 i = 0; i < prim.getIndexCount(); i += 3)
- {
- // When processing indices, flip winding order if needed
- if (hasNegativeScale)
- {
- // Flip winding order for negative scale
- indices.push_back(prim.mIndexArray[i]);
- indices.push_back(prim.mIndexArray[i + 2]); // Swap these two
- indices.push_back(prim.mIndexArray[i + 1]);
- }
- else
- {
- indices.push_back(prim.mIndexArray[i]);
- indices.push_back(prim.mIndexArray[i + 1]);
- indices.push_back(prim.mIndexArray[i + 2]);
- }
- }
-
- face.fillFromLegacyData(faceVertices, indices);
- face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0);
- face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0);
-
- pModel->getVolumeFaces().push_back(face);
- pModel->getMaterialList().push_back(materialName);
- }
- }
-
- // Call normalizeVolumeFacesAndWeights to compute proper extents
- pModel->normalizeVolumeFacesAndWeights();
-
- // Fill joint names, bind matrices and remap weight indices
- if (skinIdx >= 0)
- {
- LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx];
- LLMeshSkinInfo& skin_info = pModel->mSkinInfo;
- S32 valid_joints_count = mValidJointsCount[skinIdx];
-
- S32 replacement_index = 0;
- std::vector<S32> gltfindex_to_joitindex_map;
- size_t jointCnt = gltf_skin.mJoints.size();
- gltfindex_to_joitindex_map.resize(jointCnt);
-
- if (valid_joints_count > LL_MAX_JOINTS_PER_MESH_OBJECT)
- {
- std::map<std::string, S32> goup_use_count;
- // Assume that 'Torso' group is always in use since that's what everything else is attached to
- goup_use_count["Torso"] = 1;
- // Note that Collisions and Extra groups are all over the place, might want to include them from the start
- // or add individual when parents are added
-
- // Check which groups are in use
- for (size_t i = 0; i < jointCnt; ++i)
- {
- std::string& joint_name = mJointNames[skinIdx][i];
- if (!joint_name.empty())
- {
- if (gltf_joint_index_use[i] > 0)
- {
- const JointGroups &group = mJointGroups[joint_name];
- // Joint in use, increment it's groups
- goup_use_count[group.mGroup]++;
- goup_use_count[group.mParentGroup]++;
- }
- }
- }
-
- // 1. add joints that are in use directly
- for (size_t i = 0; i < jointCnt; ++i)
- {
- // Process joint name and idnex
- S32 joint = gltf_skin.mJoints[i];
- if (gltf_joint_index_use[i] <= 0)
- {
- // unsupported (-1) joint, drop it
- // unused (0) joint, drop it
- continue;
- }
-
- if (addJointToModelSkin(skin_info, skinIdx, i))
- {
- gltfindex_to_joitindex_map[i] = replacement_index++;
- }
- }
-
- // 2. add joints from groups that this model's joints belong to
- // It's perfectly valid to have more joints than is in use
- // Ex: sandals that make your legs digitigrade despite not skining to
- // knees or the like.
- // Todo: sort and add by usecount
- for (size_t i = 0; i < jointCnt; ++i)
- {
- S32 joint = gltf_skin.mJoints[i];
- if (gltf_joint_index_use[i] != 0)
- {
- // this step needs only joints that have zero uses
- continue;
- }
- if (skin_info.mInvBindMatrix.size() > LL_MAX_JOINTS_PER_MESH_OBJECT)
- {
- break;
- }
- const std::string& legal_name = mJointNames[skinIdx][i];
- std::string group_name = mJointGroups[legal_name].mGroup;
- if (goup_use_count[group_name] > 0)
- {
- if (addJointToModelSkin(skin_info, skinIdx, i))
- {
- gltfindex_to_joitindex_map[i] = replacement_index++;
- }
- }
- }
- }
- else
- {
- // Less than 110, just add every valid joint
- for (size_t i = 0; i < jointCnt; ++i)
- {
- // Process joint name and idnex
- S32 joint = gltf_skin.mJoints[i];
- if (gltf_joint_index_use[i] < 0)
- {
- // unsupported (-1) joint, drop it
- continue;
- }
-
- if (addJointToModelSkin(skin_info, skinIdx, i))
- {
- gltfindex_to_joitindex_map[i] = replacement_index++;
- }
- }
- }
-
- if (skin_info.mInvBindMatrix.size() > LL_MAX_JOINTS_PER_MESH_OBJECT)
- {
- LL_WARNS("GLTF_IMPORT") << "Too many jonts in " << pModel->mLabel
- << " Count: " << (S32)skin_info.mInvBindMatrix.size()
- << " Limit:" << (S32)LL_MAX_JOINTS_PER_MESH_OBJECT << LL_ENDL;
- LLSD args;
- args["Message"] = "ModelTooManyJoint";
- args["MODEL_NAME"] = pModel->mLabel;
- args["JOINT_COUNT"] = (S32)skin_info.mInvBindMatrix.size();
- args["MAX"] = (S32)LL_MAX_JOINTS_PER_MESH_OBJECT;
- mWarningsArray.append(args);
- }
-
- // Remap indices for pModel->mSkinWeights
- for (auto& weights : pModel->mSkinWeights)
- {
- for (auto& weight : weights.second)
- {
- weight.mJointIdx = gltfindex_to_joitindex_map[weight.mJointIdx];
- }
- }
- }
-
- return true;
-}
-
-void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx)
-{
- const LL::GLTF::Skin& skin = mGLTFAsset.mSkins[skin_idx];
-
- LL_INFOS("GLTF_DEBUG") << "populateJointFromSkin: Processing skin " << skin_idx << " with " << skin.mJoints.size() << " joints" << LL_ENDL;
-
- if (skin.mInverseBindMatrices > 0 && skin.mJoints.size() != skin.mInverseBindMatricesData.size())
- {
- LL_INFOS("GLTF_IMPORT") << "Bind matrices count mismatch joints count" << LL_ENDL;
- LLSD args;
- args["Message"] = "InvBindCountMismatch";
- mWarningsArray.append(args);
- }
-
- S32 joint_count = (S32)skin.mJoints.size();
- S32 inverse_count = (S32)skin.mInverseBindMatricesData.size();
- if (mInverseBindMatrices.size() <= skin_idx)
- {
- mInverseBindMatrices.resize(skin_idx + 1);
- mAlternateBindMatrices.resize(skin_idx + 1);
- mJointNames.resize(skin_idx + 1);
- mValidJointsCount.resize(skin_idx + 1, 0);
- }
-
- // fill up joints related data
- joints_data_map_t joints_data;
- joints_name_to_node_map_t names_to_nodes;
- for (S32 i = 0; i < joint_count; i++)
- {
- S32 joint = skin.mJoints[i];
- LL::GLTF::Node jointNode = mGLTFAsset.mNodes[joint];
- JointNodeData& data = joints_data[joint];
- data.mNodeIdx = joint;
- data.mJointListIdx = i;
- data.mGltfRestMatrix = buildGltfRestMatrix(joint, skin);
- data.mGltfMatrix = jointNode.mMatrix;
- data.mOverrideMatrix = glm::mat4(1.f);
-
- if (mJointMap.find(jointNode.mName) != mJointMap.end())
- {
- data.mName = mJointMap[jointNode.mName];
- data.mIsValidViewerJoint = true;
- mValidJointsCount[skin_idx]++;
- }
- else
- {
- data.mName = jointNode.mName;
- data.mIsValidViewerJoint = false;
- }
- names_to_nodes[data.mName] = joint;
-
- for (S32 child : jointNode.mChildren)
- {
- JointNodeData& child_data = joints_data[child];
- child_data.mParentNodeIdx = joint;
- child_data.mIsParentValidViewerJoint = data.mIsValidViewerJoint;
- }
- }
-
- 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)
- {
- buildOverrideMatrix(viewer_data, joints_data, names_to_nodes, ident, ident);
- }
-
- for (S32 i = 0; i < joint_count; i++)
- {
- S32 joint = skin.mJoints[i];
- LL::GLTF::Node jointNode = mGLTFAsset.mNodes[joint];
- std::string legal_name(jointNode.mName);
- bool legal_joint = false;
- if (mJointMap.find(legal_name) != mJointMap.end())
- {
- legal_name = mJointMap[legal_name];
- legal_joint = true;
- mJointNames[skin_idx].push_back(legal_name);
- }
- else
- {
- mJointNames[skin_idx].emplace_back();
- }
-
- // Compute bind matrices
-
- if (!legal_joint)
- {
- // Add placeholder to not break index.
- // Not going to be used by viewer, will be stripped from skin_info.
- LLMatrix4 gltf_transform;
- gltf_transform.setIdentity();
- mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
- }
- else if (inverse_count > i)
- {
- // Transalte existing bind matrix to viewer's skeleton
- // todo: probably should be 'to viewer's overriden skeleton'
- glm::mat4 original_bind_matrix = glm::inverse(skin.mInverseBindMatricesData[i]);
- glm::mat4 rotated_original = coord_system_rotation * original_bind_matrix;
- glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(joints_data, joint, legal_name);
- glm::mat4 tranlated_original = skeleton_transform * rotated_original;
- glm::mat4 final_inverse_bind_matrix = glm::inverse(tranlated_original);
-
- LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(final_inverse_bind_matrix));
- LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " Translated val: " << gltf_transform << LL_ENDL;
- mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
- }
- else
- {
- // If bind matrices aren't present (they are optional in gltf),
- // assume an identy matrix
- // todo: find a model with this, might need to use rotated matrix
- glm::mat4 inv_bind(1.0f);
- glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(joints_data, joint, legal_name);
- inv_bind = glm::inverse(skeleton_transform * inv_bind);
-
- LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(inv_bind));
- LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " Generated val: " << gltf_transform << LL_ENDL;
- mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
- }
-
- // Compute Alternative matrices also known as overrides
- LLMatrix4 original_joint_transform(glm::value_ptr(joints_data[joint].mOverrideMatrix));
-
- // Viewer seems to care only about translation part,
- // but for parity with collada taking original value
- LLMatrix4 newInverse = LLMatrix4(mInverseBindMatrices[skin_idx].back().getF32ptr());
- newInverse.setTranslation(original_joint_transform.getTranslation());
-
- LL_INFOS("GLTF_DEBUG") << "mAlternateBindMatrix name: " << legal_name << " val: " << newInverse << LL_ENDL;
- mAlternateBindMatrices[skin_idx].push_back(LLMatrix4a(newInverse));
-
- if (legal_joint)
- {
- // Might be needed for uploader UI to correctly identify overriden joints
- // but going to be incorrect if multiple skins are present
- mJointList[legal_name] = newInverse;
- mJointsFromNode.push_front(legal_name);
- }
- }
-}
-
-void LLGLTFLoader::populateJointGroups()
-{
- std::string parent;
- for (auto& viewer_data : mViewerJointData)
- {
- buildJointGroup(viewer_data, parent);
- }
-}
-
-
-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];
- jount_group_data.mGroup = viewer_data.mGroup;
- jount_group_data.mParentGroup = parent_group;
-
- for (LLJointData& child_data : viewer_data.mChildren)
- {
- buildJointGroup(child_data, viewer_data.mGroup);
- }
-}
-
-void LLGLTFLoader::buildOverrideMatrix(LLJointData& viewer_data, joints_data_map_t &gltf_nodes, joints_name_to_node_map_t &names_to_nodes, glm::mat4& parent_rest, glm::mat4& parent_support_rest) const
-{
- glm::mat4 new_lefover(1.f);
- glm::mat4 rest(1.f);
- joints_name_to_node_map_t::iterator found_node = names_to_nodes.find(viewer_data.mName);
- if (found_node != names_to_nodes.end())
- {
- S32 gltf_node_idx = found_node->second;
- JointNodeData& node = gltf_nodes[gltf_node_idx];
- node.mIsOverrideValid = true;
- node.mViewerRestMatrix = viewer_data.mRestMatrix;
-
- glm::mat4 gltf_joint_rest_pose = coord_system_rotation * node.mGltfRestMatrix;
- if (mApplyXYRotation)
- {
- gltf_joint_rest_pose = coord_system_rotationxy * gltf_joint_rest_pose;
- }
-
- glm::mat4 translated_joint;
- // Example:
- // Viewer has pelvis->spine1->spine2->torso.
- // gltf example model has pelvis->torso
- // By doing glm::inverse(transalted_rest_spine2) * gltf_rest_torso
- // We get what torso would have looked like if gltf had a spine2
- if (viewer_data.mIsJoint)
- {
- translated_joint = glm::inverse(parent_rest) * gltf_joint_rest_pose;
- }
- else
- {
- translated_joint = glm::inverse(parent_support_rest) * gltf_joint_rest_pose;
- }
-
- glm::vec3 translation_override;
- glm::vec3 skew;
- glm::vec3 scale;
- glm::vec4 perspective;
- glm::quat rotation;
- glm::decompose(translated_joint, scale, rotation, translation_override, skew, perspective);
-
- 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);
-
- rest = parent_rest * override_joint;
- node.mOverrideRestMatrix = rest;
- }
- else
- {
- // No override for this joint
- rest = parent_rest * viewer_data.mJointMatrix;
- }
-
- glm::mat4 support_rest(1.f);
- if (viewer_data.mSupport == LLJointData::SUPPORT_BASE)
- {
- support_rest = rest;
- }
- else
- {
- support_rest = parent_support_rest;
- }
-
- for (LLJointData& child_data : viewer_data.mChildren)
- {
- buildOverrideMatrix(child_data, gltf_nodes, names_to_nodes, rest, support_rest);
- }
-}
-
-glm::mat4 LLGLTFLoader::buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const
-{
- // This is inefficient since we are recalculating some joints multiple times over
- // Todo: cache it?
-
- if (joint_node_index < 0 || joint_node_index >= static_cast<S32>(mGLTFAsset.mNodes.size()))
- {
- return glm::mat4(1.0f);
- }
-
- const auto& node = mGLTFAsset.mNodes[joint_node_index];
-
- // Find and apply parent transform if it exists
- for (size_t i = 0; i < mGLTFAsset.mNodes.size(); ++i)
- {
- const auto& potential_parent = mGLTFAsset.mNodes[i];
- auto it = std::find(potential_parent.mChildren.begin(), potential_parent.mChildren.end(), joint_node_index);
-
- if (it != potential_parent.mChildren.end())
- {
- // Found parent
- if (std::find(gltf_skin.mJoints.begin(), gltf_skin.mJoints.end(), joint_node_index) != gltf_skin.mJoints.end())
- {
- // parent is a joint - recursively combine transform
- // assumes that matrix is already valid
- return buildGltfRestMatrix(static_cast<S32>(i), gltf_skin) * node.mMatrix;
- }
- }
- }
- // Should we return armature or stop earlier?
- return node.mMatrix;
-}
-
-glm::mat4 LLGLTFLoader::buildGltfRestMatrix(S32 joint_node_index, const joints_data_map_t& joint_data) const
-{
- // This is inefficient since we are recalculating some joints multiple times over
- // Todo: cache it?
-
- if (joint_node_index < 0 || joint_node_index >= static_cast<S32>(mGLTFAsset.mNodes.size()))
- {
- return glm::mat4(1.0f);
- }
-
- auto& data = joint_data.at(joint_node_index);
-
- if (data.mParentNodeIdx >=0)
- {
- return buildGltfRestMatrix(data.mParentNodeIdx, joint_data) * data.mGltfMatrix;
- }
- // Should we return armature or stop earlier?
- return data.mGltfMatrix;
-}
-
-// This function computes the transformation matrix needed to convert from GLTF skeleton space
-// to viewer skeleton space for a specific joint
-
-glm::mat4 LLGLTFLoader::computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const
-{
- const JointNodeData& node_data = joints_data_map.at(gltf_node_index);
- if (!node_data.mIsOverrideValid)
- {
- // For now assume they are identical and return an identity (for ease of debuging)
- return glm::mat4(1.0f);
- }
-
- // Get the GLTF joint's rest pose (in GLTF coordinate system)
- const glm::mat4 &gltf_joint_rest_pose = node_data.mGltfRestMatrix;
- glm::mat4 rest_pose = coord_system_rotation * gltf_joint_rest_pose;
-
- LL_INFOS("GLTF_DEBUG") << "rest matrix for joint " << joint_name << ": ";
-
- LLMatrix4 transform(glm::value_ptr(rest_pose));
-
- LL_CONT << transform << LL_ENDL;
-
- // Compute transformation from GLTF space to viewer space
- // This assumes both skeletons are in rest pose initially
- return node_data.mOverrideRestMatrix * glm::inverse(rest_pose);
-}
-
-bool LLGLTFLoader::checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx)
-{
- glm::mat4 gltf_joint_rest = buildGltfRestMatrix(joint_idx, gltf_skin);
- glm::mat4 test_mat = glm::inverse(gltf_joint_rest) * gltf_skin.mInverseBindMatricesData[bind_indx];
- // Normally for shoulders it should be something close to
- // {1,0,0,0;0,-1,0,0;0,0,-1,0;0,0,0,1}
- // rotated one will look like
- // {0,0,0,-1;1,0,0,0;0,-1,0,0;0,0,0,1}
- // Todo: This is a cheap hack,
- // figure out how rotation is supposed to work
- return abs(test_mat[0][0]) < 0.5 && abs(test_mat[1][1]) < 0.5 && abs(test_mat[2][2]) < 0.5;
-}
-
-void LLGLTFLoader::checkForXYrotation(const LL::GLTF::Skin& gltf_skin)
-{
- // HACK: figure out model's rotation from shoulders' matrix.
- // This is wrong on many levels:
- // Too limited (only models that have shoulders),
- // Will not work well with things that emulate 3 hands in some manner
- // Only supports xy 90 degree rotation
- // Todo: figure out how to find skeleton's orientation Correctly
- // when model is rotated at a triangle level
- constexpr char right_shoulder_str[] = "mShoulderRight";
- constexpr char left_shoulder_str[] = "mShoulderLeft";
-
- S32 size = (S32)gltf_skin.mJoints.size();
- S32 joints_found = 0;
- for (S32 i= 0; i < size; i++)
- {
- S32 joint = gltf_skin.mJoints[i];
- auto joint_node = mGLTFAsset.mNodes[joint];
-
- // todo: we are doing this search thing everywhere,
- // just pre-translate every joint
- JointMap::iterator found = mJointMap.find(joint_node.mName);
- if (found == mJointMap.end())
- {
- // unsupported joint
- continue;
- }
- if (found->second == right_shoulder_str || found->second == left_shoulder_str)
- {
- if (checkForXYrotation(gltf_skin, joint, i))
- {
- joints_found++;
- }
- else
- {
- return;
- }
- }
- }
-
- if (joints_found == 2)
- {
- // Both joints in a weird position/rotation, assume rotated model
- mApplyXYRotation = true;
- }
-}
-
-std::string LLGLTFLoader::extractTextureToTempFile(S32 textureIndex, const std::string& texture_type)
-{
- if (textureIndex < 0 || textureIndex >= mGLTFAsset.mTextures.size())
- return "";
-
- S32 sourceIndex = mGLTFAsset.mTextures[textureIndex].mSource;
- if (sourceIndex < 0 || sourceIndex >= mGLTFAsset.mImages.size())
- return "";
-
- LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex];
-
- // Handle URI-based textures
- if (!image.mUri.empty())
- {
- return image.mUri; // Return URI directly
- }
-
- // Handle embedded textures
- if (image.mBufferView >= 0)
- {
- if (image.mBufferView < mGLTFAsset.mBufferViews.size())
- {
- const LL::GLTF::BufferView& buffer_view = mGLTFAsset.mBufferViews[image.mBufferView];
- if (buffer_view.mBuffer < mGLTFAsset.mBuffers.size())
- {
- const LL::GLTF::Buffer& buffer = mGLTFAsset.mBuffers[buffer_view.mBuffer];
-
- if (buffer_view.mByteOffset + buffer_view.mByteLength <= buffer.mData.size())
- {
- // Extract image data
- const U8* data_ptr = &buffer.mData[buffer_view.mByteOffset];
- U32 data_size = buffer_view.mByteLength;
-
- // Determine the file extension
- std::string extension = ".png"; // Default
- if (!image.mMimeType.empty())
- {
- if (image.mMimeType == "image/jpeg")
- extension = ".jpg";
- else if (image.mMimeType == "image/png")
- extension = ".png";
- }
- else if (data_size >= 4)
- {
- if (data_ptr[0] == 0xFF && data_ptr[1] == 0xD8)
- extension = ".jpg"; // JPEG magic bytes
- else if (data_ptr[0] == 0x89 && data_ptr[1] == 0x50 && data_ptr[2] == 0x4E && data_ptr[3] == 0x47)
- extension = ".png"; // PNG magic bytes
- }
-
- // Create a temporary file
- std::string temp_dir = gDirUtilp->getTempDir();
- std::string temp_filename = temp_dir + gDirUtilp->getDirDelimiter() +
- "gltf_embedded_" + texture_type + "_" + std::to_string(sourceIndex) + extension;
-
- // Write the image data to the temporary file
- std::ofstream temp_file(temp_filename, std::ios::binary);
- if (temp_file.is_open())
- {
- temp_file.write(reinterpret_cast<const char*>(data_ptr), data_size);
- temp_file.close();
-
- LL_INFOS("GLTF_IMPORT") << "Extracted embedded " << texture_type << " texture to: " << temp_filename << LL_ENDL;
- return temp_filename;
- }
- else
- {
- LL_WARNS("GLTF_IMPORT") << "Failed to create temporary file for " << texture_type << " texture: " << temp_filename << LL_ENDL;
-
- LLSD args;
- args["Message"] = "FailedToCreateTempFile";
- args["TEXTURE_INDEX"] = sourceIndex;
- args["TEXTURE_TYPE"] = texture_type;
- args["TEMP_FILE"] = temp_filename;
- mWarningsArray.append(args);
- }
- }
- }
- }
- }
-
- return "";
-}
-
-void LLGLTFLoader::notifyUnsupportedExtension(bool unsupported)
-{
- std::vector<std::string> extensions = unsupported ? mGLTFAsset.mUnsupportedExtensions : mGLTFAsset.mIgnoredExtensions;
- if (extensions.size() > 0)
- {
- LLSD args;
- args["Message"] = unsupported ? "UnsupportedExtension" : "IgnoredExtension";
- std::string del;
- std::string ext;
- for (auto& extension : extensions)
- {
- ext += del;
- ext += extension;
- del = ",";
- }
- args["EXT"] = ext;
- mWarningsArray.append(args);
- }
-}
-
diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h
deleted file mode 100644
index 048f7df7f5..0000000000
--- a/indra/newview/gltf/llgltfloader.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/**
- * @file LLGLTFLoader.h
- * @brief LLGLTFLoader class definition
- *
- * $LicenseInfo:firstyear=2022&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2022, 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_LLGLTFLoader_H
-#define LL_LLGLTFLoader_H
-
-#include "tinygltf/tiny_gltf.h"
-
-#include "asset.h"
-
-#include "llglheaders.h"
-#include "lljointdata.h"
-#include "llmodelloader.h"
-
-class LLGLTFLoader : public LLModelLoader
-{
- public:
- typedef std::map<std::string, LLImportMaterial> material_map;
- typedef std::map<std::string, std::string> joint_viewer_parent_map_t;
- typedef std::map<std::string, glm::mat4> joint_viewer_rest_map_t;
- typedef std::map<S32, glm::mat4> joint_node_mat4_map_t;
-
- struct JointNodeData
- {
- JointNodeData()
- : mJointListIdx(-1)
- , mNodeIdx(-1)
- , mParentNodeIdx(-1)
- , mIsValidViewerJoint(false)
- , mIsParentValidViewerJoint(false)
- , mIsOverrideValid(false)
- {
-
- }
- S32 mJointListIdx;
- S32 mNodeIdx;
- S32 mParentNodeIdx;
- glm::mat4 mGltfRestMatrix;
- glm::mat4 mViewerRestMatrix;
- glm::mat4 mOverrideRestMatrix;
- glm::mat4 mGltfMatrix;
- glm::mat4 mOverrideMatrix;
- std::string mName;
- bool mIsValidViewerJoint;
- bool mIsParentValidViewerJoint;
- bool mIsOverrideValid;
- };
- typedef std::map <S32, JointNodeData> joints_data_map_t;
- typedef std::map <std::string, S32> joints_name_to_node_map_t;
-
- LLGLTFLoader(std::string filename,
- S32 lod,
- LLModelLoader::load_callback_t load_cb,
- LLModelLoader::joint_lookup_func_t joint_lookup_func,
- LLModelLoader::texture_load_func_t texture_load_func,
- LLModelLoader::state_callback_t state_cb,
- void * opaque_userdata,
- JointTransformMap & jointTransformMap,
- JointNameSet & jointsFromNodes,
- std::map<std::string, std::string> &jointAliasMap,
- U32 maxJointsPerMesh,
- U32 modelLimit,
- std::vector<LLJointData> viewer_skeleton); //,
- //bool preprocess );
- virtual ~LLGLTFLoader();
-
- virtual bool OpenFile(const std::string &filename);
-
- struct GLTFVertex
- {
- glm::vec3 position;
- glm::vec3 normal;
- glm::vec2 uv0;
- glm::u16vec4 joints;
- glm::vec4 weights;
- };
-
-protected:
- LL::GLTF::Asset mGLTFAsset;
- tinygltf::Model mGltfModel;
- bool mGltfLoaded;
- bool mApplyXYRotation = false;
- U32 mGeneratedModelLimit;
-
- // GLTF isn't aware of viewer's skeleton and uses it's own,
- // so need to take viewer's joints and use them to
- // recalculate iverse bind matrices
- std::vector<LLJointData> mViewerJointData;
-
- // vector of vectors because of a posibility of having more than one skin
- typedef std::vector<LLMeshSkinInfo::matrix_list_t> bind_matrices_t;
- typedef std::vector<std::vector<std::string> > joint_names_t;
- bind_matrices_t mInverseBindMatrices;
- bind_matrices_t mAlternateBindMatrices;
- joint_names_t mJointNames; // empty string when no legal name for a given idx
-
- // what group a joint belongs to.
- // For purpose of stripping unused groups when joints are over limit.
- struct JointGroups
- {
- std::string mGroup;
- std::string mParentGroup;
- };
- typedef std::map<std::string, JointGroups> joint_to_group_map_t;
- joint_to_group_map_t mJointGroups;
-
- // per skin joint count, needs to be tracked for the sake of limits check.
- std::vector<S32> mValidJointsCount;
-
-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 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;
- glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const joints_data_map_t& joint_data) const;
- 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);
-
- std::string extractTextureToTempFile(S32 textureIndex, const std::string& texture_type);
-
- void notifyUnsupportedExtension(bool unsupported);
-
- // bool mPreprocessGLTF;
-
- /* Below inherited from dae loader - unknown if/how useful here
-
- void processElement(gltfElement *element, bool &badElement, GLTF *gltf);
- void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin);
-
- material_map getMaterials(LLModel *model, gltfInstance_geometry *instance_geo, GLTF *gltf);
- LLImportMaterial profileToMaterial(gltfProfile_COMMON *material, GLTF *gltf);
- LLColor4 getGltfColor(gltfElement *element);
-
- gltfElement *getChildFromElement(gltfElement *pElement, std::string const &name);
-
- bool isNodeAJoint(gltfNode *pNode);
- void processJointNode(gltfNode *pNode, std::map<std::string, LLMatrix4> &jointTransforms);
- void extractTranslation(gltfTranslate *pTranslate, LLMatrix4 &transform);
- void extractTranslationViaElement(gltfElement *pTranslateElement, LLMatrix4 &transform);
- void extractTranslationViaSID(gltfElement *pElement, LLMatrix4 &transform);
- void buildJointToNodeMappingFromScene(gltfElement *pRoot);
- void processJointToNodeMapping(gltfNode *pNode);
- void processChildJoints(gltfNode *pParentNode);
-
- bool verifyCount(int expected, int result);
-
- // Verify that a controller matches vertex counts
- bool verifyController(gltfController *pController);
-
- static bool addVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh, LLSD &log_msg);
- static bool createVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh);
-
- static LLModel *loadModelFromGltfMesh(gltfMesh *mesh);
-
- // Loads a mesh breaking it into one or more models as necessary
- // to get around volume face limitations while retaining >8 materials
- //
- bool loadModelsFromGltfMesh(gltfMesh *mesh, std::vector<LLModel *> &models_out, U32 submodel_limit);
-
- static std::string getElementLabel(gltfElement *element);
- static size_t getSuffixPosition(std::string label);
- static std::string getLodlessLabel(gltfElement *element);
-
- static std::string preprocessGLTF(std::string filename);
- */
-
-};
-#endif // LL_LLGLTFLLOADER_H
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index 3cb5e9a0d7..9faead9533 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -317,7 +317,7 @@ void GLTFSceneManager::load(const std::string& filename)
{
std::shared_ptr<Asset> asset = std::make_shared<Asset>();
- if (asset->load(filename, true))
+ if (asset->load(filename))
{
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
asset->updateTransforms();
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 41e954b7fa..716e6cd9e3 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -59,7 +59,7 @@ LLFilePicker LLFilePicker::sInstance;
#define XML_FILTER L"XML files (*.xml)\0*.xml\0"
#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
-#define MODEL_FILTER L"Model files (*.dae, *.gltf, *.glb)\0*.dae;*.gltf;*.glb\0"
+#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
#define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0"
#define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
@@ -217,8 +217,6 @@ bool LLFilePicker::setupFilter(ELoadFilter filter)
break;
case FFLOAD_MODEL:
mOFN.lpstrFilter = MODEL_FILTER \
- COLLADA_FILTER \
- MATERIAL_FILTER \
L"\0";
break;
case FFLOAD_MATERIAL:
@@ -673,8 +671,6 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
case FFLOAD_HDRI:
allowedv->push_back("exr");
case FFLOAD_MODEL:
- allowedv->push_back("gltf");
- allowedv->push_back("glb");
case FFLOAD_COLLADA:
allowedv->push_back("dae");
break;
diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp
index b94c31ec04..5ee93be061 100644
--- a/indra/newview/llfloaterbvhpreview.cpp
+++ b/indra/newview/llfloaterbvhpreview.cpp
@@ -179,7 +179,7 @@ void LLFloaterBvhPreview::setAnimCallbacks()
getChild<LLUICtrl>("ease_out_time")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateEaseOut, this, _1));
}
-std::map <std::string, std::string> LLFloaterBvhPreview::getJointAliases()
+std::map<std::string, std::string, std::less<>> LLFloaterBvhPreview::getJointAliases()
{
LLPointer<LLVOAvatar> av = (LLVOAvatar*)mAnimPreview->getDummyAvatar();
return av->getJointAliases();
@@ -252,7 +252,7 @@ bool LLFloaterBvhPreview::postBuild()
ELoadStatus load_status = E_ST_OK;
S32 line_number = 0;
- std::map<std::string, std::string> joint_alias_map = getJointAliases();
+ auto joint_alias_map = getJointAliases();
loaderp = new LLBVHLoader(file_buffer, load_status, line_number, joint_alias_map);
std::string status = getString(STATUS[load_status]);
diff --git a/indra/newview/llfloaterbvhpreview.h b/indra/newview/llfloaterbvhpreview.h
index ae64521492..bb69ab65ef 100644
--- a/indra/newview/llfloaterbvhpreview.h
+++ b/indra/newview/llfloaterbvhpreview.h
@@ -108,7 +108,7 @@ public:
S32 status, LLExtStat ext_status);
private:
void setAnimCallbacks() ;
- std::map <std::string, std::string> getJointAliases();
+ std::map<std::string, std::string, std::less<>> getJointAliases();
protected:
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 84b9cb18f8..47471edb92 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -64,7 +64,6 @@
#include "llcallbacklist.h"
#include "llviewertexteditor.h"
#include "llviewernetwork.h"
-#include "llmaterialeditor.h"
//static
@@ -620,9 +619,11 @@ void LLFloaterModelPreview::onJointListSelection()
LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
+ LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list");
LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr");
joints_pos->deleteAllItems();
+ joints_scale->deleteAllItems();
LLScrollListItem *selected = joints_list->getFirstSelected();
if (selected)
@@ -756,7 +757,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode);
break;
default:
- LL_ERRS() << "Only supposed to be called to generate models" << LL_ENDL;
+ LL_ERRS() << "Only supposed to be called to generate models, val: " << mode << LL_ENDL;
break;
}
@@ -1487,7 +1488,7 @@ void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)
{
// Populate table
- std::map<std::string, std::string> joint_alias_map;
+ std::map<std::string, std::string, std::less<>> joint_alias_map;
mModelPreview->getJointAliases(joint_alias_map);
S32 conflicts = 0;
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 378f5fdf91..28160177f6 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -137,8 +137,7 @@ LLFloaterComboOptions* LLFloaterComboOptions::showUI(
{
combo_picker->mComboOptions->addSimpleElement(*iter);
}
- // select 'Bulk Upload All' option
- combo_picker->mComboOptions->selectNthItem((S32)options.size() - 1);
+ combo_picker->mComboOptions->selectFirstItem();
combo_picker->openFloater(LLSD(title));
combo_picker->setFocus(true);
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index fc0a3ec58f..49c0006f66 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -30,7 +30,7 @@
#include "llmodelloader.h"
#include "lldaeloader.h"
-#include "gltf/llgltfloader.h"
+#include "llgltfloader.h"
#include "llfloatermodelpreview.h"
#include "llagent.h"
@@ -40,7 +40,6 @@
#include "lldrawable.h"
#include "llface.h"
#include "lliconctrl.h"
-#include "lljointdata.h"
#include "llmatrix4a.h"
#include "llmeshrepository.h"
#include "llmeshoptimizer.h"
@@ -781,7 +780,7 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
mLODFile[lod] = filename;
- std::map<std::string, std::string> joint_alias_map;
+ std::map<std::string, std::string, std::less<>> joint_alias_map;
getJointAliases(joint_alias_map);
LLHandle<LLModelPreview> preview_handle = getHandle();
@@ -811,9 +810,6 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
}
else
{
- LLVOAvatar* av = getPreviewAvatar();
- std::vector<LLJointData> viewer_skeleton;
- av->getJointMatricesAndHierarhy(viewer_skeleton);
mModelLoader = new LLGLTFLoader(
filename,
lod,
@@ -826,8 +822,7 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
mJointsFromNode,
joint_alias_map,
LLSkinningUtil::getMaxJointCount(),
- gSavedSettings.getU32("ImporterModelLimit"),
- viewer_skeleton);
+ gSavedSettings.getU32("ImporterModelLimit"));
}
if (force_disable_slm)
@@ -3095,48 +3090,25 @@ void LLModelPreview::lookupLODModelFiles(S32 lod)
S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS;
std::string lod_filename = mLODFile[LLModel::LOD_HIGH];
+ std::string ext = ".dae";
std::string lod_filename_lower(lod_filename);
LLStringUtil::toLower(lod_filename_lower);
-
- // Check for each supported file extension
- std::vector<std::string> supported_exts = { ".dae", ".gltf", ".glb" };
- std::string found_ext;
- std::string::size_type ext_pos = std::string::npos;
-
- for (const auto& ext : supported_exts)
+ std::string::size_type i = lod_filename_lower.rfind(ext);
+ if (i != std::string::npos)
{
- std::string::size_type i = lod_filename_lower.rfind(ext);
- if (i != std::string::npos)
- {
- ext_pos = i;
- found_ext = ext;
- break;
- }
+ lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext);
}
-
- if (ext_pos != std::string::npos)
+ if (gDirUtilp->fileExists(lod_filename))
{
- // Replace extension with LOD suffix + original extension
- std::string lod_file_to_check = lod_filename;
- lod_file_to_check.replace(ext_pos, found_ext.size(), getLodSuffix(next_lod) + found_ext);
-
- if (gDirUtilp->fileExists(lod_file_to_check))
- {
- LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
- if (fmp)
- {
- fmp->setCtrlLoadFromFile(next_lod);
- }
- loadModel(lod_file_to_check, next_lod);
- }
- else
+ LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+ if (fmp)
{
- lookupLODModelFiles(next_lod);
+ fmp->setCtrlLoadFromFile(next_lod);
}
+ loadModel(lod_filename, next_lod);
}
else
{
- // No recognized extension found, continue with next LOD
lookupLODModelFiles(next_lod);
}
}
diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp
index 47f58afa00..cee43f3cff 100644
--- a/indra/newview/llskinningutil.cpp
+++ b/indra/newview/llskinningutil.cpp
@@ -135,12 +135,6 @@ void LLSkinningUtil::initSkinningMatrixPalette(
initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar);
- if (skin->mInvBindMatrix.size() < count )
- {
- // faulty model? mInvBindMatrix.size() should have matched mJointNames.size()
- return;
- }
-
LLMatrix4a world[LL_CHARACTER_MAX_ANIMATED_JOINTS];
for (S32 j = 0; j < count; ++j)
@@ -360,8 +354,7 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a
{
rig_info_tab[joint_num].setIsRiggedTo(true);
- size_t bind_poses_size = skin->mBindPoseMatrix.size();
- const LLMatrix4a& mat = bind_poses_size > joint_index ? skin->mBindPoseMatrix[joint_index] : LLMatrix4a::identity();
+ const LLMatrix4a& mat = skin->mBindPoseMatrix[joint_index];
LLVector4a pos_joint_space;
mat.affineTransform(pos, pos_joint_space);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index d9a3ec3004..dcba891f9f 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -6306,13 +6306,13 @@ const LLUUID& LLVOAvatar::getID() const
// getJoint()
//-----------------------------------------------------------------------------
// RN: avatar joints are multi-rooted to include screen-based attachments
-LLJoint *LLVOAvatar::getJoint( const std::string &name )
+LLJoint* LLVOAvatar::getJoint(std::string_view name)
{
joint_map_t::iterator iter = mJointMap.find(name);
- LLJoint* jointp = NULL;
+ LLJoint* jointp = nullptr;
- if (iter == mJointMap.end() || iter->second == NULL)
+ if (iter == mJointMap.end() || iter->second == nullptr)
{ //search for joint and cache found joint in lookup table
if (mJointAliasMap.empty())
{
@@ -6329,7 +6329,7 @@ LLJoint *LLVOAvatar::getJoint( const std::string &name )
canonical_name = name;
}
jointp = mRoot->findJoint(canonical_name);
- mJointMap[name] = jointp;
+ mJointMap[std::string(name)] = jointp;
}
else
{ //return cached pointer
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index ab27c5752d..9eb8d3f880 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -202,7 +202,7 @@ public:
void startDefaultMotions();
void dumpAnimationState();
- virtual LLJoint* getJoint(const std::string &name);
+ virtual LLJoint* getJoint(std::string_view name);
LLJoint* getJoint(S32 num);
void initAllJoints();
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 90ff4067f2..ebba9ba291 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -697,17 +697,17 @@ void LLVOAvatarSelf::idleUpdate(LLAgent &agent, const F64 &time)
}
// virtual
-LLJoint *LLVOAvatarSelf::getJoint(const std::string &name)
+LLJoint* LLVOAvatarSelf::getJoint(std::string_view name)
{
std::lock_guard lock(mJointMapMutex);
- LLJoint *jointp = NULL;
+ LLJoint* jointp = nullptr;
jointp = LLVOAvatar::getJoint(name);
if (!jointp && mScreenp)
{
jointp = mScreenp->findJoint(name);
if (jointp)
{
- mJointMap[name] = jointp;
+ mJointMap[std::string(name)] = jointp;
}
}
if (jointp && jointp != mScreenp && jointp != mRoot)
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index f9bea41b1d..f7cd974ab0 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -90,7 +90,7 @@ public:
/*virtual*/ bool hasMotionFromSource(const LLUUID& source_id);
/*virtual*/ void stopMotionFromSource(const LLUUID& source_id);
/*virtual*/ void requestStopMotion(LLMotion* motion);
- /*virtual*/ LLJoint* getJoint(const std::string &name);
+ /*virtual*/ LLJoint* getJoint(std::string_view name);
/*virtual*/ void renderJoints();
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..90223fcda8 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -14,7 +14,7 @@
legacy_header_height="25">
<string name="status_idle"></string>
- <string name="status_parse_error">Error: Model parsing issue - see log for details.</string>
+ <string name="status_parse_error">Error: Dae parsing issue - see log for details.</string>
<string name="status_bind_shape_orientation">Warning: bind shape matrix is not in standard X-forward orientation.</string>
<string name="status_material_mismatch">Error: Material of model is not a subset of reference model.</string>
<string name="status_reading_file">Loading...</string>
@@ -45,7 +45,6 @@
<string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string>
<string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string>
<string name="ModelLoaded">Model [MODEL_NAME] loaded</string>
- <string name="InvBindCountMismatch">Bind matrices count mismatch joints count</string>
<string name="IncompleteTC">Texture coordinates data is not complete.</string>
<string name="PositionNaN">Found NaN while loading position data from DAE-Model, invalid model.</string>
@@ -62,22 +61,6 @@
<string name="ParsingErrorNoScene">Document has no visual_scene</string>
<string name="ParsingErrorPositionInvalidModel">Unable to process mesh without position data. Invalid model.</string>
- <!-- GLTF specific messages -->
- <string name="NoScenesFound">No scenes defined in GLTF file</string>
- <string name="InvalidMeshReference">Node [NODE_NAME] references invalid mesh [MESH_INDEX] (total meshes: [TOTAL_MESHES])</string>
- <string name="InvalidGeometryNonTriangulated">Mesh [MESH_NAME] primitive [PRIMITIVE_INDEX]: Invalid geometry with [INDEX_COUNT] indices (must be triangulated)</string>
- <string name="EmptyVertexArray">Mesh [MESH_NAME] primitive [PRIMITIVE_INDEX]: Empty vertex array</string>
- <string name="ErrorIndexLimit">Unable to process mesh [MESH_NAME] due to 65,534 vertex limit. Vertex count: [VERTEX_COUNT]</string>
- <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="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>
-
<panel
follows="top|left"
height="595"
@@ -1723,6 +1706,7 @@ Analysed:
height="408"/>
<panel
follows="right|bottom"
+ can_resize="false"
height="140"
layout="topleft"
name="right_panel"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 82e77119ab..a51feeb7ab 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -9453,11 +9453,8 @@ Unable to upload texture: &apos;[NAME]&apos;
icon="alertmodal.tga"
name="CannotUploadMaterial"
type="alertmodal">
-Unable to upload material file. The file may be corrupted, in an unsupported format, or contain invalid data. Please check that you're using a valid GLTF/GLB file with proper material definitions.
+There was a problem uploading the file
<tag>fail</tag>
- <usetemplate
- name="okbutton"
- yestext="OK"/>
</notification>
<notification