187 lines
7.4 KiB
C#
187 lines
7.4 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
// ver 1.0
|
|
// Copyright (c) 2019 gatosyocora
|
|
|
|
namespace VRCDeveloperTool
|
|
{
|
|
public class ShapeKeyMixer : EditorWindow
|
|
{
|
|
private SkinnedMeshRenderer renderer;
|
|
|
|
private List<string> shapeKeyNames;
|
|
private bool[] selectedShapeKeys;
|
|
private bool isOpenedBlendShape = true;
|
|
private string combinedShapeKeyName = "";
|
|
private bool deleteOriginShapeKey = true;
|
|
private Vector2 shapeKeyScrollPos = Vector2.zero;
|
|
|
|
|
|
[MenuItem("VRCDeveloperTool/Mesh/ShapeKey Mixer")]
|
|
private static void Open()
|
|
{
|
|
GetWindow<ShapeKeyMixer>("ShapeKey Mixer");
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
renderer = null;
|
|
shapeKeyNames = null;
|
|
selectedShapeKeys = null;
|
|
isOpenedBlendShape = true;
|
|
combinedShapeKeyName = "";
|
|
shapeKeyScrollPos = Vector2.zero;
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
using (var check = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
renderer = EditorGUILayout.ObjectField(
|
|
"SkinnedMeshRenderer",
|
|
renderer,
|
|
typeof(SkinnedMeshRenderer),
|
|
true
|
|
) as SkinnedMeshRenderer;
|
|
|
|
|
|
if (check.changed)
|
|
{
|
|
if (renderer != null)
|
|
{
|
|
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
|
|
selectedShapeKeys = new bool[shapeKeyNames.Count()];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shapeKeyNames != null)
|
|
{
|
|
isOpenedBlendShape = EditorGUILayout.Foldout(isOpenedBlendShape, "Shape Keys");
|
|
if (isOpenedBlendShape)
|
|
{
|
|
using (new EditorGUI.IndentLevelScope())
|
|
using (var scroll = new EditorGUILayout.ScrollViewScope(shapeKeyScrollPos, GUI.skin.box))
|
|
{
|
|
shapeKeyScrollPos = scroll.scrollPosition;
|
|
for (int i = 0; i < shapeKeyNames.Count(); i++)
|
|
{
|
|
selectedShapeKeys[i] = EditorGUILayout.ToggleLeft(shapeKeyNames[i], selectedShapeKeys[i]);
|
|
}
|
|
}
|
|
}
|
|
deleteOriginShapeKey = EditorGUILayout.Toggle("Delete Origin ShapeKey", deleteOriginShapeKey);
|
|
combinedShapeKeyName = EditorGUILayout.TextField("Mixed ShapeKey Name", combinedShapeKeyName);
|
|
}
|
|
|
|
using (new EditorGUI.DisabledScope(renderer == null || combinedShapeKeyName == "" || (selectedShapeKeys != null && selectedShapeKeys.Sum(x => x ? 1 : 0) <= 1)))
|
|
{
|
|
if (GUILayout.Button("Mix ShapeKeys"))
|
|
{
|
|
// 2つ以上が選択されている
|
|
if (selectedShapeKeys.Sum(x => x ? 1 : 0) > 1)
|
|
{
|
|
// 選択されている要素のインデックスの配列
|
|
var selectedBlendShapeIndexs = selectedShapeKeys
|
|
.Select((isSelect, index) => new { Index = index, Value = isSelect })
|
|
.Where(x => x.Value)
|
|
.Select(x => x.Index)
|
|
.ToArray();
|
|
|
|
MixShapeKey(renderer, selectedBlendShapeIndexs, combinedShapeKeyName, deleteOriginShapeKey);
|
|
}
|
|
|
|
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
|
|
selectedShapeKeys = new bool[shapeKeyNames.Count()];
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool MixShapeKey(SkinnedMeshRenderer renderer, int[] selectedShapeKeyIndexs, string combinedBlendShapeName, bool deleteOriginShapeKey)
|
|
{
|
|
var mesh = renderer.sharedMesh;
|
|
if (mesh == null) return false;
|
|
|
|
var mesh_custom = Instantiate(mesh);
|
|
|
|
mesh_custom.ClearBlendShapes();
|
|
|
|
int frameIndex = 0;
|
|
string shapeKeyName;
|
|
float weight;
|
|
Vector3[] deltaVertices, deltaNormals, deltaTangents;
|
|
|
|
var combinedDeltaVertices = new Vector3[mesh.vertexCount];
|
|
var combinedDeltaNormals = new Vector3[mesh.vertexCount];
|
|
var combinedDeltaTangents = new Vector3[mesh.vertexCount];
|
|
float combinedWeight = 0;
|
|
|
|
for (int blendShapeIndex = 0; blendShapeIndex < mesh.blendShapeCount; blendShapeIndex++)
|
|
{
|
|
deltaVertices = new Vector3[mesh.vertexCount];
|
|
deltaNormals = new Vector3[mesh.vertexCount];
|
|
deltaTangents = new Vector3[mesh.vertexCount];
|
|
mesh.GetBlendShapeFrameVertices(blendShapeIndex, frameIndex, deltaVertices, deltaNormals, deltaTangents);
|
|
weight = mesh.GetBlendShapeFrameWeight(blendShapeIndex, frameIndex);
|
|
|
|
if (selectedShapeKeyIndexs.Contains(blendShapeIndex))
|
|
{
|
|
for (int i = 0; i < mesh.vertexCount; i++)
|
|
{
|
|
combinedDeltaVertices[i] += deltaVertices[i];
|
|
combinedDeltaNormals[i] += deltaNormals[i];
|
|
combinedDeltaTangents[i] += deltaTangents[i];
|
|
combinedWeight = Mathf.Max(combinedWeight, weight);
|
|
}
|
|
|
|
if (!deleteOriginShapeKey)
|
|
{
|
|
shapeKeyName = mesh.GetBlendShapeName(blendShapeIndex);
|
|
mesh_custom.AddBlendShapeFrame(shapeKeyName, weight, deltaVertices, deltaNormals, deltaTangents);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shapeKeyName = mesh.GetBlendShapeName(blendShapeIndex);
|
|
mesh_custom.AddBlendShapeFrame(shapeKeyName, weight, deltaVertices, deltaNormals, deltaTangents);
|
|
}
|
|
}
|
|
|
|
if (selectedShapeKeyIndexs.Length > 0)
|
|
mesh_custom.AddBlendShapeFrame(combinedShapeKeyName, combinedWeight, combinedDeltaVertices, combinedDeltaNormals, combinedDeltaTangents);
|
|
|
|
Undo.RecordObject(renderer, "Renderer " + renderer.name);
|
|
renderer.sharedMesh = mesh_custom;
|
|
|
|
var path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)) + "/" + mesh.name + "_custom.asset";
|
|
AssetDatabase.CreateAsset(mesh_custom, AssetDatabase.GenerateUniqueAssetPath(path));
|
|
AssetDatabase.SaveAssets();
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// SkinnedMeshRendererのもつメッシュのシェイプキーの名前のリストを取得する
|
|
/// </summary>
|
|
/// <param name="renderer"></param>
|
|
/// <returns></returns>
|
|
private List<string> GetBlendShapeListFromRenderer(SkinnedMeshRenderer renderer)
|
|
{
|
|
List<string> shapeKeyNames = new List<string>();
|
|
var mesh = renderer.sharedMesh;
|
|
|
|
if (mesh != null)
|
|
for (int i = 0; i < mesh.blendShapeCount; i++)
|
|
shapeKeyNames.Add(mesh.GetBlendShapeName(i));
|
|
|
|
return shapeKeyNames;
|
|
}
|
|
}
|
|
}
|