From 8c0163bcb48df56112a625550d411741c20c5846 Mon Sep 17 00:00:00 2001 From: Dave Houlton Date: Wed, 13 Apr 2022 12:32:58 -0600 Subject: SL-17214 initial loader class skeleton --- indra/llprimitive/llgltfloader.cpp | 336 +++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 indra/llprimitive/llgltfloader.cpp (limited to 'indra/llprimitive/llgltfloader.cpp') diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp new file mode 100644 index 0000000000..001623ac9e --- /dev/null +++ b/indra/llprimitive/llgltfloader.cpp @@ -0,0 +1,336 @@ +/** + * @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" + +#include + +#include "llsdserialize.h" +#include "lljoint.h" + +#include "glh/glh_linear.h" +#include "llmatrix4a.h" + +#include +#include + +static const std::string lod_suffix[LLModel::NUM_LODS] = +{ + "_LOD0", + "_LOD1", + "_LOD2", + "", + "_PHYS", +}; + +const U32 LIMIT_MATERIALS_OUTPUT = 12; + +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 ), + mGeneratedModelLimit(modelLimit), + //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; + + // Load a tinygltf model fom a file. Assumes that the input filename has already been + // been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish. + if (std::string::npos == filename.rfind(".gltf")) + { // file is binary + mGltfLoaded = loader.LoadBinaryFromFile(&mGltfModel, &error_msg, &warn_msg, filename); + } + else + { // file is ascii + mGltfLoaded = loader.LoadASCIIFromFile(&mGltfModel, &error_msg, &warn_msg, filename); + } + + if (!mGltfLoaded) + { + if (!warn_msg.empty()) + LL_WARNS() << "gltf load warning: " << warn_msg.c_str() << LL_ENDL; + if (!error_msg.empty()) + LL_WARNS() << "gltf load error: " << error_msg.c_str() << LL_ENDL; + return false; + } + + mMeshesLoaded = parseMeshes(); + if (mMeshesLoaded) uploadMeshes(); + + mMaterialsLoaded = parseMaterials(); + if (mMaterialsLoaded) uploadMaterials(); + + return (mMeshesLoaded || mMaterialsLoaded); +} + +bool LLGLTFLoader::parseMeshes() +{ + if (!mGltfLoaded) return false; + + // 2022-04 DJH Volume params from dae example. TODO understand PCODE + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + + for (tinygltf::Mesh mesh : mGltfModel.meshes) + { + LLModel *pModel = new LLModel(volume_params, 0.f); + + if (populateModelFromMesh(pModel, mesh) && + (LLModel::NO_ERRORS == pModel->getStatus()) && + validate_model(pModel)) + { + mModelList.push_back(pModel); + } + else + { + setLoadState(ERROR_MODEL + pModel->getStatus()); + delete(pModel); + return false; + } + } + return true; +} + +bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh) +{ + pModel->mLabel = mesh.name; + int pos_idx, norm_idx, tan_idx, uv0_idx, uv1_idx, color0_idx, color1_idx; + tinygltf::Accessor indices_a, positions_a, normals_a, uv0_a, color0_a; + + auto prims = mesh.primitives; + for (auto prim : prims) + { + if (prim.indices >= 0) indices_a = mGltfModel.accessors[prim.indices]; + + pos_idx = (prim.attributes.count("POSITION") > 0) ? prim.attributes.at("POSITION") : -1; + if (pos_idx >= 0) + { + positions_a = mGltfModel.accessors[pos_idx]; + if (TINYGLTF_COMPONENT_TYPE_FLOAT != positions_a.componentType) + continue; + auto positions_bv = mGltfModel.bufferViews[positions_a.bufferView]; + auto positions_buf = mGltfModel.buffers[positions_bv.buffer]; + //auto type = positions_vb. + //if (positions_buf.name + } + + norm_idx = (prim.attributes.count("NORMAL") > 0) ? prim.attributes.at("NORMAL") : -1; + tan_idx = (prim.attributes.count("TANGENT") > 0) ? prim.attributes.at("TANGENT") : -1; + uv0_idx = (prim.attributes.count("TEXCOORDS_0") > 0) ? prim.attributes.at("TEXCOORDS_0") : -1; + uv1_idx = (prim.attributes.count("TEXCOORDS_1") > 0) ? prim.attributes.at("TEXCOORDS_1") : -1; + color0_idx = (prim.attributes.count("COLOR_0") > 0) ? prim.attributes.at("COLOR_0") : -1; + color1_idx = (prim.attributes.count("COLOR_1") > 0) ? prim.attributes.at("COLOR_1") : -1; + + if (prim.mode == TINYGLTF_MODE_TRIANGLES) + { + //auto pos = mesh. TODO resume here DJH 2022-04 + } + } + + //pModel->addFace() + return false; +} + +bool LLGLTFLoader::parseMaterials() +{ + if (!mGltfLoaded) return false; + + // fill local texture data structures + mSamplers.clear(); + for (auto in_sampler : mGltfModel.samplers) + { + gltf_sampler sampler{ 0 }; + sampler.magFilter = in_sampler.magFilter; + sampler.minFilter = in_sampler.minFilter; + 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{ 0 }; + image.numChannels = in_image.component; + image.bytesPerChannel = in_image.bits >> 3; + image.pixelType = in_image.pixel_type; + image.size = 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{ 0 }; + tex.image_idx = in_tex.source; + tex.sampler_idx = in_tex.sampler; + + if (tex.image_idx >= mImages.size() || tex.sampler_idx >= 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{ 0 }; + mat.name = gltf_material.name; + + mat.normalScale = gltf_material.normalTexture.scale; + mat.normalTexIdx = gltf_material.normalTexture.index; + mat.normalTexCoordIdx = gltf_material.normalTexture.texCoord; + + mat.occlusionScale = gltf_material.occlusionTexture.strength; + mat.occlusionTexIdx = gltf_material.occlusionTexture.index; + mat.occlusionTexCoordIdx = gltf_material.occlusionTexture.texCoord; + + mat.emissiveColor.set(gltf_material.emissiveFactor.data()); + mat.emissiveColorTexIdx = gltf_material.emissiveTexture.index; + mat.emissiveColorTexCoordIdx = gltf_material.emissiveTexture.texCoord; + + mat.alphaMode = gltf_material.alphaMode; + mat.alphaMask = gltf_material.alphaCutoff; + + tinygltf::PbrMetallicRoughness& pbr = gltf_material.pbrMetallicRoughness; + mat.hasPBR = true; + + mat.pbr.baseColor.set(pbr.baseColorFactor.data()); + mat.pbr.baseColorTexIdx = pbr.baseColorTexture.index; + mat.pbr.baseColorTexCoordIdx = pbr.baseColorTexture.texCoord; + + mat.pbr.metalness = pbr.metallicFactor; + mat.pbr.roughness = pbr.roughnessFactor; + mat.pbr.metalRoughTexIdx = pbr.metallicRoughnessTexture.index; + mat.pbr.metalRoughTexCoordIdx = pbr.metallicRoughnessTexture.texCoord; + + if (mat.normalTexIdx >= mTextures.size() || + mat.occlusionTexIdx >= mTextures.size() || + mat.emissiveColorTexIdx >= mTextures.size() || + mat.pbr.baseColorTexIdx >= mTextures.size() || + mat.pbr.metalRoughTexIdx >= mTextures.size()) + { + LL_WARNS("GLTF_IMPORT") << "Texture resource index error" << LL_ENDL; + return false; + } + + if (mat.normalTexCoordIdx > 0 || // May have to loosen this condition + mat.occlusionTexCoordIdx > 0 || + mat.emissiveColorTexCoordIdx > 0 || + mat.pbr.baseColorTexCoordIdx > 0 || + mat.pbr.metalRoughTexCoordIdx > 0) + { + 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); +} + +// TODO: convert raw index buffers to UUIDs +void LLGLTFLoader::uploadMaterials() +{ + llassert(0); +} + -- cgit v1.3 From adaaccd3d74dd05b596693ef7de90aeef20b5f9d Mon Sep 17 00:00:00 2001 From: Dave Houlton Date: Tue, 17 May 2022 15:54:00 -0600 Subject: SL-17214 additional glTF validation, remove dead code from DAE loader --- indra/llprimitive/CMakeLists.txt | 2 ++ indra/llprimitive/lldaeloader.cpp | 42 ------------------------------------ indra/llprimitive/lldaeloader.h | 3 --- indra/llprimitive/llgltfloader.cpp | 44 ++++++++++++++++++++++---------------- indra/llprimitive/llgltfloader.h | 6 +++++- 5 files changed, 32 insertions(+), 65 deletions(-) (limited to 'indra/llprimitive/llgltfloader.cpp') diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 9d75dab31e..2395841eae 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -10,12 +10,14 @@ include(LLCoreHttp) include(LLXML) include(LLPhysicsExtensions) include(LLCharacter) +include(LLRender) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} ${LIBS_PREBUILT_DIR}/include/collada ${LIBS_PREBUILT_DIR}/include/collada/1.4 ${LLCHARACTER_INCLUDE_DIRS} diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 94f8500dab..68b29f01da 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -2504,20 +2504,6 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& return (status == LLModel::NO_ERRORS); } -////static -//LLModel* LLDAELoader::loadModelFromDomMesh(domMesh *mesh) -//{ -// LLVolumeParams volume_params; -// volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); -// LLModel* ret = new LLModel(volume_params, 0.f); -// createVolumeFacesFromDomMesh(ret, mesh); -// if (ret->mLabel.empty()) -// { -// ret->mLabel = getElementLabel(mesh); -// } -// return ret; -//} - //static diff version supports creating multiple models when material counts spill // over the 8 face server-side limit // @@ -2608,31 +2594,3 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector& mo return true; } - -bool LLDAELoader::createVolumeFacesFromDomMesh(LLModel* pModel, domMesh* mesh) -{ - if (mesh) - { - pModel->ClearFacesAndMaterials(); - - LLSD placeholder; - addVolumeFacesFromDomMesh(pModel, mesh, placeholder); - - if (pModel->getNumVolumeFaces() > 0) - { - pModel->normalizeVolumeFaces(); - pModel->optimizeVolumeFaces(); - - if (pModel->getNumVolumeFaces() > 0) - { - return true; - } - } - } - else - { - LL_WARNS() << "no mesh found" << LL_ENDL; - } - - return false; -} diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h index 9e80980ddf..52ad908870 100644 --- a/indra/llprimitive/lldaeloader.h +++ b/indra/llprimitive/lldaeloader.h @@ -90,9 +90,6 @@ protected: bool verifyController( domController* pController ); static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh, LLSD& log_msg); - static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh); - - //static LLModel* loadModelFromDomMesh(domMesh* mesh); // Loads a mesh breaking it into one or more models as necessary // to get around volume face limitations while retaining >8 materials diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp index 001623ac9e..bc9c4760f7 100644 --- a/indra/llprimitive/llgltfloader.cpp +++ b/indra/llprimitive/llgltfloader.cpp @@ -211,9 +211,9 @@ bool LLGLTFLoader::parseMaterials() mSamplers.clear(); for (auto in_sampler : mGltfModel.samplers) { - gltf_sampler sampler{ 0 }; - sampler.magFilter = in_sampler.magFilter; - sampler.minFilter = in_sampler.minFilter; + 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 @@ -223,10 +223,10 @@ bool LLGLTFLoader::parseMaterials() mImages.clear(); for (auto in_image : mGltfModel.images) { - gltf_image image{ 0 }; + gltf_image image; image.numChannels = in_image.component; - image.bytesPerChannel = in_image.bits >> 3; - image.pixelType = in_image.pixel_type; + 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 = in_image.image.size(); image.height = in_image.height; image.width = in_image.width; @@ -250,7 +250,7 @@ bool LLGLTFLoader::parseMaterials() mTextures.clear(); for (auto in_tex : mGltfModel.textures) { - gltf_texture tex{ 0 }; + gltf_texture tex; tex.image_idx = in_tex.source; tex.sampler_idx = in_tex.sampler; @@ -266,18 +266,21 @@ bool LLGLTFLoader::parseMaterials() // parse each material for (tinygltf::Material gltf_material : mGltfModel.materials) { - gltf_render_material mat{ 0 }; + gltf_render_material mat; mat.name = gltf_material.name; mat.normalScale = gltf_material.normalTexture.scale; + mat.hasNormalTex = gltf_material.normalTexture.index > 0; mat.normalTexIdx = gltf_material.normalTexture.index; mat.normalTexCoordIdx = gltf_material.normalTexture.texCoord; mat.occlusionScale = gltf_material.occlusionTexture.strength; + mat.hasOcclusionTex = gltf_material.occlusionTexture.index > 0; mat.occlusionTexIdx = gltf_material.occlusionTexture.index; mat.occlusionTexCoordIdx = gltf_material.occlusionTexture.texCoord; mat.emissiveColor.set(gltf_material.emissiveFactor.data()); + mat.hasEmissiveTex = gltf_material.emissiveTexture.index > 0; mat.emissiveColorTexIdx = gltf_material.emissiveTexture.index; mat.emissiveColorTexCoordIdx = gltf_material.emissiveTexture.texCoord; @@ -288,29 +291,31 @@ bool LLGLTFLoader::parseMaterials() mat.hasPBR = true; mat.pbr.baseColor.set(pbr.baseColorFactor.data()); + mat.pbr.hasBaseTex = pbr.baseColorTexture.index > 0; mat.pbr.baseColorTexIdx = pbr.baseColorTexture.index; mat.pbr.baseColorTexCoordIdx = pbr.baseColorTexture.texCoord; mat.pbr.metalness = pbr.metallicFactor; mat.pbr.roughness = pbr.roughnessFactor; + mat.pbr.hasMRTex = pbr.metallicRoughnessTexture.index > 0; mat.pbr.metalRoughTexIdx = pbr.metallicRoughnessTexture.index; mat.pbr.metalRoughTexCoordIdx = pbr.metallicRoughnessTexture.texCoord; - if (mat.normalTexIdx >= mTextures.size() || - mat.occlusionTexIdx >= mTextures.size() || - mat.emissiveColorTexIdx >= mTextures.size() || - mat.pbr.baseColorTexIdx >= mTextures.size() || - mat.pbr.metalRoughTexIdx >= mTextures.size()) + if ((mat.hasNormalTex && (mat.normalTexIdx >= mTextures.size())) || + (mat.hasOcclusionTex && (mat.occlusionTexIdx >= mTextures.size())) || + (mat.hasEmissiveTex && (mat.emissiveColorTexIdx >= mTextures.size())) || + (mat.pbr.hasBaseTex && (mat.pbr.baseColorTexIdx >= mTextures.size())) || + (mat.pbr.hasMRTex && (mat.pbr.metalRoughTexIdx >= mTextures.size()))) { LL_WARNS("GLTF_IMPORT") << "Texture resource index error" << LL_ENDL; return false; } - if (mat.normalTexCoordIdx > 0 || // May have to loosen this condition - mat.occlusionTexCoordIdx > 0 || - mat.emissiveColorTexCoordIdx > 0 || - mat.pbr.baseColorTexCoordIdx > 0 || - mat.pbr.metalRoughTexCoordIdx > 0) + if ((mat.hasNormalTex && (mat.normalTexCoordIdx > 2)) || // mesh can have up to 3 sets of UV + (mat.hasOcclusionTex && (mat.occlusionTexCoordIdx > 2)) || + (mat.hasEmissiveTex && (mat.emissiveColorTexCoordIdx > 2)) || + (mat.pbr.hasBaseTex && (mat.pbr.baseColorTexCoordIdx > 2)) || + (mat.pbr.hasMRTex && (mat.pbr.metalRoughTexCoordIdx > 2))) { LL_WARNS("GLTF_IMPORT") << "Image texcoord index error" << LL_ENDL; return false; @@ -331,6 +336,7 @@ void LLGLTFLoader::uploadMeshes() // TODO: convert raw index buffers to UUIDs void LLGLTFLoader::uploadMaterials() { - llassert(0); + //llassert(0); + } diff --git a/indra/llprimitive/llgltfloader.h b/indra/llprimitive/llgltfloader.h index 9bffeef4ab..08e9836d07 100644 --- a/indra/llprimitive/llgltfloader.h +++ b/indra/llprimitive/llgltfloader.h @@ -27,8 +27,9 @@ #ifndef LL_LLGLTFLoader_H #define LL_LLGLTFLoader_H -#include "tinygltf\tiny_gltf.h" +#include "tinygltf/tiny_gltf.h" +#include "llglheaders.h" #include "llmodelloader.h" typedef struct // gltf sampler @@ -74,6 +75,8 @@ typedef struct // gltf_pbrMR_material U32 metalRoughTexIdx; // always linear, roughness in G channel, metalness in B channel U32 metalRoughTexCoordIdx; + + bool hasBaseTex, hasMRTex; } gltf_pbr; typedef struct // render material @@ -100,6 +103,7 @@ typedef struct // render material // TODO: Add traditional (diffuse, normal, specular) UUIDs here, or add this struct to LL_TextureEntry?? bool hasPBR; + bool hasNormalTex, hasOcclusionTex, hasEmissiveTex; gltf_pbr pbr; } gltf_render_material; -- cgit v1.3 From c9ebb970ee916ace16e88508dc1a178336a91d52 Mon Sep 17 00:00:00 2001 From: Dave Houlton Date: Wed, 1 Jun 2022 14:37:20 -0600 Subject: SL-17214 re-work gltf data organization --- indra/llprimitive/llgltfloader.cpp | 138 +++++++++++++++++++++++++++---------- indra/llprimitive/llgltfloader.h | 70 +++++++++---------- 2 files changed, 132 insertions(+), 76 deletions(-) (limited to 'indra/llprimitive/llgltfloader.cpp') diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp index bc9c4760f7..3ec11f70c6 100644 --- a/indra/llprimitive/llgltfloader.cpp +++ b/indra/llprimitive/llgltfloader.cpp @@ -45,6 +45,9 @@ // TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE #include "tinygltf\tiny_gltf.h" + +// TODO: includes inherited from dae loader. Validate / prune + #include #include "llsdserialize.h" @@ -120,9 +123,9 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) if (!mGltfLoaded) { if (!warn_msg.empty()) - LL_WARNS() << "gltf load warning: " << warn_msg.c_str() << LL_ENDL; + LL_WARNS("GLTF_IMPORT") << "gltf load warning: " << warn_msg.c_str() << LL_ENDL; if (!error_msg.empty()) - LL_WARNS() << "gltf load error: " << error_msg.c_str() << LL_ENDL; + LL_WARNS("GLTF_IMPORT") << "gltf load error: " << error_msg.c_str() << LL_ENDL; return false; } @@ -251,10 +254,11 @@ bool LLGLTFLoader::parseMaterials() for (auto in_tex : mGltfModel.textures) { gltf_texture tex; - tex.image_idx = in_tex.source; - tex.sampler_idx = in_tex.sampler; + tex.imageIdx = in_tex.source; + tex.samplerIdx = in_tex.sampler; + tex.imageUuid.setNull(); - if (tex.image_idx >= mImages.size() || tex.sampler_idx >= mSamplers.size()) + if (tex.imageIdx >= mImages.size() || tex.samplerIdx >= mSamplers.size()) { LL_WARNS("GLTF_IMPORT") << "Texture sampler/image index error" << LL_ENDL; return false; @@ -269,53 +273,53 @@ bool LLGLTFLoader::parseMaterials() 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.hasNormalTex = gltf_material.normalTexture.index >= 0; mat.normalTexIdx = gltf_material.normalTexture.index; - mat.normalTexCoordIdx = gltf_material.normalTexture.texCoord; + mat.normalTexCoords = gltf_material.normalTexture.texCoord; mat.occlusionScale = gltf_material.occlusionTexture.strength; - mat.hasOcclusionTex = gltf_material.occlusionTexture.index > 0; + mat.hasOcclusionTex = gltf_material.occlusionTexture.index >= 0; mat.occlusionTexIdx = gltf_material.occlusionTexture.index; - mat.occlusionTexCoordIdx = gltf_material.occlusionTexture.texCoord; + mat.occlusionTexCoords = gltf_material.occlusionTexture.texCoord; mat.emissiveColor.set(gltf_material.emissiveFactor.data()); - mat.hasEmissiveTex = gltf_material.emissiveTexture.index > 0; - mat.emissiveColorTexIdx = gltf_material.emissiveTexture.index; - mat.emissiveColorTexCoordIdx = gltf_material.emissiveTexture.texCoord; + 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; - tinygltf::PbrMetallicRoughness& pbr = gltf_material.pbrMetallicRoughness; - mat.hasPBR = true; - - mat.pbr.baseColor.set(pbr.baseColorFactor.data()); - mat.pbr.hasBaseTex = pbr.baseColorTexture.index > 0; - mat.pbr.baseColorTexIdx = pbr.baseColorTexture.index; - mat.pbr.baseColorTexCoordIdx = pbr.baseColorTexture.texCoord; - - mat.pbr.metalness = pbr.metallicFactor; - mat.pbr.roughness = pbr.roughnessFactor; - mat.pbr.hasMRTex = pbr.metallicRoughnessTexture.index > 0; - mat.pbr.metalRoughTexIdx = pbr.metallicRoughnessTexture.index; - mat.pbr.metalRoughTexCoordIdx = pbr.metallicRoughnessTexture.texCoord; - - if ((mat.hasNormalTex && (mat.normalTexIdx >= mTextures.size())) || - (mat.hasOcclusionTex && (mat.occlusionTexIdx >= mTextures.size())) || - (mat.hasEmissiveTex && (mat.emissiveColorTexIdx >= mTextures.size())) || - (mat.pbr.hasBaseTex && (mat.pbr.baseColorTexIdx >= mTextures.size())) || - (mat.pbr.hasMRTex && (mat.pbr.metalRoughTexIdx >= mTextures.size()))) + 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.normalTexCoordIdx > 2)) || // mesh can have up to 3 sets of UV - (mat.hasOcclusionTex && (mat.occlusionTexCoordIdx > 2)) || - (mat.hasEmissiveTex && (mat.emissiveColorTexCoordIdx > 2)) || - (mat.pbr.hasBaseTex && (mat.pbr.baseColorTexCoordIdx > 2)) || - (mat.pbr.hasMRTex && (mat.pbr.metalRoughTexCoordIdx > 2))) + 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; @@ -333,10 +337,68 @@ void LLGLTFLoader::uploadMeshes() llassert(0); } -// TODO: convert raw index buffers to UUIDs +// convert raw image buffers to texture UUIDs & assemble into a render material void LLGLTFLoader::uploadMaterials() { - //llassert(0); + for (gltf_render_material mat : mMaterials) // Initially 1 material per gltf file, but design for multiple + { + if (mat.hasBaseTex) + { + gltf_texture& gtex = mTextures[mat.baseColorTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + + if (mat.hasMRTex) + { + gltf_texture& gtex = mTextures[mat.metalRoughTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + + if (mat.hasNormalTex) + { + gltf_texture& gtex = mTextures[mat.normalTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + if (mat.hasOcclusionTex) + { + gltf_texture& gtex = mTextures[mat.occlusionTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + + if (mat.hasEmissiveTex) + { + gltf_texture& gtex = mTextures[mat.emissiveTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + } } +LLUUID LLGLTFLoader::imageBufferToTextureUUID(const gltf_texture& tex) +{ + //gltf_image& image = mImages[tex.imageIdx]; + //gltf_sampler& sampler = mSamplers[tex.samplerIdx]; + + // fill an LLSD container with image+sampler data + + // upload texture + + // retrieve UUID + + return LLUUID::null; +} diff --git a/indra/llprimitive/llgltfloader.h b/indra/llprimitive/llgltfloader.h index 08e9836d07..24496f6324 100644 --- a/indra/llprimitive/llgltfloader.h +++ b/indra/llprimitive/llgltfloader.h @@ -32,19 +32,21 @@ #include "llglheaders.h" #include "llmodelloader.h" +// gltf_* structs are temporary, used to organize the subset of data that eventually goes into the material LLSD + typedef struct // gltf sampler { // Uses GL enums S32 minFilter; // GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR or GL_LINEAR_MIPMAP_LINEAR S32 magFilter; // GL_NEAREST or GL_LINEAR S32 wrapS; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT S32 wrapT; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT - //S32 wrapR; // seen in some sample files, but not part of glTF 2.0 spec. Ignored. + //S32 wrapR; // Found in some sample files, but not part of glTF 2.0 spec. Ignored. std::string name; // optional, currently unused // extensions and extras are sampler optional fields that we don't support - at least initially } gltf_sampler; typedef struct // gltf image -{ // Note that glTF images are defined with row 0 at the top +{ // Note that glTF images are defined with row 0 at the top (opposite of OpenGL) U8* data; // ptr to decoded image data U32 size; // in bytes, regardless of channel width U32 width; @@ -56,34 +58,19 @@ typedef struct // gltf image typedef struct // texture { - U32 image_idx; - U32 sampler_idx; + U32 imageIdx; + U32 samplerIdx; + LLUUID imageUuid = LLUUID::null; } gltf_texture; - -// TODO: 2022-05 DJH add UUIDs for each texture -typedef struct // gltf_pbrMR_material -{ - // scalar values - LLColor4 baseColor; // linear encoding. Multiplied with vertex color, if present. - double metalness; - double roughness; - - // textures - U32 baseColorTexIdx; // always sRGB encoded - U32 baseColorTexCoordIdx; - - U32 metalRoughTexIdx; // always linear, roughness in G channel, metalness in B channel - U32 metalRoughTexCoordIdx; - - bool hasBaseTex, hasMRTex; -} gltf_pbr; - typedef struct // render material { std::string name; // scalar values + LLColor4 baseColor; // linear encoding. Multiplied with vertex color, if present. + double metalness; + double roughness; double normalScale; // scale applies only to X,Y components of normal double occlusionScale; // strength multiplier for occlusion LLColor4 emissiveColor; // emissive mulitiplier, assumed linear encoding (spec 2.0 is silent) @@ -91,20 +78,26 @@ typedef struct // render material double alphaMask; // textures - U32 normalTexIdx; // linear, valid range R[0-1], G[0-1], B[0.5-1]. Normal = texel * 2 - vec3(1.0) - U32 normalTexCoordIdx; - - U32 occlusionTexIdx; // linear, occlusion in R channel, 0 meaning fully occluded, 1 meaning not occluded - U32 occlusionTexCoordIdx; - - U32 emissiveColorTexIdx; // always stored as sRGB, in nits (candela / meter^2) - U32 emissiveColorTexCoordIdx; + U32 baseColorTexIdx; // always sRGB encoded + U32 metalRoughTexIdx; // always linear, roughness in G channel, metalness in B channel + U32 normalTexIdx; // linear, valid range R[0-1], G[0-1], B[0.5-1]. Normal = texel * 2 - vec3(1.0) + U32 occlusionTexIdx; // linear, occlusion in R channel, 0 meaning fully occluded, 1 meaning not occluded + U32 emissiveTexIdx; // always stored as sRGB, in nits (candela / meter^2) + + // texture coordinates + U32 baseColorTexCoords; + U32 metalRoughTexCoords; + U32 normalTexCoords; + U32 occlusionTexCoords; + U32 emissiveTexCoords; // TODO: Add traditional (diffuse, normal, specular) UUIDs here, or add this struct to LL_TextureEntry?? bool hasPBR; - bool hasNormalTex, hasOcclusionTex, hasEmissiveTex; - gltf_pbr pbr; + bool hasBaseTex, hasMRTex, hasNormalTex, hasOcclusionTex, hasEmissiveTex; + + // This field is populated after upload + LLUUID material_uuid = LLUUID::null; } gltf_render_material; @@ -112,7 +105,7 @@ typedef struct // gltf_mesh { std::string name; - // TODO DJH 2022-04 + // TODO add mesh import DJH 2022-04 } gltf_mesh; @@ -157,16 +150,17 @@ protected: std::vector mSamplers; private: - U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels -// bool mPreprocessGLTF; - bool parseMeshes(); void uploadMeshes(); bool parseMaterials(); void uploadMaterials(); bool populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh); + LLUUID imageBufferToTextureUUID(const gltf_texture& tex); + + U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels + // bool mPreprocessGLTF; - /* + /* Inherited from dae loader - unknown how useful here void processElement(gltfElement *element, bool &badElement, GLTF *gltf); void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin); -- cgit v1.3 From 0fe39a1f3f64c336ccbf806e89b62d158d1da110 Mon Sep 17 00:00:00 2001 From: Brad Kittenbrink Date: Thu, 23 Jun 2022 18:07:10 -0700 Subject: Correcting windows specific filesystem issues and removing unused DAE code in llgltfloader.cpp from SL-17214 --- indra/llprimitive/llgltfloader.cpp | 6 ++---- indra/llprimitive/llgltfloader.h | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'indra/llprimitive/llgltfloader.cpp') diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp index 3ec11f70c6..6041c9c273 100644 --- a/indra/llprimitive/llgltfloader.cpp +++ b/indra/llprimitive/llgltfloader.cpp @@ -24,7 +24,7 @@ * $/LicenseInfo$ */ -#include "LLGLTFLoader.h" +#include "llgltfloader.h" // Import & define single-header gltf import/export lib #define TINYGLTF_IMPLEMENTATION @@ -43,7 +43,7 @@ // 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" +#include "tinygltf/tiny_gltf.h" // TODO: includes inherited from dae loader. Validate / prune @@ -68,7 +68,6 @@ static const std::string lod_suffix[LLModel::NUM_LODS] = "_PHYS", }; -const U32 LIMIT_MATERIALS_OUTPUT = 12; LLGLTFLoader::LLGLTFLoader(std::string filename, S32 lod, @@ -94,7 +93,6 @@ LLGLTFLoader::LLGLTFLoader(std::string filename, jointsFromNodes, jointAliasMap, maxJointsPerMesh ), - mGeneratedModelLimit(modelLimit), //mPreprocessGLTF(preprocess), mMeshesLoaded(false), mMaterialsLoaded(false) diff --git a/indra/llprimitive/llgltfloader.h b/indra/llprimitive/llgltfloader.h index 91389b5845..b4d6ca1940 100644 --- a/indra/llprimitive/llgltfloader.h +++ b/indra/llprimitive/llgltfloader.h @@ -158,7 +158,6 @@ private: bool populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh); LLUUID imageBufferToTextureUUID(const gltf_texture& tex); - U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels // bool mPreprocessGLTF; /* Below inherited from dae loader - unknown if/how useful here -- cgit v1.3 From dc1dd7a274e1ce49eb68ca525eba7f2e39032fa8 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Thu, 2 Feb 2023 12:25:03 -0800 Subject: Fix some unused variable warnings in DRTVWR-559 --- indra/llprimitive/llgltfloader.cpp | 5 ++++- indra/newview/llmaterialeditor.cpp | 4 +--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'indra/llprimitive/llgltfloader.cpp') diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp index 6041c9c273..fd304f7bc9 100644 --- a/indra/llprimitive/llgltfloader.cpp +++ b/indra/llprimitive/llgltfloader.cpp @@ -167,7 +167,7 @@ bool LLGLTFLoader::parseMeshes() bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh) { pModel->mLabel = mesh.name; - int pos_idx, norm_idx, tan_idx, uv0_idx, uv1_idx, color0_idx, color1_idx; + int pos_idx; tinygltf::Accessor indices_a, positions_a, normals_a, uv0_a, color0_a; auto prims = mesh.primitives; @@ -187,12 +187,15 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh & //if (positions_buf.name } +#if 0 + int norm_idx, tan_idx, uv0_idx, uv1_idx, color0_idx, color1_idx; norm_idx = (prim.attributes.count("NORMAL") > 0) ? prim.attributes.at("NORMAL") : -1; tan_idx = (prim.attributes.count("TANGENT") > 0) ? prim.attributes.at("TANGENT") : -1; uv0_idx = (prim.attributes.count("TEXCOORDS_0") > 0) ? prim.attributes.at("TEXCOORDS_0") : -1; uv1_idx = (prim.attributes.count("TEXCOORDS_1") > 0) ? prim.attributes.at("TEXCOORDS_1") : -1; color0_idx = (prim.attributes.count("COLOR_0") > 0) ? prim.attributes.at("COLOR_0") : -1; color1_idx = (prim.attributes.count("COLOR_1") > 0) ? prim.attributes.at("COLOR_1") : -1; +#endif if (prim.mode == TINYGLTF_MODE_TRIANGLES) { diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp index 5628327ebe..14ebfa451a 100644 --- a/indra/newview/llmaterialeditor.cpp +++ b/indra/newview/llmaterialeditor.cpp @@ -770,7 +770,7 @@ void LLMaterialEditor::markChangesUnsaved(U32 dirty_flag) const LLInventoryItem* item = getItem(); if (item) { - LLPermissions perm(item->getPermissions()); + //LLPermissions perm(item->getPermissions()); bool allow_modify = canModify(mObjectUUID, item); bool source_library = mObjectUUID.isNull() && gInventory.isObjectDescendentOf(mItemUUID, gInventory.getLibraryRootFolderID()); bool source_notecard = mNotecardInventoryID.notNull(); @@ -1339,7 +1339,6 @@ bool LLMaterialEditor::updateInventoryItem(const std::string &buffer, const LLUU } else if (!task_id.isNull() && !task_url.empty()) { - LLUUID object_uuid(task_id); uploadInfo = std::make_shared(task_id, item_id, LLAssetType::AT_MATERIAL, buffer, [](LLUUID itemId, LLUUID task_id, LLUUID newAssetId, LLSD) { @@ -1390,7 +1389,6 @@ void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std: // gen a new uuid for this asset LLTransactionID tid; tid.generate(); // timestamp-based randomization + uniquification - LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); U32 next_owner_perm = LLFloaterPerms::getNextOwnerPerms("Materials"); LLUUID parent = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_MATERIAL); const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ? -- cgit v1.3 From 33e44b2059831594f91a84d07b316e8eee94bd4f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 10 Mar 2023 00:56:57 +0200 Subject: SL-19353 Mesh importer throws an error when the file extension is upper case --- indra/llprimitive/llgltfloader.cpp | 4 +++- indra/newview/llmodelpreview.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'indra/llprimitive/llgltfloader.cpp') diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp index fd304f7bc9..7394f99794 100644 --- a/indra/llprimitive/llgltfloader.cpp +++ b/indra/llprimitive/llgltfloader.cpp @@ -106,10 +106,12 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) tinygltf::TinyGLTF loader; std::string error_msg; std::string warn_msg; + std::string filename_lc(filename); + LLStringUtil::toLower(filename_lc); // Load a tinygltf model fom a file. Assumes that the input filename has already been // been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish. - if (std::string::npos == filename.rfind(".gltf")) + if (std::string::npos == filename_lc.rfind(".gltf")) { // file is binary mGltfLoaded = loader.LoadBinaryFromFile(&mGltfModel, &error_msg, &warn_msg, filename); } diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 56a48cb74d..7ae48eef8c 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -750,7 +750,9 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable // three possible file extensions, .dae .gltf .glb // check for .dae and if not then assume one of the .gl?? - if (std::string::npos != filename.rfind(".dae")) + std::string filename_lc(filename); + LLStringUtil::toLower(filename_lc); + if (std::string::npos != filename_lc.rfind(".dae")) { mModelLoader = new LLDAELoader( filename, -- cgit v1.3