Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: extracting embedded texture files for materials from glTF file #2441

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions sources/tools/Stride.Importer.3D/Material/Materials.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using System;
using System.IO;
using Silk.NET.Assimp;
using Stride.Core.Diagnostics;
using Stride.Core.Mathematics;
Expand Down Expand Up @@ -77,7 +79,7 @@ public static unsafe class Materials
MappingMode.Decal // aiTextureMapMode_Decal
};

public static unsafe MaterialStack ConvertAssimpStackCppToCs(Silk.NET.Assimp.Assimp assimp, Silk.NET.Assimp.Material* material, Silk.NET.Assimp.TextureType type, Logger logger)
public static unsafe MaterialStack ConvertAssimpStackCppToCs(Silk.NET.Assimp.Assimp assimp, Scene* scene, Silk.NET.Assimp.Material* material, Silk.NET.Assimp.TextureType type, Logger logger)
{
var ret = new MaterialStack();
var count = (int)assimp.GetMaterialTextureCount(material, type);
Expand Down Expand Up @@ -141,8 +143,14 @@ public static unsafe MaterialStack ConvertAssimpStackCppToCs(Silk.NET.Assimp.Ass
if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialMappingmodeVBase, (uint)type, (uint)iEl, ref elMappingModeV, ref pMax) != Return.Success)
elMappingModeV = (int)TextureMapMode.Wrap; // default mapping mode

// Determine the physical texture file name
if (!TryGetTextureFileName(elTexPath.AsString, scene, out var texFileName, out var errorMessage))
{
logger?.Error(errorMessage);
continue; // error !
}
el = new StackTexture(
elTexPath.AsString,
texFileName,
elTexChannel,
ConvertAssimpMappingModeCppToCs[elMappingModeU],
ConvertAssimpMappingModeCppToCs[elMappingModeV],
Expand All @@ -160,5 +168,60 @@ public static unsafe MaterialStack ConvertAssimpStackCppToCs(Silk.NET.Assimp.Ass

return ret;
}

internal static unsafe bool TryGetTextureFileName(string materialTextureFileName, Scene* scene, out string fileName, out string errorMessage)
{
var texFileName = Path.GetFileName(materialTextureFileName);
if (!texFileName.StartsWith(Assimp.EmbeddedTexnamePrefix))
{
fileName = texFileName;
errorMessage = null;
return true;
}
// Embedded texture is denoted by '*' + index value, where the texture is accessed by scene->MTextures[index]
var texIndexStringSpan = texFileName.AsSpan().Slice(Assimp.EmbeddedTexnamePrefix.Length);
if (int.TryParse(texIndexStringSpan, out int texIndex))
{
var texture = scene->MTextures[texIndex];
return TryGetTextureFileName(texture, out fileName, out errorMessage);
}
else
{
fileName = null;
errorMessage = $"Invalid texture name for embedded texture: {texFileName} - Expected a number.";
return false;
}
}

internal unsafe static bool TryGetTextureFileName(Silk.NET.Assimp.Texture* texture, out string fileName, out string errorMessage)
{
var texFileName = Path.GetFileName(texture->MFilename.AsString);
if (Path.HasExtension(texFileName))
{
fileName = texFileName;
errorMessage = null;
return true;
}
// Formats like glTF may strip the file extension from the file name, so we need to bring this back
if (texture->MHeight != 0)
{
fileName = null;
errorMessage = $"Could not determine texture file name: Texture '{fileName}' is an embedded uncompressed texture.";
return false;
}
var fileExt = System.Text.Encoding.UTF8.GetString(texture->AchFormatHint, byteCount: Assimp.Hintmaxtexturelen).Trim('\0');
if (!string.IsNullOrEmpty(fileExt))
{
fileName = texFileName + '.' + fileExt;
errorMessage = null;
return true;
}
else
{
fileName = null;
errorMessage = $"Could not determine texture file name: Embedded Texture '{fileName}' has unknown file extension.";
return false;
}
}
}
}
52 changes: 31 additions & 21 deletions sources/tools/Stride.Importer.3D/MeshConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -869,14 +869,19 @@ private void NormalizeVertexWeights(List<List<(short, float)>> controlPts, int n
}
}


