using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using UniGLTF.MeshUtility; using UnityEngine; using VRMShaders; namespace UniVRM10 { [Serializable] public class VRM10ObjectFirstPerson { [SerializeField] public List Renderers = new List(); public void SetDefault(Transform root) { Renderers.Clear(); var renderers = root.GetComponentsInChildren(); var paths = renderers.Select(x => x.transform.RelativePathFrom(root)).ToArray(); foreach (var path in paths) { Renderers.Add(new RendererFirstPersonFlags { FirstPersonFlag = UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto, Renderer = path, }); } } static int[] GetBonesThatHasAncestor(SkinnedMeshRenderer smr, Transform ancestor) { var eraseBones = smr.bones .Where(x => x.Ancestor().Any(y => y == ancestor)) .Select(x => Array.IndexOf(smr.bones, x)) .ToArray(); return eraseBones; } // // 頭部を取り除いたモデルを複製する // // renderer: 元になるSkinnedMeshRenderer // eraseBones: 削除対象になるボーンのindex private async static Task CreateHeadlessMeshAsync(SkinnedMeshRenderer renderer, int[] eraseBones, IAwaitCaller awaitCaller) { var mesh = await BoneMeshEraser.CreateErasedMeshAsync(renderer.sharedMesh, eraseBones, awaitCaller); var go = new GameObject("_headless_" + renderer.name); var erased = go.AddComponent(); erased.enabled = false; // hide erased.sharedMesh = mesh; erased.sharedMaterials = renderer.sharedMaterials; erased.bones = renderer.bones; erased.rootBone = renderer.rootBone; return erased; } bool m_done; async Task SetupSelfRendererAsync(GameObject go, UniGLTF.RuntimeGltfInstance runtime, Transform firstPersonBone, RendererFirstPersonFlags x, (int FirstPersonOnly, int ThirdPersonOnly) layer, IAwaitCaller awaitCaller = null) { switch (x.FirstPersonFlag) { case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto: { if (x.GetRenderer(go.transform) is SkinnedMeshRenderer smr) { var eraseBones = GetBonesThatHasAncestor(smr, firstPersonBone); if (eraseBones.Any()) { // オリジナルのモデルを3人称用にする smr.gameObject.layer = layer.ThirdPersonOnly; // 頭を取り除いた複製モデルを作成し、1人称用にする var headless = await CreateHeadlessMeshAsync(smr, eraseBones, awaitCaller); headless.gameObject.layer = layer.FirstPersonOnly; headless.transform.SetParent(smr.transform, false); if (runtime != null) { runtime.AddRenderer(headless); } } else { // 削除対象が含まれないので何もしない } } else if (x.GetRenderer(go.transform) is MeshRenderer mr) { if (mr.transform.Ancestors().Any(y => y == firstPersonBone)) { // 頭の子孫なので1人称では非表示に mr.gameObject.layer = layer.ThirdPersonOnly; } else { // 特に変更しない => 両方表示 } } else { throw new NotImplementedException(); } } break; case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.firstPersonOnly: // 1人称のカメラでだけ描画されるようにする x.GetRenderer(go.transform).gameObject.layer = layer.FirstPersonOnly; break; case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.thirdPersonOnly: // 3人称のカメラでだけ描画されるようにする x.GetRenderer(go.transform).gameObject.layer = layer.ThirdPersonOnly; break; case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.both: // 特に何もしない。すべてのカメラで描画される break; } } /// /// Each renderer is set according to the first person flag. /// If the flag is `auto`, headless mesh creation will be performed. /// Creating a headless mesh(Renderer) is a heavy process and can be done in threads. /// /// The target model root /// The target model is the VR user himself /// layer VRMFirstPersonOnly or 9 /// layer VRMThirdPersonOnly ir 10 /// Headless mesh creation task scheduler. By default, creation is immediate /// public async Task SetupAsync(GameObject go, IAwaitCaller awaitCaller, bool isSelf = true, int? firstPersonOnlyLayer = default, int? thirdPersonOnlyLayer = default) { if (awaitCaller == null) { throw new ArgumentNullException(); } var layer = ( Vrm10FirstPersonLayerSettings.GetFirstPersonOnlyLayer(firstPersonOnlyLayer), Vrm10FirstPersonLayerSettings.GetThirdPersonOnlyLayer(thirdPersonOnlyLayer)); if (m_done) { return; } m_done = true; var runtime = go.GetComponent(); var vrmInstance = go.GetComponent(); // NOTE: This bone must be referenced by renderers instead of the control rig bone. var firstPersonBone = vrmInstance.Humanoid.Head; var used = new HashSet(); foreach (var x in Renderers) { if (!used.Add(x.Renderer)) { // 同じ対象が複数回現れた Debug.LogWarning($"VRM10ObjectFirstPerson.SetupAsync: duplicated {x.Renderer}"); continue; } if (isSelf) { await SetupSelfRendererAsync(go, runtime, firstPersonBone, x, layer, awaitCaller); } else { switch (x.FirstPersonFlag) { case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.firstPersonOnly: if (x.GetRenderer(go.transform) is Renderer r) { // invisible r.enabled = false; runtime.VisibleRenderers.Remove(r); } break; case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto: // => Same as Both case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.both: case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.thirdPersonOnly: // do nothing break; } } } } } }