From c8499b7f01ac3f46f0764ea8195c30a4a2ec27a8 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Tue, 8 Apr 2025 13:51:21 -0400 Subject: GLTF WIP. Still working on getting transforms working proper and need to figure out our indices. --- indra/newview/gltf/llgltfloader.cpp | 431 ++++++++++++++++++++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 indra/newview/gltf/llgltfloader.cpp (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp new file mode 100644 index 0000000000..106c20d4d3 --- /dev/null +++ b/indra/newview/gltf/llgltfloader.cpp @@ -0,0 +1,431 @@ +/** + * @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 +#include + +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 &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); + + mGltfLoaded = mGLTFAsset.load(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(); + */ + + setLoadState(DONE); + + return (mMeshesLoaded); +} + +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 (auto node : mGLTFAsset.mNodes) + { + LLMatrix4 transform; + material_map mats; + auto meshidx = node.mMesh; + + if (meshidx >= 0) + { + LLModel* pModel = new LLModel(volume_params, 0.f); + auto mesh = mGLTFAsset.mMeshes[meshidx]; + + if (populateModelFromMesh(pModel, mesh, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) + { + mModelList.push_back(pModel); + 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 *= transform; + transform = mesh_translation; + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= transform; + transform = mesh_scale; + + + mScene[transform].push_back(LLModelInstance(pModel, node.mName, transform, mats)); + } + else + { + setLoadState(ERROR_MODEL + pModel->getStatus()); + delete (pModel); + return false; + } + } + } + + return true; +} + +bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh &mesh, material_map &mats) +{ + pModel->mLabel = mesh.mName; + pModel->ClearFacesAndMaterials(); + + auto prims = mesh.mPrimitives; + for (auto prim : prims) + { + // 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; + LLVolumeFace::VertexMapData::PointMap point_map; + + std::vector vertices; + std::vector indices; + + LLImportMaterial impMat; + + auto material = mGLTFAsset.mMaterials[prim.mMaterial]; + + impMat.mDiffuseColor = LLColor4::white; + + + for (U32 i = 0; i < prim.getVertexCount(); i++) + { + LLVolumeFace::VertexData vert; + vert.setPosition(prim.mPositions[i]); + vert.setNormal(prim.mNormals[i]); + vert.mTexCoord = prim.mTexCoords0[i]; + vertices.push_back(vert); + } + + for (S32 i = 0; i < prim.mIndexArray.size(); i++) + { + indices.push_back(prim.mIndexArray[i]); + } + + face.fillFromLegacyData(vertices, indices); + + pModel->getVolumeFaces().push_back(face); + pModel->getMaterialList().push_back("mat" + std::to_string(prim.mMaterial)); + mats["mat" + std::to_string(prim.mMaterial)] = impMat; + } + + return true; +} + +bool LLGLTFLoader::parseMaterials() +{ + return true; + /* + 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(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; +} -- cgit v1.2.3 From f6b066073e9a6318dc9ebac4b0137b81e2982f14 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Mon, 28 Apr 2025 03:33:03 -0400 Subject: Cursed stuff! --- indra/newview/gltf/llgltfloader.cpp | 278 ++++++++++++++++++++++++++++++++---- 1 file changed, 247 insertions(+), 31 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 106c20d4d3..ce87a9594f 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -25,6 +25,7 @@ */ #include "llgltfloader.h" +#include "meshoptimizer.h" // Import & define single-header gltf import/export lib #define TINYGLTF_IMPLEMENTATION @@ -152,20 +153,19 @@ bool LLGLTFLoader::parseMeshes() if (populateModelFromMesh(pModel, mesh, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) { mModelList.push_back(pModel); - LLVector3 mesh_scale_vector; - LLVector3 mesh_translation_vector; - pModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + LLVector3 mesh_scale_vector = LLVector3(node.mScale); + LLVector3 mesh_translation_vector = LLVector3(node.mTranslation); LLMatrix4 mesh_translation; mesh_translation.setTranslation(mesh_translation_vector); mesh_translation *= transform; - transform = mesh_translation; - + transform = LLMatrix4((float*)&node.mAssetMatrix[0][0]); + /* LLMatrix4 mesh_scale; mesh_scale.initScale(mesh_scale_vector); mesh_scale *= transform; transform = mesh_scale; - + */ mScene[transform].push_back(LLModelInstance(pModel, node.mName, transform, mats)); } @@ -189,47 +189,263 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & auto prims = mesh.mPrimitives; for (auto prim : prims) { - // 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; - LLVolumeFace::VertexMapData::PointMap point_map; + // Unfortunately, SLM does not support 32 bit indices. Filter out anything that goes beyond 16 bit. + if (prim.getVertexCount() < USHRT_MAX) + { + // 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; + LLVolumeFace::VertexMapData::PointMap point_map; + + std::vector vertices; + std::vector indices; + + LLImportMaterial impMat; + + LL::GLTF::Material* material = nullptr; + + if (prim.mMaterial >= 0) + material = &mGLTFAsset.mMaterials[prim.mMaterial]; + + impMat.mDiffuseColor = LLColor4::white; + + for (U32 i = 0; i < prim.getVertexCount(); i++) + { + GLTFVertex vert; + vert.position = glm::vec3(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2]); + vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[2][i]); + vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], prim.mTexCoords0[i][1]); + vertices.push_back(vert); + } + + for (U32 i = 0; i < prim.getIndexCount(); i++) + { + indices.push_back(prim.mIndexArray[i]); + } + + std::vector faceVertices; + + for (U32 i = 0; i < vertices.size(); i++) + { + LLVolumeFace::VertexData vert; + 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); + } + + face.fillFromLegacyData(faceVertices, indices); + + pModel->getVolumeFaces().push_back(face); + pModel->getMaterialList().push_back("mat" + std::to_string(prim.mMaterial)); + mats["mat" + std::to_string(prim.mMaterial)] = impMat; + } + } + + return true; +} +/* +LLModel::EModelStatus loadFaceFromGLTFModel(LLModel* pModel, const LL::GLTF::Mesh& mesh, material_map& mats, LLSD& log_msg) +{ + LLVolumeFace face; + std::vector verts; + std::vector indices; + + S32 pos_offset = -1; + S32 tc_offset = -1; + S32 norm_offset = -1; + + auto pos_source = mesh.mPrimitives[0].mPositions; + auto tc_source = mesh.mPrimitives[0].mNormals; + auto norm_source = mesh.mPrimitives[0].mTexCoords0; + + S32 idx_stride = 0; + + if (pos_source.size() > USHRT_MAX) + { + LL_WARNS() << "Unable to process mesh due to 16-bit index limits; invalid model; invalid model." << LL_ENDL; + LLSD args; + args["Message"] = "ParsingErrorBadElement"; + log_msg.append(args); + return LLModel::BAD_ELEMENT; + + } + + std::vector idx = mesh.mPrimitives[0].mIndexArray; + + domListOfFloats dummy; + domListOfFloats& v = pos_source ? pos_source->getFloat_array()->getValue() : dummy; + domListOfFloats& tc = tc_source ? tc_source->getFloat_array()->getValue() : dummy; + domListOfFloats& n = norm_source ? norm_source->getFloat_array()->getValue() : dummy; + + if (pos_source.size() == 0) + { + return LLModel::BAD_ELEMENT; + } + + // VFExtents change + face.mExtents[0].set(pos_source[0][0], pos_source[0][1], pos_source[0][2]); + face.mExtents[1].set(pos_source[0][0], pos_source[0][1], pos_source[0][2]); + + LLVolumeFace::VertexMapData::PointMap point_map; + + if (idx_stride <= 0 || (pos_source && pos_offset >= idx_stride) || (tc_source && tc_offset >= idx_stride) || + (norm_source && norm_offset >= idx_stride)) + { + // Looks like these offsets should fit inside idx_stride + // Might be good idea to also check idx.getCount()%idx_stride != 0 + LL_WARNS() << "Invalid pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL; + return LLModel::BAD_ELEMENT; + } + + for (U32 i = 0; i < idx.getCount(); i += idx_stride) + { + LLVolumeFace::VertexData cv; + if (pos_source) + { + cv.setPosition( + LLVector4a((F32)v[idx[i + pos_offset] * 3 + 0], (F32)v[idx[i + pos_offset] * 3 + 1], (F32)v[idx[i + pos_offset] * 3 + 2])); + } - std::vector vertices; - std::vector indices; + if (tc_source) + { + cv.mTexCoord.setVec((F32)tc[idx[i + tc_offset] * 2 + 0], (F32)tc[idx[i + tc_offset] * 2 + 1]); + } - LLImportMaterial impMat; + if (norm_source) + { + cv.setNormal(LLVector4a((F32)n[idx[i + norm_offset] * 3 + 0], + (F32)n[idx[i + norm_offset] * 3 + 1], + (F32)n[idx[i + norm_offset] * 3 + 2])); + } - auto material = mGLTFAsset.mMaterials[prim.mMaterial]; + bool found = false; - impMat.mDiffuseColor = LLColor4::white; + LLVolumeFace::VertexMapData::PointMap::iterator point_iter; + point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); + if (point_iter != point_map.end()) + { + for (U32 j = 0; j < point_iter->second.size(); ++j) + { + // We have a matching loc + // + if ((point_iter->second)[j] == cv) + { + U16 shared_index = (point_iter->second)[j].mIndex; + + // Don't share verts within the same tri, degenerate + // + U32 indx_size = static_cast(indices.size()); + U32 verts_new_tri = indx_size % 3; + if ((verts_new_tri < 1 || indices[indx_size - 1] != shared_index) && + (verts_new_tri < 2 || indices[indx_size - 2] != shared_index)) + { + found = true; + indices.push_back(shared_index); + } + break; + } + } + } - for (U32 i = 0; i < prim.getVertexCount(); i++) + if (!found) { - LLVolumeFace::VertexData vert; - vert.setPosition(prim.mPositions[i]); - vert.setNormal(prim.mNormals[i]); - vert.mTexCoord = prim.mTexCoords0[i]; - vertices.push_back(vert); + // VFExtents change + update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); + verts.push_back(cv); + if (verts.size() >= 65535) + { + // llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL; + return LLModel::VERTEX_NUMBER_OVERFLOW; + } + U16 index = (U16)(verts.size() - 1); + indices.push_back(index); + + LLVolumeFace::VertexMapData d; + d.setPosition(cv.getPosition()); + d.mTexCoord = cv.mTexCoord; + d.setNormal(cv.getNormal()); + d.mIndex = index; + if (point_iter != point_map.end()) + { + point_iter->second.push_back(d); + } + else + { + point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d); + } } - for (S32 i = 0; i < prim.mIndexArray.size(); i++) + if (indices.size() % 3 == 0 && verts.size() >= 65532) { - indices.push_back(prim.mIndexArray[i]); + std::string material; + + if (tri->getMaterial()) + { + material = std::string(tri->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + // ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + // ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + + face = LLVolumeFace(); + // VFExtents change + face.mExtents[0].set((F32)v[0], (F32)v[1], (F32)v[2]); + face.mExtents[1].set((F32)v[0], (F32)v[1], (F32)v[2]); + + verts.clear(); + indices.clear(); + point_map.clear(); } + } - face.fillFromLegacyData(vertices, indices); + if (!verts.empty()) + { + std::string material; - pModel->getVolumeFaces().push_back(face); - pModel->getMaterialList().push_back("mat" + std::to_string(prim.mMaterial)); - mats["mat" + std::to_string(prim.mMaterial)] = impMat; + if (tri->getMaterial()) + { + material = std::string(tri->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + + face_list.rbegin()->fillFromLegacyData(verts, indices); + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + // ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + // ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } } - return true; + return LLModel::NO_ERRORS; } - +*/ bool LLGLTFLoader::parseMaterials() { return true; -- cgit v1.2.3 From 008f89f7e9698028e8beaa42a64428909271ebf3 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 4 May 2025 19:05:34 -0400 Subject: More fixes - still a bit hacky but getting there. --- indra/newview/gltf/llgltfloader.cpp | 98 +++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 27 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index ce87a9594f..bb9cedb262 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -118,6 +118,8 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) return false; } + mTransform.setIdentity(); + mMeshesLoaded = parseMeshes(); if (mMeshesLoaded) uploadMeshes(); @@ -147,33 +149,44 @@ bool LLGLTFLoader::parseMeshes() if (meshidx >= 0) { - LLModel* pModel = new LLModel(volume_params, 0.f); - auto mesh = mGLTFAsset.mMeshes[meshidx]; - - if (populateModelFromMesh(pModel, mesh, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) - { - mModelList.push_back(pModel); - LLVector3 mesh_scale_vector = LLVector3(node.mScale); - LLVector3 mesh_translation_vector = LLVector3(node.mTranslation); - - LLMatrix4 mesh_translation; - mesh_translation.setTranslation(mesh_translation_vector); - mesh_translation *= transform; - transform = LLMatrix4((float*)&node.mAssetMatrix[0][0]); - /* - LLMatrix4 mesh_scale; - mesh_scale.initScale(mesh_scale_vector); - mesh_scale *= transform; - transform = mesh_scale; - */ - - mScene[transform].push_back(LLModelInstance(pModel, node.mName, transform, mats)); - } - else + if (mGLTFAsset.mMeshes.size() > meshidx) { - setLoadState(ERROR_MODEL + pModel->getStatus()); - delete (pModel); - return false; + LLModel* pModel = new LLModel(volume_params, 0.f); + auto mesh = mGLTFAsset.mMeshes[meshidx]; + if (populateModelFromMesh(pModel, mesh, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) + { + mModelList.push_back(pModel); + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(LLVector3(node.mTranslation)); + mesh_translation *= mTransform; + mTransform = mesh_translation; + mTransform.condition(); + + LLMatrix4 mesh_rotation; + mesh_rotation.initRotation(LLQuaternion(node.mRotation[0], node.mRotation[1], node.mRotation[2], node.mRotation[3])); + mesh_rotation *= mTransform; + mTransform = mesh_rotation; + mTransform.condition(); + + LLMatrix4 mesh_scale; + mesh_scale.initScale(LLVector3(glm::abs(node.mScale))); + mesh_scale *= mTransform; + mTransform = mesh_scale; + mTransform.condition(); + + LLVector3 mesh_scale_vector = LLVector3(node.mScale); + LLVector3 mesh_translation_vector = LLVector3(node.mTranslation); + + transform = mTransform; + + mScene[transform].push_back(LLModelInstance(pModel, pModel->mLabel, transform, mats)); + } + else + { + setLoadState(ERROR_MODEL + pModel->getStatus()); + delete (pModel); + return false; + } } } } @@ -243,11 +256,42 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & pModel->getMaterialList().push_back("mat" + std::to_string(prim.mMaterial)); mats["mat" + std::to_string(prim.mMaterial)] = impMat; } + else { + LL_INFOS() << "Unable to process mesh due to 16-bit index limits" << LL_ENDL; + LLSD args; + args["Message"] = "ParsingErrorBadElement"; + mWarningsArray.append(args); + return false; + } } return true; } -/* + +void LLGLTFLoader::processPrimitive(const LL::GLTF::Primitive& primitive, const LL::GLTF::Node& node) +{ + LLMatrix4 translation; + translation.setTranslation(LLVector3(node.mTranslation)); + translation *= mTransform; + mTransform = translation; + mTransform.condition(); + + LLMatrix4 rotation; + rotation = LLMatrix4(LLQuaternion(node.mRotation[0], node.mRotation[1], node.mRotation[2], node.mRotation[3])); + rotation *= mTransform; + mTransform = rotation; + mTransform.condition(); + + LLMatrix4 scale; + scale.initScale(LLVector3(glm::abs(node.mScale))); + scale *= mTransform; + mTransform = scale; + mTransform.condition(); + + if (primitive.mPositions.size()) { + } +} + /* LLModel::EModelStatus loadFaceFromGLTFModel(LLModel* pModel, const LL::GLTF::Mesh& mesh, material_map& mats, LLSD& log_msg) { LLVolumeFace face; -- cgit v1.2.3 From 3a4a2a525fe667250d014ad5d09a84a5c35aedf9 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 4 May 2025 19:22:01 -0400 Subject: Fix normals --- indra/newview/gltf/llgltfloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index bb9cedb262..3f5dbb3f87 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -227,7 +227,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & { GLTFVertex vert; vert.position = glm::vec3(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2]); - vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[2][i]); + vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], prim.mTexCoords0[i][1]); vertices.push_back(vert); } -- cgit v1.2.3 From e87c62940656aa740ddfa20bb2eed7a13cdb59c7 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 4 May 2025 21:06:10 -0400 Subject: Transforms transmigrofied --- indra/newview/gltf/llgltfloader.cpp | 75 ++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 43 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 3f5dbb3f87..3f58f512e5 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -118,7 +118,6 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) return false; } - mTransform.setIdentity(); mMeshesLoaded = parseMeshes(); if (mMeshesLoaded) uploadMeshes(); @@ -141,9 +140,10 @@ bool LLGLTFLoader::parseMeshes() LLVolumeParams volume_params; volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + mTransform.setIdentity(); for (auto node : mGLTFAsset.mNodes) { - LLMatrix4 transform; + LLMatrix4 transformation; material_map mats; auto meshidx = node.mMesh; @@ -156,30 +156,42 @@ bool LLGLTFLoader::parseMeshes() if (populateModelFromMesh(pModel, mesh, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) { mModelList.push_back(pModel); - LLMatrix4 mesh_translation; - mesh_translation.setTranslation(LLVector3(node.mTranslation)); - mesh_translation *= mTransform; - mTransform = mesh_translation; - mTransform.condition(); + LLMatrix4 saved_transform = mTransform; - LLMatrix4 mesh_rotation; - mesh_rotation.initRotation(LLQuaternion(node.mRotation[0], node.mRotation[1], node.mRotation[2], node.mRotation[3])); - mesh_rotation *= mTransform; - mTransform = mesh_rotation; + LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(node.mMatrix)); + mTransform *= gltf_transform; mTransform.condition(); - LLMatrix4 mesh_scale; - mesh_scale.initScale(LLVector3(glm::abs(node.mScale))); - mesh_scale *= mTransform; - mTransform = mesh_scale; - mTransform.condition(); + 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); - LLVector3 mesh_scale_vector = LLVector3(node.mScale); - LLVector3 mesh_translation_vector = LLVector3(node.mTranslation); + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(mesh_translation_vector); + mesh_translation *= transformation; + transformation = mesh_translation; - transform = mTransform; + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= transformation; + transformation = mesh_scale; + + if (transformation.determinant() < 0) + { // negative scales are not supported + LL_INFOS() << "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); + + } - mScene[transform].push_back(LLModelInstance(pModel, pModel->mLabel, transform, mats)); + mScene[transformation].push_back(LLModelInstance(pModel, pModel->mLabel, transformation, mats)); + stretch_extents(pModel, transformation); + mTransform = saved_transform; } else { @@ -268,29 +280,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & return true; } -void LLGLTFLoader::processPrimitive(const LL::GLTF::Primitive& primitive, const LL::GLTF::Node& node) -{ - LLMatrix4 translation; - translation.setTranslation(LLVector3(node.mTranslation)); - translation *= mTransform; - mTransform = translation; - mTransform.condition(); - - LLMatrix4 rotation; - rotation = LLMatrix4(LLQuaternion(node.mRotation[0], node.mRotation[1], node.mRotation[2], node.mRotation[3])); - rotation *= mTransform; - mTransform = rotation; - mTransform.condition(); - - LLMatrix4 scale; - scale.initScale(LLVector3(glm::abs(node.mScale))); - scale *= mTransform; - mTransform = scale; - mTransform.condition(); - - if (primitive.mPositions.size()) { - } -} /* LLModel::EModelStatus loadFaceFromGLTFModel(LLModel* pModel, const LL::GLTF::Mesh& mesh, material_map& mats, LLSD& log_msg) { -- cgit v1.2.3 From dd74b361e35a4e2516fee0b16ca0acf00da58547 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 4 May 2025 23:40:10 -0400 Subject: Fix import rotation and UVs --- indra/newview/gltf/llgltfloader.cpp | 241 +++++------------------------------- 1 file changed, 33 insertions(+), 208 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 3f58f512e5..3c723c7acd 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -153,15 +153,20 @@ bool LLGLTFLoader::parseMeshes() { LLModel* pModel = new LLModel(volume_params, 0.f); auto mesh = mGLTFAsset.mMeshes[meshidx]; - if (populateModelFromMesh(pModel, mesh, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) + if (populateModelFromMesh(pModel, mesh, node, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) { mModelList.push_back(pModel); LLMatrix4 saved_transform = mTransform; - LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(node.mMatrix)); + mTransform *= gltf_transform; mTransform.condition(); + // GLTF is +Y up, SL is +Z up + LLMatrix4 rotation; + rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); + mTransform *= rotation; + transformation = mTransform; // adjust the transformation to compensate for mesh normalization LLVector3 mesh_scale_vector; @@ -206,7 +211,7 @@ bool LLGLTFLoader::parseMeshes() return true; } -bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh &mesh, material_map &mats) +bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& node, material_map& mats) { pModel->mLabel = mesh.mName; pModel->ClearFacesAndMaterials(); @@ -222,10 +227,10 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & // count. Just go ahead and populate faces direct from the GLTF primitives here. -Geenz 2025-04-07 LLVolumeFace face; LLVolumeFace::VertexMapData::PointMap point_map; - + std::vector vertices; std::vector indices; - + LLImportMaterial impMat; LL::GLTF::Material* material = nullptr; @@ -240,7 +245,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & GLTFVertex vert; vert.position = glm::vec3(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2]); vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); - vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], prim.mTexCoords0[i][1]); + vert.uv0 = glm::vec2(prim.mTexCoords0[i][0],-prim.mTexCoords0[i][1]); vertices.push_back(vert); } @@ -250,12 +255,29 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & } std::vector faceVertices; - + glm::vec3 min = glm::vec3(0); + glm::vec3 max = glm::vec3(0); for (U32 i = 0; i < vertices.size(); i++) { LLVolumeFace::VertexData vert; - 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); + + if (vertices[i].position.x > max.x) + max.x = vertices[i].position.x; + if (vertices[i].position.y > max.y) + max.y = vertices[i].position.y; + if (vertices[i].position.z > max.z) + max.z = vertices[i].position.z; + + + if (vertices[i].position.x < min.x) + min.x = vertices[i].position.x; + if (vertices[i].position.y < min.y) + min.y = vertices[i].position.y; + if (vertices[i].position.z < min.z) + min.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); @@ -263,6 +285,8 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & } 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("mat" + std::to_string(prim.mMaterial)); @@ -280,205 +304,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh & return true; } - /* -LLModel::EModelStatus loadFaceFromGLTFModel(LLModel* pModel, const LL::GLTF::Mesh& mesh, material_map& mats, LLSD& log_msg) -{ - LLVolumeFace face; - std::vector verts; - std::vector indices; - - S32 pos_offset = -1; - S32 tc_offset = -1; - S32 norm_offset = -1; - - auto pos_source = mesh.mPrimitives[0].mPositions; - auto tc_source = mesh.mPrimitives[0].mNormals; - auto norm_source = mesh.mPrimitives[0].mTexCoords0; - - S32 idx_stride = 0; - - if (pos_source.size() > USHRT_MAX) - { - LL_WARNS() << "Unable to process mesh due to 16-bit index limits; invalid model; invalid model." << LL_ENDL; - LLSD args; - args["Message"] = "ParsingErrorBadElement"; - log_msg.append(args); - return LLModel::BAD_ELEMENT; - - } - - std::vector idx = mesh.mPrimitives[0].mIndexArray; - - domListOfFloats dummy; - domListOfFloats& v = pos_source ? pos_source->getFloat_array()->getValue() : dummy; - domListOfFloats& tc = tc_source ? tc_source->getFloat_array()->getValue() : dummy; - domListOfFloats& n = norm_source ? norm_source->getFloat_array()->getValue() : dummy; - - if (pos_source.size() == 0) - { - return LLModel::BAD_ELEMENT; - } - - // VFExtents change - face.mExtents[0].set(pos_source[0][0], pos_source[0][1], pos_source[0][2]); - face.mExtents[1].set(pos_source[0][0], pos_source[0][1], pos_source[0][2]); - - LLVolumeFace::VertexMapData::PointMap point_map; - - if (idx_stride <= 0 || (pos_source && pos_offset >= idx_stride) || (tc_source && tc_offset >= idx_stride) || - (norm_source && norm_offset >= idx_stride)) - { - // Looks like these offsets should fit inside idx_stride - // Might be good idea to also check idx.getCount()%idx_stride != 0 - LL_WARNS() << "Invalid pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - for (U32 i = 0; i < idx.getCount(); i += idx_stride) - { - LLVolumeFace::VertexData cv; - if (pos_source) - { - cv.setPosition( - LLVector4a((F32)v[idx[i + pos_offset] * 3 + 0], (F32)v[idx[i + pos_offset] * 3 + 1], (F32)v[idx[i + pos_offset] * 3 + 2])); - } - - if (tc_source) - { - cv.mTexCoord.setVec((F32)tc[idx[i + tc_offset] * 2 + 0], (F32)tc[idx[i + tc_offset] * 2 + 1]); - } - - if (norm_source) - { - cv.setNormal(LLVector4a((F32)n[idx[i + norm_offset] * 3 + 0], - (F32)n[idx[i + norm_offset] * 3 + 1], - (F32)n[idx[i + norm_offset] * 3 + 2])); - } - - bool found = false; - - LLVolumeFace::VertexMapData::PointMap::iterator point_iter; - point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); - - if (point_iter != point_map.end()) - { - for (U32 j = 0; j < point_iter->second.size(); ++j) - { - // We have a matching loc - // - if ((point_iter->second)[j] == cv) - { - U16 shared_index = (point_iter->second)[j].mIndex; - - // Don't share verts within the same tri, degenerate - // - U32 indx_size = static_cast(indices.size()); - U32 verts_new_tri = indx_size % 3; - if ((verts_new_tri < 1 || indices[indx_size - 1] != shared_index) && - (verts_new_tri < 2 || indices[indx_size - 2] != shared_index)) - { - found = true; - indices.push_back(shared_index); - } - break; - } - } - } - - if (!found) - { - // VFExtents change - update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); - verts.push_back(cv); - if (verts.size() >= 65535) - { - // llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL; - return LLModel::VERTEX_NUMBER_OVERFLOW; - } - U16 index = (U16)(verts.size() - 1); - indices.push_back(index); - - LLVolumeFace::VertexMapData d; - d.setPosition(cv.getPosition()); - d.mTexCoord = cv.mTexCoord; - d.setNormal(cv.getNormal()); - d.mIndex = index; - if (point_iter != point_map.end()) - { - point_iter->second.push_back(d); - } - else - { - point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d); - } - } - - if (indices.size() % 3 == 0 && verts.size() >= 65532) - { - std::string material; - - if (tri->getMaterial()) - { - material = std::string(tri->getMaterial()); - } - - materials.push_back(material); - face_list.push_back(face); - face_list.rbegin()->fillFromLegacyData(verts, indices); - LLVolumeFace& new_face = *face_list.rbegin(); - if (!norm_source) - { - // ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!tc_source) - { - // ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; - } - - face = LLVolumeFace(); - // VFExtents change - face.mExtents[0].set((F32)v[0], (F32)v[1], (F32)v[2]); - face.mExtents[1].set((F32)v[0], (F32)v[1], (F32)v[2]); - - verts.clear(); - indices.clear(); - point_map.clear(); - } - } - - if (!verts.empty()) - { - std::string material; - - if (tri->getMaterial()) - { - material = std::string(tri->getMaterial()); - } - - materials.push_back(material); - face_list.push_back(face); - - face_list.rbegin()->fillFromLegacyData(verts, indices); - LLVolumeFace& new_face = *face_list.rbegin(); - if (!norm_source) - { - // ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!tc_source) - { - // ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; - } - } - - return LLModel::NO_ERRORS; -} -*/ bool LLGLTFLoader::parseMaterials() { return true; -- cgit v1.2.3 From 629e56d864726eaa0340be220be8c3e3f500ca32 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Mon, 5 May 2025 05:06:46 -0400 Subject: Make sure transformation matricies are actually setup. Start getting joints in. --- indra/newview/gltf/llgltfloader.cpp | 75 ++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 6 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 3c723c7acd..26257dfc33 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -26,6 +26,7 @@ #include "llgltfloader.h" #include "meshoptimizer.h" +#include // Import & define single-header gltf import/export lib #define TINYGLTF_IMPLEMENTATION @@ -118,7 +119,6 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) return false; } - mMeshesLoaded = parseMeshes(); if (mMeshesLoaded) uploadMeshes(); @@ -141,6 +141,14 @@ bool LLGLTFLoader::parseMeshes() volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); mTransform.setIdentity(); + + // Populate the joints from skins first. + // There's not many skins - and you can pretty easily iterate through the nodes from that. + for (auto skin : mGLTFAsset.mSkins) + { + populateJointFromSkin(skin); + } + for (auto node : mGLTFAsset.mNodes) { LLMatrix4 transformation; @@ -157,10 +165,12 @@ bool LLGLTFLoader::parseMeshes() { mModelList.push_back(pModel); LLMatrix4 saved_transform = mTransform; - LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(node.mMatrix)); - mTransform *= gltf_transform; - mTransform.condition(); + // This will make sure the matrix is always valid from the node. + node.makeMatrixValid(); + + LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(node.mMatrix)); + mTransform = gltf_transform; // GLTF is +Y up, SL is +Z up LLMatrix4 rotation; @@ -168,6 +178,7 @@ bool LLGLTFLoader::parseMeshes() mTransform *= rotation; transformation = mTransform; + // adjust the transformation to compensate for mesh normalization LLVector3 mesh_scale_vector; LLVector3 mesh_translation_vector; @@ -182,7 +193,7 @@ bool LLGLTFLoader::parseMeshes() mesh_scale.initScale(mesh_scale_vector); mesh_scale *= transformation; transformation = mesh_scale; - + if (transformation.determinant() < 0) { // negative scales are not supported LL_INFOS() << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " @@ -210,12 +221,25 @@ bool LLGLTFLoader::parseMeshes() return true; } + +void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) +{ + for (auto joint : skin.mJoints) + { + auto jointNode = mGLTFAsset.mNodes[joint]; + jointNode.makeMatrixValid(); + + mJointList[jointNode.mName] = LLMatrix4(glm::value_ptr(jointNode.mMatrix)); + } +} -bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& node, material_map& mats) +bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats) { pModel->mLabel = mesh.mName; pModel->ClearFacesAndMaterials(); + auto skinIdx = nodeno.mSkin; + auto prims = mesh.mPrimitives; for (auto prim : prims) { @@ -284,6 +308,45 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& faceVertices.push_back(vert); } + if (skinIdx >= 0) + { + auto skin = mGLTFAsset.mSkins[skinIdx]; + + for (int i = 0; i < prim.mJoints.size(); i++) + { + auto accessorIdx = prim.mAttributes["JOINTS_0"]; + LL::GLTF::Accessor::ComponentType componentType = LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE; + if (accessorIdx >= 0) + { + auto accessor = mGLTFAsset.mAccessors[accessorIdx]; + componentType = accessor.mComponentType; + } + glm::u16vec4 joint; + if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE) + { + auto ujoint = glm::unpackUint4x8((U32)(prim.mJoints[i] & 0xFFFFFFFF)); + joint = glm::u16vec4(ujoint.x, ujoint.y, ujoint.z, ujoint.w); + } + else if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_SHORT) + { + joint = glm::unpackUint4x16(prim.mJoints[i]); + } + + // Look up the joint index in the skin + auto jointIndex0 = skin.mJoints[joint.x]; + auto jointIndex1 = skin.mJoints[joint.y]; + auto jointIndex2 = skin.mJoints[joint.z]; + auto jointIndex3 = skin.mJoints[joint.w]; + + // Get the nodes for these joints. + auto node0 = mGLTFAsset.mNodes[jointIndex0]; + auto node1 = mGLTFAsset.mNodes[jointIndex1]; + auto node2 = mGLTFAsset.mNodes[jointIndex2]; + auto node3 = mGLTFAsset.mNodes[jointIndex3]; + + } + } + 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); -- cgit v1.2.3 From 47a5c7a41340a18d90a5a8724762ff32f9ac8afd Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Mon, 5 May 2025 16:12:26 -0400 Subject: Make sure we're pushing to the joints name set as well. --- indra/newview/gltf/llgltfloader.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 26257dfc33..7f3f158fd7 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -230,6 +230,7 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) jointNode.makeMatrixValid(); mJointList[jointNode.mName] = LLMatrix4(glm::value_ptr(jointNode.mMatrix)); + mJointsFromNode.push_front(jointNode.mName); } } -- cgit v1.2.3 From b3d1a1cb1600ca34994050a14cdfe9e474e57d43 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 5 May 2025 20:21:45 +0300 Subject: GLTF import texture upload --- indra/newview/gltf/llgltfloader.cpp | 573 +++++++++++++++++++++++++++++------- 1 file changed, 466 insertions(+), 107 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 7f3f158fd7..15ed5f40ed 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -122,10 +122,8 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) mMeshesLoaded = parseMeshes(); if (mMeshesLoaded) uploadMeshes(); - /* mMaterialsLoaded = parseMaterials(); if (mMaterialsLoaded) uploadMaterials(); - */ setLoadState(DONE); @@ -178,7 +176,7 @@ bool LLGLTFLoader::parseMeshes() mTransform *= rotation; transformation = mTransform; - + // adjust the transformation to compensate for mesh normalization LLVector3 mesh_scale_vector; LLVector3 mesh_translation_vector; @@ -193,7 +191,7 @@ bool LLGLTFLoader::parseMeshes() mesh_scale.initScale(mesh_scale_vector); mesh_scale *= transformation; transformation = mesh_scale; - + if (transformation.determinant() < 0) { // negative scales are not supported LL_INFOS() << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " @@ -233,7 +231,7 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) mJointsFromNode.push_front(jointNode.mName); } } - + bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats) { pModel->mLabel = mesh.mName; @@ -252,25 +250,98 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& // count. Just go ahead and populate faces direct from the GLTF primitives here. -Geenz 2025-04-07 LLVolumeFace face; LLVolumeFace::VertexMapData::PointMap point_map; - + std::vector vertices; std::vector indices; - - LLImportMaterial impMat; - - LL::GLTF::Material* material = nullptr; - if (prim.mMaterial >= 0) - material = &mGLTFAsset.mMaterials[prim.mMaterial]; + LLImportMaterial impMat; + impMat.mDiffuseColor = LLColor4::white; // Default color - impMat.mDiffuseColor = LLColor4::white; + // 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 << LL_ENDL; + + // 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 + { + // Let the model preview know we need to load this texture + mNumOfFetchingTextures++; + LL_INFOS("GLTF_IMPORT") << "Adding texture to load queue: " << 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) + // Create a pseudo filename for the embedded texture + std::string pseudo_filename = "gltf_embedded_texture_" + std::to_string(sourceIndex) + ".png"; + impMat.mDiffuseMapFilename = pseudo_filename; + impMat.mDiffuseMapLabel = material->mName.empty() ? pseudo_filename : material->mName; + + // Mark for loading + mNumOfFetchingTextures++; + LL_INFOS("GLTF_IMPORT") << "Adding embedded texture to load queue: " << pseudo_filename << LL_ENDL; + } + } + } + } + } for (U32 i = 0; i < prim.getVertexCount(); i++) { GLTFVertex vert; vert.position = glm::vec3(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2]); - vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); - vert.uv0 = glm::vec2(prim.mTexCoords0[i][0],-prim.mTexCoords0[i][1]); + vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); + vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], -prim.mTexCoords0[i][1]); vertices.push_back(vert); } @@ -286,19 +357,18 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& { LLVolumeFace::VertexData vert; - if (vertices[i].position.x > max.x) + if (i == 0 || vertices[i].position.x > max.x) max.x = vertices[i].position.x; - if (vertices[i].position.y > max.y) + if (i == 0 || vertices[i].position.y > max.y) max.y = vertices[i].position.y; - if (vertices[i].position.z > max.z) + if (i == 0 || vertices[i].position.z > max.z) max.z = vertices[i].position.z; - - if (vertices[i].position.x < min.x) + if (i == 0 || vertices[i].position.x < min.x) min.x = vertices[i].position.x; - if (vertices[i].position.y < min.y) + if (i == 0 || vertices[i].position.y < min.y) min.y = vertices[i].position.y; - if (vertices[i].position.z < min.z) + if (i == 0 || vertices[i].position.z < min.z) min.z = vertices[i].position.z; LLVector4a position = LLVector4a(vertices[i].position.x, vertices[i].position.y, vertices[i].position.z); @@ -353,8 +423,26 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0); pModel->getVolumeFaces().push_back(face); - pModel->getMaterialList().push_back("mat" + std::to_string(prim.mMaterial)); - mats["mat" + std::to_string(prim.mMaterial)] = impMat; + + // 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); + } + + pModel->getMaterialList().push_back(materialName); + mats[materialName] = impMat; } else { LL_INFOS() << "Unable to process mesh due to 16-bit index limits" << LL_ENDL; @@ -370,56 +458,70 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& bool LLGLTFLoader::parseMaterials() { - return true; - /* if (!mGltfLoaded) return false; // fill local texture data structures mSamplers.clear(); - for (auto in_sampler : mGltfModel.samplers) + for (auto& in_sampler : mGLTFAsset.mSamplers) { 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 + sampler.magFilter = in_sampler.mMagFilter > 0 ? in_sampler.mMagFilter : GL_LINEAR; + sampler.minFilter = in_sampler.mMinFilter > 0 ? in_sampler.mMinFilter : GL_LINEAR; + sampler.wrapS = in_sampler.mWrapS; + sampler.wrapT = in_sampler.mWrapT; + sampler.name = in_sampler.mName; mSamplers.push_back(sampler); } mImages.clear(); - for (auto in_image : mGltfModel.images) + for (auto& in_image : mGLTFAsset.mImages) { 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(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) + image.numChannels = in_image.mComponent; + image.bytesPerChannel = in_image.mBits >> 3; // Convert bits to bytes + image.pixelType = in_image.mPixelType; + image.size = 0; // We'll calculate this below if we have valid dimensions + + // Get dimensions from the texture if available + if (in_image.mTexture && in_image.mTexture->getDiscardLevel() >= 0) { - LL_WARNS("GLTF_IMPORT") << "Unsupported image encoding" << LL_ENDL; - return false; + image.height = in_image.mTexture->getHeight(); + image.width = in_image.mTexture->getWidth(); + // Since we don't have direct access to the raw data, we'll use the dimensions to calculate size + if (image.height > 0 && image.width > 0 && image.numChannels > 0 && image.bytesPerChannel > 0) + { + image.size = static_cast(image.height * image.width * image.numChannels * image.bytesPerChannel); + } + } + else + { + // Fallback to provided dimensions + image.height = in_image.mHeight; + image.width = in_image.mWidth; + if (image.height > 0 && image.width > 0 && image.numChannels > 0 && image.bytesPerChannel > 0) + { + image.size = static_cast(image.height * image.width * image.numChannels * image.bytesPerChannel); + } } - if (image.size != image.height * image.width * image.numChannels * image.bytesPerChannel) + // If we couldn't determine the size, skip this image + if (image.size == 0) { - LL_WARNS("GLTF_IMPORT") << "Image size error" << LL_ENDL; - return false; + LL_WARNS("GLTF_IMPORT") << "Image size could not be determined" << LL_ENDL; + continue; } + // We don't have direct access to the image data, so data pointer remains nullptr + image.data = nullptr; mImages.push_back(image); } mTextures.clear(); - for (auto in_tex : mGltfModel.textures) + for (auto& in_tex : mGLTFAsset.mTextures) { gltf_texture tex; - tex.imageIdx = in_tex.source; - tex.samplerIdx = in_tex.sampler; + tex.imageIdx = in_tex.mSource; + tex.samplerIdx = in_tex.mSampler; tex.imageUuid.setNull(); if (tex.imageIdx >= mImages.size() || tex.samplerIdx >= mSamplers.size()) @@ -432,58 +534,96 @@ bool LLGLTFLoader::parseMaterials() } // parse each material - for (tinygltf::Material gltf_material : mGltfModel.materials) + mMaterials.clear(); + for (const auto& gltf_material : mGLTFAsset.mMaterials) { 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()))) + mat.name = gltf_material.mName; + + // PBR Metallic Roughness properties + mat.hasPBR = true; + + // Base color factor + mat.baseColor = LLColor4( + gltf_material.mPbrMetallicRoughness.mBaseColorFactor[0], + gltf_material.mPbrMetallicRoughness.mBaseColorFactor[1], + gltf_material.mPbrMetallicRoughness.mBaseColorFactor[2], + gltf_material.mPbrMetallicRoughness.mBaseColorFactor[3] + ); + + // Base color texture + mat.hasBaseTex = gltf_material.mPbrMetallicRoughness.mBaseColorTexture.mIndex >= 0; + mat.baseColorTexIdx = gltf_material.mPbrMetallicRoughness.mBaseColorTexture.mIndex; + mat.baseColorTexCoords = gltf_material.mPbrMetallicRoughness.mBaseColorTexture.mTexCoord; + + // Metalness and roughness + mat.metalness = gltf_material.mPbrMetallicRoughness.mMetallicFactor; + mat.roughness = gltf_material.mPbrMetallicRoughness.mRoughnessFactor; + + // Metallic-roughness texture + mat.hasMRTex = gltf_material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex >= 0; + mat.metalRoughTexIdx = gltf_material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex; + mat.metalRoughTexCoords = gltf_material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mTexCoord; + + // Normal texture + mat.normalScale = gltf_material.mNormalTexture.mScale; + mat.hasNormalTex = gltf_material.mNormalTexture.mIndex >= 0; + mat.normalTexIdx = gltf_material.mNormalTexture.mIndex; + mat.normalTexCoords = gltf_material.mNormalTexture.mTexCoord; + + // Occlusion texture + mat.occlusionScale = gltf_material.mOcclusionTexture.mStrength; + mat.hasOcclusionTex = gltf_material.mOcclusionTexture.mIndex >= 0; + mat.occlusionTexIdx = gltf_material.mOcclusionTexture.mIndex; + mat.occlusionTexCoords = gltf_material.mOcclusionTexture.mTexCoord; + + // Emissive texture and color + mat.emissiveColor = LLColor4( + gltf_material.mEmissiveFactor[0], + gltf_material.mEmissiveFactor[1], + gltf_material.mEmissiveFactor[2], + 1.0f + ); + mat.hasEmissiveTex = gltf_material.mEmissiveTexture.mIndex >= 0; + mat.emissiveTexIdx = gltf_material.mEmissiveTexture.mIndex; + mat.emissiveTexCoords = gltf_material.mEmissiveTexture.mTexCoord; + + // Convert AlphaMode enum to string + switch (gltf_material.mAlphaMode) + { + case LL::GLTF::Material::AlphaMode::OPAQUE: + mat.alphaMode = "OPAQUE"; + break; + case LL::GLTF::Material::AlphaMode::MASK: + mat.alphaMode = "MASK"; + break; + case LL::GLTF::Material::AlphaMode::BLEND: + mat.alphaMode = "BLEND"; + break; + default: + mat.alphaMode = "OPAQUE"; + break; + } + + mat.alphaMask = gltf_material.mAlphaCutoff; + + // Verify that all referenced textures are valid + 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))) + // Verify texture coordinate sets are valid (mesh can have up to 3 sets of UV) + if ((mat.hasNormalTex && (mat.normalTexCoords > 2)) || + (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; @@ -493,7 +633,6 @@ bool LLGLTFLoader::parseMaterials() } return true; - */ } // TODO: convert raw vertex buffers to UUIDs @@ -505,18 +644,34 @@ void LLGLTFLoader::uploadMeshes() // 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 + LL_INFOS("GLTF_IMPORT") << "Uploading materials, count: " << mMaterials.size() << LL_ENDL; + + for (gltf_render_material& mat : mMaterials) { - if (mat.hasBaseTex) + LL_INFOS("GLTF_IMPORT") << "Processing material: " << mat.name << LL_ENDL; + + // Process base color texture + if (mat.hasBaseTex && mat.baseColorTexIdx < mTextures.size()) { gltf_texture& gtex = mTextures[mat.baseColorTexIdx]; if (gtex.imageUuid.isNull()) { + LL_INFOS("GLTF_IMPORT") << "Loading base color texture for material " << mat.name << LL_ENDL; gtex.imageUuid = imageBufferToTextureUUID(gtex); + + if (gtex.imageUuid.notNull()) + { + LL_INFOS("GLTF_IMPORT") << "Base color texture loaded, ID: " << gtex.imageUuid.asString() << LL_ENDL; + } + else + { + LL_WARNS("GLTF_IMPORT") << "Failed to load base color texture for material " << mat.name << LL_ENDL; + } } } - if (mat.hasMRTex) + // Process other textures similarly + if (mat.hasMRTex && mat.metalRoughTexIdx < mTextures.size()) { gltf_texture& gtex = mTextures[mat.metalRoughTexIdx]; if (gtex.imageUuid.isNull()) @@ -525,7 +680,7 @@ void LLGLTFLoader::uploadMaterials() } } - if (mat.hasNormalTex) + if (mat.hasNormalTex && mat.normalTexIdx < mTextures.size()) { gltf_texture& gtex = mTextures[mat.normalTexIdx]; if (gtex.imageUuid.isNull()) @@ -534,7 +689,7 @@ void LLGLTFLoader::uploadMaterials() } } - if (mat.hasOcclusionTex) + if (mat.hasOcclusionTex && mat.occlusionTexIdx < mTextures.size()) { gltf_texture& gtex = mTextures[mat.occlusionTexIdx]; if (gtex.imageUuid.isNull()) @@ -543,7 +698,7 @@ void LLGLTFLoader::uploadMaterials() } } - if (mat.hasEmissiveTex) + if (mat.hasEmissiveTex && mat.emissiveTexIdx < mTextures.size()) { gltf_texture& gtex = mTextures[mat.emissiveTexIdx]; if (gtex.imageUuid.isNull()) @@ -552,18 +707,222 @@ void LLGLTFLoader::uploadMaterials() } } } + + // Update material map for all model instances to ensure textures are properly associated + // mScene is a std::map, not an array, so we need to iterate through it correctly + for (auto& scene_entry : mScene) + { + for (LLModelInstance& instance : scene_entry.second) + { + LLModel* model = instance.mModel; + + if (model) + { + for (size_t i = 0; i < model->getMaterialList().size(); ++i) + { + const std::string& matName = model->getMaterialList()[i]; + if (!matName.empty()) + { + // Ensure this material exists in the instance's material map + if (instance.mMaterial.find(matName) == instance.mMaterial.end()) + { + // Find material in our render materials + for (const auto& renderMat : mMaterials) + { + if (renderMat.name == matName) + { + // Create an import material from the render material + LLImportMaterial impMat; + impMat.mDiffuseColor = renderMat.baseColor; + + // Set diffuse texture if available + if (renderMat.hasBaseTex && renderMat.baseColorTexIdx < mTextures.size()) + { + const gltf_texture& gtex = mTextures[renderMat.baseColorTexIdx]; + if (!gtex.imageUuid.isNull()) + { + impMat.setDiffuseMap(gtex.imageUuid); + LL_INFOS("GLTF_IMPORT") << "Setting texture " << gtex.imageUuid.asString() << " for material " << matName << LL_ENDL; + } + } + + // Add material to instance's material map + instance.mMaterial[matName] = impMat; + LL_INFOS("GLTF_IMPORT") << "Added material " << matName << " to instance" << LL_ENDL; + break; + } + } + } + } + } + } + } + } } LLUUID LLGLTFLoader::imageBufferToTextureUUID(const gltf_texture& tex) { - //gltf_image& image = mImages[tex.imageIdx]; - //gltf_sampler& sampler = mSamplers[tex.samplerIdx]; + if (tex.imageIdx >= mImages.size() || tex.samplerIdx >= mSamplers.size()) + { + LL_WARNS("GLTF_IMPORT") << "Invalid texture indices in imageBufferToTextureUUID" << LL_ENDL; + return LLUUID::null; + } - // fill an LLSD container with image+sampler data + gltf_image& image = mImages[tex.imageIdx]; + gltf_sampler& sampler = mSamplers[tex.samplerIdx]; - // upload texture + S32 sourceIndex = tex.imageIdx; + if (sourceIndex < 0 || sourceIndex >= mGLTFAsset.mImages.size()) + { + LL_WARNS("GLTF_IMPORT") << "Invalid image index: " << sourceIndex << LL_ENDL; + return LLUUID::null; + } + + LL::GLTF::Image& source_image = mGLTFAsset.mImages[sourceIndex]; + + // If the image already has a texture loaded, use it + if (source_image.mTexture.notNull()) + { + LL_INFOS("GLTF_IMPORT") << "Using already loaded texture ID: " << source_image.mTexture->getID().asString() << LL_ENDL; + return source_image.mTexture->getID(); + } + + // Create an import material to pass to the texture load function + LLImportMaterial material; + + // Try to get the texture filename from the URI + if (!source_image.mUri.empty()) + { + std::string filename = source_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); + } + + material.mDiffuseMapFilename = filename; + material.mDiffuseMapLabel = filename; + } + else if (source_image.mBufferView >= 0) + { + // For embedded textures, create a pseudo-filename + std::string pseudo_filename = "gltf_embedded_texture_" + std::to_string(sourceIndex) + ".png"; + material.mDiffuseMapFilename = pseudo_filename; + material.mDiffuseMapLabel = pseudo_filename; + } + else + { + LL_WARNS("GLTF_IMPORT") << "No URI or buffer data for image" << LL_ENDL; + return LLUUID::null; + } + + // Create LLSD container with image and sampler data for texture upload + LLSD texture_data = LLSD::emptyMap(); + + // Image data + texture_data["width"] = LLSD::Integer(image.width); + texture_data["height"] = LLSD::Integer(image.height); + texture_data["components"] = LLSD::Integer(image.numChannels); + texture_data["bytes_per_component"] = LLSD::Integer(image.bytesPerChannel); + texture_data["pixel_type"] = LLSD::Integer(image.pixelType); + + // Sampler data + texture_data["min_filter"] = LLSD::Integer(sampler.minFilter); + texture_data["mag_filter"] = LLSD::Integer(sampler.magFilter); + texture_data["wrap_s"] = LLSD::Integer(sampler.wrapS); + texture_data["wrap_t"] = LLSD::Integer(sampler.wrapT); + + // Add URI for reference + if (!source_image.mUri.empty()) + { + texture_data["uri"] = source_image.mUri; + } + + // Check if we have a buffer view for embedded data + if (source_image.mBufferView >= 0) + { + texture_data["has_embedded_data"] = LLSD::Boolean(true); + texture_data["buffer_view"] = LLSD::Integer(source_image.mBufferView); + + // Extract embedded data for texture loading + if (source_image.mBufferView < mGLTFAsset.mBufferViews.size()) + { + const LL::GLTF::BufferView& buffer_view = mGLTFAsset.mBufferViews[source_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()) + { + // Add embedded data reference to texture_data + texture_data["buffer_index"] = LLSD::Integer(buffer_view.mBuffer); + texture_data["byte_offset"] = LLSD::Integer(buffer_view.mByteOffset); + texture_data["byte_length"] = LLSD::Integer(buffer_view.mByteLength); + + LL_INFOS("GLTF_IMPORT") << "Found embedded texture data: offset=" << buffer_view.mByteOffset + << " length=" << buffer_view.mByteLength << LL_ENDL; + } + } + } + } + + // Store the texture metadata in the binding field + std::ostringstream ostr; + LLSDSerialize::toXML(texture_data, ostr); + material.mBinding = ostr.str(); + + LL_INFOS("GLTF_IMPORT") << "Loading texture: " << material.mDiffuseMapFilename << LL_ENDL; - // retrieve UUID + // Flag to track if texture was loaded immediately + bool texture_loaded = false; + + // Call texture loading function with our import material + if (mTextureLoadFunc) + { + // Increment textures to fetch counter BEFORE calling load function + mNumOfFetchingTextures++; + + U32 result = mTextureLoadFunc(material, mOpaqueData); + + // If result is 0, texture is being loaded asynchronously + // If result is >0, texture was loaded immediately + if (result > 0) + { + // Texture was loaded immediately, so decrement counter + mNumOfFetchingTextures--; + texture_loaded = true; + + if (material.getDiffuseMap().notNull()) + { + LL_INFOS("GLTF_IMPORT") << "Texture loaded successfully, ID: " << material.getDiffuseMap().asString() << LL_ENDL; + + // Store the texture in the source image for future reference + if (source_image.mTexture.isNull()) + { + // Create and store a texture object using the UUID + source_image.mTexture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap()); + } + + return material.getDiffuseMap(); + } + } + else if (result == 0) + { + LL_INFOS("GLTF_IMPORT") << "Texture loading queued asynchronously for " << material.mDiffuseMapFilename << LL_ENDL; + } + else // result < 0, indicating error + { + // Texture loading failed, decrement counter + mNumOfFetchingTextures--; + LL_WARNS("GLTF_IMPORT") << "Texture loading failed for " << material.mDiffuseMapFilename << LL_ENDL; + } + } + else + { + LL_WARNS("GLTF_IMPORT") << "No texture loading function available" << LL_ENDL; + } return LLUUID::null; } + -- cgit v1.2.3 From 5e8f871aea8f5a4393ab8f9080c227ea1bf46c21 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 6 May 2025 18:13:24 +0300 Subject: Apply 1/100th scale to the vertex positions --- indra/newview/gltf/llgltfloader.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 15ed5f40ed..08d396d1e2 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -338,8 +338,16 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& for (U32 i = 0; i < prim.getVertexCount(); i++) { + // Apply scaling directly to the vertex positions as they're read from the file + const float DIRECT_SCALE = 0.01f; // 1/100th scale + GLTFVertex vert; - vert.position = glm::vec3(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2]); + vert.position = glm::vec3( + prim.mPositions[i][0] * DIRECT_SCALE, + prim.mPositions[i][1] * DIRECT_SCALE, + prim.mPositions[i][2] * DIRECT_SCALE + ); + vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], -prim.mTexCoords0[i][1]); vertices.push_back(vert); -- cgit v1.2.3 From 2efe514f14a0f0b405599301a14413edfce873ee Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Wed, 14 May 2025 10:33:01 -0400 Subject: Make pulling weights per vertex. --- indra/newview/gltf/llgltfloader.cpp | 88 +++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 32 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 7f3f158fd7..2db803ef3e 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -271,6 +271,32 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& vert.position = glm::vec3(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2]); vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); vert.uv0 = glm::vec2(prim.mTexCoords0[i][0],-prim.mTexCoords0[i][1]); + + if (skinIdx >= 0) + { + auto accessorIdx = prim.mAttributes["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]); + } + + vert.weights = glm::vec4(prim.mWeights[i]); + } + vertices.push_back(vert); } @@ -282,6 +308,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& std::vector faceVertices; glm::vec3 min = glm::vec3(0); glm::vec3 max = glm::vec3(0); + for (U32 i = 0; i < vertices.size(); i++) { LLVolumeFace::VertexData vert; @@ -307,45 +334,42 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& vert.setNormal(normal); vert.mTexCoord = LLVector2(vertices[i].uv0.x, vertices[i].uv0.y); faceVertices.push_back(vert); - } - if (skinIdx >= 0) - { - auto skin = mGLTFAsset.mSkins[skinIdx]; + + // create list of weights that influence this vertex + LLModel::weight_list weight_list; - for (int i = 0; i < prim.mJoints.size(); i++) - { - auto accessorIdx = prim.mAttributes["JOINTS_0"]; - LL::GLTF::Accessor::ComponentType componentType = LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE; - if (accessorIdx >= 0) - { - auto accessor = mGLTFAsset.mAccessors[accessorIdx]; - componentType = accessor.mComponentType; - } - glm::u16vec4 joint; - if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE) + weight_list.push_back(LLModel::JointWeight(vertices[i].joints.x, vertices[i].weights.x)); + weight_list.push_back(LLModel::JointWeight(vertices[i].joints.y, vertices[i].weights.y)); + weight_list.push_back(LLModel::JointWeight(vertices[i].joints.z, vertices[i].weights.z)); + weight_list.push_back(LLModel::JointWeight(vertices[i].joints.w, vertices[i].weights.w)); + + std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); + + + std::vector wght; + F32 total = 0.f; + + for (U32 i = 0; i < llmin((U32)4, (U32)weight_list.size()); ++i) + { // take up to 4 most significant weights + // Ported from the DAE loader - however, GLTF right now only supports up to four weights per vertex. + if (weight_list[i].mWeight > 0.f) { - auto ujoint = glm::unpackUint4x8((U32)(prim.mJoints[i] & 0xFFFFFFFF)); - joint = glm::u16vec4(ujoint.x, ujoint.y, ujoint.z, ujoint.w); + wght.push_back(weight_list[i]); + total += weight_list[i].mWeight; } - else if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_SHORT) + } + + F32 scale = 1.f / total; + if (scale != 1.f) + { // normalize weights + for (U32 i = 0; i < wght.size(); ++i) { - joint = glm::unpackUint4x16(prim.mJoints[i]); + wght[i].mWeight *= scale; } - - // Look up the joint index in the skin - auto jointIndex0 = skin.mJoints[joint.x]; - auto jointIndex1 = skin.mJoints[joint.y]; - auto jointIndex2 = skin.mJoints[joint.z]; - auto jointIndex3 = skin.mJoints[joint.w]; - - // Get the nodes for these joints. - auto node0 = mGLTFAsset.mNodes[jointIndex0]; - auto node1 = mGLTFAsset.mNodes[jointIndex1]; - auto node2 = mGLTFAsset.mNodes[jointIndex2]; - auto node3 = mGLTFAsset.mNodes[jointIndex3]; - } + + pModel->mSkinWeights[LLVector3(vertices[i].position)] = wght; } face.fillFromLegacyData(faceVertices, indices); -- cgit v1.2.3 From b572bf75968005388fab8ad0b3259d417c7f224c Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Wed, 14 May 2025 19:59:20 +0300 Subject: Improve mesh scaling --- indra/newview/gltf/llgltfloader.cpp | 148 ++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 30 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 52147ad5c3..2064fa4dd9 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -147,6 +147,96 @@ bool LLGLTFLoader::parseMeshes() populateJointFromSkin(skin); } + /* Two-pass mesh processing approach: + * 1. First pass: Calculate global bounds across all meshes to determine proper scaling + * and centering for the entire model. This ensures consistent normalization. + * 2. Second pass: Process each mesh node with the calculated global scale factor and + * center offset, ensuring the entire model is properly proportioned and centered. + */ + + // First pass: Calculate global bounds across all meshes in the model + LLVector3 global_min_bounds(FLT_MAX, FLT_MAX, FLT_MAX); + LLVector3 global_max_bounds(-FLT_MAX, -FLT_MAX, -FLT_MAX); + bool has_geometry = false; + + // Create coordinate system rotation matrix - GLTF is Y-up, SL is Z-up + LLMatrix4 coord_system_rotation; + coord_system_rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); + + // Gather bounds from all meshes + for (auto node : mGLTFAsset.mNodes) + { + auto meshidx = node.mMesh; + if (meshidx >= 0 && meshidx < mGLTFAsset.mMeshes.size()) + { + auto mesh = mGLTFAsset.mMeshes[meshidx]; + + // Make node matrix valid for correct transformation + node.makeMatrixValid(); + + // Apply coordinate system rotation to node transform + LLMatrix4 node_matrix(glm::value_ptr(node.mMatrix)); + LLMatrix4 node_transform = coord_system_rotation; + node_transform *= node_matrix; + + // Examine all primitives in this mesh + for (auto prim : mesh.mPrimitives) + { + if (prim.getVertexCount() >= USHRT_MAX) + continue; + + for (U32 i = 0; i < prim.getVertexCount(); i++) + { + // Transform vertex position by node transform with coordinate system rotation + LLVector4 pos(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2], 1.0f); + LLVector4 transformed_pos = pos * node_transform; + + global_min_bounds.mV[VX] = llmin(global_min_bounds.mV[VX], transformed_pos.mV[VX]); + global_min_bounds.mV[VY] = llmin(global_min_bounds.mV[VY], transformed_pos.mV[VY]); + global_min_bounds.mV[VZ] = llmin(global_min_bounds.mV[VZ], transformed_pos.mV[VZ]); + + global_max_bounds.mV[VX] = llmax(global_max_bounds.mV[VX], transformed_pos.mV[VX]); + global_max_bounds.mV[VY] = llmax(global_max_bounds.mV[VY], transformed_pos.mV[VY]); + global_max_bounds.mV[VZ] = llmax(global_max_bounds.mV[VZ], transformed_pos.mV[VZ]); + + has_geometry = true; + } + } + } + } + + // Calculate model dimensions and center point for the entire model + F32 global_scale_factor = 1.0f; + LLVector3 global_center_offset(0.0f, 0.0f, 0.0f); + + if (has_geometry) + { + // Calculate bounding box center - this will be our new origin + LLVector3 center = (global_min_bounds + global_max_bounds) * 0.5f; + global_center_offset = -center; // Offset to move center to origin + + // Calculate diagonal length of the bounding box + LLVector3 dimensions = global_max_bounds - global_min_bounds; + F32 diagonal = dimensions.length(); + + // Always scale to the target size to ensure consistent bounding box + const F32 TARGET_SIZE = 1.0f; // Target diagonal size for models in meters + + if (diagonal > 0.0f) + { + // Calculate scale factor to normalize model size to TARGET_SIZE + global_scale_factor = TARGET_SIZE / diagonal; + + LL_INFOS("GLTF_IMPORT") << "Model dimensions: " << dimensions.mV[VX] << "x" + << dimensions.mV[VY] << "x" << dimensions.mV[VZ] + << ", diagonal: " << diagonal + << ", applying global scale factor: " << global_scale_factor + << ", global centering offset: (" << global_center_offset.mV[VX] << ", " + << global_center_offset.mV[VY] << ", " << global_center_offset.mV[VZ] << ")" << LL_ENDL; + } + } + + // Second pass: Process each node with the global scale and offset for (auto node : mGLTFAsset.mNodes) { LLMatrix4 transformation; @@ -159,7 +249,9 @@ bool LLGLTFLoader::parseMeshes() { LLModel* pModel = new LLModel(volume_params, 0.f); auto mesh = mGLTFAsset.mMeshes[meshidx]; - if (populateModelFromMesh(pModel, mesh, node, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) + if (populateModelFromMesh(pModel, mesh, node, mats, global_scale_factor, global_center_offset) && + (LLModel::NO_ERRORS == pModel->getStatus()) && + validate_model(pModel)) { mModelList.push_back(pModel); LLMatrix4 saved_transform = mTransform; @@ -171,9 +263,7 @@ bool LLGLTFLoader::parseMeshes() mTransform = gltf_transform; // GLTF is +Y up, SL is +Z up - LLMatrix4 rotation; - rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); - mTransform *= rotation; + mTransform *= coord_system_rotation; transformation = mTransform; @@ -200,7 +290,6 @@ bool LLGLTFLoader::parseMeshes() args["Message"] = "NegativeScaleNormTrans"; args["LABEL"] = pModel->mLabel; mWarningsArray.append(args); - } mScene[transformation].push_back(LLModelInstance(pModel, pModel->mLabel, transformation, mats)); @@ -220,19 +309,8 @@ bool LLGLTFLoader::parseMeshes() return true; } -void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) -{ - for (auto joint : skin.mJoints) - { - auto jointNode = mGLTFAsset.mNodes[joint]; - jointNode.makeMatrixValid(); - - mJointList[jointNode.mName] = LLMatrix4(glm::value_ptr(jointNode.mMatrix)); - mJointsFromNode.push_front(jointNode.mName); - } -} - -bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats) +bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats, + const F32 scale_factor, const LLVector3& center_offset) { pModel->mLabel = mesh.mName; pModel->ClearFacesAndMaterials(); @@ -336,20 +414,18 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& } } + // Apply the global scale and center offset to all vertices for (U32 i = 0; i < prim.getVertexCount(); i++) { - // Apply scaling directly to the vertex positions as they're read from the file - const float DIRECT_SCALE = 0.01f; // 1/100th scale - GLTFVertex vert; vert.position = glm::vec3( - prim.mPositions[i][0] * DIRECT_SCALE, - prim.mPositions[i][1] * DIRECT_SCALE, - prim.mPositions[i][2] * DIRECT_SCALE + (prim.mPositions[i][0] + center_offset.mV[VX]) * scale_factor, + (prim.mPositions[i][1] + center_offset.mV[VY]) * scale_factor, + (prim.mPositions[i][2] + center_offset.mV[VZ]) * scale_factor ); - vert.position = glm::vec3(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2]); - vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); - vert.uv0 = glm::vec2(prim.mTexCoords0[i][0],-prim.mTexCoords0[i][1]); + + vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); + vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], -prim.mTexCoords0[i][1]); if (skinIdx >= 0) { @@ -357,7 +433,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& LL::GLTF::Accessor::ComponentType componentType = LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE; if (accessorIdx >= 0) { - auto accessor = mGLTFAsset.mAccessors[accessorIdx]; + auto accessor = mGLTFAsset.mAccessors[accessorIdx]; componentType = accessor.mComponentType; } @@ -412,7 +488,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& vert.mTexCoord = LLVector2(vertices[i].uv0.x, vertices[i].uv0.y); faceVertices.push_back(vert); - + // create list of weights that influence this vertex LLModel::weight_list weight_list; @@ -423,7 +499,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); - + std::vector wght; F32 total = 0.f; @@ -487,6 +563,18 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& return true; } +void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) +{ + for (auto joint : skin.mJoints) + { + auto jointNode = mGLTFAsset.mNodes[joint]; + jointNode.makeMatrixValid(); + + mJointList[jointNode.mName] = LLMatrix4(glm::value_ptr(jointNode.mMatrix)); + mJointsFromNode.push_front(jointNode.mName); + } +} + bool LLGLTFLoader::parseMaterials() { if (!mGltfLoaded) return false; -- cgit v1.2.3 From 3569cc1993dc03637b29fcd77fe5b01b6ea372fb Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Wed, 14 May 2025 22:04:55 +0300 Subject: Use correct model dimensions and bounding box rotation --- indra/newview/gltf/llgltfloader.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 2064fa4dd9..c63459b172 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -174,9 +174,8 @@ bool LLGLTFLoader::parseMeshes() // Make node matrix valid for correct transformation node.makeMatrixValid(); - // Apply coordinate system rotation to node transform LLMatrix4 node_matrix(glm::value_ptr(node.mMatrix)); - LLMatrix4 node_transform = coord_system_rotation; + LLMatrix4 node_transform; node_transform *= node_matrix; // Examine all primitives in this mesh @@ -187,7 +186,7 @@ bool LLGLTFLoader::parseMeshes() for (U32 i = 0; i < prim.getVertexCount(); i++) { - // Transform vertex position by node transform with coordinate system rotation + // Transform vertex position by node transform LLVector4 pos(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2], 1.0f); LLVector4 transformed_pos = pos * node_transform; @@ -215,21 +214,23 @@ bool LLGLTFLoader::parseMeshes() LLVector3 center = (global_min_bounds + global_max_bounds) * 0.5f; global_center_offset = -center; // Offset to move center to origin - // Calculate diagonal length of the bounding box + // Calculate dimensions of the bounding box LLVector3 dimensions = global_max_bounds - global_min_bounds; - F32 diagonal = dimensions.length(); + + // Find the maximum dimension rather than the diagonal + F32 max_dimension = llmax(dimensions.mV[VX], llmax(dimensions.mV[VY], dimensions.mV[VZ])); // Always scale to the target size to ensure consistent bounding box - const F32 TARGET_SIZE = 1.0f; // Target diagonal size for models in meters + const F32 TARGET_SIZE = 1.0f; // Target size for maximum dimension in meters - if (diagonal > 0.0f) + if (max_dimension > 0.0f) { - // Calculate scale factor to normalize model size to TARGET_SIZE - global_scale_factor = TARGET_SIZE / diagonal; + // Calculate scale factor to normalize model's maximum dimension to TARGET_SIZE + global_scale_factor = TARGET_SIZE / max_dimension; LL_INFOS("GLTF_IMPORT") << "Model dimensions: " << dimensions.mV[VX] << "x" << dimensions.mV[VY] << "x" << dimensions.mV[VZ] - << ", diagonal: " << diagonal + << ", max dimension: " << max_dimension << ", applying global scale factor: " << global_scale_factor << ", global centering offset: (" << global_center_offset.mV[VX] << ", " << global_center_offset.mV[VY] << ", " << global_center_offset.mV[VZ] << ")" << LL_ENDL; -- cgit v1.2.3 From 47d95f1eae379f16ed0ae320c9bb1a6002186e32 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Fri, 16 May 2025 19:10:23 -0400 Subject: White space. --- indra/newview/gltf/llgltfloader.cpp | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 52147ad5c3..7570c33dcf 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -174,24 +174,19 @@ bool LLGLTFLoader::parseMeshes() LLMatrix4 rotation; rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); mTransform *= rotation; - 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 (transformation.determinant() < 0) { // negative scales are not supported LL_INFOS() << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " @@ -390,21 +385,18 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& for (U32 i = 0; i < vertices.size(); i++) { LLVolumeFace::VertexData vert; - if (i == 0 || vertices[i].position.x > max.x) max.x = vertices[i].position.x; if (i == 0 || vertices[i].position.y > max.y) max.y = vertices[i].position.y; if (i == 0 || vertices[i].position.z > max.z) max.z = vertices[i].position.z; - if (i == 0 || vertices[i].position.x < min.x) min.x = vertices[i].position.x; if (i == 0 || vertices[i].position.y < min.y) min.y = vertices[i].position.y; if (i == 0 || vertices[i].position.z < min.z) min.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); @@ -422,8 +414,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& weight_list.push_back(LLModel::JointWeight(vertices[i].joints.w, vertices[i].weights.w)); std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); - - std::vector wght; F32 total = 0.f; -- cgit v1.2.3 From a9c75d8136f9df6885e525ea8f2b383fe4a22593 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Fri, 16 May 2025 19:12:40 -0400 Subject: More white space. --- indra/newview/gltf/llgltfloader.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 7570c33dcf..d9cee8b2ea 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -403,8 +403,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& vert.setNormal(normal); vert.mTexCoord = LLVector2(vertices[i].uv0.x, vertices[i].uv0.y); faceVertices.push_back(vert); - - // create list of weights that influence this vertex LLModel::weight_list weight_list; -- cgit v1.2.3 From 875a4180803aa6903bb13263a63e02b38552b742 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 15 May 2025 18:28:25 +0300 Subject: #4080 Import GLTF skin data --- indra/newview/gltf/llgltfloader.cpp | 85 ++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 11c1b05ee5..8d109b610c 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -142,7 +142,7 @@ bool LLGLTFLoader::parseMeshes() // Populate the joints from skins first. // There's not many skins - and you can pretty easily iterate through the nodes from that. - for (auto skin : mGLTFAsset.mSkins) + for (auto& skin : mGLTFAsset.mSkins) { populateJointFromSkin(skin); } @@ -164,7 +164,7 @@ bool LLGLTFLoader::parseMeshes() coord_system_rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); // Gather bounds from all meshes - for (auto node : mGLTFAsset.mNodes) + for (auto &node : mGLTFAsset.mNodes) { auto meshidx = node.mMesh; if (meshidx >= 0 && meshidx < mGLTFAsset.mMeshes.size()) @@ -238,7 +238,7 @@ bool LLGLTFLoader::parseMeshes() } // Second pass: Process each node with the global scale and offset - for (auto node : mGLTFAsset.mNodes) + for (auto &node : mGLTFAsset.mNodes) { LLMatrix4 transformation; material_map mats; @@ -281,7 +281,7 @@ bool LLGLTFLoader::parseMeshes() transformation = mesh_scale; if (transformation.determinant() < 0) { // negative scales are not supported - LL_INFOS() << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " + LL_INFOS("GLTF") << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " << pModel->mLabel << LL_ENDL; LLSD args; args["Message"] = "NegativeScaleNormTrans"; @@ -292,6 +292,60 @@ bool LLGLTFLoader::parseMeshes() mScene[transformation].push_back(LLModelInstance(pModel, pModel->mLabel, transformation, mats)); stretch_extents(pModel, transformation); mTransform = saved_transform; + + S32 skin_index = node.mSkin; + if (skin_index >= 0 && mGLTFAsset.mSkins.size() > skin_index) + { + LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skin_index]; + LLMeshSkinInfo& skin_info = pModel->mSkinInfo; + + size_t jointCnt = gltf_skin.mJoints.size(); + if (gltf_skin.mInverseBindMatrices >= 0 && jointCnt != gltf_skin.mInverseBindMatricesData.size()) + { + LL_INFOS("GLTF") << "Bind matrices count mismatch joints count" << LL_ENDL; + LLSD args; + args["Message"] = "InvBindCountMismatch"; + mWarningsArray.append(args); + } + + for (size_t i = 0; i < jointCnt; ++i) + { + // Process joint name and idnex + S32 joint = gltf_skin.mJoints[i]; + LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; + jointNode.makeMatrixValid(); + + std::string legal_name(jointNode.mName); + if (mJointMap.find(legal_name) != mJointMap.end()) + { + legal_name = mJointMap[legal_name]; + } + skin_info.mJointNames.push_back(legal_name); + skin_info.mJointNums.push_back(-1); + + if (i < gltf_skin.mInverseBindMatricesData.size()) + { + // Process bind matrix + LL::GLTF::mat4 gltf_mat = gltf_skin.mInverseBindMatricesData[i]; + LLMatrix4 gltf_transform(glm::value_ptr(gltf_mat)); + skin_info.mInvBindMatrix.push_back(LLMatrix4a(gltf_transform)); + + LL_DEBUGS("GLTF") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL; + + // Translate based of mJointList + gltf_transform.setTranslation(mJointList[legal_name].getTranslation()); + skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(gltf_transform)); + } + } + + // "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. + LLMatrix4 bind_shape; + bind_shape.setIdentity(); + skin_info.mBindShapeMatrix.loadu(bind_shape); + } } else { @@ -560,10 +614,29 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) for (auto joint : skin.mJoints) { auto jointNode = mGLTFAsset.mNodes[joint]; + + std::string legal_name(jointNode.mName); + if (mJointMap.find(legal_name) != mJointMap.end()) + { + legal_name = mJointMap[legal_name]; + } + else + { + LL_INFOS("GLTF") << "Rigged to unrecognized joint name : " + << legal_name << LL_ENDL; + LLSD args; + args["Message"] = "UnrecognizedJoint"; + args["[NAME]"] = legal_name; + mWarningsArray.append(args); + } + jointNode.makeMatrixValid(); - mJointList[jointNode.mName] = LLMatrix4(glm::value_ptr(jointNode.mMatrix)); - mJointsFromNode.push_front(jointNode.mName); + LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(jointNode.mMatrix)); + mJointList[legal_name] = gltf_transform; + mJointsFromNode.push_front(legal_name); + + LL_DEBUGS("GLTF") << "mJointList name: " << legal_name << " val: " << gltf_transform << LL_ENDL; } } -- cgit v1.2.3 From a9e9c03762f176e0f930d74ccfc96e3c04112b13 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 20 May 2025 20:10:14 +0300 Subject: #4080 Rigged mesh support #2 --- indra/newview/gltf/llgltfloader.cpp | 248 ++++++++++++++++++++++++------------ 1 file changed, 164 insertions(+), 84 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 8d109b610c..a5fb4108c9 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -208,7 +208,8 @@ bool LLGLTFLoader::parseMeshes() F32 global_scale_factor = 1.0f; LLVector3 global_center_offset(0.0f, 0.0f, 0.0f); - if (has_geometry) + if (has_geometry + && mJointList.empty()) // temporary disable offset and scaling for rigged meshes { // Calculate bounding box center - this will be our new origin LLVector3 center = (global_min_bounds + global_max_bounds) * 0.5f; @@ -292,60 +293,6 @@ bool LLGLTFLoader::parseMeshes() mScene[transformation].push_back(LLModelInstance(pModel, pModel->mLabel, transformation, mats)); stretch_extents(pModel, transformation); mTransform = saved_transform; - - S32 skin_index = node.mSkin; - if (skin_index >= 0 && mGLTFAsset.mSkins.size() > skin_index) - { - LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skin_index]; - LLMeshSkinInfo& skin_info = pModel->mSkinInfo; - - size_t jointCnt = gltf_skin.mJoints.size(); - if (gltf_skin.mInverseBindMatrices >= 0 && jointCnt != gltf_skin.mInverseBindMatricesData.size()) - { - LL_INFOS("GLTF") << "Bind matrices count mismatch joints count" << LL_ENDL; - LLSD args; - args["Message"] = "InvBindCountMismatch"; - mWarningsArray.append(args); - } - - for (size_t i = 0; i < jointCnt; ++i) - { - // Process joint name and idnex - S32 joint = gltf_skin.mJoints[i]; - LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; - jointNode.makeMatrixValid(); - - std::string legal_name(jointNode.mName); - if (mJointMap.find(legal_name) != mJointMap.end()) - { - legal_name = mJointMap[legal_name]; - } - skin_info.mJointNames.push_back(legal_name); - skin_info.mJointNums.push_back(-1); - - if (i < gltf_skin.mInverseBindMatricesData.size()) - { - // Process bind matrix - LL::GLTF::mat4 gltf_mat = gltf_skin.mInverseBindMatricesData[i]; - LLMatrix4 gltf_transform(glm::value_ptr(gltf_mat)); - skin_info.mInvBindMatrix.push_back(LLMatrix4a(gltf_transform)); - - LL_DEBUGS("GLTF") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL; - - // Translate based of mJointList - gltf_transform.setTranslation(mJointList[legal_name].getTranslation()); - skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(gltf_transform)); - } - } - - // "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. - LLMatrix4 bind_shape; - bind_shape.setIdentity(); - skin_info.mBindShapeMatrix.loadu(bind_shape); - } } else { @@ -366,7 +313,33 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& pModel->mLabel = mesh.mName; pModel->ClearFacesAndMaterials(); - auto skinIdx = nodeno.mSkin; + S32 skinIdx = nodeno.mSkin; + + // 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 gltf_joint_index_use_count; + 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_count.resize(jointCnt); + + S32 replacement_index = 0; + for (size_t i = 0; i < jointCnt; ++i) + { + // Process joint name and idnex + S32 joint = gltf_skin.mJoints[i]; + LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; + jointNode.makeMatrixValid(); + + std::string legal_name(jointNode.mName); + if (mJointMap.find(legal_name) == mJointMap.end()) + { + gltf_joint_index_use_count[i] = -1; // mark as unsupported + } + } + } auto prims = mesh.mPrimitives; for (auto prim : prims) @@ -536,39 +509,70 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& vert.mTexCoord = LLVector2(vertices[i].uv0.x, vertices[i].uv0.y); faceVertices.push_back(vert); - // create list of weights that influence this vertex - LLModel::weight_list weight_list; - - weight_list.push_back(LLModel::JointWeight(vertices[i].joints.x, vertices[i].weights.x)); - weight_list.push_back(LLModel::JointWeight(vertices[i].joints.y, vertices[i].weights.y)); - weight_list.push_back(LLModel::JointWeight(vertices[i].joints.z, vertices[i].weights.z)); - weight_list.push_back(LLModel::JointWeight(vertices[i].joints.w, vertices[i].weights.w)); + 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_count[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_count[vertices[i].joints.x]++; + } + if (gltf_joint_index_use_count[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_count[vertices[i].joints.y]++; + } + if (gltf_joint_index_use_count[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_count[vertices[i].joints.z]++; + } + if (gltf_joint_index_use_count[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_count[vertices[i].joints.w]++; + } - std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); + std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); - std::vector wght; - F32 total = 0.f; + std::vector wght; + F32 total = 0.f; - for (U32 i = 0; i < llmin((U32)4, (U32)weight_list.size()); ++i) - { // take up to 4 most significant weights - // Ported from the DAE loader - however, GLTF right now only supports up to four weights per vertex. - if (weight_list[i].mWeight > 0.f) + for (U32 i = 0; i < llmin((U32)4, (U32)weight_list.size()); ++i) { + // 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[i]); total += weight_list[i].mWeight; } - } - F32 scale = 1.f / total; - if (scale != 1.f) - { // normalize weights - for (U32 i = 0; i < wght.size(); ++i) + if (total != 0.f) + { + F32 scale = 1.f / total; + if (scale != 1.f) + { // normalize weights + for (U32 i = 0; i < wght.size(); ++i) + { + wght[i].mWeight *= scale; + } + } + } + + if (wght.size() > 0) { - wght[i].mWeight *= scale; + pModel->mSkinWeights[LLVector3(vertices[i].position)] = wght; } } - - pModel->mSkinWeights[LLVector3(vertices[i].position)] = wght; } face.fillFromLegacyData(faceVertices, indices); @@ -606,6 +610,85 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& } } + // Fill joint names, bind matrices and prepare to remap weight indices + if (skinIdx >= 0) + { + LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx]; + LLMeshSkinInfo& skin_info = pModel->mSkinInfo; + + size_t jointCnt = gltf_skin.mJoints.size(); + if (gltf_skin.mInverseBindMatrices >= 0 && jointCnt != gltf_skin.mInverseBindMatricesData.size()) + { + LL_INFOS("GLTF") << "Bind matrices count mismatch joints count" << LL_ENDL; + LLSD args; + args["Message"] = "InvBindCountMismatch"; + mWarningsArray.append(args); + } + + std::vector gltfindex_to_joitindex_map; + gltfindex_to_joitindex_map.resize(jointCnt); + + S32 replacement_index = 0; + for (size_t i = 0; i < jointCnt; ++i) + { + // Process joint name and idnex + S32 joint = gltf_skin.mJoints[i]; + if (gltf_joint_index_use_count[i] <= 0) + { + // Unused (0) or unsupported (-1) joint, drop it + continue; + } + LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; + jointNode.makeMatrixValid(); + + std::string legal_name(jointNode.mName); + if (mJointMap.find(legal_name) != mJointMap.end()) + { + legal_name = mJointMap[legal_name]; + } + else + { + llassert(false); // should have been stopped by gltf_joint_index_use_count[i] == -1 + continue; + } + gltfindex_to_joitindex_map[i] = replacement_index++; + + skin_info.mJointNames.push_back(legal_name); + skin_info.mJointNums.push_back(-1); + + if (i < gltf_skin.mInverseBindMatricesData.size()) + { + // Process bind matrix + LL::GLTF::mat4 gltf_mat = gltf_skin.mInverseBindMatricesData[i]; + LLMatrix4 gltf_transform(glm::value_ptr(gltf_mat)); + skin_info.mInvBindMatrix.push_back(LLMatrix4a(gltf_transform)); + + LL_INFOS("GLTF") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL; + + // Translate based of mJointList + gltf_transform.setTranslation(mJointList[legal_name].getTranslation()); // name is supposed to be in mJointList + skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(gltf_transform)); + } + } + + // "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. + LLMatrix4 bind_shape; + bind_shape.setIdentity(); + skin_info.mBindShapeMatrix.loadu(bind_shape); + + // 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; } @@ -622,12 +705,9 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) } else { - LL_INFOS("GLTF") << "Rigged to unrecognized joint name : " - << legal_name << LL_ENDL; - LLSD args; - args["Message"] = "UnrecognizedJoint"; - args["[NAME]"] = legal_name; - mWarningsArray.append(args); + // ignore unrecognized joint + LL_DEBUGS("GLTF") << "Ignoring joing: " << legal_name << LL_ENDL; + continue; } jointNode.makeMatrixValid(); -- cgit v1.2.3 From 30aa14d0b3f4e87580982e8a8f936bad1283e21a Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Wed, 21 May 2025 21:00:06 -0400 Subject: Make loading the asset into VRAM optional. --- indra/newview/gltf/llgltfloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index a5fb4108c9..22c7c5d13e 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -108,7 +108,7 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) std::string filename_lc(filename); LLStringUtil::toLower(filename_lc); - mGltfLoaded = mGLTFAsset.load(filename); + mGltfLoaded = mGLTFAsset.load(filename, false); if (!mGltfLoaded) { -- cgit v1.2.3 From f5d4d3f86224df61bacabf219b6d053bcdebc3ef Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Thu, 22 May 2025 19:53:35 +0300 Subject: #4109 Improve handling of GLTF transform hierarchy --- indra/newview/gltf/llgltfloader.cpp | 44 ++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 22c7c5d13e..871936db95 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -307,6 +307,34 @@ bool LLGLTFLoader::parseMeshes() return true; } +void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) +{ + auto& node = asset.mNodes[node_index]; + + // Start with this node's transform + glm::mat4 node_transform = node.mMatrix; + + // Find parent node and apply its transform if it exists + for (auto& other_node : asset.mNodes) + { + for (auto& child_index : other_node.mChildren) + { + if (child_index == node_index) + { + // Found a parent, recursively get its combined transform + glm::mat4 parent_transform; + computeCombinedNodeTransform(asset, static_cast(&other_node - &asset.mNodes[0]), parent_transform); + + // Apply parent transform to current node transform + node_transform = parent_transform * node_transform; + break; + } + } + } + + combined_transform = node_transform; +} + bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats, const F32 scale_factor, const LLVector3& center_offset) { @@ -438,14 +466,24 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& } } + // Compute combined transform for this node considering parent hierarchy + S32 node_index = static_cast(&nodeno - &mGLTFAsset.mNodes[0]); + glm::mat4 combined_transform; + computeCombinedNodeTransform(mGLTFAsset, node_index, combined_transform); + // Apply the global scale and center offset to all vertices for (U32 i = 0; i < prim.getVertexCount(); i++) { + // Transform vertex position with combined hierarchy transform + glm::vec4 pos(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2], 1.0f); + glm::vec4 transformed_pos = combined_transform * pos; + + // Apply scaling and centering after hierarchy transform GLTFVertex vert; vert.position = glm::vec3( - (prim.mPositions[i][0] + center_offset.mV[VX]) * scale_factor, - (prim.mPositions[i][1] + center_offset.mV[VY]) * scale_factor, - (prim.mPositions[i][2] + center_offset.mV[VZ]) * scale_factor + (transformed_pos.x + center_offset.mV[VX]) * scale_factor, + (transformed_pos.y + center_offset.mV[VY]) * scale_factor, + (transformed_pos.z + center_offset.mV[VZ]) * scale_factor ); vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); -- cgit v1.2.3 From d713be4bbe8c5d35aaeeff7744906f4fc1833643 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Fri, 23 May 2025 01:08:58 +0300 Subject: #4109 Use correct GLTF coordinate system rotation --- indra/newview/gltf/llgltfloader.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 871936db95..cfc55903d5 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -176,6 +176,7 @@ bool LLGLTFLoader::parseMeshes() LLMatrix4 node_matrix(glm::value_ptr(node.mMatrix)); LLMatrix4 node_transform; + node_transform *= coord_system_rotation; // Apply coordinate rotation first node_transform *= node_matrix; // Examine all primitives in this mesh @@ -261,25 +262,24 @@ bool LLGLTFLoader::parseMeshes() // This will make sure the matrix is always valid from the node. node.makeMatrixValid(); - LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(node.mMatrix)); - mTransform = gltf_transform; - - // GLTF is +Y up, SL is +Z up - mTransform *= coord_system_rotation; - + 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 (transformation.determinant() < 0) { // negative scales are not supported LL_INFOS("GLTF") << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " @@ -471,10 +471,16 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& glm::mat4 combined_transform; computeCombinedNodeTransform(mGLTFAsset, node_index, combined_transform); + // Create coordinate system rotation matrix - GLTF is Y-up, SL is Z-up + glm::mat4 coord_system_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + + // Apply coordinate system rotation to the combined transform + combined_transform = coord_system_rotation * combined_transform; + // Apply the global scale and center offset to all vertices for (U32 i = 0; i < prim.getVertexCount(); i++) { - // Transform vertex position with combined hierarchy transform + // Transform vertex position with combined hierarchy transform (including coord rotation) glm::vec4 pos(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2], 1.0f); glm::vec4 transformed_pos = combined_transform * pos; @@ -486,7 +492,11 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& (transformed_pos.z + center_offset.mV[VZ]) * scale_factor ); - vert.normal = glm::vec3(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); + // Also rotate the normal vector + glm::vec4 normal_vec(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2], 0.0f); + glm::vec4 transformed_normal = coord_system_rotation * normal_vec; + vert.normal = glm::normalize(glm::vec3(transformed_normal)); + vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], -prim.mTexCoords0[i][1]); if (skinIdx >= 0) -- cgit v1.2.3 From 71d543ff07dba5a530e5c96891ccb6629e2fa173 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Fri, 23 May 2025 02:07:05 +0300 Subject: #4109 #4080 Apply coordinate rotation to GLTF inverse bind matrices --- indra/newview/gltf/llgltfloader.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index cfc55903d5..f0dcbf9cf4 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -257,10 +257,6 @@ bool LLGLTFLoader::parseMeshes() validate_model(pModel)) { mModelList.push_back(pModel); - LLMatrix4 saved_transform = mTransform; - - // This will make sure the matrix is always valid from the node. - node.makeMatrixValid(); mTransform.setIdentity(); transformation = mTransform; @@ -292,7 +288,6 @@ bool LLGLTFLoader::parseMeshes() mScene[transformation].push_back(LLModelInstance(pModel, pModel->mLabel, transformation, mats)); stretch_extents(pModel, transformation); - mTransform = saved_transform; } else { @@ -708,7 +703,17 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& { // Process bind matrix LL::GLTF::mat4 gltf_mat = gltf_skin.mInverseBindMatricesData[i]; - LLMatrix4 gltf_transform(glm::value_ptr(gltf_mat)); + + // For inverse bind matrices, we need to: + // 1. Get the original bind matrix by inverting + // 2. Apply coordinate rotation to the original + // 3. Invert again to get the rotated inverse bind matrix + glm::mat4 original_bind_matrix = glm::inverse(gltf_mat); + glm::mat4 coord_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + glm::mat4 rotated_original = coord_rotation * original_bind_matrix; + glm::mat4 rotated_inverse_bind_matrix = glm::inverse(rotated_original); + + LLMatrix4 gltf_transform(glm::value_ptr(rotated_inverse_bind_matrix)); skin_info.mInvBindMatrix.push_back(LLMatrix4a(gltf_transform)); LL_INFOS("GLTF") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL; @@ -742,6 +747,9 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) { + // Create coordinate system rotation matrix - GLTF is Y-up, SL is Z-up + glm::mat4 coord_system_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + for (auto joint : skin.mJoints) { auto jointNode = mGLTFAsset.mNodes[joint]; @@ -754,13 +762,17 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) else { // ignore unrecognized joint - LL_DEBUGS("GLTF") << "Ignoring joing: " << legal_name << LL_ENDL; + LL_DEBUGS("GLTF") << "Ignoring joint: " << legal_name << LL_ENDL; continue; } jointNode.makeMatrixValid(); - LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(jointNode.mMatrix)); + // Apply coordinate system rotation to joint transform + glm::mat4 gltf_joint_matrix = jointNode.mMatrix; + glm::mat4 rotated_joint_matrix = coord_system_rotation * gltf_joint_matrix; + + LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(rotated_joint_matrix)); mJointList[legal_name] = gltf_transform; mJointsFromNode.push_front(legal_name); -- cgit v1.2.3 From 8a9f09ea0ff03ead5d5040c45d7c0a56c325ec07 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Fri, 23 May 2025 02:59:47 +0300 Subject: #4109 #4080 Undistort GLTF rigged mesh in Z-up coordinate transformation --- indra/newview/gltf/llgltfloader.cpp | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index f0dcbf9cf4..f028c8cba8 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -716,11 +716,17 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& LLMatrix4 gltf_transform(glm::value_ptr(rotated_inverse_bind_matrix)); skin_info.mInvBindMatrix.push_back(LLMatrix4a(gltf_transform)); - LL_INFOS("GLTF") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL; + LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL; - // Translate based of mJointList - gltf_transform.setTranslation(mJointList[legal_name].getTranslation()); // name is supposed to be in mJointList - skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(gltf_transform)); + // For alternate bind matrix, use the ORIGINAL joint transform (before rotation) + // Get the original joint node and use its matrix directly + S32 joint = gltf_skin.mJoints[i]; + LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; + jointNode.makeMatrixValid(); + LLMatrix4 original_joint_transform(glm::value_ptr(jointNode.mMatrix)); + + LL_INFOS("GLTF_DEBUG") << "mAlternateBindMatrix name: " << legal_name << " val: " << original_joint_transform << LL_ENDL; + skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(original_joint_transform)); } } @@ -750,6 +756,8 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) // Create coordinate system rotation matrix - GLTF is Y-up, SL is Z-up glm::mat4 coord_system_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + LL_INFOS("GLTF_DEBUG") << "populateJointFromSkin: Processing " << skin.mJoints.size() << " joints" << LL_ENDL; + for (auto joint : skin.mJoints) { auto jointNode = mGLTFAsset.mNodes[joint]; @@ -768,15 +776,31 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) jointNode.makeMatrixValid(); - // Apply coordinate system rotation to joint transform + // Debug: Log original joint matrix glm::mat4 gltf_joint_matrix = jointNode.mMatrix; + LL_INFOS("GLTF_DEBUG") << "Joint '" << legal_name << "' original matrix:" << LL_ENDL; + for(int i = 0; i < 4; i++) + { + LL_INFOS("GLTF_DEBUG") << " [" << gltf_joint_matrix[i][0] << ", " << gltf_joint_matrix[i][1] + << ", " << gltf_joint_matrix[i][2] << ", " << gltf_joint_matrix[i][3] << "]" << LL_ENDL; + } + + // Apply coordinate system rotation to joint transform glm::mat4 rotated_joint_matrix = coord_system_rotation * gltf_joint_matrix; + // Debug: Log rotated joint matrix + LL_INFOS("GLTF_DEBUG") << "Joint '" << legal_name << "' rotated matrix:" << LL_ENDL; + for(int i = 0; i < 4; i++) + { + LL_INFOS("GLTF_DEBUG") << " [" << rotated_joint_matrix[i][0] << ", " << rotated_joint_matrix[i][1] + << ", " << rotated_joint_matrix[i][2] << ", " << rotated_joint_matrix[i][3] << "]" << LL_ENDL; + } + LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(rotated_joint_matrix)); mJointList[legal_name] = gltf_transform; mJointsFromNode.push_front(legal_name); - LL_DEBUGS("GLTF") << "mJointList name: " << legal_name << " val: " << gltf_transform << LL_ENDL; + LL_INFOS("GLTF_DEBUG") << "mJointList name: " << legal_name << " val: " << gltf_transform << LL_ENDL; } } -- cgit v1.2.3 From 4faebc08301f57e5191d29010c00e72748a45323 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sat, 24 May 2025 19:45:52 +0300 Subject: #4109 Remove workaround code --- indra/newview/gltf/llgltfloader.cpp | 109 +++--------------------------------- 1 file changed, 9 insertions(+), 100 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index f028c8cba8..c2211838d1 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -147,100 +147,14 @@ bool LLGLTFLoader::parseMeshes() populateJointFromSkin(skin); } - /* Two-pass mesh processing approach: - * 1. First pass: Calculate global bounds across all meshes to determine proper scaling - * and centering for the entire model. This ensures consistent normalization. - * 2. Second pass: Process each mesh node with the calculated global scale factor and - * center offset, ensuring the entire model is properly proportioned and centered. - */ - - // First pass: Calculate global bounds across all meshes in the model - LLVector3 global_min_bounds(FLT_MAX, FLT_MAX, FLT_MAX); - LLVector3 global_max_bounds(-FLT_MAX, -FLT_MAX, -FLT_MAX); - bool has_geometry = false; - - // Create coordinate system rotation matrix - GLTF is Y-up, SL is Z-up - LLMatrix4 coord_system_rotation; - coord_system_rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); - - // Gather bounds from all meshes - for (auto &node : mGLTFAsset.mNodes) - { - auto meshidx = node.mMesh; - if (meshidx >= 0 && meshidx < mGLTFAsset.mMeshes.size()) - { - auto mesh = mGLTFAsset.mMeshes[meshidx]; - - // Make node matrix valid for correct transformation - node.makeMatrixValid(); - - LLMatrix4 node_matrix(glm::value_ptr(node.mMatrix)); - LLMatrix4 node_transform; - node_transform *= coord_system_rotation; // Apply coordinate rotation first - node_transform *= node_matrix; - - // Examine all primitives in this mesh - for (auto prim : mesh.mPrimitives) - { - if (prim.getVertexCount() >= USHRT_MAX) - continue; - - for (U32 i = 0; i < prim.getVertexCount(); i++) - { - // Transform vertex position by node transform - LLVector4 pos(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2], 1.0f); - LLVector4 transformed_pos = pos * node_transform; - - global_min_bounds.mV[VX] = llmin(global_min_bounds.mV[VX], transformed_pos.mV[VX]); - global_min_bounds.mV[VY] = llmin(global_min_bounds.mV[VY], transformed_pos.mV[VY]); - global_min_bounds.mV[VZ] = llmin(global_min_bounds.mV[VZ], transformed_pos.mV[VZ]); - - global_max_bounds.mV[VX] = llmax(global_max_bounds.mV[VX], transformed_pos.mV[VX]); - global_max_bounds.mV[VY] = llmax(global_max_bounds.mV[VY], transformed_pos.mV[VY]); - global_max_bounds.mV[VZ] = llmax(global_max_bounds.mV[VZ], transformed_pos.mV[VZ]); - - has_geometry = true; - } - } - } - } - - // Calculate model dimensions and center point for the entire model - F32 global_scale_factor = 1.0f; - LLVector3 global_center_offset(0.0f, 0.0f, 0.0f); - - if (has_geometry - && mJointList.empty()) // temporary disable offset and scaling for rigged meshes + for (auto& node : mGLTFAsset.mNodes) { - // Calculate bounding box center - this will be our new origin - LLVector3 center = (global_min_bounds + global_max_bounds) * 0.5f; - global_center_offset = -center; // Offset to move center to origin - - // Calculate dimensions of the bounding box - LLVector3 dimensions = global_max_bounds - global_min_bounds; - - // Find the maximum dimension rather than the diagonal - F32 max_dimension = llmax(dimensions.mV[VX], llmax(dimensions.mV[VY], dimensions.mV[VZ])); - - // Always scale to the target size to ensure consistent bounding box - const F32 TARGET_SIZE = 1.0f; // Target size for maximum dimension in meters - - if (max_dimension > 0.0f) - { - // Calculate scale factor to normalize model's maximum dimension to TARGET_SIZE - global_scale_factor = TARGET_SIZE / max_dimension; - - LL_INFOS("GLTF_IMPORT") << "Model dimensions: " << dimensions.mV[VX] << "x" - << dimensions.mV[VY] << "x" << dimensions.mV[VZ] - << ", max dimension: " << max_dimension - << ", applying global scale factor: " << global_scale_factor - << ", global centering offset: (" << global_center_offset.mV[VX] << ", " - << global_center_offset.mV[VY] << ", " << global_center_offset.mV[VZ] << ")" << LL_ENDL; - } + // Make node matrix valid for correct transformation + node.makeMatrixValid(); } - // Second pass: Process each node with the global scale and offset - for (auto &node : mGLTFAsset.mNodes) + // Process each node + for (auto& node : mGLTFAsset.mNodes) { LLMatrix4 transformation; material_map mats; @@ -252,7 +166,7 @@ bool LLGLTFLoader::parseMeshes() { LLModel* pModel = new LLModel(volume_params, 0.f); auto mesh = mGLTFAsset.mMeshes[meshidx]; - if (populateModelFromMesh(pModel, mesh, node, mats, global_scale_factor, global_center_offset) && + if (populateModelFromMesh(pModel, mesh, node, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) { @@ -292,7 +206,7 @@ bool LLGLTFLoader::parseMeshes() else { setLoadState(ERROR_MODEL + pModel->getStatus()); - delete (pModel); + delete pModel; return false; } } @@ -330,8 +244,7 @@ void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S3 combined_transform = node_transform; } -bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats, - const F32 scale_factor, const LLVector3& center_offset) +bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats) { pModel->mLabel = mesh.mName; pModel->ClearFacesAndMaterials(); @@ -481,11 +394,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& // Apply scaling and centering after hierarchy transform GLTFVertex vert; - vert.position = glm::vec3( - (transformed_pos.x + center_offset.mV[VX]) * scale_factor, - (transformed_pos.y + center_offset.mV[VY]) * scale_factor, - (transformed_pos.z + center_offset.mV[VZ]) * scale_factor - ); + vert.position = glm::vec3(transformed_pos.x, transformed_pos.y, transformed_pos.z); // Also rotate the normal vector glm::vec4 normal_vec(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2], 0.0f); -- cgit v1.2.3 From 3eab945d904610d30d1efd6ab8dd25d17822dbc7 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sat, 24 May 2025 20:41:55 +0300 Subject: #4109 Refactor LLGLTFLoader::populateModelFromMesh() --- indra/newview/gltf/llgltfloader.cpp | 56 +++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 31 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index c2211838d1..40dae9a7c3 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -251,6 +251,20 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& S32 skinIdx = nodeno.mSkin; + // Pre-compute coordinate system rotation matrix (GLTF Y-up to SL Z-up) + static const glm::mat4 coord_system_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + + // Compute final combined transform matrix (hierarchy + coordinate rotation) + S32 node_index = static_cast(&nodeno - &mGLTFAsset.mNodes[0]); + glm::mat4 hierarchy_transform; + computeCombinedNodeTransform(mGLTFAsset, node_index, hierarchy_transform); + + // Combine transforms: coordinate rotation applied to hierarchy transform + const glm::mat4 final_transform = coord_system_rotation * hierarchy_transform; + + // 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 gltf_joint_index_use_count; @@ -286,11 +300,9 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& // 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; - LLVolumeFace::VertexMapData::PointMap point_map; - + LLVolumeFace face; std::vector vertices; - std::vector indices; + std::vector indices; LLImportMaterial impMat; impMat.mDiffuseColor = LLColor4::white; // Default color @@ -374,32 +386,19 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& } } - // Compute combined transform for this node considering parent hierarchy - S32 node_index = static_cast(&nodeno - &mGLTFAsset.mNodes[0]); - glm::mat4 combined_transform; - computeCombinedNodeTransform(mGLTFAsset, node_index, combined_transform); - - // Create coordinate system rotation matrix - GLTF is Y-up, SL is Z-up - glm::mat4 coord_system_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - - // Apply coordinate system rotation to the combined transform - combined_transform = coord_system_rotation * combined_transform; - // Apply the global scale and center offset to all vertices for (U32 i = 0; i < prim.getVertexCount(); i++) { - // Transform vertex position with combined hierarchy transform (including coord rotation) + // 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 = combined_transform * pos; + glm::vec4 transformed_pos = final_transform * pos; - // Apply scaling and centering after hierarchy transform GLTFVertex vert; - vert.position = glm::vec3(transformed_pos.x, transformed_pos.y, transformed_pos.z); + vert.position = glm::vec3(transformed_pos); - // Also rotate the normal vector - glm::vec4 normal_vec(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2], 0.0f); - glm::vec4 transformed_normal = coord_system_rotation * normal_vec; - vert.normal = glm::normalize(glm::vec3(transformed_normal)); + // 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); vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], -prim.mTexCoords0[i][1]); @@ -610,19 +609,14 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& if (i < gltf_skin.mInverseBindMatricesData.size()) { - // Process bind matrix + // Use pre-computed coord_system_rotation instead of recreating it LL::GLTF::mat4 gltf_mat = gltf_skin.mInverseBindMatricesData[i]; - // For inverse bind matrices, we need to: - // 1. Get the original bind matrix by inverting - // 2. Apply coordinate rotation to the original - // 3. Invert again to get the rotated inverse bind matrix glm::mat4 original_bind_matrix = glm::inverse(gltf_mat); - glm::mat4 coord_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - glm::mat4 rotated_original = coord_rotation * original_bind_matrix; + glm::mat4 rotated_original = coord_system_rotation * original_bind_matrix; glm::mat4 rotated_inverse_bind_matrix = glm::inverse(rotated_original); - LLMatrix4 gltf_transform(glm::value_ptr(rotated_inverse_bind_matrix)); + LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(rotated_inverse_bind_matrix)); skin_info.mInvBindMatrix.push_back(LLMatrix4a(gltf_transform)); LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL; -- cgit v1.2.3 From e8eac13b7ba238172c7d5d677cff744bff8dff6d Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sat, 24 May 2025 21:10:10 +0300 Subject: #4109 Refactor LLGLTFLoader::populateModelFromMesh() #2 --- indra/newview/gltf/llgltfloader.cpp | 49 +++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 40dae9a7c3..2e8521b597 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -434,25 +434,36 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& indices.push_back(prim.mIndexArray[i]); } + // Check for empty vertex array before processing + if (vertices.empty()) + { + LL_WARNS("GLTF_IMPORT") << "Empty vertex array for primitive" << LL_ENDL; + continue; // Skip this primitive + } + std::vector faceVertices; - glm::vec3 min = glm::vec3(0); - glm::vec3 max = glm::vec3(0); + 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; - if (i == 0 || vertices[i].position.x > max.x) - max.x = vertices[i].position.x; - if (i == 0 || vertices[i].position.y > max.y) - max.y = vertices[i].position.y; - if (i == 0 || vertices[i].position.z > max.z) - max.z = vertices[i].position.z; - if (i == 0 || vertices[i].position.x < min.x) - min.x = vertices[i].position.x; - if (i == 0 || vertices[i].position.y < min.y) - min.y = vertices[i].position.y; - if (i == 0 || vertices[i].position.z < min.z) - min.z = vertices[i].position.z; + + // 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); @@ -499,12 +510,12 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& std::vector wght; F32 total = 0.f; - for (U32 i = 0; i < llmin((U32)4, (U32)weight_list.size()); ++i) + 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[i]); - total += weight_list[i].mWeight; + wght.push_back(weight_list[j]); + total += weight_list[j].mWeight; } if (total != 0.f) @@ -512,9 +523,9 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& F32 scale = 1.f / total; if (scale != 1.f) { // normalize weights - for (U32 i = 0; i < wght.size(); ++i) + for (U32 j = 0; j < wght.size(); ++j) { - wght[i].mWeight *= scale; + wght[j].mWeight *= scale; } } } -- cgit v1.2.3 From 83fa366de9df385daec05c262f88eefd86af6adf Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sun, 25 May 2025 00:53:25 +0300 Subject: #4109 Fix inside-out geometry from negative scale transforms in GLTF loader --- indra/newview/gltf/llgltfloader.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 2e8521b597..a64df86770 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -262,6 +262,9 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& // Combine transforms: coordinate rotation applied to hierarchy transform const glm::mat4 final_transform = coord_system_rotation * hierarchy_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))); @@ -429,9 +432,22 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& vertices.push_back(vert); } - for (U32 i = 0; i < prim.getIndexCount(); i++) + // When processing indices, flip winding order if needed + for (U32 i = 0; i < prim.getIndexCount(); i += 3) { - indices.push_back(prim.mIndexArray[i]); + 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]); + } } // Check for empty vertex array before processing -- cgit v1.2.3 From d6419f729e3997c3d7cd67c7ff4ee38ab835c509 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sun, 25 May 2025 01:21:36 +0300 Subject: #4109 Fix GLTF model extents calculation --- indra/newview/gltf/llgltfloader.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index a64df86770..3312e61595 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -588,6 +588,9 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& } } + // Call normalizeVolumeFaces to compute proper extents + pModel->normalizeVolumeFaces(); + // Fill joint names, bind matrices and prepare to remap weight indices if (skinIdx >= 0) { -- cgit v1.2.3 From b423900792aca2e094fac85f9aeb34b26f9c2109 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sun, 25 May 2025 19:28:24 +0300 Subject: #4105 Fix duplicate GLTF model instances causing upload errors --- indra/newview/gltf/llgltfloader.cpp | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 3312e61595..17f52af6b1 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -153,6 +153,9 @@ bool LLGLTFLoader::parseMeshes() node.makeMatrixValid(); } + // Track how many times each mesh name has been used + std::map mesh_name_counts; + // Process each node for (auto& node : mGLTFAsset.mNodes) { @@ -166,7 +169,17 @@ bool LLGLTFLoader::parseMeshes() { LLModel* pModel = new LLModel(volume_params, 0.f); auto mesh = mGLTFAsset.mMeshes[meshidx]; - if (populateModelFromMesh(pModel, mesh, node, mats) && + + // Get base mesh name and track usage + std::string base_name = mesh.mName; + if (base_name.empty()) + { + base_name = "mesh_" + std::to_string(meshidx); + } + + S32 instance_count = mesh_name_counts[base_name]++; + + if (populateModelFromMesh(pModel, mesh, node, mats, instance_count) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) { @@ -244,9 +257,25 @@ void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S3 combined_transform = node_transform; } -bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats) +bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats, S32 instance_count) { - pModel->mLabel = mesh.mName; + // Create unique model name + std::string base_name = mesh.mName; + if (base_name.empty()) + { + S32 mesh_index = static_cast(&mesh - &mGLTFAsset.mMeshes[0]); + base_name = "mesh_" + std::to_string(mesh_index); + } + + 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; -- cgit v1.2.3 From d342aa79c24fe20d06a018eabdb03912d11d4702 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 26 May 2025 17:31:37 +0300 Subject: #4080 Rigged mesh support #3 --- indra/newview/gltf/llgltfloader.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 17f52af6b1..de7c341cdf 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -690,6 +690,13 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& LL_INFOS("GLTF_DEBUG") << "mAlternateBindMatrix name: " << legal_name << " val: " << original_joint_transform << LL_ENDL; skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(original_joint_transform)); } + else + { + // For gltf mInverseBindMatrices are optional, but not for viewer + // todo: get a model that triggers this + skin_info.mInvBindMatrix.push_back(LLMatrix4a(mJointList[legal_name])); // might need to be an 'identity' + skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(mJointList[legal_name])); + } } // "Bind Shape Matrix" is supposed to transform the geometry of the skinned mesh -- cgit v1.2.3 From a364b5ee1d4c009b2e571fde66272ae06e434c21 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 26 May 2025 17:43:56 +0300 Subject: #4109 Add validation for non-triangulated geometry in GLTF loader --- indra/newview/gltf/llgltfloader.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index de7c341cdf..93ed904f2e 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -461,6 +461,16 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& vertices.push_back(vert); } + if (prim.getIndexCount() % 3 != 0) + { + LL_WARNS("GLTF_IMPORT") << "Invalid primitive: index count " << prim.getIndexCount() + << " is not divisible by 3. GLTF files must contain triangulated geometry." << LL_ENDL; + LLSD args; + args["Message"] = "InvalidGeometryNonTriangulated"; + mWarningsArray.append(args); + continue; // Skip this primitive + } + // When processing indices, flip winding order if needed for (U32 i = 0; i < prim.getIndexCount(); i += 3) { -- cgit v1.2.3 From d9d800886d9694121c987654b72c40b972516aac Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 26 May 2025 21:27:09 +0300 Subject: #4080 Rigged mesh support #4 --- indra/newview/gltf/llgltfloader.cpp | 40 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 93ed904f2e..82666761df 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -67,6 +67,14 @@ static const std::string lod_suffix[LLModel::NUM_LODS] = "_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 +); + LLGLTFLoader::LLGLTFLoader(std::string filename, S32 lod, @@ -203,9 +211,21 @@ bool LLGLTFLoader::parseMeshes() mesh_scale *= transformation; transformation = mesh_scale; + // "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: This appers 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") << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " + LL_INFOS("GLTF_IMPORT") << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " << pModel->mLabel << LL_ENDL; LLSD args; args["Message"] = "NegativeScaleNormTrans"; @@ -280,9 +300,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& S32 skinIdx = nodeno.mSkin; - // Pre-compute coordinate system rotation matrix (GLTF Y-up to SL Z-up) - static const glm::mat4 coord_system_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - // Compute final combined transform matrix (hierarchy + coordinate rotation) S32 node_index = static_cast(&nodeno - &mGLTFAsset.mNodes[0]); glm::mat4 hierarchy_transform; @@ -619,7 +636,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mats[materialName] = impMat; } else { - LL_INFOS() << "Unable to process mesh due to 16-bit index limits" << LL_ENDL; + LL_INFOS("GLTF_IMPORT") << "Unable to process mesh due to 16-bit index limits" << LL_ENDL; LLSD args; args["Message"] = "ParsingErrorBadElement"; mWarningsArray.append(args); @@ -639,7 +656,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& size_t jointCnt = gltf_skin.mJoints.size(); if (gltf_skin.mInverseBindMatrices >= 0 && jointCnt != gltf_skin.mInverseBindMatricesData.size()) { - LL_INFOS("GLTF") << "Bind matrices count mismatch joints count" << LL_ENDL; + LL_INFOS("GLTF_IMPORT") << "Bind matrices count mismatch joints count" << LL_ENDL; LLSD args; args["Message"] = "InvBindCountMismatch"; mWarningsArray.append(args); @@ -709,14 +726,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& } } - // "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. - LLMatrix4 bind_shape; - bind_shape.setIdentity(); - skin_info.mBindShapeMatrix.loadu(bind_shape); - // Remap indices for pModel->mSkinWeights for (auto& weights : pModel->mSkinWeights) { @@ -732,9 +741,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) { - // Create coordinate system rotation matrix - GLTF is Y-up, SL is Z-up - glm::mat4 coord_system_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - LL_INFOS("GLTF_DEBUG") << "populateJointFromSkin: Processing " << skin.mJoints.size() << " joints" << LL_ENDL; for (auto joint : skin.mJoints) -- cgit v1.2.3 From 3f0aa3383a25d203dcdf35d0b7937cbcba3ea5e3 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 26 May 2025 22:03:55 +0300 Subject: #4109 Improve LLGLTFLoader::computeCombinedNodeTransform() --- indra/newview/gltf/llgltfloader.cpp | 39 +++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 17 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 82666761df..5922385358 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -251,30 +251,35 @@ bool LLGLTFLoader::parseMeshes() void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) { - auto& node = asset.mNodes[node_index]; + if (node_index < 0 || node_index >= static_cast(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(node).makeMatrixValid(); // Start with this node's transform - glm::mat4 node_transform = node.mMatrix; + combined_transform = node.mMatrix; - // Find parent node and apply its transform if it exists - for (auto& other_node : asset.mNodes) + // Find and apply parent transform if it exists + for (size_t i = 0; i < asset.mNodes.size(); ++i) { - for (auto& child_index : other_node.mChildren) - { - if (child_index == node_index) - { - // Found a parent, recursively get its combined transform - glm::mat4 parent_transform; - computeCombinedNodeTransform(asset, static_cast(&other_node - &asset.mNodes[0]), parent_transform); + const auto& potential_parent = asset.mNodes[i]; + auto it = std::find(potential_parent.mChildren.begin(), potential_parent.mChildren.end(), node_index); - // Apply parent transform to current node transform - node_transform = parent_transform * node_transform; - break; - } + if (it != potential_parent.mChildren.end()) + { + // Found parent - recursively get its combined transform and apply it + glm::mat4 parent_transform; + computeCombinedNodeTransform(asset, static_cast(i), parent_transform); + combined_transform = parent_transform * combined_transform; + return; // Early exit - a node can only have one parent } } - - combined_transform = node_transform; } bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats, S32 instance_count) -- cgit v1.2.3 From 0d99487d46d6ccf7000723c35ac78018b5763dea Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Tue, 27 May 2025 20:28:00 +0300 Subject: #4107 upload the model ignoring unsupported extension --- indra/newview/gltf/llgltfloader.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 5922385358..236c75a125 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -127,6 +127,13 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) return false; } + if (mGLTFAsset.mUnsupportedExtension) + { + LLSD args; + args["Message"] = "UnsupportedExtension"; + mWarningsArray.append(args); + } + mMeshesLoaded = parseMeshes(); if (mMeshesLoaded) uploadMeshes(); -- cgit v1.2.3 From 078cc9b0c1dc00b55bad9b3152651a66e8e2e79f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 26 May 2025 21:27:09 +0300 Subject: #4080 Rigged mesh support #5 --- indra/newview/gltf/llgltfloader.cpp | 110 ++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 24 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 236c75a125..1f8733f4ff 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -155,6 +155,12 @@ bool LLGLTFLoader::parseMeshes() mTransform.setIdentity(); + for (auto& node : mGLTFAsset.mNodes) + { + // Make node matrix valid for correct transformation + node.makeMatrixValid(); + } + // Populate the joints from skins first. // There's not many skins - and you can pretty easily iterate through the nodes from that. for (auto& skin : mGLTFAsset.mSkins) @@ -162,12 +168,6 @@ bool LLGLTFLoader::parseMeshes() populateJointFromSkin(skin); } - for (auto& node : mGLTFAsset.mNodes) - { - // Make node matrix valid for correct transformation - node.makeMatrixValid(); - } - // Track how many times each mesh name has been used std::map mesh_name_counts; @@ -218,17 +218,21 @@ bool LLGLTFLoader::parseMeshes() mesh_scale *= transformation; transformation = mesh_scale; - // "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: This appers 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 (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 @@ -256,7 +260,7 @@ bool LLGLTFLoader::parseMeshes() return true; } -void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) +void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const { if (node_index < 0 || node_index >= static_cast(asset.mNodes.size())) { @@ -342,7 +346,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& // Process joint name and idnex S32 joint = gltf_skin.mJoints[i]; LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; - jointNode.makeMatrixValid(); std::string legal_name(jointNode.mName); if (mJointMap.find(legal_name) == mJointMap.end()) @@ -688,7 +691,6 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& continue; } LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; - jointNode.makeMatrixValid(); std::string legal_name(jointNode.mName); if (mJointMap.find(legal_name) != mJointMap.end()) @@ -723,8 +725,15 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& // Get the original joint node and use its matrix directly S32 joint = gltf_skin.mJoints[i]; LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; - jointNode.makeMatrixValid(); - LLMatrix4 original_joint_transform(glm::value_ptr(jointNode.mMatrix)); + glm::mat4 joint_mat = jointNode.mMatrix; + S32 root_joint = findValidRootJoint(joint, gltf_skin); // skeleton can have multiple real roots + if (root_joint == joint) + { + // This is very likely incomplete in some way. + // Root shouldn't be the only one to need full coordinate fix + joint_mat = coord_system_rotation * joint_mat; + } + LLMatrix4 original_joint_transform(glm::value_ptr(joint_mat)); LL_INFOS("GLTF_DEBUG") << "mAlternateBindMatrix name: " << legal_name << " val: " << original_joint_transform << LL_ENDL; skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(original_joint_transform)); @@ -771,8 +780,6 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) continue; } - jointNode.makeMatrixValid(); - // Debug: Log original joint matrix glm::mat4 gltf_joint_matrix = jointNode.mMatrix; LL_INFOS("GLTF_DEBUG") << "Joint '" << legal_name << "' original matrix:" << LL_ENDL; @@ -801,6 +808,61 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin) } } +S32 LLGLTFLoader::findValidRootJoint(S32 source_joint, const LL::GLTF::Skin& gltf_skin) const +{ + S32 root_joint = 0; + S32 found_joint = source_joint; + S32 size = (S32)gltf_skin.mJoints.size(); + do + { + root_joint = found_joint; + 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::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_joint); + if (it != jointNode.mChildren.end()) + { + found_joint = joint; + break; + } + } + } + } while (root_joint != found_joint); + + return root_joint; +} + +S32 LLGLTFLoader::findGLTFRootJoint(const LL::GLTF::Skin& gltf_skin) const +{ + S32 root_joint = 0; + S32 found_joint = 0; + S32 size = (S32)gltf_skin.mJoints.size(); + do + { + root_joint = found_joint; + for (S32 i = 0; i < size; i++) + { + S32 joint = gltf_skin.mJoints[i]; + const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint]; + std::vector::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_joint); + if (it != jointNode.mChildren.end()) + { + found_joint = joint; + break; + } + } + } while (root_joint != found_joint); + + LL_INFOS("GLTF_DEBUG") << "mJointList name: "; + const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[root_joint]; + LL_CONT << jointNode.mName << " index: " << root_joint << LL_ENDL; + return root_joint; +} + bool LLGLTFLoader::parseMaterials() { if (!mGltfLoaded) return false; -- cgit v1.2.3 From 4c60231c3fa52a0875ff5ddd7cc4e416f839da95 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 28 May 2025 12:02:50 +0300 Subject: #4080 Rigged mesh support #6 For now not touching normalizeVolumeFaces() to not brick dae upload --- indra/newview/gltf/llgltfloader.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 1f8733f4ff..f16efe2ff1 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -468,6 +468,8 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& if (skinIdx >= 0) { + vert.weights = glm::vec4(prim.mWeights[i]); + auto accessorIdx = prim.mAttributes["JOINTS_0"]; LL::GLTF::Accessor::ComponentType componentType = LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE; if (accessorIdx >= 0) @@ -487,8 +489,11 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& { vert.joints = glm::unpackUint4x16(prim.mJoints[i]); } - - vert.weights = glm::vec4(prim.mWeights[i]); + else + { + vert.joints = glm::zero(); + vert.weights = glm::zero(); + } } vertices.push_back(vert); } @@ -659,8 +664,8 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& } } - // Call normalizeVolumeFaces to compute proper extents - pModel->normalizeVolumeFaces(); + // Call normalizeVolumeFacesAndWeights to compute proper extents + pModel->normalizeVolumeFacesAndWeights(); // Fill joint names, bind matrices and prepare to remap weight indices if (skinIdx >= 0) -- cgit v1.2.3 From be40d20bca15b97ccba557dc530fe55a92456ebf Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Thu, 29 May 2025 20:20:29 +0300 Subject: #4190 provide unsupported extension info in log --- indra/newview/gltf/llgltfloader.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index f16efe2ff1..2461a878fb 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -127,10 +127,19 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) return false; } - if (mGLTFAsset.mUnsupportedExtension) + if (mGLTFAsset.mUnsupportedExtensions.size() > 0) { LLSD args; args["Message"] = "UnsupportedExtension"; + std::string del; + std::string ext; + for (auto& extension : mGLTFAsset.mUnsupportedExtensions) + { + ext += del; + ext += extension; + del = ","; + } + args["EXT"] = ext; mWarningsArray.append(args); } -- cgit v1.2.3 From 136149d1a196d2c0c15b9977937e64ccd26c1a49 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Fri, 30 May 2025 03:06:33 +0300 Subject: #4191 skip loading model compressed with Draco --- indra/newview/gltf/llgltfloader.cpp | 43 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'indra/newview/gltf/llgltfloader.cpp') diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 2461a878fb..5cdd7f09e0 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -111,8 +111,6 @@ 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); @@ -120,28 +118,11 @@ bool LLGLTFLoader::OpenFile(const std::string &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; + notifyUnsupportedExtension(true); return false; } - if (mGLTFAsset.mUnsupportedExtensions.size() > 0) - { - LLSD args; - args["Message"] = "UnsupportedExtension"; - std::string del; - std::string ext; - for (auto& extension : mGLTFAsset.mUnsupportedExtensions) - { - ext += del; - ext += extension; - del = ","; - } - args["EXT"] = ext; - mWarningsArray.append(args); - } + notifyUnsupportedExtension(false); mMeshesLoaded = parseMeshes(); if (mMeshesLoaded) uploadMeshes(); @@ -1347,3 +1328,23 @@ LLUUID LLGLTFLoader::imageBufferToTextureUUID(const gltf_texture& tex) return LLUUID::null; } +void LLGLTFLoader::notifyUnsupportedExtension(bool unsupported) +{ + std::vector 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); + } +} + -- cgit v1.2.3