Files
UnrealEngine/Engine/Source/Editor/LandscapeEditor/Private/LandscapeTiledImage.cpp
2025-05-18 13:04:45 +08:00

232 lines
6.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LandscapeTiledImage.h"
#include "HAL/FileManager.h"
#include "Internationalization/Regex.h"
#include "LandscapeImageFileCache.h"
#include "Internationalization/Internationalization.h"
#define LOCTEXT_NAMESPACE "LandscapeEditor"
TMap<FString, FString> FLandscapeTiledImage::Tokens = {
{ TEXT("u"), TEXT("<u>") },
{ TEXT("v"), TEXT("<v>") },
{ TEXT("x"), TEXT("<x>") },
{ TEXT("y"), TEXT("<y>") }
};
FString FLandscapeTiledImage::GetTokenRegex(const FString& Prefix)
{
return Prefix + TEXT("(-?[0-9]+)");
}
FLandscapeTiledImage::FLandscapeTiledImage()
{
}
template <typename T>
FLandscapeFileInfo FLandscapeTiledImage::Load(const TCHAR* Filename)
{
FLandscapeFileInfo Result;
TArray<FString> FoundFiles;
FindFiles(Filename, FoundFiles);
FString Path = FPaths::GetPath(Filename);
FString FilenameNoPath = FPaths::GetCleanFilename(Filename);
FString RegexFilename(FilenameNoPath);
TMap<int32, FString> Groups;
for (const TPair<FString, FString>& It : Tokens)
{
if (int32 StartIndex = RegexFilename.Find(It.Value); StartIndex != INDEX_NONE)
{
Groups.Add(StartIndex, It.Value);
}
}
Groups.KeySort([](int32 A, int32 B) { return A < B; });
for (const TPair<FString, FString>& It : Tokens)
{
if (int32 start = RegexFilename.Find(It.Value); start != INDEX_NONE)
{
RegexFilename.RemoveAt(start, It.Value.Len());
RegexFilename.InsertAt(start, GetTokenRegex(""));
}
}
const bool bExactFilename = RegexFilename == FilenameNoPath;
SizeInTiles = FIntPoint::NoneValue;
TileResolution = FIntPoint::NoneValue;
if (FoundFiles.Num() == 1 && bExactFilename)
{
FIntPoint TileCoord(0, 0);
TileFilenames.Add(TileCoord, FString(Filename));
}
else
{
for (const FString& FoundFilname : FoundFiles)
{
FRegexPattern Pattern(RegexFilename);
// Regex test the filename without the path so that conflicting symbols (e.g. brackets) in the path don't confuse it.
FRegexMatcher Matcher(Pattern, FoundFilname);
if (Matcher.FindNext())
{
int32 X = -1;
int32 Y = -1;
int32 Group = 1;
for (const TPair<int32, FString> &it : Groups)
{
if (it.Value == "<u>")
{
X = FCString::Atoi(*Matcher.GetCaptureGroup(Group++)) - 1;
}
else if (it.Value == "<x>")
{
X = FCString::Atoi(*Matcher.GetCaptureGroup(Group++));
}
else if (it.Value == "<v>")
{
Y = FCString::Atoi(*Matcher.GetCaptureGroup(Group++)) - 1;
}
else if (it.Value == "<y>")
{
Y = FCString::Atoi(*Matcher.GetCaptureGroup(Group++));
}
}
if (X >= 0 && Y >= 0)
{
FIntPoint TileCoord(X, Y);
FString FullPath = FPaths::Combine(Path, FoundFilname);
TileFilenames.Add(TileCoord, FullPath);
}
}
}
}
for (const TPair<FIntPoint, FString> & Tile : TileFilenames)
{
FLandscapeImageDataRef ImageData;
FLandscapeImageFileCache& LandscapeImageFileCache = FModuleManager::GetModuleChecked<ILandscapeEditorModule>("LandscapeEditor").GetImageFileCache();
FLandscapeFileInfo TileResult = LandscapeImageFileCache.FindImage<T>(*Tile.Value, ImageData);
if (TileResult.ResultCode == ELandscapeImportResult::Error)
{
return TileResult;
}
else if (TileResult.ResultCode == ELandscapeImportResult::Warning)
{
Result.ResultCode = TileResult.ResultCode;
Result.ErrorMessage = TileResult.ErrorMessage;
}
SizeInTiles.X = FMath::Max(SizeInTiles.X, Tile.Key.X + 1);
SizeInTiles.Y = FMath::Max(SizeInTiles.Y, Tile.Key.Y + 1);
uint32 Width = ImageData.Resolution.X;
uint32 Height = ImageData.Resolution.Y;
if (TileResolution == FIntPoint::NoneValue)
{
TileResolution.X = Width;
TileResolution.Y = Height;
}
else if (Width != TileResolution.X && Height != TileResolution.Y)
{
Result.ResultCode = ELandscapeImportResult::Error;
Result.ErrorMessage = LOCTEXT("FileReadErrorTiledResolutionMismatch", "Mismatched resolution found in tiled image");
return Result;
}
}
if (TileFilenames.Num() == 0)
{
Result.ResultCode = ELandscapeImportResult::Error;
Result.ErrorMessage = LOCTEXT("FileReadErrorNoFilesFound", "No files found");
return Result;
}
// Check for int overflows due to too large SizeInTiles values from the filenames
if (SizeInTiles.X > std::numeric_limits<int32>::max() / TileResolution.X ||
SizeInTiles.Y > std::numeric_limits<int32>::max() / TileResolution.Y ||
SizeInTiles.X <= 0 || SizeInTiles.Y <= 0)
{
Result.ResultCode = ELandscapeImportResult::Error;
Result.ErrorMessage = LOCTEXT("FileReadErrorTileCoordsInvalid", "Invalid tiled image coordinates");
return Result;
}
Result.PossibleResolutions.Add(FLandscapeFileResolution(GetResolution().X, GetResolution().Y));
return Result;
}
template FLandscapeFileInfo FLandscapeTiledImage::Load<uint8>(const TCHAR* Filename);
template FLandscapeFileInfo FLandscapeTiledImage::Load<uint16>(const TCHAR* Filename);
void FLandscapeTiledImage::FindFiles(const TCHAR* FilenamePattern, TArray<FString>& OutPaths)
{
FString GlobFilename(FilenamePattern);
for (const TPair<FString, FString>& It : Tokens)
{
GlobFilename = GlobFilename.Replace(*It.Value, TEXT("*"));
}
OutPaths.Empty();
IFileManager::Get().FindFiles(OutPaths, *GlobFilename, true, false);
}
bool FLandscapeTiledImage::CheckTiledNamePath(const FString& Filename, FString& OutTiledFilenamePattern)
{
const FString Extension = FPaths::GetExtension(Filename);
const FString Root = FPaths::GetPath(Filename);
const FString BaseFilename = FPaths::GetBaseFilename(Filename);
bool HasMatch = true;
FString CurrentFilename = BaseFilename;
int32 TokenIndex = 0;
for (const TPair<FString, FString>& It : Tokens)
{
while (true)
{
FRegexMatcher Matcher(FRegexPattern(GetTokenRegex(It.Key)), CurrentFilename);
if (!Matcher.FindNext())
{
break;
}
int32 MatchBegin = Matcher.GetMatchBeginning();
int32 MatchEnd = Matcher.GetMatchEnding();
CurrentFilename.RemoveAt(MatchBegin, MatchEnd - MatchBegin);
CurrentFilename.InsertAt(MatchBegin, It.Key + It.Value);
}
}
const FString PatternFilename = CurrentFilename + FString(".") + Extension;
FString TiledFilenamePattern = FPaths::Combine(Root, PatternFilename);
TArray<FString> FoundFiles;
FLandscapeTiledImage::FindFiles(*TiledFilenamePattern, FoundFiles);
const bool bFoundTiledFiles = FoundFiles.Num() > 0 && TiledFilenamePattern != Filename;
if (bFoundTiledFiles )
{
OutTiledFilenamePattern = TiledFilenamePattern;
}
return bFoundTiledFiles;
}
#undef LOCTEXT_NAMESPACE