private unsafe void ExtractEmbededTexture(Scene* scene, string importFieName)
{
string dir = Path.GetDirectoryName(importFieName);
for (uint i = 0; i < scene->MNumTextures; ++i)
{
var texture=scene->MTextures[i];
string fullName = Path.Combine(dir, Path.GetFileName(texture->MFilename));
var texture = scene->MTextures[i];
if (!Material.Materials.TryGetTextureFileName(texture, out var texFileName, out var errorMessage))
{
Logger.Error(errorMessage);
continue;
}
string fullName = Path.Combine(dir, texFileName);
CreateTextureFile(texture, fullName);
}
}
Expand All @@ -903,7 +908,7 @@ private unsafe Dictionary<string, MaterialAsset> ExtractMaterials(Scene* scene,
{
var lMaterial = scene->MMaterials[i];
var materialName = materialNames[(IntPtr)lMaterial];
materials.Add(materialName, ProcessMeshMaterial(lMaterial));
materials.Add(materialName, ProcessMeshMaterial(scene, lMaterial));
}
return materials;
}
Expand All @@ -923,7 +928,7 @@ private unsafe void GenerateMaterialNames(Scene* scene, Dictionary<IntPtr, strin
GenerateUniqueNames(materialNames, baseNames, i => (IntPtr)scene->MMaterials[i]);
}

private unsafe MaterialAsset ProcessMeshMaterial(Silk.NET.Assimp.Material* pMaterial)
private unsafe MaterialAsset ProcessMeshMaterial(Scene* scene, Silk.NET.Assimp.Material* pMaterial)
{
var finalMaterial = new MaterialAsset();

Expand Down Expand Up @@ -955,16 +960,16 @@ private unsafe MaterialAsset ProcessMeshMaterial(Silk.NET.Assimp.Material* pMate
if (hasDiffColor == false)
SetMaterialColorFlag(pMaterial, Assimp.MatkeyBaseColor, ref hasDiffColor, ref diffColor, true);

BuildLayeredSurface(pMaterial, hasDiffColor, false, diffColor.ToStrideColor(), 0.0f, TextureType.Diffuse, finalMaterial);
BuildLayeredSurface(pMaterial, hasSpecColor, false, specColor.ToStrideColor(), 0.0f, TextureType.Specular, finalMaterial);
BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Normals, finalMaterial);
BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Displacement, finalMaterial);
BuildLayeredSurface(pMaterial, hasAmbientColor, false, ambientColor.ToStrideColor(), 0.0f, TextureType.Ambient, finalMaterial);
BuildLayeredSurface(pMaterial, false, hasOpacity, dummyColor.ToStrideColor(), opacity, TextureType.Opacity, finalMaterial);
BuildLayeredSurface(pMaterial, false, hasSpecPower, dummyColor.ToStrideColor(), specPower, TextureType.Shininess, finalMaterial);
BuildLayeredSurface(pMaterial, hasEmissiveColor, false, emissiveColor.ToStrideColor(), 0.0f, TextureType.Emissive, finalMaterial);
BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Height, finalMaterial);
BuildLayeredSurface(pMaterial, hasReflectiveColor, false, reflectiveColor.ToStrideColor(), 0.0f, TextureType.Reflection, finalMaterial);
BuildLayeredSurface(scene, pMaterial, hasDiffColor, false, diffColor.ToStrideColor(), 0.0f, TextureType.Diffuse, finalMaterial);
BuildLayeredSurface(scene, pMaterial, hasSpecColor, false, specColor.ToStrideColor(), 0.0f, TextureType.Specular, finalMaterial);
BuildLayeredSurface(scene, pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Normals, finalMaterial);
BuildLayeredSurface(scene, pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Displacement, finalMaterial);
BuildLayeredSurface(scene, pMaterial, hasAmbientColor, false, ambientColor.ToStrideColor(), 0.0f, TextureType.Ambient, finalMaterial);
BuildLayeredSurface(scene, pMaterial, false, hasOpacity, dummyColor.ToStrideColor(), opacity, TextureType.Opacity, finalMaterial);
BuildLayeredSurface(scene, pMaterial, false, hasSpecPower, dummyColor.ToStrideColor(), specPower, TextureType.Shininess, finalMaterial);
BuildLayeredSurface(scene, pMaterial, hasEmissiveColor, false, emissiveColor.ToStrideColor(), 0.0f, TextureType.Emissive, finalMaterial);
BuildLayeredSurface(scene, pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Height, finalMaterial);
BuildLayeredSurface(scene, pMaterial, hasReflectiveColor, false, reflectiveColor.ToStrideColor(), 0.0f, TextureType.Reflection, finalMaterial);

