442 lines
18 KiB
C#
442 lines
18 KiB
C#
using Thousandto.Core.Asset;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using UnityEditor;
|
||
using UnityEditor.SceneManagement;
|
||
using UnityEngine;
|
||
|
||
namespace Thousandto.Editor.Test
|
||
{
|
||
/// <summary>
|
||
/// 从lightmap中扣除对应Renderer的光照贴图,并且与同一网格的
|
||
/// 光照贴图合并成一个新的lightmap
|
||
/// </summary>
|
||
public static class LightMapSpliter
|
||
{
|
||
public class RendererInfo
|
||
{
|
||
public string MapName;
|
||
public string RootGoName;
|
||
//索引
|
||
public int Index;
|
||
public Renderer Renderer;
|
||
public Mesh Mesh;
|
||
//截取的lightmap
|
||
public Texture2D MyLightmap;
|
||
public Vector2 Position;
|
||
//原始bound
|
||
public Vector4 OldBound;
|
||
public Vector4 OldScaledBound;
|
||
|
||
//裁剪矩形,从完整lightmap中裁剪范围
|
||
public Rect CutRect;
|
||
//拆分lightmap后的新bound
|
||
public Vector4 MyLightmapScaleInfo;
|
||
}
|
||
|
||
public static void Start()
|
||
{
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将gameobject通过名字中的index来分类
|
||
/// </summary>
|
||
/// <param name="rootGos"></param>
|
||
/// <returns></returns>
|
||
public static Dictionary<int, List<GameObject>> SortAsIndex(GameObject[] rootGos)
|
||
{
|
||
Dictionary<int, List<GameObject>> retDict = new Dictionary<int, List<GameObject>>();
|
||
for (int i = 0; i < rootGos.Length; ++i)
|
||
{
|
||
var strArray = rootGos[i].name.Split('_');
|
||
if (strArray.Length != 2)
|
||
continue;
|
||
int index = int.Parse(strArray[1]);
|
||
Utils.AddToDict(retDict, index, rootGos[i]);
|
||
}
|
||
|
||
return retDict;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建新的lightmap,并且生成配置文件
|
||
/// </summary>
|
||
/// <param name="rootGos"></param>
|
||
public static void CreateCombinedLightmap(GameObject[] rootGos)
|
||
{
|
||
RequirePrefabLightmapInfoScript(rootGos);
|
||
var sortGos = SortAsIndex(rootGos);
|
||
var itor = sortGos.GetEnumerator();
|
||
List<string> configInfos = new List<string>();
|
||
PackDataInfoList pdList = new PackDataInfoList();
|
||
EditorUtility.DisplayProgressBar("新lightmap", "拆分lightmap", 0);
|
||
int counter = 0;
|
||
while (itor.MoveNext())
|
||
{
|
||
EditorUtility.DisplayProgressBar("新lightmap", "拆分lightmap", (counter++)/ (float)sortGos.Count);
|
||
var gos = itor.Current.Value.ToArray();
|
||
var rdInfoList = ScanRendererInfos(gos);
|
||
Rect rect;
|
||
Rect[] packedRects;
|
||
//CombineRendererInfos(rdInfoList, out rect);
|
||
CombineRendererInfos2(rdInfoList, out rect, out packedRects);
|
||
SetNewLightmapScaleOffset(rdInfoList);
|
||
|
||
//var tex = CombineTextures(rdInfoList, (int)rect.width, (int)rect.height);
|
||
var path = ConfigDefine.GetSavePrefabPath("Lightmap_" + itor.Current.Key + ".exr");
|
||
|
||
//SaveLightmapTexture(path, tex);
|
||
|
||
configInfos.Add(string.Format("{0}\t{1}", itor.Current.Key, Path.GetFileNameWithoutExtension(path)));
|
||
|
||
//构造图集打包信息,传递到外部调用EXR工具
|
||
PackDataInfo pdInfo = new PackDataInfo();
|
||
pdInfo.PackImgWidth = (int)rect.width;
|
||
pdInfo.PackImgHeight = (int)rect.height;
|
||
pdInfo.SaveFileNameList.Add(Path.GetFullPath(path));
|
||
for (int i = 0; i < rdInfoList.Count; ++i)
|
||
{
|
||
//lightmap原始路径
|
||
var lightmapTex = LightMapUtil.GetFullLightmap(rdInfoList[i].Renderer);
|
||
var lightmapPath = AssetDatabase.GetAssetPath(lightmapTex);
|
||
pdInfo.LightmapPathList.Add(Path.GetFullPath(lightmapPath));
|
||
|
||
var rect1 = new Rect();
|
||
rect1 = rdInfoList[i].CutRect;
|
||
//rect1.x = rdInfoList[i].OldScaledBound.x * lightmapTex.width;
|
||
//rect1.y = rdInfoList[i].OldScaledBound.y * lightmapTex.height;
|
||
//rect1.width = rect2.width;
|
||
//rect1.height = rect2.height;
|
||
pdInfo.SourceRectList.Add(rect1);
|
||
|
||
var rect2 = new Rect();
|
||
rect2.x = packedRects[i].x * pdInfo.PackImgWidth;
|
||
rect2.y = packedRects[i].y * pdInfo.PackImgHeight;
|
||
rect2.width = rect1.width;// packedRects[i].width * pdInfo.PackImgWidth;
|
||
rect2.height = rect1.height;// packedRects[i].height * pdInfo.PackImgHeight;
|
||
pdInfo.PackedRectList.Add(rect2);
|
||
}
|
||
|
||
pdList.AddPackData(pdInfo);
|
||
}
|
||
|
||
Utils.SyncPrefabInEditor(rootGos);
|
||
SaveLightmapConfig(configInfos.ToArray());
|
||
var tempFile = System.IO.Path.GetTempFileName();
|
||
var tempFullPath = Path.Combine(Path.GetTempPath(), tempFile);
|
||
pdList.Write(tempFullPath);
|
||
|
||
//Utils.CallExe("", tempFullPath);
|
||
UnityEngine.Debug.Log("Temp path: " + tempFullPath);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 给gameobject添加脚本,保存Renderer的一些信息给prefab使用
|
||
/// </summary>
|
||
/// <param name="rootGos"></param>
|
||
public static void RequirePrefabLightmapInfoScript(GameObject[] rootGos)
|
||
{
|
||
for (int i = 0; i < rootGos.Length; ++i)
|
||
{
|
||
var script = rootGos[i].GetComponent<PrefabLightmapInfo>();
|
||
if (script == null)
|
||
{
|
||
script = rootGos[i].AddComponent<PrefabLightmapInfo>();
|
||
}
|
||
|
||
var prefabIndex = TerrainConfigLoader.GetIndexFromGameObjectName(rootGos[i].name);
|
||
//var index = TerrainConfigLoader.GenerateLightmapIndex(prefabIndex);
|
||
script.OnBakeFinish();
|
||
script.SetMeshIndex(prefabIndex);
|
||
}
|
||
}
|
||
|
||
public static void SaveLightmapConfig(string[] infoArray)
|
||
{
|
||
var path = ConfigDefine.GetConfigPath(TerrainConfigDefine.LIGHTMAP_CONFIG_FILENAME);
|
||
File.WriteAllLines(path, infoArray);
|
||
}
|
||
|
||
public static string SaveLightmapTexture(string path, Texture2D tex)
|
||
{
|
||
var bytes = tex.EncodeToPNG();
|
||
File.WriteAllBytes(path, bytes);
|
||
AssetDatabase.ImportAsset(path);
|
||
|
||
TextureImporter ti = (TextureImporter)AssetImporter.GetAtPath(path);
|
||
ti.textureType = TextureImporterType.Lightmap;
|
||
ti.SaveAndReimport();
|
||
|
||
return path;
|
||
}
|
||
|
||
public static Texture2D CombineLightmap(GameObject[] rootGos)
|
||
{
|
||
|
||
var rdInfoList = ScanRendererInfos(rootGos);
|
||
|
||
Rect rect;
|
||
Rect[] packedList;
|
||
CombineRendererInfos2(rdInfoList, out rect, out packedList);
|
||
|
||
var tex = CombineTextures(rdInfoList, (int)rect.width, (int)rect.height);
|
||
return tex;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 收集所有Renderer的信息
|
||
/// </summary>
|
||
/// <param name="rootGos"></param>
|
||
/// <returns></returns>
|
||
public static List<RendererInfo> ScanRendererInfos(GameObject[] rootGos)
|
||
{
|
||
List<RendererInfo> rdInfoListh = new List<RendererInfo>();
|
||
for (int i = 0; i < rootGos.Length; ++i)
|
||
{
|
||
var renderers = rootGos[i].GetComponentsInChildren<Renderer>();
|
||
for (int j = 0; j < renderers.Length; ++j)
|
||
{
|
||
Vector4 bound;
|
||
Vector4 scaleBound;
|
||
Rect pickRect;
|
||
var myLightmap = LightMapUtil.PickLightmap(renderers[j], out bound, out scaleBound, out pickRect);
|
||
if (myLightmap != null)
|
||
{
|
||
RendererInfo rdInfo = new RendererInfo();
|
||
rdInfo.MapName = EditorSceneManager.GetActiveScene().name;
|
||
rdInfo.RootGoName = rootGos[i].name;
|
||
rdInfo.Renderer = renderers[j];
|
||
rdInfo.MyLightmap = myLightmap;
|
||
rdInfo.OldBound = bound;
|
||
rdInfo.OldScaledBound = scaleBound;
|
||
rdInfo.CutRect = pickRect;
|
||
rdInfoListh.Add(rdInfo);
|
||
}
|
||
}
|
||
|
||
}
|
||
rdInfoListh.Sort((x1, x2) =>
|
||
{
|
||
return -(x1.MyLightmap.width * x1.MyLightmap.height - x2.MyLightmap.width * x2.MyLightmap.height);
|
||
});
|
||
|
||
return rdInfoListh;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 纹理合并
|
||
/// </summary>
|
||
/// <param name="rdInfoList"></param>
|
||
/// <param name="width"></param>
|
||
/// <param name="height"></param>
|
||
/// <returns></returns>
|
||
public static Texture2D CombineTextures(List<RendererInfo> rdInfoList, int width, int height)
|
||
{
|
||
Texture2D tex = LightMapUtil.CreateEmptyTexture(width, height, TextureFormat.ARGB32);
|
||
|
||
for (int i = 0; i < rdInfoList.Count; ++i)
|
||
{
|
||
var rdInfo = rdInfoList[i];
|
||
var pixels = rdInfoList[i].MyLightmap.GetPixels();
|
||
tex.SetPixels((int)rdInfo.Position.x, (int)rdInfo.Position.y, rdInfo.MyLightmap.width, rdInfo.MyLightmap.height, pixels);
|
||
}
|
||
tex.alphaIsTransparency = true;
|
||
tex.Apply();
|
||
return tex;
|
||
}
|
||
|
||
//设置新的lightmapScaleOffset
|
||
public static void SetNewLightmapScaleOffset(List<RendererInfo> rdInfoList)
|
||
{
|
||
for (int i = 0; i < rdInfoList.Count; ++i)
|
||
{
|
||
//重置scaleOffset
|
||
var script = rdInfoList[i].Renderer.gameObject.GetComponentInParent<PrefabLightmapInfo>();
|
||
if (script != null)
|
||
{
|
||
script.SetLightmapScaleOffset(rdInfoList[i].Renderer, rdInfoList[i].MyLightmapScaleInfo);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 合并光照贴图,计算位置和范围 lightmapScaleOffset
|
||
/// </summary>
|
||
/// <param name="rdInfoList"></param>
|
||
/// <param name="bound"></param>
|
||
public static void CombineRendererInfos(List<RendererInfo> rdInfoList, out Rect bound)
|
||
{
|
||
bound = new Rect(0, 0, 128, 128);
|
||
while (true)
|
||
{
|
||
bool finish = true;
|
||
List<Rect> rectList = new List<Rect>();
|
||
rectList.Add(bound);
|
||
for (int i = 0; i < rdInfoList.Count; ++i)
|
||
{
|
||
Vector2 pos = Vector2.zero;
|
||
if (InsertRect(rectList, new Rect(0, 0, rdInfoList[i].MyLightmap.width, rdInfoList[i].MyLightmap.height), out pos))
|
||
{
|
||
rdInfoList[i].Position = pos;
|
||
|
||
var blockW = rdInfoList[i].MyLightmap.width;
|
||
var blockH = rdInfoList[i].MyLightmap.height;
|
||
var texWidth = bound.width;
|
||
var texHeight = bound.height;
|
||
var sourceBounds = rdInfoList[i].OldBound;
|
||
|
||
var scaleUVX = (blockW) / (texWidth * (sourceBounds.z - sourceBounds.x));
|
||
var offsetUVX = pos.x / (float)texWidth - scaleUVX * sourceBounds.x;
|
||
|
||
var scaleUVY = (blockH) / (texHeight * (sourceBounds.w - sourceBounds.y));
|
||
var offsetUVY = pos.y / (float)texHeight - scaleUVY * sourceBounds.y;
|
||
|
||
rdInfoList[i].MyLightmapScaleInfo = new Vector4(scaleUVX, scaleUVY, offsetUVX, offsetUVY);
|
||
}
|
||
else
|
||
{
|
||
bound.width *= 2;
|
||
bound.height *= 2;
|
||
finish = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (finish)
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 合并光照贴图,计算位置和范围 lightmapScaleOffset
|
||
/// </summary>
|
||
/// <param name="rdInfoList"></param>
|
||
/// <param name="bound"></param>
|
||
public static void CombineRendererInfos2(List<RendererInfo> rdInfoList, out Rect bound, out Rect[] packedRects)
|
||
{
|
||
packedRects = new Rect[0];
|
||
|
||
//计算总面积
|
||
int areaAll = 0;
|
||
for(int i = 0; i < rdInfoList.Count; ++i)
|
||
{
|
||
areaAll += (rdInfoList[i].MyLightmap.width * rdInfoList[i].MyLightmap.height);
|
||
}
|
||
|
||
//计算最接近这个面积的宽高尺寸
|
||
int size = 128;
|
||
while(true)
|
||
{
|
||
if (size * size > areaAll)
|
||
break;
|
||
size *= 2;
|
||
}
|
||
|
||
bound = new Rect(0, 0, size, size);
|
||
|
||
if (areaAll == 0)
|
||
return;
|
||
|
||
while (true)
|
||
{
|
||
List<Rect> rectList = new List<Rect>();
|
||
List<Texture2D> texList = new List<Texture2D>();
|
||
rectList.Add(bound);
|
||
for (int i = 0; i < rdInfoList.Count; ++i)
|
||
{
|
||
Texture2D tex = new Texture2D(rdInfoList[i].MyLightmap.width, rdInfoList[i].MyLightmap.height);
|
||
texList.Add(tex);
|
||
}
|
||
|
||
Texture2D combined = new Texture2D((int)bound.width, (int)bound.height, TextureFormat.ARGB32, false);
|
||
|
||
packedRects = combined.PackTextures(texList.ToArray(), 0, size);
|
||
|
||
if (packedRects == null || packedRects.Length == 0 ||
|
||
packedRects[0].width * bound.width < texList[0].width || packedRects[0].height * bound.height < texList[0].height)
|
||
{
|
||
bound.width *= 2;
|
||
bound.height *= 2;
|
||
size *= 2;
|
||
}
|
||
else
|
||
{
|
||
bound.width = combined.width;
|
||
bound.height = combined.height;
|
||
for (int i = 0; i < rdInfoList.Count; ++i)
|
||
{
|
||
Vector2 pos = new Vector2(packedRects[i].x, packedRects[i].y);
|
||
{
|
||
rdInfoList[i].Position = pos;
|
||
rdInfoList[i].Position.x *= bound.width;
|
||
rdInfoList[i].Position.y *= bound.height;
|
||
|
||
//为了消除接缝黑边
|
||
var ignorPixels = 1f;
|
||
var blockW = rdInfoList[i].MyLightmap.width;
|
||
var blockH = rdInfoList[i].MyLightmap.height;
|
||
var texWidth = bound.width;
|
||
var texHeight = bound.height;
|
||
var sourceBounds = rdInfoList[i].OldBound;
|
||
|
||
var scaleUVX = (blockW - ignorPixels) / (texWidth * (sourceBounds.z - sourceBounds.x));
|
||
var offsetUVX = (pos.x) / (float)texWidth - scaleUVX * sourceBounds.x;
|
||
|
||
var scaleUVY = (blockH - ignorPixels) / (texHeight * (sourceBounds.w - sourceBounds.y));
|
||
var offsetUVY = (pos.y) / (float)texHeight - scaleUVY * sourceBounds.y;
|
||
|
||
rdInfoList[i].MyLightmapScaleInfo = new Vector4(scaleUVX, scaleUVY, offsetUVX, offsetUVY);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 简陋的图集打包算法
|
||
/// </summary>
|
||
/// <param name="spaceRectList"></param>
|
||
/// <param name="rect"></param>
|
||
/// <param name="insertPos"></param>
|
||
/// <returns></returns>
|
||
public static bool InsertRect(List<Rect> spaceRectList, Rect rect, out Vector2 insertPos)
|
||
{
|
||
bool ret = false;
|
||
insertPos = Vector2.zero;
|
||
for(int i = 0; i < spaceRectList.Count; ++i)
|
||
{
|
||
//放到一个矩形中后,会产生3个小矩形碎片,重新添加到空余矩形列表中
|
||
if(spaceRectList[i].width >= rect.width && spaceRectList[i].height >= rect.height)
|
||
{
|
||
var startPos = spaceRectList[i].position;
|
||
var endPos = startPos + new Vector2(rect.width, rect.height);
|
||
var moreWidth = spaceRectList[i].width - rect.width;
|
||
var moreHeight = spaceRectList[i].height - rect.height;
|
||
Rect bottom = new Rect(startPos.x, startPos.y + rect.height, rect.width, moreHeight);
|
||
Rect right = new Rect(startPos.x + rect.width, startPos.y, moreWidth, rect.height);
|
||
Rect cross = new Rect(startPos.x + rect.width, startPos.y + rect.height, moreWidth, moreHeight);
|
||
|
||
spaceRectList.RemoveAt(i);
|
||
if(bottom.width > 0 && bottom.height > 0) spaceRectList.Add(bottom);
|
||
if(right.width > 0 && right.height > 0) spaceRectList.Add(right);
|
||
if (cross.width > 0 && cross.height > 0) spaceRectList.Add(cross);
|
||
|
||
insertPos = startPos;
|
||
ret = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
spaceRectList.Sort((x1, x2) =>
|
||
{
|
||
return (int)(x1.width * x1.height - x2.width * x2.height);
|
||
});
|
||
|
||
return ret;
|
||
}
|
||
}
|
||
}
|