mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-11 13:04:17 -05:00
227 lines
6.5 KiB
C#
227 lines
6.5 KiB
C#
#pragma warning disable 0414, 0649
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using System.Linq;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UniGLTF.MeshUtility;
|
|
using System.IO;
|
|
|
|
namespace VRM
|
|
{
|
|
public class MeshIntegratorWizard : ScriptableWizard
|
|
{
|
|
[SerializeField]
|
|
GameObject m_root;
|
|
|
|
[Header("Validation")]
|
|
[SerializeField]
|
|
Material[] m_uniqueMaterials;
|
|
|
|
[Serializable]
|
|
struct MaterialKey
|
|
{
|
|
public string Shader;
|
|
public KeyValuePair<string, object>[] Properties;
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (!(obj is MaterialKey))
|
|
{
|
|
return base.Equals(obj);
|
|
}
|
|
|
|
var key = (MaterialKey)obj;
|
|
|
|
return Shader == key.Shader
|
|
&& Properties.SequenceEqual(key.Properties)
|
|
;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return base.GetHashCode();
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
struct MaterialList
|
|
{
|
|
public Material[] Materials;
|
|
|
|
public MaterialList(Material[] list)
|
|
{
|
|
Materials = list;
|
|
}
|
|
}
|
|
[SerializeField]
|
|
MaterialList[] m_duplicateMaterials;
|
|
|
|
[Serializable]
|
|
public class ExcludeItem
|
|
{
|
|
public Mesh Mesh;
|
|
public bool Exclude;
|
|
}
|
|
|
|
[Header("Options")]
|
|
[SerializeField]
|
|
List<ExcludeItem> m_excludes = new List<ExcludeItem>();
|
|
|
|
[Header("Result")]
|
|
[SerializeField]
|
|
MeshMap[] integrationResults;
|
|
|
|
public static void CreateWizard()
|
|
{
|
|
ScriptableWizard.DisplayWizard<MeshIntegratorWizard>("MeshIntegratorWizard", "Integrate and close window", "Integrate");
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
m_root = Selection.activeGameObject;
|
|
OnValidate();
|
|
}
|
|
|
|
static object GetPropertyValue(Shader shader, int i, Material m)
|
|
{
|
|
var propType = ShaderUtil.GetPropertyType(shader, i);
|
|
switch (propType)
|
|
{
|
|
case ShaderUtil.ShaderPropertyType.Color:
|
|
return m.GetColor(ShaderUtil.GetPropertyName(shader, i));
|
|
|
|
case ShaderUtil.ShaderPropertyType.Range:
|
|
case ShaderUtil.ShaderPropertyType.Float:
|
|
return m.GetFloat(ShaderUtil.GetPropertyName(shader, i));
|
|
|
|
case ShaderUtil.ShaderPropertyType.Vector:
|
|
return m.GetVector(ShaderUtil.GetPropertyName(shader, i));
|
|
|
|
case ShaderUtil.ShaderPropertyType.TexEnv:
|
|
return m.GetTexture(ShaderUtil.GetPropertyName(shader, i));
|
|
|
|
default:
|
|
throw new NotImplementedException(propType.ToString());
|
|
}
|
|
}
|
|
|
|
static MaterialKey GetMaterialKey(Material m)
|
|
{
|
|
var key = new MaterialKey
|
|
{
|
|
Shader = m.shader.name,
|
|
};
|
|
|
|
key.Properties = Enumerable.Range(0, ShaderUtil.GetPropertyCount(m.shader))
|
|
.Select(x => new KeyValuePair<string, object>(
|
|
ShaderUtil.GetPropertyName(m.shader, x),
|
|
GetPropertyValue(m.shader, x, m))
|
|
)
|
|
.OrderBy(x => x.Key)
|
|
.ToArray()
|
|
;
|
|
|
|
return key;
|
|
}
|
|
|
|
void OnValidate()
|
|
{
|
|
if (m_root == null
|
|
|| !PrefabUtility.IsPartOfAnyPrefab(m_root) || m_root.transform.parent != null)
|
|
{
|
|
Debug.LogWarning("Invalidate");
|
|
m_uniqueMaterials = new Material[] { };
|
|
m_duplicateMaterials = new MaterialList[] { };
|
|
m_excludes.Clear();
|
|
return;
|
|
}
|
|
|
|
Debug.Log("OnValidate");
|
|
m_uniqueMaterials = MeshIntegratorUtility.EnumerateSkinnedMeshRenderer(m_root.transform, false)
|
|
.SelectMany(x => x.sharedMaterials)
|
|
.Distinct()
|
|
.ToArray();
|
|
|
|
m_duplicateMaterials = m_uniqueMaterials
|
|
.GroupBy(x => GetMaterialKey(x), x => x)
|
|
.Select(x => new MaterialList(x.ToArray()))
|
|
.Where(x => x.Materials.Length > 1)
|
|
.ToArray()
|
|
;
|
|
|
|
var exclude_map = new Dictionary<Mesh, ExcludeItem>();
|
|
var excludes = new List<ExcludeItem>();
|
|
foreach (var x in m_root.GetComponentsInChildren<Renderer>())
|
|
{
|
|
var mesh = x.GetMesh();
|
|
if (mesh == null)
|
|
{
|
|
continue;
|
|
}
|
|
var item = new ExcludeItem { Mesh = mesh };
|
|
excludes.Add(item);
|
|
exclude_map[mesh] = item;
|
|
}
|
|
foreach (var x in m_excludes)
|
|
{
|
|
if (exclude_map.TryGetValue(x.Mesh, out ExcludeItem item))
|
|
{
|
|
// update
|
|
item.Exclude = x.Exclude;
|
|
}
|
|
}
|
|
m_excludes.Clear();
|
|
foreach (var kv in exclude_map)
|
|
{
|
|
m_excludes.Add(kv.Value);
|
|
}
|
|
}
|
|
|
|
void OnWizardUpdate()
|
|
{
|
|
helpString = "select target mesh root";
|
|
}
|
|
|
|
void Integrate()
|
|
{
|
|
if (m_root == null)
|
|
{
|
|
Debug.LogWarning("no root object");
|
|
return;
|
|
}
|
|
|
|
var prefabPath = AssetDatabase.GetAssetPath(m_root);
|
|
var path = EditorUtility.SaveFilePanel("save prefab", Path.GetDirectoryName(prefabPath), m_root.name, "prefab");
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var assetPath = UniGLTF.UnityPath.FromFullpath(path);
|
|
if (!assetPath.IsUnderAssetsFolder)
|
|
{
|
|
Debug.LogWarning($"{path} is not asset path");
|
|
return;
|
|
}
|
|
|
|
var excludes = m_excludes.Where(x => x.Exclude).Select(x => x.Mesh);
|
|
integrationResults = MeshIntegratorEditor.Integrate(m_root, assetPath, excludes).Select(x => x.MeshMap).ToArray();
|
|
}
|
|
|
|
void OnWizardCreate()
|
|
{
|
|
Debug.Log("OnWizardCreate");
|
|
Integrate();
|
|
|
|
// close
|
|
}
|
|
|
|
void OnWizardOtherButton()
|
|
{
|
|
Debug.Log("OnWizardOtherButton");
|
|
Integrate();
|
|
}
|
|
}
|
|
}
|