return finalMaterial;
}
Expand All @@ -989,7 +994,7 @@ private bool IsNotBlackColor(System.Numerics.Vector4 diffColor)
return diffColor != System.Numerics.Vector4.Zero;
}

private unsafe void BuildLayeredSurface(Silk.NET.Assimp.Material* pMat, bool hasBaseColor, bool hasBaseValue, Color4 baseColor, float baseValue, TextureType textureType, MaterialAsset finalMaterial)
private unsafe void BuildLayeredSurface(Scene* scene, Silk.NET.Assimp.Material* pMat, bool hasBaseColor, bool hasBaseValue, Color4 baseColor, float baseValue, TextureType textureType, MaterialAsset finalMaterial)
{
var nbTextures = assimp.GetMaterialTextureCount(pMat, textureType);

Expand All @@ -1008,7 +1013,7 @@ private unsafe void BuildLayeredSurface(Silk.NET.Assimp.Material* pMat, bool has
}
else
{
computeColorNode = GenerateOneTextureTypeLayers(pMat, textureType, textureCount, finalMaterial);
computeColorNode = GenerateOneTextureTypeLayers(scene, pMat, textureType, textureCount, finalMaterial);
}

if (computeColorNode == null)
Expand All @@ -1020,7 +1025,7 @@ private unsafe void BuildLayeredSurface(Silk.NET.Assimp.Material* pMat, bool has
{
if (assimp.GetMaterialTextureCount(pMat, TextureType.Lightmap) > 0)
{
var lightMap = GenerateOneTextureTypeLayers(pMat, TextureType.Lightmap, textureCount, finalMaterial);
var lightMap = GenerateOneTextureTypeLayers(scene, pMat, TextureType.Lightmap, textureCount, finalMaterial);
if (lightMap != null)
computeColorNode = new ComputeBinaryColor(computeColorNode, lightMap, BinaryOperator.Add);
}
Expand Down Expand Up @@ -1085,9 +1090,9 @@ private unsafe void BuildLayeredSurface(Silk.NET.Assimp.Material* pMat, bool has
}
}

private unsafe IComputeColor GenerateOneTextureTypeLayers(Silk.NET.Assimp.Material* pMat, TextureType textureType, int textureCount, MaterialAsset finalMaterial)
private unsafe IComputeColor GenerateOneTextureTypeLayers(Scene* scene, Silk.NET.Assimp.Material* pMat, TextureType textureType, int textureCount, MaterialAsset finalMaterial)
{
var stack = Material.Materials.ConvertAssimpStackCppToCs(assimp, pMat, textureType, Logger);
var stack = Material.Materials.ConvertAssimpStackCppToCs(assimp, scene, pMat, textureType, Logger);

var compositionFathers = new Stack<IComputeColor>();

Expand Down Expand Up @@ -1345,7 +1350,12 @@ private unsafe List<string> ExtractTextureDependencies(Scene* scene)

if (assimp.GetMaterialTexture(lMaterial, textureType, j, ref path, ref mapping, ref uvIndex, ref blend, ref textureOp, ref mapMode, ref flags) == Return.Success)
{
var relFileName = Path.GetFileName(path.AsString);
if (!Material.Materials.TryGetTextureFileName(path.AsString, scene, out var texFileName, out var errorMessage))
{
Logger.Error(errorMessage);
break;
}
var relFileName = texFileName;
var fileNameToUse = Path.Combine(vfsInputPath, relFileName);
textureNames.Add(fileNameToUse);
break;
Expand Down