您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
199 行
8.5 KiB
199 行
8.5 KiB
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<RendererFirstPersonFlags> Renderers = new List<RendererFirstPersonFlags>();
|
|
public void SetDefault(Transform root)
|
|
{
|
|
Renderers.Clear();
|
|
|
|
var renderers = root.GetComponentsInChildren<Renderer>();
|
|
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;
|
|
}
|
|
|
|
// <summary>
|
|
// 頭部を取り除いたモデルを複製する
|
|
// </summary>
|
|
// <parameter>renderer: 元になるSkinnedMeshRenderer</parameter>
|
|
// <parameter>eraseBones: 削除対象になるボーンのindex</parameter>
|
|
private async static Task<SkinnedMeshRenderer> 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<SkinnedMeshRenderer>();
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="go">The target model root</param>
|
|
/// <param name="isSelf">The target model is the VR user himself</param>
|
|
/// <param name="firstPersonOnlyLayer">layer VRMFirstPersonOnly or 9</param>
|
|
/// <param name="thirdPersonOnlyLayer">layer VRMThirdPersonOnly ir 10</param>
|
|
/// <param name="awaitCaller">Headless mesh creation task scheduler. By default, creation is immediate</param>
|
|
/// <returns></returns>
|
|
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<UniGLTF.RuntimeGltfInstance>();
|
|
var vrmInstance = go.GetComponent<Vrm10Instance>();
|
|
// NOTE: This bone must be referenced by renderers instead of the control rig bone.
|
|
var firstPersonBone = vrmInstance.Humanoid.Head;
|
|
|
|
var used = new HashSet<string>();
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|