239 lines
7.6 KiB
C#
239 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
|
|
namespace UniGLTF.MeshUtility
|
|
{
|
|
/// <summary>
|
|
/// - Freeze
|
|
/// - Integration
|
|
/// - Split
|
|
///
|
|
/// - Implement runtime logic => Process a hierarchy in scene. Do not process prefab.
|
|
/// - Implement undo
|
|
///
|
|
/// </summary>
|
|
public class GltfMeshUtility
|
|
{
|
|
/// <summary>
|
|
/// Same as VRM-0 normalization
|
|
/// - Mesh
|
|
/// - Node
|
|
/// - InverseBindMatrices
|
|
/// </summary>
|
|
public bool FreezeBlendShapeRotationAndScaling = false;
|
|
|
|
public List<MeshIntegrationGroup> MeshIntegrationGroups = new List<MeshIntegrationGroup>();
|
|
|
|
/// <summary>
|
|
/// Create a headless model and solve VRM.FirstPersonFlag.Auto
|
|
/// </summary>
|
|
public bool GenerateMeshForFirstPersonAuto = false;
|
|
|
|
/// <summary>
|
|
/// Split into having and not having BlendShape
|
|
/// </summary>
|
|
public bool SplitByBlendShape = false;
|
|
|
|
static MeshIntegrationGroup.MeshIntegrationTypes TypeFromName(string name)
|
|
{
|
|
var key = name.ToLower();
|
|
if (key.Contains("first"))
|
|
{
|
|
return MeshIntegrationGroup.MeshIntegrationTypes.FirstPersonOnly;
|
|
}
|
|
if (key.Contains("third"))
|
|
{
|
|
return MeshIntegrationGroup.MeshIntegrationTypes.ThirdPersonOnly;
|
|
}
|
|
if (key.Contains("auto"))
|
|
{
|
|
return MeshIntegrationGroup.MeshIntegrationTypes.Auto;
|
|
}
|
|
return MeshIntegrationGroup.MeshIntegrationTypes.Both;
|
|
}
|
|
|
|
protected MeshIntegrationGroup _GetOrCreateGroup(string name)
|
|
{
|
|
foreach (var g in MeshIntegrationGroups)
|
|
{
|
|
if (g.Name == name)
|
|
{
|
|
return g;
|
|
}
|
|
}
|
|
MeshIntegrationGroups.Add(new MeshIntegrationGroup
|
|
{
|
|
Name = name,
|
|
IntegrationType = TypeFromName(name),
|
|
});
|
|
return MeshIntegrationGroups.Last();
|
|
}
|
|
|
|
protected bool _HasRenderer(Renderer r)
|
|
{
|
|
foreach (var g in MeshIntegrationGroups)
|
|
{
|
|
foreach (var x in g.Renderers)
|
|
{
|
|
if (x == r)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public virtual void UpdateMeshIntegrationGroups(GameObject root)
|
|
{
|
|
MeshIntegrationGroups.Clear();
|
|
if (root == null)
|
|
{
|
|
return;
|
|
}
|
|
var group = _GetOrCreateGroup("all mesh");
|
|
group.Renderers.AddRange(root.GetComponentsInChildren<Renderer>());
|
|
}
|
|
|
|
public void IntegrateAll(GameObject root)
|
|
{
|
|
if (root == null)
|
|
{
|
|
return;
|
|
}
|
|
MeshIntegrationGroups.Add(new MeshIntegrationGroup
|
|
{
|
|
Name = "All",
|
|
IntegrationType = MeshIntegrationGroup.MeshIntegrationTypes.Both,
|
|
Renderers = root.GetComponentsInChildren<Renderer>().ToList(),
|
|
});
|
|
}
|
|
|
|
static GameObject GetOrCreateEmpty(GameObject go, string name)
|
|
{
|
|
foreach (var child in go.transform.GetChildren())
|
|
{
|
|
if (child.name == name
|
|
&& child.localPosition == Vector3.zero
|
|
&& child.localScale == Vector3.one
|
|
&& child.localRotation == Quaternion.identity)
|
|
{
|
|
return child.gameObject;
|
|
}
|
|
}
|
|
var empty = new GameObject(name);
|
|
empty.transform.SetParent(go.transform, false);
|
|
return empty;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="go">MeshIntegrationGroup を作ったとき root</param>
|
|
/// <param name="instance">go が prefab だった場合に instance されたもの</param>
|
|
/// <returns></returns>
|
|
public virtual IEnumerable<MeshIntegrationGroup> CopyInstantiate(GameObject go, GameObject instance)
|
|
{
|
|
if (instance == null)
|
|
{
|
|
foreach (var g in MeshIntegrationGroups)
|
|
{
|
|
yield return g;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var g in MeshIntegrationGroups)
|
|
{
|
|
yield return g.CopyInstantiate(go, instance);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual (List<MeshIntegrationResult>, List<GameObject>) Process(
|
|
GameObject target, IEnumerable<MeshIntegrationGroup> groupCopy)
|
|
{
|
|
if (FreezeBlendShapeRotationAndScaling)
|
|
{
|
|
// MeshをBakeする
|
|
var meshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(target);
|
|
|
|
// - ヒエラルキーから回転・拡縮を除去する
|
|
// - BakeされたMeshで置き換える
|
|
// - bindPoses を再計算する
|
|
BoneNormalizer.Replace(target, meshMap, true, true);
|
|
}
|
|
|
|
var newList = new List<GameObject>();
|
|
|
|
var empty = GetOrCreateEmpty(target, "mesh");
|
|
|
|
var results = new List<MeshIntegrationResult>();
|
|
foreach (var group in groupCopy)
|
|
{
|
|
if (TryIntegrate(empty, group, out var resultAndAdded))
|
|
{
|
|
var (result, newGo) = resultAndAdded;
|
|
results.Add(result);
|
|
newList.AddRange(newGo);
|
|
}
|
|
}
|
|
|
|
return (results, newList);
|
|
}
|
|
|
|
public void Clear(List<MeshIntegrationResult> results)
|
|
{
|
|
// 用が済んだ 統合前 の renderer を削除する
|
|
foreach (var result in results)
|
|
{
|
|
foreach (var r in result.SourceMeshRenderers)
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
GameObject.Destroy(r.gameObject.GetComponent<MeshFilter>());
|
|
GameObject.Destroy(r);
|
|
}
|
|
else
|
|
{
|
|
GameObject.DestroyImmediate(r.gameObject.GetComponent<MeshFilter>());
|
|
GameObject.DestroyImmediate(r);
|
|
}
|
|
}
|
|
foreach (var r in result.SourceSkinnedMeshRenderers)
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
GameObject.Destroy(r);
|
|
}
|
|
else
|
|
{
|
|
GameObject.DestroyImmediate(r, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
MeshIntegrationGroups.Clear();
|
|
}
|
|
|
|
protected virtual bool TryIntegrate(GameObject empty,
|
|
MeshIntegrationGroup group, out (MeshIntegrationResult, GameObject[]) resultAndAdded)
|
|
{
|
|
if (MeshIntegrator.TryIntegrate(group, SplitByBlendShape
|
|
? MeshIntegrator.BlendShapeOperation.Split
|
|
: MeshIntegrator.BlendShapeOperation.Use, out var result))
|
|
{
|
|
var newGo = result.AddIntegratedRendererTo(empty).ToArray();
|
|
resultAndAdded = (result, newGo);
|
|
return true;
|
|
}
|
|
|
|
resultAndAdded = default;
|
|
return false;
|
|
}
|
|
}
|
|
}
|