parent
0da9456e91
commit
fe7c213a77
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2cc6cd0fb49146789026812735f60505
|
||||
timeCreated: 1704259084
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ea835a762fa042a883b89cafbaf5929a
|
||||
timeCreated: 1716894113
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using PhxhSDK.Phxh.Others;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PhxhSDK.Phxh.AutoCombine.Inspector
|
||||
{
|
||||
[CustomEditor(typeof(CmpSkinCombine))]
|
||||
public class CmpSkinCombineInspector : UnityEditor.Editor
|
||||
{
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
// base.OnInspectorGUI();
|
||||
var cmp = target as CmpSkinCombine;
|
||||
if (cmp == null) return;
|
||||
if (GUILayout.Button("转换所有Mesh为SkinMesh", GUILayout.Height(40)))
|
||||
{
|
||||
AutoCombine.ConvertMeshToSkinMesh(cmp.gameObject);
|
||||
}
|
||||
if (GUILayout.Button("合并所有SkinMesh", GUILayout.Height(40)))
|
||||
{
|
||||
AutoCombine.CombineSkinMesh(cmp.gameObject, new string[] { "_MainTex", "_MaskTex" });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d4cd9c040ace42bfa152950483bb1c0d
|
||||
timeCreated: 1716894123
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 17313f90a5414192b618e320758a754a
|
||||
timeCreated: 1703670894
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
using System.Collections.Generic;
|
||||
using Performance.Data;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
namespace Performance
|
||||
{
|
||||
public class AutoObjCombine
|
||||
{
|
||||
|
||||
private GameObject _rootObj;
|
||||
|
||||
private Dictionary<Shader, ShaderGroupCombiner> _shaderCombinerDic = new Dictionary<Shader, ShaderGroupCombiner>();
|
||||
private string _savePath;
|
||||
private string _objName;
|
||||
|
||||
public AutoObjCombine(GameObject rootObj)
|
||||
{
|
||||
_rootObj = rootObj;
|
||||
_objName = _rootObj.name;
|
||||
}
|
||||
|
||||
public void DoCombine(string savePath)
|
||||
{
|
||||
_savePath = savePath;
|
||||
_ProtectPath();
|
||||
_CollectAllRenderers();
|
||||
_CombineToNewObj();
|
||||
}
|
||||
|
||||
private void _ProtectPath()
|
||||
{
|
||||
if (!_savePath.EndsWith("/"))
|
||||
{
|
||||
_savePath += "/";
|
||||
}
|
||||
_savePath += _rootObj.name + "_Combined";
|
||||
AssetDatabase.DeleteAsset(_savePath);
|
||||
}
|
||||
|
||||
private void _CombineToNewObj()
|
||||
{
|
||||
var newObj = Object.Instantiate(_rootObj);
|
||||
|
||||
// 删除所有meshrenderderers和meshfilters
|
||||
var allMeshRenderers = newObj.GetComponentsInChildren<MeshRenderer>();
|
||||
var allMeshFilters = newObj.GetComponentsInChildren<MeshFilter>();
|
||||
for (int i = 0; i < allMeshRenderers.Length; i++)
|
||||
{
|
||||
Object.DestroyImmediate(allMeshRenderers[i]);
|
||||
}
|
||||
for (int i = 0; i < allMeshFilters.Length; i++)
|
||||
{
|
||||
Object.DestroyImmediate(allMeshFilters[i]);
|
||||
}
|
||||
|
||||
foreach (var kvp in _shaderCombinerDic)
|
||||
{
|
||||
var combineObj = kvp.Value.CombineToSingleObject(_savePath, new List<Material>());
|
||||
if (combineObj == null)
|
||||
{
|
||||
DebugUtil.LogError("合并对象失败, 可能是没有找到 MeshFilter 或 MeshRenderer");
|
||||
continue;
|
||||
}
|
||||
combineObj.transform.SetParent(newObj.transform);
|
||||
}
|
||||
|
||||
// 确保_savePath存在, 是从Assets目录开始的
|
||||
if (!AssetDatabase.IsValidFolder(_savePath))
|
||||
{
|
||||
var realPath = Application.dataPath + "/" + _savePath.Replace("Assets/", "");
|
||||
System.IO.Directory.CreateDirectory(realPath);
|
||||
}
|
||||
|
||||
// 把newObj保存到指定路径
|
||||
var prefabPath = _savePath + "/" + _objName + ".prefab";
|
||||
PrefabUtility.SaveAsPrefabAsset(newObj, prefabPath);
|
||||
|
||||
// 清理新创建的 GameObject
|
||||
Object.DestroyImmediate(newObj);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private void _CollectAllRenderers()
|
||||
{
|
||||
var allMeshRenderers = _rootObj.GetComponentsInChildren<MeshRenderer>();
|
||||
for (int i = 0; i < allMeshRenderers.Length; i++)
|
||||
{
|
||||
var meshRenderer = allMeshRenderers[i];
|
||||
if (meshRenderer == null || meshRenderer.sharedMaterial == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var shader = meshRenderer.sharedMaterial.shader;
|
||||
if (!_shaderCombinerDic.TryGetValue(shader, out var combiner))
|
||||
{
|
||||
combiner = new ShaderGroupCombiner(shader);
|
||||
_shaderCombinerDic[shader] = combiner;
|
||||
}
|
||||
combiner.AddRenderer(meshRenderer);
|
||||
}
|
||||
|
||||
DebugUtil.Log($"共收集到 {allMeshRenderers.Length} 个 MeshRenderer, 分别属于 {_shaderCombinerDic.Count} 个 Shader");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d22e9d4f920e4e5191e4f0781cd52dc6
|
||||
timeCreated: 1752576177
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7c21427ed9e049f49be82588f2c2ef72
|
||||
timeCreated: 1703671650
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using PhxhSDK;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Directory = System.IO.Directory;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Performance.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// 负责保存合并后的资源
|
||||
/// </summary>
|
||||
public class AssetSaver
|
||||
{
|
||||
/// <summary>
|
||||
/// 贴图类型定义
|
||||
/// </summary>
|
||||
public enum TextureType
|
||||
{
|
||||
Main,
|
||||
Mask
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 贴图信息结构体
|
||||
/// </summary>
|
||||
public struct TextureInfo
|
||||
{
|
||||
public TextureType Type;
|
||||
public string PropertyName;
|
||||
public string FileName;
|
||||
public Texture2D[] SourceTextures;
|
||||
public Texture2D CombinedTexture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存单个贴图到文件
|
||||
/// </summary>
|
||||
public static Texture2D SaveTexture(Texture2D texture, string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保路径以.png结尾
|
||||
if (!filePath.EndsWith(".png"))
|
||||
{
|
||||
filePath += ".png";
|
||||
}
|
||||
|
||||
string directory = Path.GetDirectoryName(filePath);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
// 将纹理编码为PNG并保存
|
||||
byte[] bytes = texture.EncodeToPNG();
|
||||
File.WriteAllBytes(filePath, bytes);
|
||||
|
||||
// 刷新资源数据库以识别新文件
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
// 加载保存后的纹理资源
|
||||
Texture2D savedTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(filePath);
|
||||
if (savedTexture == null)
|
||||
{
|
||||
Debug.LogError($"Failed to load saved texture from: {filePath}");
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.Log($"Texture saved and loaded successfully from: {filePath}");
|
||||
return savedTexture;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error saving texture to {filePath}: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理并保存多个类型的贴图
|
||||
/// </summary>
|
||||
public static Dictionary<TextureType, Texture2D> ProcessAndSaveTextures(
|
||||
Texture2D[] mainTextures,
|
||||
Texture2D[] maskTextures,
|
||||
string outputPath, string suffix = "")
|
||||
{
|
||||
var textureInfos = new[]
|
||||
{
|
||||
new TextureInfo
|
||||
{
|
||||
Type = TextureType.Main,
|
||||
PropertyName = "_MainTex",
|
||||
FileName = $"CombinedMain{suffix}.png",
|
||||
SourceTextures = mainTextures
|
||||
},
|
||||
new TextureInfo
|
||||
{
|
||||
Type = TextureType.Mask,
|
||||
PropertyName = "_MaskTex",
|
||||
FileName = $"CombinedMask{suffix}.png",
|
||||
SourceTextures = maskTextures
|
||||
}
|
||||
};
|
||||
|
||||
var results = new Dictionary<TextureType, Texture2D>();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var info in textureInfos)
|
||||
{
|
||||
if (info.SourceTextures == null || info.SourceTextures.Length == 0)
|
||||
{
|
||||
Debug.LogWarning($"No {info.Type} textures to process");
|
||||
continue;
|
||||
}
|
||||
|
||||
Texture2D combinedTexture = TextureUtils.CombineTextures(info.FileName, info.SourceTextures);
|
||||
if (combinedTexture != null)
|
||||
{
|
||||
Texture2D savedTexture = SaveTexture(combinedTexture, $"{outputPath}{info.FileName}");
|
||||
if (savedTexture != null)
|
||||
{
|
||||
results[info.Type] = savedTexture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.Count > 0)
|
||||
{
|
||||
ReloadAssets();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error processing textures: {ex.Message}");
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建资源输出路径
|
||||
/// </summary>
|
||||
public static string GetGroupOutputPath(string directory, string groupName, string shaderName)
|
||||
{
|
||||
var outputPath = $"{directory}/{groupName}_{ConvertShaderName(shaderName)}";
|
||||
if (!Directory.Exists(outputPath))
|
||||
{
|
||||
Directory.CreateDirectory(outputPath);
|
||||
}
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存材质和网格资源
|
||||
/// </summary>
|
||||
public static void SaveAssets(Material material, Mesh mesh, string outputPath, string suffix = "")
|
||||
{
|
||||
var materialPath = $"{outputPath}/Combined{suffix}.mat";
|
||||
var meshPath = $"{outputPath}/Combined{suffix}.asset";
|
||||
|
||||
AssetDatabase.CreateAsset(material, materialPath);
|
||||
AssetDatabase.CreateAsset(mesh, meshPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新资源数据库
|
||||
/// </summary>
|
||||
public static void ReloadAssets()
|
||||
{
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换着色器名称为有效的路径名
|
||||
/// </summary>
|
||||
public static string ConvertShaderName(string shaderName)
|
||||
{
|
||||
return shaderName.Replace("/", "_");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ecbef4ed2ec344443b5212107a789743
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Performance.Data
|
||||
{
|
||||
public class AutoCombieMeshData
|
||||
{
|
||||
|
||||
public Mesh mesh;
|
||||
public List<GameObject> objs = new List<GameObject>();
|
||||
|
||||
public AutoCombieMeshData(Mesh mesh)
|
||||
{
|
||||
this.mesh = mesh;
|
||||
}
|
||||
|
||||
public void AddObj(GameObject obj)
|
||||
{
|
||||
objs.Add(obj);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 11d7a1f0d53d4c45b0e32129671d2242
|
||||
timeCreated: 1703671666
|
||||
|
|
@ -0,0 +1,478 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using PhxhSDK;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Directory = System.IO.Directory;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Performance.Data
|
||||
{
|
||||
public class ShaderGroupCombiner
|
||||
{
|
||||
private const string MainTextureProperty = "_MainTex";
|
||||
private const string MaskTextureProperty = "_MaskTex";
|
||||
private const string DefaultGroupName = "Default";
|
||||
private const int DefaultTextureUnitSize = 256;
|
||||
private const int InitialRendererCapacity = 1000;
|
||||
|
||||
public readonly Shader shader;
|
||||
public readonly string groupName;
|
||||
private readonly int _textureUnitSize;
|
||||
public readonly Renderer[] renderers;
|
||||
private int _rendererCount;
|
||||
public int textureGridWidth;
|
||||
public Vector2Int TextureSize { get; private set; }
|
||||
|
||||
// 缓存字段
|
||||
private readonly HashSet<Material> _cachedMaterials;
|
||||
private Texture2D[] _cachedMainTextures;
|
||||
private Texture2D[] _cachedMaskTextures;
|
||||
private bool _isPreCalculated;
|
||||
private readonly Dictionary<Material, int> _materialTextureIndices;
|
||||
private readonly Dictionary<Renderer, Vector2[]> _rendererUVs;
|
||||
|
||||
public ShaderGroupCombiner(
|
||||
Shader shader,
|
||||
int textureUnitSize = DefaultTextureUnitSize,
|
||||
string groupName = DefaultGroupName)
|
||||
{
|
||||
this.shader = shader ?? throw new System.ArgumentNullException(nameof(shader));
|
||||
_textureUnitSize = textureUnitSize;
|
||||
this.groupName = groupName ?? DefaultGroupName;
|
||||
|
||||
_materialTextureIndices = new Dictionary<Material, int>();
|
||||
_rendererUVs = new Dictionary<Renderer, Vector2[]>();
|
||||
_cachedMaterials = new HashSet<Material>();
|
||||
renderers = new Renderer[InitialRendererCapacity];
|
||||
_rendererCount = 0;
|
||||
}
|
||||
|
||||
public void AddRenderer(Renderer renderer)
|
||||
{
|
||||
if (renderer == null)
|
||||
{
|
||||
Debug.LogError("Renderer cannot be null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_rendererCount >= renderers.Length)
|
||||
{
|
||||
Debug.LogError($"Renderer array is full. Consider increasing the initial size from {InitialRendererCapacity}.");
|
||||
return;
|
||||
}
|
||||
|
||||
renderers[_rendererCount++] = renderer;
|
||||
_isPreCalculated = false;
|
||||
}
|
||||
|
||||
public void PreCalculate()
|
||||
{
|
||||
if (_rendererCount == 0)
|
||||
{
|
||||
Debug.LogWarning("No renderers to process");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isPreCalculated) return;
|
||||
|
||||
try
|
||||
{
|
||||
CollectUniqueMaterials();
|
||||
if (_cachedMaterials.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("No valid materials found");
|
||||
return;
|
||||
}
|
||||
|
||||
var (mainTextures, maskTextures) = CollectTexturePairs(_cachedMaterials);
|
||||
_cachedMainTextures = mainTextures;
|
||||
_cachedMaskTextures = maskTextures;
|
||||
|
||||
TextureSize = TextureUtils.CalculateTextureSize(_cachedMainTextures, _textureUnitSize);
|
||||
textureGridWidth = Mathf.CeilToInt(Mathf.Sqrt(_cachedMainTextures.Length));
|
||||
PreCalculateMaterialIndices();
|
||||
PreCalculateUVCoordinates();
|
||||
|
||||
_isPreCalculated = true;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error during pre-calculation: {ex.Message}");
|
||||
_isPreCalculated = false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public GameObject CombineToSingleObject(string outputPath, List<Material> sampleMaterials, string prefix = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(outputPath))
|
||||
{
|
||||
Debug.LogError("Output path cannot be null or empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!IsShaderSupported())
|
||||
{
|
||||
Debug.LogError($"Unsupported shader: {shader.name}");
|
||||
return null;
|
||||
}
|
||||
|
||||
ValidatePreCalculation();
|
||||
return ProcessNLDShaders(outputPath, sampleMaterials, prefix);
|
||||
}
|
||||
|
||||
private void PreCalculateMaterialIndices()
|
||||
{
|
||||
if (_materialTextureIndices.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("Material texture indices not properly initialized in CollectTexturePairs");
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetMainTexture(Material material, out Texture2D mainTexture)
|
||||
{
|
||||
mainTexture = material.GetTexture(MainTextureProperty) as Texture2D;
|
||||
return mainTexture != null;
|
||||
}
|
||||
|
||||
private void PreCalculateUVCoordinates()
|
||||
{
|
||||
_rendererUVs.Clear();
|
||||
for (int i = 0; i < _rendererCount; i++)
|
||||
{
|
||||
var renderer = renderers[i];
|
||||
var meshFilter = renderer.GetComponent<MeshFilter>();
|
||||
if (meshFilter == null || meshFilter.sharedMesh == null) continue;
|
||||
|
||||
var material = renderer.sharedMaterial;
|
||||
if (material == null) continue;
|
||||
|
||||
var uvs = CalculateUVCoordinates(meshFilter.sharedMesh, material);
|
||||
_rendererUVs[renderer] = uvs;
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidatePreCalculation()
|
||||
{
|
||||
if (!_isPreCalculated)
|
||||
{
|
||||
Debug.LogWarning("Pre-calculation not performed. Running PreCalculate now...");
|
||||
PreCalculate();
|
||||
}
|
||||
}
|
||||
|
||||
private void CollectUniqueMaterials()
|
||||
{
|
||||
_cachedMaterials.Clear();
|
||||
for (int i = 0; i < _rendererCount; i++)
|
||||
{
|
||||
var renderer = renderers[i];
|
||||
if (renderer?.sharedMaterial != null)
|
||||
{
|
||||
_cachedMaterials.Add(renderer.sharedMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private (Texture2D[] mainTextures, Texture2D[] maskTextures) CollectTexturePairs(IEnumerable<Material> materials)
|
||||
{
|
||||
if (materials == null)
|
||||
{
|
||||
Debug.LogWarning("No materials to process textures from");
|
||||
return (System.Array.Empty<Texture2D>(), System.Array.Empty<Texture2D>());
|
||||
}
|
||||
|
||||
var materialTexturePairs = new List<(Material material, Texture2D mainTexture, Texture2D maskTexture)>();
|
||||
var invalidMaterials = new List<Material>();
|
||||
|
||||
// 先收集所有有效的材质和其对应的贴图对
|
||||
foreach (var material in materials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
|
||||
if (TryGetMainTexture(material, out var mainTexture))
|
||||
{
|
||||
TryGetMaskTexture(material, out var maskTexture);
|
||||
// 即使maskTexture为null也加入列表,保持索引一致性
|
||||
materialTexturePairs.Add((material, mainTexture, maskTexture));
|
||||
}
|
||||
else
|
||||
{
|
||||
invalidMaterials.Add(material);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建结果数组,确保主贴图和mask贴图的索引一一对应
|
||||
var mainTextures = new Texture2D[materialTexturePairs.Count];
|
||||
var maskTextures = new Texture2D[materialTexturePairs.Count];
|
||||
|
||||
for (int i = 0; i < materialTexturePairs.Count; i++)
|
||||
{
|
||||
mainTextures[i] = materialTexturePairs[i].mainTexture;
|
||||
maskTextures[i] = materialTexturePairs[i].maskTexture;
|
||||
|
||||
// 记录材质的贴图索引,用于后续UV计算
|
||||
_materialTextureIndices[materialTexturePairs[i].material] = i;
|
||||
}
|
||||
|
||||
if (invalidMaterials.Count > 0)
|
||||
{
|
||||
Debug.LogError($"Found {invalidMaterials.Count} materials with invalid textures");
|
||||
foreach (var material in invalidMaterials)
|
||||
{
|
||||
Debug.LogError($"Material: {material.name}", material);
|
||||
}
|
||||
}
|
||||
|
||||
return (mainTextures, maskTextures);
|
||||
}
|
||||
|
||||
private bool TryGetMaskTexture(Material material, out Texture2D maskTexture)
|
||||
{
|
||||
maskTexture = material.GetTexture(MaskTextureProperty) as Texture2D;
|
||||
return maskTexture != null;
|
||||
}
|
||||
|
||||
private bool IsShaderSupported()
|
||||
{
|
||||
return shader.name switch
|
||||
{
|
||||
"NLD_URP/NLD_Charactor_Lambert" => true,
|
||||
"NLD_URP/NLD_Charactor" => true,
|
||||
"NLD_URP/NLD_Scene_Lambert" => true,
|
||||
"NLD_URP/NLD_Scene_Lambert_DoubleSide" => true,
|
||||
"NLD_URP/NLD_Scene_Lightmap" => true,
|
||||
"NLD_URP/NLD_Scene_Opaque" => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private void ValidateMaterialTextures(Material material)
|
||||
{
|
||||
if (material == null)
|
||||
{
|
||||
Debug.LogError("Material cannot be null");
|
||||
return;
|
||||
}
|
||||
|
||||
var missingTextures = new List<string>();
|
||||
|
||||
if (material.GetTexture(MainTextureProperty) == null)
|
||||
{
|
||||
missingTextures.Add(MainTextureProperty);
|
||||
}
|
||||
if (material.GetTexture(MaskTextureProperty) == null)
|
||||
{
|
||||
missingTextures.Add(MaskTextureProperty);
|
||||
}
|
||||
|
||||
if (missingTextures.Count > 0)
|
||||
{
|
||||
Debug.LogError($"Material {material.name} is missing textures: {string.Join(", ", missingTextures)}", material);
|
||||
}
|
||||
}
|
||||
|
||||
private GameObject ProcessNLDShaders(string outputDirectory, List<Material> sampleMaterials, string prefix = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
{
|
||||
Debug.LogError("Output directory cannot be null or empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_rendererCount == 0)
|
||||
{
|
||||
Debug.LogError("No renderers to process");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string sceneName = Path.GetFileNameWithoutExtension(outputDirectory);
|
||||
//string groupOutputPath = AssetSaver.GetGroupOutputPath(outputDirectory, groupName, shader.name);
|
||||
string groupOutputPath = $"{outputDirectory}/"; // 简化保存的名字
|
||||
if (!PostProcessInstructor.DicNumber.TryGetValue(shader.name, out int number))
|
||||
PostProcessInstructor.DicNumber[shader.name] = PostProcessInstructor.DicNumber.Count;
|
||||
|
||||
string suffix = $"_{sceneName}_{PostProcessInstructor.DicNumber[shader.name]}{groupName.Replace("Group", string.Empty).Replace("Default", string.Empty)}";
|
||||
|
||||
var textureResults = AssetSaver.ProcessAndSaveTextures(
|
||||
_cachedMainTextures,
|
||||
_cachedMaskTextures,
|
||||
groupOutputPath,
|
||||
suffix
|
||||
);
|
||||
|
||||
if (!textureResults.TryGetValue(AssetSaver.TextureType.Main, out var combinedMainTexture))
|
||||
{
|
||||
Debug.LogError("Failed to process main texture");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!textureResults.TryGetValue(AssetSaver.TextureType.Mask, out var combinedMaskTexture))
|
||||
{
|
||||
Debug.LogWarning("No mask texture found, proceeding with only main texture");
|
||||
}
|
||||
|
||||
var firstMaterial = new List<Material>(_cachedMaterials)[0];
|
||||
var resultMaterial = CreateCombinedMaterial(
|
||||
firstMaterial,
|
||||
combinedMainTexture,
|
||||
combinedMaskTexture,
|
||||
sampleMaterials
|
||||
);
|
||||
|
||||
if (resultMaterial == null)
|
||||
{
|
||||
Debug.LogError("Failed to create combined material");
|
||||
return null;
|
||||
}
|
||||
|
||||
var resultMesh = CreateCombinedMesh();
|
||||
if (resultMesh == null)
|
||||
{
|
||||
Debug.LogError("Failed to create combined mesh");
|
||||
return null;
|
||||
}
|
||||
|
||||
var resultObject = CreateResultObject(resultMaterial, resultMesh);
|
||||
if (resultObject == null)
|
||||
{
|
||||
Debug.LogError("Failed to create result object");
|
||||
return null;
|
||||
}
|
||||
|
||||
AssetSaver.SaveAssets(resultMaterial, resultMesh, groupOutputPath, suffix);
|
||||
return resultObject;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error processing NLD shaders: {ex.Message}\n{ex.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Material CreateCombinedMaterial(Material firstMaterial, Texture2D mainTexture, Texture2D maskTexture, List<Material> sampleMaterials)
|
||||
{
|
||||
var sampleMaterial = FindSampleMaterial(firstMaterial, sampleMaterials);
|
||||
var resultMaterial = new Material(sampleMaterial);
|
||||
|
||||
if (mainTexture != null)
|
||||
{
|
||||
resultMaterial.SetTexture(MainTextureProperty, mainTexture);
|
||||
Debug.Log($"Setting main texture on combined material: {mainTexture.name}, size: {mainTexture.width}x{mainTexture.height}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Main texture is null when creating combined material");
|
||||
}
|
||||
|
||||
if (maskTexture != null)
|
||||
{
|
||||
resultMaterial.SetTexture(MaskTextureProperty, maskTexture);
|
||||
Debug.Log($"Setting mask texture on combined material: {maskTexture.name}, size: {maskTexture.width}x{maskTexture.height}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("No mask texture available for combined material, shader effects may not work properly");
|
||||
}
|
||||
|
||||
return resultMaterial;
|
||||
}
|
||||
|
||||
private Mesh CreateCombinedMesh()
|
||||
{
|
||||
var combineInstances = new List<CombineInstance>();
|
||||
var combinedUVs = new List<Vector2>();
|
||||
|
||||
for (int i = 0; i < _rendererCount; i++)
|
||||
{
|
||||
var renderer = renderers[i];
|
||||
var meshFilter = renderer.GetComponent<MeshFilter>();
|
||||
if (meshFilter == null || meshFilter.sharedMesh == null) continue;
|
||||
|
||||
var combineInstance = CreateCombineInstance(renderer);
|
||||
if (_rendererUVs.TryGetValue(renderer, out var uvs))
|
||||
{
|
||||
combineInstances.Add(combineInstance);
|
||||
combinedUVs.AddRange(uvs);
|
||||
}
|
||||
}
|
||||
|
||||
return CreateFinalMesh(combineInstances, combinedUVs);
|
||||
}
|
||||
|
||||
private CombineInstance CreateCombineInstance(Renderer renderer)
|
||||
{
|
||||
return new CombineInstance
|
||||
{
|
||||
mesh = renderer.GetComponent<MeshFilter>().sharedMesh,
|
||||
transform = renderer.transform.localToWorldMatrix
|
||||
};
|
||||
}
|
||||
|
||||
private Vector2[] CalculateUVCoordinates(Mesh mesh, Material material)
|
||||
{
|
||||
if (_materialTextureIndices.TryGetValue(material, out var textureIndex))
|
||||
{
|
||||
var uvs = mesh.uv;
|
||||
var xIndex = textureIndex % textureGridWidth;
|
||||
var yIndex = textureIndex / textureGridWidth;
|
||||
var uvScale = new Vector2(1f / textureGridWidth, 1f / textureGridWidth);
|
||||
|
||||
var result = new Vector2[uvs.Length];
|
||||
for (int i = 0; i < uvs.Length; i++)
|
||||
{
|
||||
result[i] = new Vector2(
|
||||
uvs[i].x * uvScale.x + xIndex * uvScale.x,
|
||||
uvs[i].y * uvScale.y + yIndex * uvScale.y
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Debug.LogWarning($"No texture index found for material: {material.name}");
|
||||
return mesh.uv;
|
||||
}
|
||||
|
||||
private Mesh CreateFinalMesh(List<CombineInstance> combineInstances, List<Vector2> combinedUVs)
|
||||
{
|
||||
var resultMesh = new Mesh
|
||||
{
|
||||
indexFormat = UnityEngine.Rendering.IndexFormat.UInt32
|
||||
};
|
||||
|
||||
resultMesh.CombineMeshes(combineInstances.ToArray(), true, true);
|
||||
resultMesh.uv = combinedUVs.ToArray();
|
||||
resultMesh.uv2 = null;
|
||||
resultMesh.uv3 = null;
|
||||
Unwrapping.GenerateSecondaryUVSet(resultMesh);
|
||||
|
||||
return resultMesh;
|
||||
}
|
||||
|
||||
private GameObject CreateResultObject(Material material, Mesh mesh)
|
||||
{
|
||||
var objectName = $"{groupName}_{AssetSaver.ConvertShaderName(shader.name)}";
|
||||
var resultObject = new GameObject(objectName);
|
||||
|
||||
var resultRenderer = resultObject.AddComponent<MeshRenderer>();
|
||||
var resultFilter = resultObject.AddComponent<MeshFilter>();
|
||||
|
||||
resultFilter.sharedMesh = mesh;
|
||||
resultRenderer.sharedMaterial = material;
|
||||
|
||||
return resultObject;
|
||||
}
|
||||
|
||||
private Material FindSampleMaterial(Material firstMaterial, List<Material> sampleMaterials)
|
||||
{
|
||||
if (sampleMaterials == null) return firstMaterial;
|
||||
|
||||
return sampleMaterials.Find(sample => sample.shader.name == firstMaterial.shader.name)
|
||||
?? firstMaterial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6d493dc01f7824c28b0970ee59143400
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,323 @@
|
|||
using System.Collections.Generic;
|
||||
using Performance.Data;
|
||||
using PhxhSDK;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Performance
|
||||
{
|
||||
public class PerformanceWindow : EditorWindow
|
||||
{
|
||||
|
||||
// [MenuItem("Tools/性能优化/打开窗口")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<PerformanceWindow>();
|
||||
window.titleContent = new GUIContent("性能优化");
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private GameObject _selectObj;
|
||||
private bool _isAnalysised;
|
||||
[SerializeField]
|
||||
private List<string> _skipChilds = new List<string>()
|
||||
{
|
||||
"Trees", // 树
|
||||
"Stuffs", // 树
|
||||
"Ground", // 地面,走大贴图
|
||||
"Lights", // 灯光
|
||||
"Fog", // 雾,是一个特殊的材质
|
||||
"Global Volume", // 全局后处理
|
||||
};
|
||||
private List<AutoCombieMeshData> _cacheMeshes = new List<AutoCombieMeshData>();
|
||||
private List<ShaderGroupCombiner> _cacheShaders = new List<ShaderGroupCombiner>();
|
||||
|
||||
private Vector2 _scrollPos1;
|
||||
private Vector2 _scrollPos2;
|
||||
|
||||
private SerializedObject _serializedObject;
|
||||
private SerializedProperty _serializedProperty;
|
||||
private int _forceUnitTextureSize = 256;
|
||||
|
||||
private int _totalCount;
|
||||
|
||||
private GameObject _cacheRootObj;
|
||||
|
||||
private void _InitFiled()
|
||||
{
|
||||
_serializedObject = new SerializedObject(this);
|
||||
_serializedProperty = _serializedObject.FindProperty("_skipChilds");
|
||||
_cacheRootObj = GameObject.Find("CombieRoot");
|
||||
_selectObj = GameObject.Find("Art");
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
// 自动勾选所有材质的GPU Instancing
|
||||
if (GUILayout.Button("自动勾选所有材质的GPU Instancing"))
|
||||
{
|
||||
AutoSetAllGpuInst.AutoSet();
|
||||
}
|
||||
|
||||
if (_serializedObject == null)
|
||||
{
|
||||
_InitFiled();
|
||||
}
|
||||
_serializedObject.Update();
|
||||
EditorGUILayout.PropertyField(_serializedProperty, true);
|
||||
_serializedObject.ApplyModifiedProperties();
|
||||
|
||||
var selectObj = EditorGUILayout.ObjectField("场景根节点", _selectObj, typeof(GameObject), true) as GameObject;
|
||||
if (selectObj != _selectObj)
|
||||
{
|
||||
_selectObj = selectObj;
|
||||
_cacheMeshes.Clear();
|
||||
_cacheShaders.Clear();
|
||||
_isAnalysised = false;
|
||||
_totalCount = 0;
|
||||
_cacheRootObj = GameObject.Find("CombieRoot");
|
||||
}
|
||||
|
||||
_forceUnitTextureSize = EditorGUILayout.IntField("强制单张贴图大小", _forceUnitTextureSize);
|
||||
if (GUILayout.Button("分析"))
|
||||
{
|
||||
_DoAnalysis();
|
||||
}
|
||||
|
||||
if (_isAnalysised)
|
||||
{
|
||||
GUILayout.Label($"共有不同的Mesh: {_cacheMeshes.Count}个");
|
||||
GUILayout.Label($"预计合并后的顶点数量: {_totalCount}个");
|
||||
_scrollPos1 = EditorGUILayout.BeginScrollView(_scrollPos1, GUILayout.Height(100));
|
||||
GUILayout.BeginVertical();
|
||||
foreach (var mesh in _cacheMeshes)
|
||||
{
|
||||
GUILayout.Label($" {mesh.objs.Count}个 {mesh.mesh.name}");
|
||||
}
|
||||
GUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
GUILayout.Label($"共有不同的Shader: {_cacheShaders.Count}个");
|
||||
_scrollPos2 = EditorGUILayout.BeginScrollView(_scrollPos2, GUILayout.Height(100));
|
||||
GUILayout.BeginVertical();
|
||||
foreach (var shader in _cacheShaders)
|
||||
{
|
||||
GUILayout.Label($" {shader.renderers.Length}个 {shader.shader.name} 预计贴图大小: {shader.textureGridWidth}");
|
||||
}
|
||||
GUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
if (GUILayout.Button("自动合并"))
|
||||
{
|
||||
_DoCombie();
|
||||
}
|
||||
}
|
||||
|
||||
if (_cacheRootObj != null)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
// 还原和切换
|
||||
if (GUILayout.Button("还原"))
|
||||
{
|
||||
_ShowCombie(false);
|
||||
}
|
||||
if (GUILayout.Button("切换"))
|
||||
{
|
||||
_ShowCombie(true);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
private void _ShowCombie(bool show)
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
// 隐藏原始节点
|
||||
_SetOldObjActive(false);
|
||||
// 显示合并节点
|
||||
_cacheRootObj.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 隐藏合并节点
|
||||
_cacheRootObj.SetActive(false);
|
||||
// 显示原始节点
|
||||
_SetOldObjActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void _SetOldObjActive(bool active)
|
||||
{
|
||||
if (_selectObj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var childCount = _selectObj.transform.childCount;
|
||||
for (var i = 0; i < childCount; ++i)
|
||||
{
|
||||
var child = _selectObj.transform.GetChild(i);
|
||||
if (_skipChilds.Contains(child.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
child.gameObject.SetActive(active);
|
||||
}
|
||||
}
|
||||
|
||||
private void _DoCombie()
|
||||
{
|
||||
// 强制替换
|
||||
if (_cacheRootObj == null)
|
||||
{
|
||||
var findObj = GameObject.Find("CombieRoot");
|
||||
if (findObj != null)
|
||||
{
|
||||
DestroyImmediate(findObj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyImmediate(_cacheRootObj);
|
||||
}
|
||||
_cacheRootObj = new GameObject("CombieRoot");
|
||||
var sceneDir = SceneManager.GetActiveScene();
|
||||
|
||||
for (int i = 0; i < _cacheShaders.Count; i++)
|
||||
{
|
||||
var shaderData = _cacheShaders[i];
|
||||
var combieObj = shaderData.CombineToSingleObject($"{sceneDir}/Combined", new List<Material>());
|
||||
if (combieObj == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
combieObj.transform.SetParent(_cacheRootObj.transform);
|
||||
}
|
||||
|
||||
// 隐藏原始节点
|
||||
_SetOldObjActive(false);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private List<Renderer> _CollectRenderers()
|
||||
{
|
||||
var result = new List<Renderer>();
|
||||
var childCount = _selectObj.transform.childCount;
|
||||
for (var i = 0; i < childCount; ++i)
|
||||
{
|
||||
var child = _selectObj.transform.GetChild(i);
|
||||
if (_skipChilds.Contains(child.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var renderers = child.GetComponentsInChildren<Renderer>();
|
||||
result.AddRange(renderers);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void _DoAnalysis()
|
||||
{
|
||||
if (_selectObj == null)
|
||||
{
|
||||
Debug.LogError("请先选择场景根节点");
|
||||
return;
|
||||
}
|
||||
|
||||
var renderers = _CollectRenderers();
|
||||
_CollectMeshs(renderers);
|
||||
_CollectShaders(renderers);
|
||||
|
||||
_isAnalysised = true;
|
||||
}
|
||||
|
||||
private void _CollectShaders(List<Renderer> renderers)
|
||||
{
|
||||
_cacheShaders.Clear();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
var materials = renderer.sharedMaterials;
|
||||
if (materials.Length > 1)
|
||||
{
|
||||
Debug.LogError(renderer.name + "材质数量大于1", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
var shareMaterial = renderer.sharedMaterial;
|
||||
if (shareMaterial == null)
|
||||
{
|
||||
Debug.LogError(renderer.name + "没有材质", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
var shader = shareMaterial.shader;
|
||||
if (shader == null)
|
||||
{
|
||||
Debug.LogError(renderer.name + "没有Shader", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
var shaderData = _GetShaderData(shader);
|
||||
shaderData.AddRenderer(renderer);
|
||||
}
|
||||
// 预计算贴图大小
|
||||
foreach (var shaderData in _cacheShaders)
|
||||
{
|
||||
shaderData.PreCalculate();
|
||||
}
|
||||
}
|
||||
|
||||
private void _CollectMeshs(List<Renderer> renderers)
|
||||
{
|
||||
_totalCount = 0;
|
||||
_cacheMeshes.Clear();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
var meshFilter = renderer.GetComponent<MeshFilter>();
|
||||
if (meshFilter == null)
|
||||
{
|
||||
Debug.LogError(renderer.name + "没有MeshFilter", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
var mesh = meshFilter.sharedMesh;
|
||||
if (mesh == null)
|
||||
{
|
||||
Debug.LogError(renderer.name + "没有Mesh", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
var meshData = _GetMeshData(mesh);
|
||||
_totalCount += mesh.vertexCount;
|
||||
meshData.AddObj(renderer.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private ShaderGroupCombiner _GetShaderData(Shader shader)
|
||||
{
|
||||
foreach (var shaderData in _cacheShaders)
|
||||
{
|
||||
if (shaderData.shader == shader)
|
||||
{
|
||||
return shaderData;
|
||||
}
|
||||
}
|
||||
|
||||
var newShaderData = new ShaderGroupCombiner(shader, _forceUnitTextureSize);
|
||||
_cacheShaders.Add(newShaderData);
|
||||
return newShaderData;
|
||||
}
|
||||
|
||||
private AutoCombieMeshData _GetMeshData(Mesh mesh)
|
||||
{
|
||||
foreach (var meshData in _cacheMeshes)
|
||||
{
|
||||
if (meshData.mesh == mesh)
|
||||
{
|
||||
return meshData;
|
||||
}
|
||||
}
|
||||
|
||||
var newMeshData = new AutoCombieMeshData(mesh);
|
||||
_cacheMeshes.Add(newMeshData);
|
||||
return newMeshData;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 90dcfacca7654dee9b981e0bd4238416
|
||||
timeCreated: 1703670909
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Performance.Data;
|
||||
using PhxhSDK;
|
||||
using UnityEngine;
|
||||
|
||||
public class SceneCombineUtil : Singlenton<SceneCombineUtil>
|
||||
{
|
||||
private int _totalCount;
|
||||
private List<AutoCombieMeshData> _cacheMeshes = new List<AutoCombieMeshData>();
|
||||
private List<ShaderGroupCombiner> _cacheShaders = new List<ShaderGroupCombiner>();
|
||||
private List<Material> _sampleMaterials = new List<Material>();
|
||||
private int _forceUnitTextureSize = 256;
|
||||
|
||||
public List<GameObject> CombineGameObjectsByGroups(Dictionary<string, List<Renderer>> rendererGroups,
|
||||
Transform root,
|
||||
string outputPath,
|
||||
int postProcessTextureUnitSize = 256,
|
||||
List<Material> sampleMaterials = null,string prefix = "")
|
||||
{
|
||||
if (sampleMaterials != null)
|
||||
{
|
||||
_sampleMaterials.AddRange(sampleMaterials);
|
||||
}
|
||||
_forceUnitTextureSize = postProcessTextureUnitSize;
|
||||
|
||||
List<GameObject> result = new List<GameObject>();
|
||||
|
||||
foreach (var group in rendererGroups)
|
||||
{
|
||||
string groupName = group.Key;
|
||||
List<Renderer> groupRenderers = group.Value;
|
||||
|
||||
// 为每个组创建单独的输出路径
|
||||
string groupOutputPath = $"{outputPath}";
|
||||
|
||||
// 收集该组的网格和着色器
|
||||
_CollectMeshs(groupRenderers);
|
||||
_CollectShaders(groupRenderers, groupName);
|
||||
|
||||
// 合并该组
|
||||
var groupResult = _DoCombine(root, groupOutputPath, prefix);
|
||||
result.AddRange(groupResult);
|
||||
|
||||
// 清理缓存,准备下一组
|
||||
_cacheMeshes.Clear();
|
||||
_cacheShaders.Clear();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[System.Obsolete("Use CombineGameObjectsByGroups instead")]
|
||||
public List<GameObject> CombineGameObjects(List<Renderer> renderers,
|
||||
Transform root,
|
||||
string outputPath,
|
||||
int postProcessTextureUnitSize = 256,
|
||||
List<Material> sampleMaterials = null, string prefix = "")
|
||||
{
|
||||
var rendererGroups = new Dictionary<string, List<Renderer>>
|
||||
{
|
||||
{ "Default", renderers }
|
||||
};
|
||||
return CombineGameObjectsByGroups(rendererGroups, root, outputPath, postProcessTextureUnitSize, sampleMaterials, prefix);
|
||||
}
|
||||
|
||||
private List<GameObject> _DoCombine(Transform root, string outputPath, string prefix = "")
|
||||
{
|
||||
// 强制替换
|
||||
// if (_cacheRootObj == null)
|
||||
// {
|
||||
// var findObj = GameObject.Find("CombieRoot");
|
||||
// if (findObj != null)
|
||||
// {
|
||||
// DestroyImmediate(findObj);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// DestroyImmediate(_cacheRootObj);
|
||||
// }
|
||||
//_cacheRootObj = new GameObject("CombieRoot");
|
||||
|
||||
List<GameObject> result = new();
|
||||
|
||||
for (int i = 0; i < _cacheShaders.Count; i++)
|
||||
{
|
||||
var shaderData = _cacheShaders[i];
|
||||
var combineObj = shaderData.CombineToSingleObject(outputPath, _sampleMaterials, prefix);
|
||||
if (combineObj != null)
|
||||
{
|
||||
result.Add(combineObj);
|
||||
combineObj.transform.SetParent(root);
|
||||
}
|
||||
//combieObj.transform.SetParent(_cacheRootObj.transform);
|
||||
}
|
||||
|
||||
return result;
|
||||
// 隐藏原始节点
|
||||
//_SetOldObjActive(false);
|
||||
//AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private void _CollectShaders(List<Renderer> renderers, string groupName = "Default")
|
||||
{
|
||||
_cacheShaders.Clear();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
var materials = renderer.sharedMaterials;
|
||||
if (materials.Length > 1)
|
||||
{
|
||||
Debug.LogError(renderer.name + "材质数量大于1", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
var shareMaterial = renderer.sharedMaterial;
|
||||
if (shareMaterial == null)
|
||||
{
|
||||
Debug.LogError(renderer.name + "没有材质", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
var shader = shareMaterial.shader;
|
||||
if (shader == null)
|
||||
{
|
||||
Debug.LogError(renderer.name + "没有Shader", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
var shaderData = _GetShaderData(shader, groupName);
|
||||
shaderData.AddRenderer(renderer);
|
||||
}
|
||||
|
||||
// 预计算贴图大小
|
||||
foreach (var shaderData in _cacheShaders)
|
||||
{
|
||||
shaderData.PreCalculate();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Renderer> _CollectRenderers(List<GameObject> gameObjects)
|
||||
{
|
||||
var result = new List<Renderer>();
|
||||
foreach (var go in gameObjects)
|
||||
{
|
||||
var renderer = go.GetComponent<Renderer>();
|
||||
if (renderer != null)
|
||||
result.Add(renderer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void _CollectMeshs(List<Renderer> renderers)
|
||||
{
|
||||
_totalCount = 0;
|
||||
_cacheMeshes.Clear();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
var meshFilter = renderer.GetComponent<MeshFilter>();
|
||||
if (meshFilter == null)
|
||||
{
|
||||
Debug.LogError(renderer.name + "没有MeshFilter", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
var mesh = meshFilter.sharedMesh;
|
||||
if (mesh == null)
|
||||
{
|
||||
Debug.LogError(renderer.name + "没有Mesh", renderer.gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
var meshData = _GetMeshData(mesh);
|
||||
_totalCount += mesh.vertexCount;
|
||||
meshData.AddObj(renderer.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private ShaderGroupCombiner _GetShaderData(Shader shader, string groupName = "Default")
|
||||
{
|
||||
foreach (var shaderData in _cacheShaders)
|
||||
{
|
||||
if (shaderData.shader == shader && shaderData.groupName == groupName)
|
||||
{
|
||||
return shaderData;
|
||||
}
|
||||
}
|
||||
|
||||
var newShaderData = new ShaderGroupCombiner(shader, _forceUnitTextureSize, groupName);
|
||||
_cacheShaders.Add(newShaderData);
|
||||
return newShaderData;
|
||||
}
|
||||
|
||||
private AutoCombieMeshData _GetMeshData(Mesh mesh)
|
||||
{
|
||||
foreach (var meshData in _cacheMeshes)
|
||||
{
|
||||
if (meshData.mesh == mesh)
|
||||
{
|
||||
return meshData;
|
||||
}
|
||||
}
|
||||
|
||||
var newMeshData = new AutoCombieMeshData(mesh);
|
||||
_cacheMeshes.Add(newMeshData);
|
||||
return newMeshData;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4923e8f03afee074d96c8e493cd939c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 77f685ff4f492e605f782aab45c6265e01c0f283
|
||||
Subproject commit 72d5ff680bfab96d675a4d42a9bd032a0fa3bbed
|
||||
Loading…
Reference in New Issue