using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace UniGLTF.MeshUtility { /// /// - Freeze /// - Integration /// - Split /// /// - Implement runtime logic => Process a hierarchy in scene. Do not process prefab. /// - Implement undo /// /// public class GltfMeshUtility { /// /// Same as VRM-0 normalization /// - Mesh /// - Node /// - InverseBindMatrices /// public bool FreezeBlendShapeRotationAndScaling = false; public List MeshIntegrationGroups = new List(); /// /// Create a headless model and solve VRM.FirstPersonFlag.Auto /// public bool GenerateMeshForFirstPersonAuto = false; /// /// Split into having and not having BlendShape /// 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()); } public void IntegrateAll(GameObject root) { if (root == null) { return; } MeshIntegrationGroups.Add(new MeshIntegrationGroup { Name = "All", IntegrationType = MeshIntegrationGroup.MeshIntegrationTypes.Both, Renderers = root.GetComponentsInChildren().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; } /// /// /// /// MeshIntegrationGroup を作ったとき root /// go が prefab だった場合に instance されたもの /// public virtual IEnumerable 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, List) Process( GameObject target, IEnumerable groupCopy) { if (FreezeBlendShapeRotationAndScaling) { // MeshをBakeする var meshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(target); // - ヒエラルキーから回転・拡縮を除去する // - BakeされたMeshで置き換える // - bindPoses を再計算する BoneNormalizer.Replace(target, meshMap, true, true); } var newList = new List(); var empty = GetOrCreateEmpty(target, "mesh"); var results = new List(); 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 results) { // 用が済んだ 統合前 の renderer を削除する foreach (var result in results) { foreach (var r in result.SourceMeshRenderers) { if (Application.isPlaying) { GameObject.Destroy(r.gameObject.GetComponent()); GameObject.Destroy(r); } else { GameObject.DestroyImmediate(r.gameObject.GetComponent()); 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; } } }