GLTF import texture upload
parent
d679e7f3ba
commit
b3d1a1cb16
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue