diff --git a/Assets/VRM/UniVRM/Scripts/FirstPerson/VRMFirstPerson.cs b/Assets/VRM/UniVRM/Scripts/FirstPerson/VRMFirstPerson.cs index e6a8ab9bf..7f1bd1ef6 100644 --- a/Assets/VRM/UniVRM/Scripts/FirstPerson/VRMFirstPerson.cs +++ b/Assets/VRM/UniVRM/Scripts/FirstPerson/VRMFirstPerson.cs @@ -125,7 +125,7 @@ namespace VRM return FirstPersonFlag.Auto; } - foreach(var x in context.GLTF.extensions.VRM.firstPerson.meshAnnotations) + foreach (var x in context.GLTF.extensions.VRM.firstPerson.meshAnnotations) { if (x.mesh == index) { @@ -136,14 +136,16 @@ namespace VRM return FirstPersonFlag.Auto; } - void CreateHeadlessModel(Renderer _renderer, Transform EraseRoot) + /// + /// ヘッドレスモデルを作成した場合に返す + /// + Mesh CreateHeadlessModel(Renderer _renderer, Transform EraseRoot) { { var renderer = _renderer as SkinnedMeshRenderer; if (renderer != null) { - CreateHeadlessModelForSkinnedMeshRenderer(renderer, EraseRoot); - return; + return CreateHeadlessModelForSkinnedMeshRenderer(renderer, EraseRoot); } } @@ -153,16 +155,18 @@ namespace VRM if (renderer != null) { CreateHeadlessModelForMeshRenderer(renderer, EraseRoot); - return; + return null; } } // ここには来ない + return null; } - + public static void SetupLayers() { - if (!TriedSetupLayer) { + if (!TriedSetupLayer) + { TriedSetupLayer = true; int layer = LayerMask.NameToLayer("VRMFirstPersonOnly"); FIRSTPERSON_ONLY_LAYER = (layer == -1) ? FIRSTPERSON_ONLY_LAYER : layer; @@ -185,54 +189,69 @@ namespace VRM } } - private static void CreateHeadlessModelForSkinnedMeshRenderer(SkinnedMeshRenderer renderer, Transform eraseRoot) + /// + /// ヘッドレスモデルを作成する。 + /// + /// 以下の場合は作成しない。 + /// + /// * 削除対象が無い場合 + /// * 全部削除対象の場合 + /// + /// + private static Mesh CreateHeadlessModelForSkinnedMeshRenderer(SkinnedMeshRenderer renderer, Transform eraseRoot) { SetupLayers(); + var bones = renderer.bones; + + var eraseBones = bones.Select((x, i) => + { + // 祖先に削除対象が存在するか + bool erase = x.Ancestor().Any(y => y == eraseRoot); + return new + { + i, + erase, + }; + }) + .Where(x => x.erase) + .Select(x => x.i) + .ToArray() + ; + if (eraseBones.Length == 0) + { + // 削除対象が存在しない + return null; + } + + // 元のメッシュを三人称に変更(自分からは見えない) renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER; + // 削除対象のボーンに対するウェイトを保持する三角形を除外して、一人称用のモデルを複製する + var headlessMesh = BoneMeshEraser.CreateErasedMesh(renderer.sharedMesh, eraseBones); + if (headlessMesh.triangles.Length == 0) + { + // 一人称用のmeshには描画すべき部分が無い(全部削除された) + UnityEngine.Object.Destroy(headlessMesh); + return null; + } + + // 一人称用のモデルのセットアップ var go = new GameObject("_headless_" + renderer.name); go.layer = FIRSTPERSON_ONLY_LAYER; go.transform.SetParent(renderer.transform, false); - - var m_eraseBones = renderer.bones.Select(x => - { - var eb = new BoneMeshEraser.EraseBone - { - Bone = x, - }; - - if (eraseRoot != null) - { - // 首の子孫を消去 - if (eb.Bone.Ancestor().Any(y => y == eraseRoot)) - { - //Debug.LogFormat("erase {0}", x); - eb.Erase = true; - } - } - - return eb; - }) - .ToArray(); - - var bones = renderer.bones; - var eraseBones = m_eraseBones - .Where(x => x.Erase) - .Select(x => bones.IndexOf(x.Bone)) - .ToArray(); - - var mesh = BoneMeshEraser.CreateErasedMesh(renderer.sharedMesh, eraseBones); - var erased = go.AddComponent(); - erased.sharedMesh = mesh; + erased.sharedMesh = headlessMesh; erased.sharedMaterials = renderer.sharedMaterials; - erased.bones = renderer.bones; + erased.bones = bones; erased.rootBone = renderer.rootBone; erased.updateWhenOffscreen = true; + return headlessMesh; } bool m_done; + List m_headlessMeshes = new List(); + /// /// 配下のモデルのレイヤー設定など /// @@ -246,7 +265,13 @@ namespace VRM switch (x.FirstPersonFlag) { case FirstPersonFlag.Auto: - CreateHeadlessModel(x.Renderer, FirstPersonBone); + { + var headlessMesh = CreateHeadlessModel(x.Renderer, FirstPersonBone); + if (headlessMesh != null) + { + m_headlessMeshes.Add(headlessMesh); + } + } break; case FirstPersonFlag.FirstPersonOnly: @@ -263,5 +288,18 @@ namespace VRM } } } + + void OnDestroy() + { + foreach (var mesh in m_headlessMeshes) + { + if (mesh != null) + { + // Debug.LogFormat("[VRMFirstPerson] OnDestroy: {0}", mesh); + UnityEngine.Object.Destroy(mesh); + } + } + m_headlessMeshes.Clear(); + } } }