wabbajack/Wabbajack.Compiler/CompilationSteps/MatchSimilarTextures.cs

96 lines
4.5 KiB
C#
Raw Normal View History

2021-09-27 12:42:46 +00:00
using System.Linq;
2021-06-17 23:09:03 +00:00
using System.Threading.Tasks;
2021-09-27 12:42:46 +00:00
using Microsoft.Extensions.Logging;
using Wabbajack.DTOs;
using Wabbajack.DTOs.Directives;
using Wabbajack.Hashing.PHash;
using Wabbajack.Paths;
using Wabbajack.VFS;
2021-06-17 23:09:03 +00:00
2021-09-27 12:42:46 +00:00
namespace Wabbajack.Compiler.CompilationSteps
2021-06-17 23:09:03 +00:00
{
public class MatchSimilarTextures : ACompilationStep
{
private ILookup<RelativePath, VirtualFile> _byName;
public MatchSimilarTextures(ACompiler compiler) : base(compiler)
{
_byName = _compiler.IndexedFiles.SelectMany(kv => kv.Value)
.Where(f => f.Name.FileName.Extension == DDS)
2021-09-27 12:42:46 +00:00
.Where(f => f.ImageState != default)
2021-06-17 23:09:03 +00:00
.ToLookup(f => f.Name.FileName.FileNameWithoutExtension);
}
private const float PerceptualTolerance = 0.80f;
2021-06-17 23:09:03 +00:00
private static Extension DDS = new(".dds");
private static string[] PostFixes = new[] {"_n", "_d", "_s"};
2021-06-17 23:09:03 +00:00
public override async ValueTask<Directive?> Run(RawSourceFile source)
{
if (source.File.Name.FileName.Extension == DDS && source.File.ImageState != null)
2021-06-17 23:09:03 +00:00
{
2021-09-27 12:42:46 +00:00
_compiler._logger.LogInformation("Looking for texture match for {source}", source.File.FullPath);
(float Similarity, VirtualFile File) found = _byName[source.Path.FileNameWithoutExtension]
2021-09-27 12:42:46 +00:00
.Select(f => (ImageLoader.ComputeDifference(f.ImageState!.PerceptualHash, source.File.ImageState.PerceptualHash), f))
.Select(f =>
{
return f;
})
2021-06-17 23:09:03 +00:00
.OrderByDescending(f => f.Item1)
.FirstOrDefault();
if (found == default || found.Similarity <= PerceptualTolerance)
{
// This looks bad, but it's fairly simple: normal and displacement textures don't match very well
// via perceptual hashing. So instead we'll try to find a diffuse map with the same name, and look
// for normal maps in the same folders. Example: roof_n.dds didn't match, so find a match betweeen
// roof.dds and a perceptual match in the downloads. Then try to find a roof_n.dds in the same folder
// as the match we found for roof.dds.
found = default;
var r = from postfix in PostFixes
where source.File.Name.FileName.FileNameWithoutExtension.EndsWith(postfix)
let mainFileName =
source.File.Name.FileName.FileNameWithoutExtension.ToString()[..^postfix.Length] +
".dds"
2021-09-27 12:42:46 +00:00
let mainFile = source.File.InSameFolder(mainFileName.ToRelativePath())
where mainFile != null
from mainMatch in _byName[mainFile.FullPath.FileName.FileNameWithoutExtension]
where mainMatch.ImageState != null
where mainFile.ImageState != null
2021-09-27 12:42:46 +00:00
let similarity = ImageLoader.ComputeDifference(mainFile.ImageState.PerceptualHash, mainMatch.ImageState.PerceptualHash)
where similarity >= PerceptualTolerance
orderby similarity descending
let foundFile = mainMatch.InSameFolder(source.Path.FileName)
where foundFile != null
select (similarity, postfix, mainFile, mainMatch, foundFile);
2021-09-27 12:42:46 +00:00
foreach (var record in r)
{
_compiler._logger.LogInformation("Found Match for {source} {data}", source.File.Name, record.foundFile.ImageState);
}
var foundRec = r.FirstOrDefault();
if (foundRec == default)
{
return null;
}
found = (foundRec.similarity, foundRec.foundFile);
}
2021-09-27 12:42:46 +00:00
_compiler._logger.LogInformation("Found Match for {source} {sourceData} {destData}", source.File.Name, source.File.ImageState, found.File.ImageState);
2021-06-17 23:09:03 +00:00
var rv = source.EvolveTo<TransformedTexture>();
rv.ArchiveHashPath = found.File.MakeRelativePaths();
2021-09-27 12:42:46 +00:00
rv.ImageState = source.File.ImageState!;
2021-06-17 23:09:03 +00:00
return rv;
}
return null;
}
}
}