Files
JJBB/Assets/Editor/Scripts/SkinnedMeshFix/SkinnedMeshFix.cs

262 lines
6.4 KiB
C#
Raw Permalink Normal View History

2024-08-23 15:49:34 +08:00
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
public class SkinnedMeshFix : EditorWindow
{
private const char _splitChar = '/';
[MenuItem("ProTool/Skinned Mesh Fix")]
public static void ShowWindow()
{
GetWindow<SkinnedMeshFix>();
}
private GameObject _source;
private GameObject _target;
private void OnGUI()
{
GameObjectField("Correct Sample", ref _source);
GUILayout.Space(5f);
GameObjectField("To Fix", ref _target);
GUILayout.Space(5f);
if (GUILayout.Button("Fix Item", GUILayout.Width(200f)))
{
if (_source == null || _target == null)
Debug.LogError("No gameObject to fix or the source is not set!");
else
{
var sourceRenderer = _source.GetComponentInChildren<SkinnedMeshRenderer>();
var targetRenderer = _target.GetComponentInChildren<SkinnedMeshRenderer>();
var targetMesh = targetRenderer.sharedMesh;
if (sourceRenderer == null || targetRenderer == null)
Debug.LogError("No SkinnedMeshRenderer found on one of the gameObjects!");
else if (targetMesh == null)
Debug.LogError("Target gameObject has no mesh!");
else
{
var boneIndices = new List<BoneIndex>();
var error = false;
var boneNames = new string[sourceRenderer.bones.Length];
for (var i = 0; i < boneNames.Length; i++)
{
var path = GetRelativeName(_source.transform, sourceRenderer.bones[i]);
Debug.LogWarning(path);
var targetBone = FindBone(_target.transform, path);
if (targetBone == null)
{
Debug.LogWarning("Target doesn't contain the bone " + path);
error = true;
}
else
{
int? targetId = null;
for (var j = 0; j < targetRenderer.bones.Length; j++)
{
if (targetRenderer.bones[j] == targetBone)
{
targetId = j;
break;
}
}
if (targetId == null)
{
Debug.LogError(
string.Format("Target doesn't attach the bone {0} in SkinnedMeshRenderer!", path));
error = true;
}
else
{
Debug.LogWarning("Bone Switch To " + i + " to " + targetId.Value);
boneIndices.Add(new BoneIndex(i, targetId.Value));
}
}
if (error)
break;
}
if (!error)
{
// Fix bones
var bones = new Transform[boneIndices.Count];
var bindposes = new Matrix4x4[boneIndices.Count];
for (var i = 0; i < bones.Length; i++)
{
var id = boneIndices[i].target;
bones[i] = targetRenderer.bones[id];
bindposes[i] = targetMesh.bindposes[id];
}
targetRenderer.bones = bones;
foreach (var bone in targetRenderer.bones)
{
Debug.LogWarning(bone.GetHierarchyName());
}
// Fix Mesh
var newMesh = new Mesh
{
vertices = targetMesh.vertices,
uv = targetMesh.uv,
uv2 = targetMesh.uv2,
uv3 = targetMesh.uv3,
uv4 = targetMesh.uv4,
normals = targetMesh.normals,
tangents = targetMesh.tangents,
indexFormat = targetMesh.indexFormat,
bindposes = bindposes,
};
for (var i = 0; i < targetMesh.subMeshCount; i++)
{
newMesh.SetTriangles(targetMesh.GetTriangles(i), i);
}
var boneWeights = targetMesh.boneWeights;
for (var i = 0; i < boneWeights.Length; i++)
{
var boneWeight = boneWeights[i];
if (boneWeight.weight0 > 0)
{
var id = GetConvertedIndex(boneWeight.boneIndex0, boneIndices);
if (id == null)
{
boneWeight.weight0 = 0;
boneWeight.boneIndex0 = 0;
}
else
{
boneWeight.boneIndex0 = id.Value;
}
}
if (boneWeight.weight1 > 0)
{
var id = GetConvertedIndex(boneWeight.boneIndex1, boneIndices);
if (id == null)
{
boneWeight.weight1 = 0;
boneWeight.boneIndex1 = 0;
}
else
{
boneWeight.boneIndex1 = id.Value;
}
}
if (boneWeight.weight2 > 0)
{
var id = GetConvertedIndex(boneWeight.boneIndex2, boneIndices);
if (id == null)
{
boneWeight.weight2 = 0;
boneWeight.boneIndex2 = 0;
}
else
{
boneWeight.boneIndex2 = id.Value;
}
}
if (boneWeight.weight3 > 0)
{
var id = GetConvertedIndex(boneWeight.boneIndex3, boneIndices);
if (id == null)
{
boneWeight.weight3 = 0;
boneWeight.boneIndex3 = 0;
}
else
{
boneWeight.boneIndex3 = id.Value;
}
}
boneWeights[i] = boneWeight;
}
newMesh.boneWeights = boneWeights;
newMesh.UploadMeshData(true);
var path = AssetDatabase.GetAssetPath(targetMesh);
if (string.IsNullOrEmpty(path))
{
error = true;
Debug.LogError("Target mesh is not an asset in project!");
}
else
{
path = path.MoveUp().Open(Path.GetFileNameWithoutExtension(targetMesh.name) + ".asset");
AssetDatabase.CreateAsset(newMesh, path);
AssetDatabase.Refresh();
}
if (!error)
{
targetRenderer.sharedMesh = newMesh;
// Prefab Utility
}
}
}
}
}
}
private int? GetConvertedIndex(int index, List<BoneIndex> indexList)
{
var boneIndex = indexList.Find(a => a.target == index);
if (boneIndex == null)
return null;
else
return boneIndex.source;
}
private void GameObjectField(string label, ref GameObject field)
{
field = EditorGUILayout.ObjectField(label, field, typeof(GameObject), true) as GameObject;
}
// ReSharper disable once SuggestBaseTypeForParameter
private string GetRelativeName(Transform root, Transform target)
{
var builder = new StringBuilder();
builder.Append(target.name);
while (target.parent != root)
{
target = target.parent;
builder.Insert(0, target.name + _splitChar);
}
return builder.ToString();
}
private Transform FindBone(Transform root, string relativePath)
{
var segments = relativePath.Split(_splitChar);
for (var i = 0; i < segments.Length; i++)
{
root = root.Find(segments[i]);
if (root == null)
break;
}
return root;
}
private class BoneIndex
{
public readonly int source;
public readonly int target;
public BoneIndex(int source, int target)
{
this.source = source;
this.target = target;
}
}
}