KopMap/Assets/MindPowerSdk/EditorWindow/TextureMergerWindow.cs

369 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}