369 lines
15 KiB
C#
369 lines
15 KiB
C#
using UnityEngine;
|
||
using UnityEditor;
|
||
using System.IO;
|
||
using System.Collections.Generic;
|
||
|
||
public class TextureMergerWindow : EditorWindow
|
||
{
|
||
// 可选的目标合并贴图分辨率选项
|
||
public enum MergeResolution
|
||
{
|
||
_1024 = 1024,
|
||
_2048 = 2048,
|
||
_4096 = 4096,
|
||
_8192 = 8192
|
||
}
|
||
|
||
private TextAsset terrainInfo; // 记录贴图信息的 TextAsset(例如 TerrainInfo.txt)
|
||
private MergeResolution selectedResolution = MergeResolution._1024;
|
||
|
||
// 自定义行列设置,默认不启用
|
||
private bool useCustomGrid = false;
|
||
private int customGridCount = 8; // 自定义行列数(行数=列数,例如8行8列)
|
||
|
||
// 附加贴图设置(法线、金属度、平滑度、高度)
|
||
private bool _isMergeNormalMaps;
|
||
private string _mergeNormalNameSuffix = "_normal"; // 法线贴图后缀
|
||
|
||
private bool _isMergeMetallicMaps;
|
||
private string _mergeMetallicMapSuffix = "_metallic"; // 金属度贴图后缀
|
||
|
||
private bool _isMergeSmoothnessMaps;
|
||
private string _mergeSmoothnessMapNameSuffix = "_smoothness"; // 平滑度贴图后缀
|
||
|
||
private bool _isMergeHeightMaps;
|
||
private string _mergeHeightMapNameSuffix = "_height"; // 高度贴图后缀
|
||
|
||
// 保存路径
|
||
private string saveFolder = Application.dataPath;
|
||
|
||
[MenuItem("Window/Texture Merger")]
|
||
public static void ShowWindow()
|
||
{
|
||
GetWindow<TextureMergerWindow>("Texture Merger");
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
GUILayout.Label("贴图合并设置", EditorStyles.boldLabel);
|
||
|
||
terrainInfo =
|
||
(TextAsset)EditorGUILayout.ObjectField("Terrain Info (TextAsset)", terrainInfo, typeof(TextAsset), false);
|
||
selectedResolution = (MergeResolution)EditorGUILayout.EnumPopup("合并后贴图分辨率", selectedResolution);
|
||
|
||
useCustomGrid = EditorGUILayout.Toggle("使用自定义行列", useCustomGrid);
|
||
if (useCustomGrid)
|
||
{
|
||
customGridCount = EditorGUILayout.IntField("网格数量 (行=列)", customGridCount);
|
||
if (customGridCount < 1)
|
||
customGridCount = 1;
|
||
}
|
||
|
||
// 附加贴图设置
|
||
GUILayout.Space(10);
|
||
GUILayout.Label("附加贴图设置", EditorStyles.boldLabel);
|
||
|
||
_isMergeNormalMaps = EditorGUILayout.Toggle("合并法线贴图", _isMergeNormalMaps);
|
||
if (_isMergeNormalMaps)
|
||
_mergeNormalNameSuffix = EditorGUILayout.TextField("法线贴图后缀", _mergeNormalNameSuffix);
|
||
|
||
_isMergeMetallicMaps = EditorGUILayout.Toggle("合并金属度贴图", _isMergeMetallicMaps);
|
||
if (_isMergeMetallicMaps)
|
||
_mergeMetallicMapSuffix = EditorGUILayout.TextField("金属度贴图后缀", _mergeMetallicMapSuffix);
|
||
|
||
_isMergeSmoothnessMaps = EditorGUILayout.Toggle("合并平滑度贴图", _isMergeSmoothnessMaps);
|
||
if (_isMergeSmoothnessMaps)
|
||
_mergeSmoothnessMapNameSuffix = EditorGUILayout.TextField("平滑度贴图后缀", _mergeSmoothnessMapNameSuffix);
|
||
|
||
_isMergeHeightMaps = EditorGUILayout.Toggle("合并高度贴图", _isMergeHeightMaps);
|
||
if (_isMergeHeightMaps)
|
||
_mergeHeightMapNameSuffix = EditorGUILayout.TextField("高度贴图后缀", _mergeHeightMapNameSuffix);
|
||
|
||
// 保存路径选择
|
||
GUILayout.Space(10);
|
||
GUILayout.Label("保存路径设置", EditorStyles.boldLabel);
|
||
if (GUILayout.Button("选择保存路径"))
|
||
{
|
||
string folder = EditorUtility.OpenFolderPanel("选择保存路径", Application.dataPath, "");
|
||
if (!string.IsNullOrEmpty(folder))
|
||
{
|
||
saveFolder = folder;
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.LabelField("当前保存路径:", string.IsNullOrEmpty(saveFolder) ? "未选择" : saveFolder);
|
||
|
||
GUILayout.Space(10);
|
||
if (GUILayout.Button("合并贴图"))
|
||
{
|
||
if (terrainInfo == null)
|
||
{
|
||
EditorUtility.DisplayDialog("错误", "请先指定包含贴图信息的 TextAsset", "确定");
|
||
return;
|
||
}
|
||
|
||
MergeTextures(terrainInfo, (int)selectedResolution);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据 terrainInfo 中的信息加载基础贴图及附加贴图,并分别合并到目标分辨率的贴图上
|
||
/// </summary>
|
||
private void MergeTextures(TextAsset terrainInfo, int targetResolution)
|
||
{
|
||
// 准备各贴图列表
|
||
List<Texture2D> baseTextures = new List<Texture2D>();
|
||
List<Texture2D> normalTextures = new List<Texture2D>();
|
||
List<Texture2D> metallicTextures = new List<Texture2D>();
|
||
List<Texture2D> smoothnessTextures = new List<Texture2D>();
|
||
List<Texture2D> heightTextures = new List<Texture2D>();
|
||
|
||
string[] lines = terrainInfo.text.Split(new char[] { '\n' }, System.StringSplitOptions.RemoveEmptyEntries);
|
||
foreach (string line in lines)
|
||
{
|
||
string trimmedLine = line.Trim();
|
||
if (string.IsNullOrEmpty(trimmedLine))
|
||
continue;
|
||
|
||
// 每行要求格式为:其他信息\t贴图名称
|
||
string[] parts = trimmedLine.Split(new char[] { '\t' }, System.StringSplitOptions.RemoveEmptyEntries);
|
||
if (parts.Length < 2)
|
||
continue;
|
||
|
||
string texName = Path.GetFileNameWithoutExtension(parts[1]);
|
||
|
||
// 加载基础贴图
|
||
string basePath = "Textures/Terrain/" + texName;
|
||
Texture2D baseTex = Resources.Load<Texture2D>(basePath);
|
||
if (baseTex != null)
|
||
{
|
||
baseTextures.Add(baseTex);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("在路径 " + basePath + " 找不到基础贴图");
|
||
}
|
||
|
||
// 加载法线贴图(必须存在,否则提示错误)
|
||
if (_isMergeNormalMaps)
|
||
{
|
||
string normalPath = "Textures/Terrain/" + texName + _mergeNormalNameSuffix;
|
||
Texture2D normalTex = Resources.Load<Texture2D>(normalPath);
|
||
if (normalTex != null)
|
||
{
|
||
normalTextures.Add(normalTex);
|
||
}
|
||
else
|
||
{
|
||
EditorUtility.DisplayDialog("错误", "法线贴图缺失: " + normalPath, "确定");
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 加载金属度贴图
|
||
if (_isMergeMetallicMaps)
|
||
{
|
||
string metallicPath = "Textures/Terrain/" + texName + _mergeMetallicMapSuffix;
|
||
Texture2D metallicTex = Resources.Load<Texture2D>(metallicPath);
|
||
if (metallicTex != null)
|
||
{
|
||
metallicTextures.Add(metallicTex);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("在路径 " + metallicPath + " 找不到金属度贴图");
|
||
}
|
||
}
|
||
|
||
// 加载平滑度贴图
|
||
if (_isMergeSmoothnessMaps)
|
||
{
|
||
string smoothnessPath = "Textures/Terrain/" + texName + _mergeSmoothnessMapNameSuffix;
|
||
Texture2D smoothnessTex = Resources.Load<Texture2D>(smoothnessPath);
|
||
if (smoothnessTex != null)
|
||
{
|
||
smoothnessTextures.Add(smoothnessTex);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("在路径 " + smoothnessPath + " 找不到平滑度贴图");
|
||
}
|
||
}
|
||
|
||
// 加载高度贴图
|
||
if (_isMergeHeightMaps)
|
||
{
|
||
string heightPath = "Textures/Terrain/" + texName + _mergeHeightMapNameSuffix;
|
||
Texture2D heightTex = Resources.Load<Texture2D>(heightPath);
|
||
if (heightTex != null)
|
||
{
|
||
heightTextures.Add(heightTex);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("在路径 " + heightPath + " 找不到高度贴图");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (baseTextures.Count == 0)
|
||
{
|
||
Debug.LogError("没有找到可合并的基础贴图!");
|
||
return;
|
||
}
|
||
|
||
// 合并基础贴图
|
||
Texture2D mergedBase = MergeTextureList(baseTextures, targetResolution);
|
||
SaveTexture(mergedBase,
|
||
Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder,
|
||
"MergedTexture.png"));
|
||
|
||
// 合并法线贴图
|
||
if (_isMergeNormalMaps)
|
||
{
|
||
if (normalTextures.Count != baseTextures.Count)
|
||
{
|
||
EditorUtility.DisplayDialog("错误", "法线贴图数量与基础贴图数量不匹配!", "确定");
|
||
return;
|
||
}
|
||
|
||
Texture2D mergedNormal = MergeTextureList(normalTextures, targetResolution);
|
||
SaveTexture(mergedNormal,
|
||
Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder,
|
||
"MergedTexture" + _mergeNormalNameSuffix + ".png"));
|
||
}
|
||
|
||
// 合并金属度贴图
|
||
if (_isMergeMetallicMaps && metallicTextures.Count > 0)
|
||
{
|
||
Texture2D mergedMetallic = MergeTextureList(metallicTextures, targetResolution);
|
||
SaveTexture(mergedMetallic,
|
||
Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder,
|
||
"MergedTexture" + _mergeMetallicMapSuffix + ".png"));
|
||
}
|
||
|
||
// 合并平滑度贴图
|
||
if (_isMergeSmoothnessMaps && smoothnessTextures.Count > 0)
|
||
{
|
||
Texture2D mergedSmoothness = MergeTextureList(smoothnessTextures, targetResolution);
|
||
SaveTexture(mergedSmoothness,
|
||
Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder,
|
||
"MergedTexture" + _mergeSmoothnessMapNameSuffix + ".png"));
|
||
}
|
||
|
||
// 合并高度贴图
|
||
if (_isMergeHeightMaps && heightTextures.Count > 0)
|
||
{
|
||
Texture2D mergedHeight = MergeTextureList(heightTextures, targetResolution);
|
||
SaveTexture(mergedHeight,
|
||
Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder,
|
||
"MergedTexture" + _mergeHeightMapNameSuffix + ".png"));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将传入的贴图列表按照设定的网格规则合并到一张贴图中
|
||
/// </summary>
|
||
private Texture2D MergeTextureList(List<Texture2D> textureList, int targetResolution)
|
||
{
|
||
int gridCols, gridRows, tileSize, intermediateWidth, intermediateHeight;
|
||
if (useCustomGrid)
|
||
{
|
||
gridCols = customGridCount;
|
||
gridRows = customGridCount;
|
||
int maxTiles = gridCols * gridRows;
|
||
if (textureList.Count > maxTiles)
|
||
{
|
||
Debug.LogWarning("贴图数量超过设定的网格数(" + maxTiles + "),将只合并前 " + maxTiles + " 个贴图。");
|
||
textureList = textureList.GetRange(0, maxTiles);
|
||
}
|
||
|
||
tileSize = targetResolution / gridCols;
|
||
intermediateWidth = targetResolution;
|
||
intermediateHeight = targetResolution;
|
||
}
|
||
else
|
||
{
|
||
int count = textureList.Count;
|
||
gridCols = Mathf.CeilToInt(Mathf.Sqrt(count));
|
||
gridRows = Mathf.CeilToInt((float)count / gridCols);
|
||
tileSize = 512;
|
||
intermediateWidth = gridCols * tileSize;
|
||
intermediateHeight = gridRows * tileSize;
|
||
}
|
||
|
||
// 创建中间纹理,填充黑色背景
|
||
Texture2D intermediateTexture =
|
||
new Texture2D(intermediateWidth, intermediateHeight, TextureFormat.RGBA32, false);
|
||
Color[] fillColor = new Color[intermediateWidth * intermediateHeight];
|
||
for (int i = 0; i < fillColor.Length; i++)
|
||
fillColor[i] = Color.black;
|
||
intermediateTexture.SetPixels(fillColor);
|
||
|
||
// 将每张贴图缩放后依次放入中间纹理
|
||
for (int i = 0; i < textureList.Count; i++)
|
||
{
|
||
Texture2D srcTex = textureList[i];
|
||
Texture2D resizedTex = ScaleTexture(srcTex, tileSize, tileSize);
|
||
int col = i % gridCols;
|
||
int row = i / gridCols;
|
||
int startX = col * tileSize;
|
||
int startY = (gridRows - 1 - row) * tileSize;
|
||
intermediateTexture.SetPixels(startX, startY, tileSize, tileSize, resizedTex.GetPixels());
|
||
}
|
||
|
||
intermediateTexture.Apply();
|
||
|
||
// 最终贴图:初始化为目标分辨率,并将中间纹理缩放后居中放置
|
||
Texture2D finalTexture = new Texture2D(targetResolution, targetResolution, TextureFormat.RGBA32, false);
|
||
Color[] finalFill = new Color[targetResolution * targetResolution];
|
||
for (int i = 0; i < finalFill.Length; i++)
|
||
finalFill[i] = Color.black;
|
||
finalTexture.SetPixels(finalFill);
|
||
|
||
float scaleFactor = Mathf.Min((float)targetResolution / intermediateWidth,
|
||
(float)targetResolution / intermediateHeight);
|
||
int scaledWidth = Mathf.RoundToInt(intermediateWidth * scaleFactor);
|
||
int scaledHeight = Mathf.RoundToInt(intermediateHeight * scaleFactor);
|
||
Texture2D scaledIntermediate = ScaleTexture(intermediateTexture, scaledWidth, scaledHeight);
|
||
int offsetX = (targetResolution - scaledWidth) / 2;
|
||
int offsetY = (targetResolution - scaledHeight) / 2;
|
||
finalTexture.SetPixels(offsetX, offsetY, scaledWidth, scaledHeight, scaledIntermediate.GetPixels());
|
||
finalTexture.Apply();
|
||
|
||
return finalTexture;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用 RenderTexture 和 Graphics.Blit 进行高性能的贴图缩放
|
||
/// 1. 结果贴图统一使用 RGBA32 格式
|
||
/// 2. Blit 后调用 GL.Flush() 确保渲染命令执行完毕
|
||
/// 3. 保存并恢复之前的 RenderTexture.active
|
||
/// </summary>
|
||
private Texture2D ScaleTexture(Texture2D source, int targetWidth, int targetHeight)
|
||
{
|
||
RenderTexture rt = RenderTexture.GetTemporary(targetWidth, targetHeight);
|
||
rt.filterMode = FilterMode.Bilinear;
|
||
RenderTexture previous = RenderTexture.active;
|
||
RenderTexture.active = rt;
|
||
Graphics.Blit(source, rt);
|
||
GL.Flush();
|
||
Texture2D result = new Texture2D(targetWidth, targetHeight, TextureFormat.RGBA32, false);
|
||
result.ReadPixels(new Rect(0, 0, targetWidth, targetHeight), 0, 0);
|
||
result.Apply();
|
||
RenderTexture.active = previous;
|
||
RenderTexture.ReleaseTemporary(rt);
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将生成的贴图保存为 PNG 文件,并刷新 AssetDatabase
|
||
/// </summary>
|
||
private void SaveTexture(Texture2D texture, string path)
|
||
{
|
||
byte[] pngData = texture.EncodeToPNG();
|
||
File.WriteAllBytes(path, pngData);
|
||
Debug.Log("合并贴图已保存至: " + path);
|
||
AssetDatabase.Refresh();
|
||
}
|
||
} |