GLTF import texture upload

meow-7.2.2
Andrey Lihatskiy 2025-05-05 20:21:45 +03:00
parent d679e7f3ba
commit b3d1a1cb16
1 changed files with 455 additions and 96 deletions

View File

@ -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<GLTFVertex> vertices;
std::vector<U16> indices;
LLImportMaterial impMat;
impMat.mDiffuseColor = LLColor4::white; // Default color
LL::GLTF::Material* material = nullptr;
// Process material if available
if (prim.mMaterial >= 0 && prim.mMaterial < mGLTFAsset.mMaterials.size())
{
LL::GLTF::Material* material = &mGLTFAsset.mMaterials[prim.mMaterial];
if (prim.mMaterial >= 0)
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]
);
impMat.mDiffuseColor = LLColor4::white;
// 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<U32>(in_image.image.size());
image.height = in_image.height;
image.width = in_image.width;
image.data = in_image.image.data();
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
if (in_image.as_is)
// 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<U32>(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<U32>(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;
mat.name = gltf_material.mName;
tinygltf::PbrMetallicRoughness& pbr = gltf_material.pbrMetallicRoughness;
mat.hasPBR = true; // Always true, for now
// PBR Metallic Roughness properties
mat.hasPBR = true;
mat.baseColor.set(pbr.baseColorFactor.data());
mat.hasBaseTex = pbr.baseColorTexture.index >= 0;
mat.baseColorTexIdx = pbr.baseColorTexture.index;
mat.baseColorTexCoords = pbr.baseColorTexture.texCoord;
// 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]
);
mat.metalness = pbr.metallicFactor;
mat.roughness = pbr.roughnessFactor;
mat.hasMRTex = pbr.metallicRoughnessTexture.index >= 0;
mat.metalRoughTexIdx = pbr.metallicRoughnessTexture.index;
mat.metalRoughTexCoords = pbr.metallicRoughnessTexture.texCoord;
// 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;
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;
// Metalness and roughness
mat.metalness = gltf_material.mPbrMetallicRoughness.mMetallicFactor;
mat.roughness = gltf_material.mPbrMetallicRoughness.mRoughnessFactor;
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;
// 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;
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;
// 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;
mat.alphaMode = gltf_material.alphaMode;
mat.alphaMask = gltf_material.alphaCutoff;
// 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;
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())))
// 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<LLMatrix4, model_instance_list>, 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;
}
// retrieve UUID
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;
// 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;
}