Skip to content

Commit

Permalink
Merge pull request #1206 from ousttrue/fix/first_person_setup_async
Browse files Browse the repository at this point in the history
[1.0] FirstPerson 初期化を整理
  • Loading branch information
ousttrue authored Sep 9, 2021
2 parents 153fca3 + 715006a commit 64af0d2
Show file tree
Hide file tree
Showing 23 changed files with 2,599 additions and 93 deletions.
44 changes: 38 additions & 6 deletions Assets/UniGLTF/Runtime/MeshUtility/BoneMeshEraser.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;

using VRMShaders;

namespace UniGLTF.MeshUtility
{
Expand Down Expand Up @@ -35,6 +36,13 @@ public override string ToString()
}
}

/// <summary>
/// skip triangles that has weight for exclude, return valid triangle count
/// </summary>
/// <param name="triangles"></param>
/// <param name="bws"></param>
/// <param name="exclude"></param>
/// <returns></returns>
static int ExcludeTriangles(int[] triangles, BoneWeight[] bws, int[] exclude)
{
int count = 0;
Expand Down Expand Up @@ -116,8 +124,28 @@ private static ExcludeBoneIndex AreBoneContains(ref int[] exclude, int boneIndex
return new ExcludeBoneIndex(b0, b1, b2, b3);
}

public static Mesh CreateErasedMesh(Mesh src, int[] eraseBoneIndices)
/// <summary>
/// Erase triangles that has boneWeight for eraseBone
/// </summary>
/// <param name="indices"></param>
/// <param name="boneWeights"></param>
/// <param name="eraseBoneIndices"></param>
/// <returns></returns>
public static int[] GetExcludedIndices(int[] indices, BoneWeight[] boneWeights, int[] eraseBoneIndices)
{
var count = ExcludeTriangles(indices, boneWeights, eraseBoneIndices);
var dst = new int[count];
Array.Copy(indices, 0, dst, 0, count);
return dst;
}

public static async Task<Mesh> CreateErasedMeshAsync(Mesh src, int[] eraseBoneIndices, IAwaitCaller awaitCaller)
{
if (awaitCaller == null)
{
awaitCaller = new ImmediateCaller();
}

/*
Debug.LogFormat("{0} exclude: {1}",
src.name,
Expand All @@ -140,16 +168,20 @@ public static Mesh CreateErasedMesh(Mesh src, int[] eraseBoneIndices)
mesh.subMeshCount = src.subMeshCount;
for (int i = 0; i < src.subMeshCount; ++i)
{
var indices = src.GetIndices(i);
var count = ExcludeTriangles(indices, mesh.boneWeights, eraseBoneIndices);
var dst = new int[count];
Array.Copy(indices, 0, dst, 0, count);
var dst = await awaitCaller.Run(() => GetExcludedIndices(src.GetIndices(i), mesh.boneWeights, eraseBoneIndices));
mesh.SetIndices(dst, MeshTopology.Triangles, i);
}

return mesh;
}

public static Mesh CreateErasedMesh(Mesh src, int[] eraseBoneIndices)
{
var task = CreateErasedMeshAsync(src, eraseBoneIndices, new ImmediateCaller());
task.Wait();
return task.Result;
}

public static IEnumerable<Transform> Ancestor(this Transform t)
{
yield return t;
Expand Down
5 changes: 5 additions & 0 deletions Assets/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ public static RuntimeGltfInstance AttachTo(GameObject go, ImporterContext contex
return loaded;
}

public void AddRenderers(IEnumerable<Renderer> renderers)
{
_renderers.AddRange(renderers);
}

public void ShowMeshes()
{
foreach (var r in Renderers)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using UnityEngine;

namespace UniVRM10
{
public static class Vrm10FirstPersonLayerSettings
{
public const int DEFAULT_FIRSTPERSON_ONLY_LAYER = 9;
public const string FIRSTPERSON_ONLY_LAYER_NAME = "VRMFirstPersonOnly";

public const int DEFAULT_THIRDPERSON_ONLY_LAYER = 10;
public const string THIRDPERSON_ONLY_LAYER_NAME = "VRMThirdPersonOnly";

public static int GetLayer(int? arg, string name, int fallback)
{
if (arg.HasValue)
{
return arg.Value;
}
var layer = LayerMask.NameToLayer(name);
if (layer != -1)
{
return layer;
}
return fallback;
}

public static int GetFirstPersonOnlyLayer(int? arg)
{
return GetLayer(arg, FIRSTPERSON_ONLY_LAYER_NAME, DEFAULT_FIRSTPERSON_ONLY_LAYER);
}

public static int GetThirdPersonOnlyLayer(int? arg)
{
return GetLayer(arg, THIRDPERSON_ONLY_LAYER_NAME, DEFAULT_THIRDPERSON_ONLY_LAYER);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

168 changes: 93 additions & 75 deletions Assets/VRM10/Runtime/Components/VRM10Object/VRM10ObjectFirstPerson.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UniGLTF.MeshUtility;
using UnityEngine;
using VRMShaders;

namespace UniVRM10
{
Expand All @@ -27,24 +29,6 @@ public void SetDefault(Transform root)
}
}

// If no layer names are set, use the default layer IDs.
// Otherwise use the two Unity layers called "VRMFirstPersonOnly" and "VRMThirdPersonOnly".
public static bool TriedSetupLayer = false;
public static int FIRSTPERSON_ONLY_LAYER = 9;
public static int THIRDPERSON_ONLY_LAYER = 10;

public static void SetupLayers()
{
if (!TriedSetupLayer)
{
TriedSetupLayer = true;
int layer = LayerMask.NameToLayer("VRMFirstPersonOnly");
FIRSTPERSON_ONLY_LAYER = (layer == -1) ? FIRSTPERSON_ONLY_LAYER : layer;
layer = LayerMask.NameToLayer("VRMThirdPersonOnly");
THIRDPERSON_ONLY_LAYER = (layer == -1) ? THIRDPERSON_ONLY_LAYER : layer;
}
}

static int[] GetBonesThatHasAncestor(SkinnedMeshRenderer smr, Transform ancestor)
{
var eraseBones = smr.bones
Expand All @@ -59,9 +43,9 @@ static int[] GetBonesThatHasAncestor(SkinnedMeshRenderer smr, Transform ancestor
// </summary>
// <parameter>renderer: 元になるSkinnedMeshRenderer</parameter>
// <parameter>eraseBones: 削除対象になるボーンのindex</parameter>
private static SkinnedMeshRenderer CreateHeadlessMesh(SkinnedMeshRenderer renderer, int[] eraseBones)
private async static Task<SkinnedMeshRenderer> CreateHeadlessMeshAsync(SkinnedMeshRenderer renderer, int[] eraseBones, IAwaitCaller awaitCaller)
{
var mesh = BoneMeshEraser.CreateErasedMesh(renderer.sharedMesh, eraseBones);
var mesh = await BoneMeshEraser.CreateErasedMeshAsync(renderer.sharedMesh, eraseBones, awaitCaller);

var go = new GameObject("_headless_" + renderer.name);
var erased = go.AddComponent<SkinnedMeshRenderer>();
Expand All @@ -76,77 +60,111 @@ private static SkinnedMeshRenderer CreateHeadlessMesh(SkinnedMeshRenderer render

bool m_done;

/// <summary>
/// 配下のモデルのレイヤー設定など
/// </summary>
public void Setup(GameObject go)
async Task<SkinnedMeshRenderer> SetupRendererAsync(GameObject go, Transform FirstPersonBone, RendererFirstPersonFlags x,
(int FirstPersonOnly, int ThirdPersonOnly) layer, IAwaitCaller awaitCaller = null)
{
SetupLayers();
if (m_done)
{
return;
}
m_done = true;

var FirstPersonBone = go.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Head);
foreach (var x in Renderers)
switch (x.FirstPersonFlag)
{
switch (x.FirstPersonFlag)
{
case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto:
case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto:
{
if (x.GetRenderer(go.transform) is SkinnedMeshRenderer smr)
{
if (x.GetRenderer(go.transform) is SkinnedMeshRenderer smr)
var eraseBones = GetBonesThatHasAncestor(smr, FirstPersonBone);
if (eraseBones.Any())
{
var eraseBones = GetBonesThatHasAncestor(smr, FirstPersonBone);
if (eraseBones.Any())
{
// オリジナルのモデルを3人称用にする
smr.gameObject.layer = THIRDPERSON_ONLY_LAYER;

// 頭を取り除いた複製モデルを作成し、1人称用にする
var headless = CreateHeadlessMesh(smr, eraseBones);
headless.gameObject.layer = FIRSTPERSON_ONLY_LAYER;
headless.transform.SetParent(smr.transform, false);
}
else
{
// 削除対象が含まれないので何もしない
}
// オリジナルのモデルを3人称用にする
smr.gameObject.layer = layer.ThirdPersonOnly;

// 頭を取り除いた複製モデルを作成し、1人称用にする
var headless = await CreateHeadlessMeshAsync(smr, eraseBones, awaitCaller);
headless.enabled = false;
headless.gameObject.layer = layer.FirstPersonOnly;
headless.transform.SetParent(smr.transform, false);
return headless;
}
else if (x.GetRenderer(go.transform) is MeshRenderer mr)
else
{
// 削除対象が含まれないので何もしない
}
}
else if (x.GetRenderer(go.transform) is MeshRenderer mr)
{
if (mr.transform.Ancestors().Any(y => y == FirstPersonBone))
{
if (mr.transform.Ancestors().Any(y => y == FirstPersonBone))
{
// 頭の子孫なので1人称では非表示に
mr.gameObject.layer = THIRDPERSON_ONLY_LAYER;
}
else
{
// 特に変更しない => 両方表示
}
// 頭の子孫なので1人称では非表示に
mr.gameObject.layer = layer.ThirdPersonOnly;
}
else
{
throw new NotImplementedException();
// 特に変更しない => 両方表示
}
}
break;
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;
}

return null;
}

/// <summary>
/// Setup first person
///
/// * SetupLayers
/// * Each renderer is set according to the first person flag. If the flag is `auto`, headless mesh creation will be performed. It's a heavy process.
/// * If visible is false, the created headless mesh will be hidden.
///
/// </summary>
/// <param name="go"></param>
/// <param name="firstPersonOnlyLayer"></param>
/// <param name="thirdPersonOnlyLayer"></param>
/// <param name="awaitCaller"></param>
/// <returns></returns>
public async Task<List<SkinnedMeshRenderer>> SetupAsync(GameObject go, int? firstPersonOnlyLayer = default, int? thirdPersonOnlyLayer = default, IAwaitCaller awaitCaller = default)
{
if (awaitCaller == null)
{
awaitCaller = new ImmediateCaller();
}

case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.firstPersonOnly:
// 1人称のカメラでだけ描画されるようにする
x.GetRenderer(go.transform).gameObject.layer = FIRSTPERSON_ONLY_LAYER;
break;
var layer = (
Vrm10FirstPersonLayerSettings.GetFirstPersonOnlyLayer(firstPersonOnlyLayer),
Vrm10FirstPersonLayerSettings.GetThirdPersonOnlyLayer(thirdPersonOnlyLayer));

case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.thirdPersonOnly:
// 3人称のカメラでだけ描画されるようにする
x.GetRenderer(go.transform).gameObject.layer = THIRDPERSON_ONLY_LAYER;
break;
var created = new List<SkinnedMeshRenderer>();
if (m_done)
{
return created;
}
m_done = true;

case UniGLTF.Extensions.VRMC_vrm.FirstPersonType.both:
// 特に何もしない。すべてのカメラで描画される
break;
var FirstPersonBone = go.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Head);
foreach (var x in Renderers)
{
var renderer = await SetupRendererAsync(go, FirstPersonBone, x, layer, awaitCaller);
if (renderer)
{
created.Add(renderer);
}
}
return created;
}
}
}
8 changes: 8 additions & 0 deletions Assets/VRM10/Samples/VRM10FirstPersonSample.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions Assets/VRM10/Samples/VRM10FirstPersonSample/VRM10CanvasManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Linq;
using UnityEngine;
using UnityEngine.UI;


namespace UniVRM10.FirstPersonSample
{
public class VRM10CanvasManager : MonoBehaviour
{
[SerializeField]
public Button LoadVRMButton;

[SerializeField]
public Button LoadBVHButton;

private void Reset()
{
LoadVRMButton = GameObject.FindObjectsOfType<Button>().FirstOrDefault(x => x.name == "LoadVRM");
LoadBVHButton = GameObject.FindObjectsOfType<Button>().FirstOrDefault(x => x.name == "LoadBVH");
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 64af0d2

Please sign in to comment.