262 lines
6.4 KiB
C#
262 lines
6.4 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|