diff options
-rw-r--r-- | indra/newview/gltf/llgltfloader.cpp | 377 | ||||
-rw-r--r-- | indra/newview/gltf/llgltfloader.h | 3 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_model_preview.xml | 2 |
3 files changed, 65 insertions, 317 deletions
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index e495abfe75..056d3df1a6 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -623,67 +623,11 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& else if (image.mBufferView >= 0) { // For embedded textures (no URI but has buffer data) - // Extract the texture to a temporary file immediately - if (image.mBufferView < mGLTFAsset.mBufferViews.size()) + std::string temp_filename = extractTextureToTempFile(texIndex, "base_color"); + if (!temp_filename.empty()) { - const LL::GLTF::BufferView& buffer_view = mGLTFAsset.mBufferViews[image.mBufferView]; - if (buffer_view.mBuffer < mGLTFAsset.mBuffers.size()) - { - const LL::GLTF::Buffer& buffer = mGLTFAsset.mBuffers[buffer_view.mBuffer]; - - if (buffer_view.mByteOffset + buffer_view.mByteLength <= buffer.mData.size()) - { - // Extract image data - const U8* data_ptr = &buffer.mData[buffer_view.mByteOffset]; - U32 data_size = buffer_view.mByteLength; - - // Determine the file extension - std::string extension = ".png"; // Default - if (!image.mMimeType.empty()) - { - if (image.mMimeType == "image/jpeg") - extension = ".jpg"; - else if (image.mMimeType == "image/png") - extension = ".png"; - } - else if (data_size >= 4) - { - if (data_ptr[0] == 0xFF && data_ptr[1] == 0xD8) - extension = ".jpg"; // JPEG magic bytes - else if (data_ptr[0] == 0x89 && data_ptr[1] == 0x50 && data_ptr[2] == 0x4E && data_ptr[3] == 0x47) - extension = ".png"; // PNG magic bytes - } - - // Create a temporary file - std::string temp_dir = gDirUtilp->getTempDir(); - std::string temp_filename = temp_dir + gDirUtilp->getDirDelimiter() + - "gltf_embedded_" + std::to_string(sourceIndex) + extension; - - // Write the image data to the temporary file - std::ofstream temp_file(temp_filename, std::ios::binary); - if (temp_file.is_open()) - { - temp_file.write(reinterpret_cast<const char*>(data_ptr), data_size); - temp_file.close(); - - // Use the temp file as the texture filename - impMat.mDiffuseMapFilename = temp_filename; - impMat.mDiffuseMapLabel = material->mName.empty() ? temp_filename : material->mName; - - LL_INFOS("GLTF_IMPORT") << "Extracted embedded texture to: " << temp_filename << LL_ENDL; - } - else - { - LL_WARNS("GLTF_IMPORT") << "Failed to create temporary file for embedded texture" << LL_ENDL; - - LLSD args; - args["Message"] = "FailedToCreateTempFile"; - args["TEXTURE_INDEX"] = sourceIndex; - args["TEMP_FILE"] = temp_filename; - mWarningsArray.append(args); - } - } - } + impMat.mDiffuseMapFilename = temp_filename; + impMat.mDiffuseMapLabel = material->mName.empty() ? temp_filename : material->mName; } } } @@ -1730,286 +1674,89 @@ void LLGLTFLoader::uploadMeshes() // convert raw image buffers to texture UUIDs & assemble into a render material void LLGLTFLoader::uploadMaterials() { - LL_INFOS("GLTF_IMPORT") << "Uploading materials, count: " << mMaterials.size() << LL_ENDL; - - for (gltf_render_material& mat : mMaterials) - { - 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; - } - } - } - - // Process other textures similarly - if (mat.hasMRTex && mat.metalRoughTexIdx < mTextures.size()) - { - gltf_texture& gtex = mTextures[mat.metalRoughTexIdx]; - if (gtex.imageUuid.isNull()) - { - gtex.imageUuid = imageBufferToTextureUUID(gtex); - } - } - - if (mat.hasNormalTex && mat.normalTexIdx < mTextures.size()) - { - gltf_texture& gtex = mTextures[mat.normalTexIdx]; - if (gtex.imageUuid.isNull()) - { - gtex.imageUuid = imageBufferToTextureUUID(gtex); - } - } - - if (mat.hasOcclusionTex && mat.occlusionTexIdx < mTextures.size()) - { - gltf_texture& gtex = mTextures[mat.occlusionTexIdx]; - if (gtex.imageUuid.isNull()) - { - gtex.imageUuid = imageBufferToTextureUUID(gtex); - } - } - - if (mat.hasEmissiveTex && mat.emissiveTexIdx < mTextures.size()) - { - gltf_texture& gtex = mTextures[mat.emissiveTexIdx]; - if (gtex.imageUuid.isNull()) - { - gtex.imageUuid = imageBufferToTextureUUID(gtex); - } - } - } - - // 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) +std::string LLGLTFLoader::extractTextureToTempFile(S32 textureIndex, const std::string& texture_type) { - if (tex.imageIdx >= mImages.size() || tex.samplerIdx >= mSamplers.size()) - { - LL_WARNS("GLTF_IMPORT") << "Invalid texture indices in imageBufferToTextureUUID" << LL_ENDL; - return LLUUID::null; - } - - gltf_image& image = mImages[tex.imageIdx]; - gltf_sampler& sampler = mSamplers[tex.samplerIdx]; + if (textureIndex < 0 || textureIndex >= mGLTFAsset.mTextures.size()) + return ""; - S32 sourceIndex = tex.imageIdx; + S32 sourceIndex = mGLTFAsset.mTextures[textureIndex].mSource; if (sourceIndex < 0 || sourceIndex >= mGLTFAsset.mImages.size()) - { - LL_WARNS("GLTF_IMPORT") << "Invalid image index: " << sourceIndex << LL_ENDL; - return LLUUID::null; - } + return ""; - LL::GLTF::Image& source_image = mGLTFAsset.mImages[sourceIndex]; + LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex]; - // If the image already has a texture loaded, use it - if (source_image.mTexture.notNull()) + // Handle URI-based textures + if (!image.mUri.empty()) { - LL_INFOS("GLTF_IMPORT") << "Using already loaded texture ID: " << source_image.mTexture->getID().asString() << LL_ENDL; - return source_image.mTexture->getID(); + return image.mUri; // Return URI directly } - // 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()) + // Handle embedded textures + if (image.mBufferView >= 0) { - 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) + if (image.mBufferView < mGLTFAsset.mBufferViews.size()) { - 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]; + const LL::GLTF::BufferView& buffer_view = mGLTFAsset.mBufferViews[image.mBufferView]; if (buffer_view.mBuffer < mGLTFAsset.mBuffers.size()) { const LL::GLTF::Buffer& buffer = mGLTFAsset.mBuffers[buffer_view.mBuffer]; + if (buffer_view.mByteOffset + buffer_view.mByteLength <= buffer.mData.size()) { - // 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); + // Extract image data + const U8* data_ptr = &buffer.mData[buffer_view.mByteOffset]; + U32 data_size = 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); + // Determine the file extension + std::string extension = ".png"; // Default + if (!image.mMimeType.empty()) + { + if (image.mMimeType == "image/jpeg") + extension = ".jpg"; + else if (image.mMimeType == "image/png") + extension = ".png"; + } + else if (data_size >= 4) + { + if (data_ptr[0] == 0xFF && data_ptr[1] == 0xD8) + extension = ".jpg"; // JPEG magic bytes + else if (data_ptr[0] == 0x89 && data_ptr[1] == 0x50 && data_ptr[2] == 0x4E && data_ptr[3] == 0x47) + extension = ".png"; // PNG magic bytes + } - // 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; + // Create a temporary file + std::string temp_dir = gDirUtilp->getTempDir(); + std::string temp_filename = temp_dir + gDirUtilp->getDirDelimiter() + + "gltf_embedded_" + texture_type + "_" + std::to_string(sourceIndex) + extension; - if (material.getDiffuseMap().notNull()) - { - LL_INFOS("GLTF_IMPORT") << "Texture loaded successfully, ID: " << material.getDiffuseMap().asString() << LL_ENDL; + // Write the image data to the temporary file + std::ofstream temp_file(temp_filename, std::ios::binary); + if (temp_file.is_open()) + { + temp_file.write(reinterpret_cast<const char*>(data_ptr), data_size); + temp_file.close(); - // 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()); + LL_INFOS("GLTF_IMPORT") << "Extracted embedded " << texture_type << " texture to: " << temp_filename << LL_ENDL; + return temp_filename; + } + else + { + LL_WARNS("GLTF_IMPORT") << "Failed to create temporary file for " << texture_type << " texture: " << temp_filename << LL_ENDL; + + LLSD args; + args["Message"] = "FailedToCreateTempFile"; + args["TEXTURE_INDEX"] = sourceIndex; + args["TEXTURE_TYPE"] = texture_type; + args["TEMP_FILE"] = temp_filename; + mWarningsArray.append(args); + } } - - return 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; + return ""; } void LLGLTFLoader::notifyUnsupportedExtension(bool unsupported) diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h index 393df8c4ee..e0b3664012 100644 --- a/indra/newview/gltf/llgltfloader.h +++ b/indra/newview/gltf/llgltfloader.h @@ -230,7 +230,8 @@ private: glm::mat4 computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const; bool checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx); void checkForXYrotation(const LL::GLTF::Skin& gltf_skin); - LLUUID imageBufferToTextureUUID(const gltf_texture& tex); + + std::string extractTextureToTempFile(S32 textureIndex, const std::string& texture_type); void notifyUnsupportedExtension(bool unsupported); diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml index 4c60320a94..80b6427738 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -71,7 +71,7 @@ <string name="IgnoredExtension">Model uses unsupported extension: [EXT], related material properties are ignored</string> <string name="UnsupportedExtension">Unable to load model, unsupported extension: [EXT]</string> <string name="TooManyMeshParts">Model contains [PART_COUNT] mesh parts. Maximum allowed: [LIMIT]</string> - <string name="FailedToCreateTempFile">Failed to create temporary file for embedded texture [TEXTURE_INDEX]: [TEMP_FILE]</string> + <string name="FailedToCreateTempFile">Failed to create temporary file for embedded [TEXTURE_TYPE] texture [TEXTURE_INDEX]: [TEMP_FILE]</string> <string name="SkinJointsOverLimit">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, maximum is: [MAX]. Unused joints will be stripped on per model basis.</string> <string name="ModelTooManyJoint">Model [MODEL_NAME] uses [JOINT_COUNT], maximum: [MAX], upload might fail</string> |