Skip to content

Commit

Permalink
[Importer] Fix extracting embedded texture files for materials, eg. f…
Browse files Browse the repository at this point in the history
…rom glTF file (#2441)

Co-authored-by: Basewq <Basewq@users.noreply.github.com>
  • Loading branch information
Basewq and Basewq authored Sep 12, 2024
1 parent 26954a5 commit c21272c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 23 deletions.
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

0 comments on commit c21272c

Please sign in to comment.