我正在尝试制作一个程序来合并 fbx 模型。每个模型都有多个节点。一些节点有一个特定的名称,这意味着它们充当其他模型的连接点。
我已经解决了几乎所有有关合并的问题,除了纹理问题。网格和材质索引已更新,但当涉及到材质作为文件路径引用的纹理时,我遇到了一个奇怪的问题。 为了解决合并具有不同完整路径的模型的问题,纹理会导致问题,因为它们是通过模型文件的相对路径引用的。
加载以下基本代码时:
// Initialize the Assimp context
AssimpContext importer = new AssimpContext();
// Load the model with desired post-processing flags
Scene model = importer.ImportFile(filePath, PostProcessPreset.TargetRealTimeMaximumQuality |
PostProcessSteps.Triangulate |
PostProcessSteps.FlipUVs |
PostProcessSteps.EmbedTextures |
PostProcessSteps.GlobalScale |
PostProcessSteps.ValidateDataStructure);
材质存储的路径看起来很奇怪。 例如,与 fbx 文件本身位于同一文件夹中的文件“Normal.jpg”的相对路径存储为“*1 mal.jpg”,另一个存储在名为“GPV-2.fbm\BaseColor”的 .fbm 子文件夹中。 jpg”为“*0 -2 BG.fbm\BaseColor.jpg”。 对我来说,前 3 个字符似乎在导入过程中的某个时刻被覆盖。
我使用 Assimp.NET 5.0.0-beta1 包。
以前有人见过这样的事情吗?路径看起来像这样的原因是什么?我该如何解决它?
由于 Assimp.NET 加载纹理没有问题,我猜导入期间路径是好的。也许我可以以某种方式从 assimp 中获取完整路径?
Path.Combine 不起作用。我尝试手动剪切碎片,但丢失的字符仍然会导致问题,如果没有其他方法,我会尝试文件搜索。
我尝试查看 Assimp 代码,但找不到它到底是如何导入的。
我也尝试与ChatGPT讨论它,但也无法解决它,也无法给我指示去哪里寻找。
同时我找到了解决方案。
如果引用了纹理,那么 GLB 文件实际上引用了某种图像,例如旁边的 JPEG,那么模型中的材质将引用作为文件路径。 然而,在嵌入纹理的情况下,它们会转换为压缩的二进制形式并添加到场景的“纹理”属性中。然后材质使用“*index”作为文件路径来知道它不是外部引用,而是嵌入的引用。由于字符串处理方面的差异,“ ”可能来自 C++/C# 兼容性错误。
相对于我想要做的实际任务(合并模型),现在的解决方案相当简单。我刚刚将嵌入纹理添加到根模型纹理中,然后调整子模型材质中的索引,然后再将它们添加到根中。
private static void MergeModelData(string path, Scene model, Scene subModel)
{
UpdateMeshIndices(model, subModel.RootNode);
foreach (Mesh mesh in subModel.Meshes)
{
mesh.MaterialIndex += model.MaterialCount;
}
int idx = model.Textures.Count;
model.Textures.AddRange(subModel.Textures);
foreach (Material mat in subModel.Materials)
{
AppendMaterial(model, mat, mat.TextureAmbient, path, idx);
AppendMaterial(model, mat, mat.TextureAmbientOcclusion, path, idx);
AppendMaterial(model, mat, mat.TextureDiffuse, path, idx);
AppendMaterial(model, mat, mat.TextureDisplacement, path, idx);
AppendMaterial(model, mat, mat.TextureEmissive, path, idx);
AppendMaterial(model, mat, mat.TextureHeight, path, idx);
AppendMaterial(model, mat, mat.TextureLightMap, path, idx);
AppendMaterial(model, mat, mat.TextureNormal, path, idx);
AppendMaterial(model, mat, mat.TextureOpacity, path, idx);
AppendMaterial(model, mat, mat.TextureReflection, path, idx);
AppendMaterial(model, mat, mat.TextureSpecular, path, idx);
}
model.Meshes.AddRange(subModel.Meshes);
}
private static void UpdateMeshIndices(Scene model, Node currentNode)
{
if (currentNode.HasMeshes)
{
for (int i = 0; i < currentNode.MeshIndices.Count; i++)
{
currentNode.MeshIndices[i] += model.MeshCount;
}
}
foreach (Node child in currentNode.Children)
{
UpdateMeshIndices(model, child);
}
}
private static void AppendMaterial(Scene model, Material mat, TextureSlot slot, string path, int idx)
{
if (slot.FilePath == null)
{
return;
}
string texPath;
SwitchTextureInMaterial(mat, slot, $"*{idx++}\0");
model.Materials.Add(mat);
}
private static void SwitchTextureInMaterial(Material mat, TextureSlot slot, string texPath)
{
TextureSlot ts = new TextureSlot();
ts.FilePath = texPath;
ts.TextureType = slot.TextureType;
ts.TextureIndex = slot.TextureIndex;
ts.Mapping = slot.Mapping;
ts.UVIndex = slot.UVIndex;
ts.BlendFactor = slot.BlendFactor;
ts.Operation = slot.Operation;
ts.WrapModeU = slot.WrapModeU;
ts.WrapModeV = slot.WrapModeV;
ts.Flags = slot.Flags;
mat.RemoveMaterialTexture(slot);
mat.AddMaterialTexture(in ts);
}
我希望我能帮助下一个尝试同样做法的人。 =)