-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AvatarRoot API: Non-VRChat avatar support in VRCSDK projects #71
base: main
Are you sure you want to change the base?
Changes from all commits
e01ccf7
cebd6be
25e047c
10545ea
0893781
3a3bcc1
fa1ebb6
3547fd3
976dc74
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,12 @@ | |
#if NDMF_VRCSDK3_AVATARS | ||
using VRC.SDK3.Avatars.Components; | ||
#endif | ||
#if NDMF_VRM0 | ||
using VRM; | ||
#endif | ||
#if NDMF_VRM1 | ||
using UniVRM10; | ||
#endif | ||
|
||
namespace nadena.dev.ndmf.runtime | ||
{ | ||
|
@@ -94,14 +100,23 @@ public static string AvatarRootPath(GameObject child) | |
/// <returns></returns> | ||
public static bool IsAvatarRoot(Transform target) | ||
{ | ||
// First, look for platform specific avatar descriptors | ||
// TODO: ignore nested avatar descriptors? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. たぶん以下の扱いをちゃんと考えないといけない
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 考えました #432 |
||
#if NDMF_VRCSDK3_AVATARS | ||
return target.GetComponent<VRCAvatarDescriptor>(); | ||
#else | ||
if (target.GetComponent<VRCAvatarDescriptor>()) return true; | ||
#endif | ||
#if NDMF_VRM0 | ||
if (target.GetComponent<VRMMeta>()) return true; | ||
#endif | ||
#if NDMF_VRM1 | ||
if (target.GetComponent<Vrm10Instance>()) return true; | ||
#endif | ||
|
||
// Then, look for Animators, which is the generic avatar root as long as there are no Animators in its parents | ||
var an = target.GetComponent<Animator>(); | ||
if (!an) return false; | ||
var parent = target.transform.parent; | ||
return !(parent && parent.GetComponentInParent<Animator>()); | ||
#endif | ||
} | ||
|
||
/// <summary> | ||
|
@@ -130,6 +145,7 @@ public static IEnumerable<GameObject> FindAvatarRoots(GameObject root = null) | |
else | ||
{ | ||
GameObject priorRoot = null; | ||
// TODO: allow generic avatars in VRChat projects? | ||
#if NDMF_VRCSDK3_AVATARS | ||
var candidates = root.GetComponentsInChildren<VRCAvatarDescriptor>(); | ||
#else | ||
|
@@ -171,17 +187,15 @@ public static Transform FindAvatarInParents(Transform target) | |
/// <returns></returns> | ||
internal static IEnumerable<Transform> FindAvatarsInScene(Scene scene) | ||
{ | ||
var list = new List<Transform>(); | ||
foreach (var root in scene.GetRootGameObjects()) | ||
{ | ||
#if NDMF_VRCSDK3_AVATARS | ||
foreach (var avatar in root.GetComponentsInChildren<VRCAvatarDescriptor>()) | ||
#else | ||
foreach (var avatar in root.GetComponentsInChildren<Animator>()) | ||
#endif | ||
{ | ||
if (IsAvatarRoot(avatar.transform)) yield return avatar.transform; | ||
if (IsAvatarRoot(avatar.transform)) list.Add(avatar.transform); | ||
} | ||
} | ||
return list; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
using nadena.dev.ndmf.runtime; | ||
using NUnit.Framework; | ||
using UnityEngine; | ||
|
||
namespace UnitTests.AvatarRootTests | ||
{ | ||
public class AvatarRoot : TestBase | ||
{ | ||
private GameObject CreateGenericRoot(string name) => CreatePlatformRoot(name, isVRC: false, isVRM0: false, isVRM1: false); | ||
private GameObject CreateVRCRoot(string name) => CreatePlatformRoot(name, isVRC: true, isVRM0: false, isVRM1: false); | ||
private GameObject CreateVRM0Root(string name) => CreatePlatformRoot(name, isVRC: false, isVRM0: true, isVRM1: false); | ||
private GameObject CreateVRM1Root(string name) => CreatePlatformRoot(name, isVRC: false, isVRM0: false, isVRM1: true); | ||
private GameObject CreateHybridRoot(string name) => CreatePlatformRoot(name, isVRC: true, isVRM0: true, isVRM1: true); | ||
|
||
private Transform parentAvatar; | ||
private Transform childAvatar; | ||
|
||
private void NoAvatars() | ||
{ | ||
Assert.That(RuntimeUtil.IsAvatarRoot(parentAvatar), Is.False); | ||
Assert.That(RuntimeUtil.IsAvatarRoot(childAvatar), Is.False); | ||
Assert.That(RuntimeUtil.FindAvatarInParents(parentAvatar), Is.Null); | ||
Assert.That(RuntimeUtil.FindAvatarInParents(childAvatar), Is.Null); | ||
Assert.That(RuntimeUtil.FindAvatarsInScene(parentAvatar.gameObject.scene), Is.EquivalentTo(System.Array.Empty<Transform>())); | ||
} | ||
|
||
private void ParentIsAvatar() | ||
{ | ||
Assert.That(RuntimeUtil.IsAvatarRoot(parentAvatar), Is.True); | ||
Assert.That(RuntimeUtil.IsAvatarRoot(childAvatar), Is.False); | ||
Assert.That(RuntimeUtil.FindAvatarInParents(parentAvatar), Is.EqualTo(parentAvatar)); | ||
Assert.That(RuntimeUtil.FindAvatarInParents(childAvatar), Is.EqualTo(parentAvatar)); | ||
Assert.That(RuntimeUtil.FindAvatarsInScene(parentAvatar.gameObject.scene), Is.EquivalentTo(new [] { parentAvatar })); | ||
} | ||
|
||
private void ChildIsAvatar() | ||
{ | ||
Assert.That(RuntimeUtil.IsAvatarRoot(parentAvatar), Is.False); | ||
Assert.That(RuntimeUtil.IsAvatarRoot(childAvatar), Is.True); | ||
Assert.That(RuntimeUtil.FindAvatarInParents(parentAvatar), Is.EqualTo(null)); | ||
Assert.That(RuntimeUtil.FindAvatarInParents(childAvatar), Is.EqualTo(childAvatar)); | ||
Assert.That(RuntimeUtil.FindAvatarsInScene(parentAvatar.gameObject.scene), Is.EquivalentTo(new [] { childAvatar })); | ||
} | ||
|
||
private void ParentAndChildAreAvatars() | ||
{ | ||
Assert.That(RuntimeUtil.IsAvatarRoot(parentAvatar), Is.True); | ||
Assert.That(RuntimeUtil.IsAvatarRoot(childAvatar), Is.True); | ||
Assert.That(RuntimeUtil.FindAvatarInParents(parentAvatar), Is.EqualTo(parentAvatar)); | ||
Assert.That(RuntimeUtil.FindAvatarInParents(childAvatar), Is.EqualTo(childAvatar)); | ||
Assert.That(RuntimeUtil.FindAvatarsInScene(parentAvatar.gameObject.scene), Is.EquivalentTo(new [] { parentAvatar, childAvatar })); | ||
} | ||
|
||
[Test] | ||
public void TestGenericContainsGeneric() | ||
{ | ||
parentAvatar = CreateGenericRoot("parent").transform; | ||
childAvatar = CreateGenericRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
#if NDMF_VRCSDK3_AVATARS || NDMF_VRM0 || NDMF_VRM1 | ||
NoAvatars(); | ||
#else | ||
// Use fallback heuristic | ||
ParentIsAvatar(); | ||
#endif | ||
} | ||
|
||
#if NDMF_VRCSDK3_AVATARS | ||
[Test] | ||
public void TestGenericContainsVRC() | ||
{ | ||
parentAvatar = CreateGenericRoot("parent").transform; | ||
childAvatar = CreateVRCRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ChildIsAvatar(); | ||
} | ||
|
||
[Test] | ||
public void TestVRCContainsGeneric() | ||
{ | ||
parentAvatar = CreateVRCRoot("parent").transform; | ||
childAvatar = CreateGenericRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ParentIsAvatar(); | ||
} | ||
|
||
[Test] | ||
public void TestVRCContainsVRC() | ||
{ | ||
parentAvatar = CreateVRCRoot("parent").transform; | ||
childAvatar = CreateVRCRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ParentAndChildAreAvatars(); | ||
} | ||
#endif | ||
|
||
#if NDMF_VRM0 | ||
[Test] | ||
public void TestGenericContainsVRM0() | ||
{ | ||
parentAvatar = CreateGenericRoot("parent").transform; | ||
childAvatar = CreateVRM1Root("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ChildIsAvatar(); | ||
} | ||
|
||
[Test] | ||
public void TestVRM0ContainsGeneric() | ||
{ | ||
parentAvatar = CreateVRM1Root("parent").transform; | ||
childAvatar = CreateGenericRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ParentIsAvatar(); | ||
} | ||
|
||
[Test] | ||
public void TestVRM0ContainsVRM0() | ||
{ | ||
parentAvatar = CreateVRM1Root("parent").transform; | ||
childAvatar = CreateVRM1Root("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ParentAndChildAreAvatars(); | ||
} | ||
#endif | ||
|
||
#if NDMF_VRCSDK3_AVATARS && NDMF_VRM0 | ||
[Test] | ||
public void TestGenericContainsHybrid() | ||
{ | ||
parentAvatar = CreateGenericRoot("parent").transform; | ||
childAvatar = CreateHybridRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ChildIsAvatar(); | ||
} | ||
|
||
[Test] | ||
public void TestHybridContainsGeneric() | ||
{ | ||
parentAvatar = CreateHybridRoot("parent").transform; | ||
childAvatar = CreateGenericRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ParentIsAvatar(); | ||
} | ||
|
||
[Test] | ||
public void TestHybridContainsHybrid() | ||
{ | ||
parentAvatar = CreateHybridRoot("parent").transform; | ||
childAvatar = CreateHybridRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ParentAndChildAreAvatars(); | ||
} | ||
|
||
[Test] | ||
public void TestVRCContainsVRM0() | ||
{ | ||
parentAvatar = CreateVRCRoot("parent").transform; | ||
childAvatar = CreateVRM0Root("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ParentAndChildAreAvatars(); | ||
} | ||
|
||
[Test] | ||
public void TestVRM0ContainsVRC() | ||
{ | ||
parentAvatar = CreateVRM0Root("parent").transform; | ||
childAvatar = CreateVRCRoot("child").transform; | ||
|
||
childAvatar.parent = parentAvatar; | ||
|
||
ParentAndChildAreAvatars(); | ||
} | ||
#endif | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
こちらも要更新?(仮実装です)
ndmf/Editor/PreviewSystem/ComputeContext/SingleObjectQueries.cs
Lines 33 to 44 in 23fa9c4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そうかもしれません・・・
NDMF 1.5.0 リリース後に作業を再開する予定でしたが、この機会に全体を見直しておきます。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
了解です。