Skip to content

Commit

Permalink
Implemented OscPhysBone (#13, #14)
Browse files Browse the repository at this point in the history
* Implemented `OscPhysBone` (#13)

* Add test for `OscPhysBone`.

* Fixed test.
  • Loading branch information
ChanyaVRC authored Jul 9, 2022
1 parent eb56c1a commit 0f19b1d
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 0 deletions.
163 changes: 163 additions & 0 deletions src/vrcosclib.Test/Avatar/OscPhysBoneTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using BuildSoft.OscCore;
using BuildSoft.VRChat.Osc.Test;
using NUnit.Framework;

namespace BuildSoft.VRChat.Osc.Avatar.Test;

[TestOf(typeof(OscPhysBone))]
public class OscPhysBoneTests
{
private OscAvatarConfig _avatar = null!;
private OscServer _server = null!;
private const string AvatarId = "avtr_id_for_test";
private const string PhysBoneParam = "PhysBoneParam";

[SetUp]
public void Setup()
{
Directory.CreateDirectory(OscUtility.VRChatOscPath);
Directory.Move(OscUtility.VRChatOscPath, OscUtility.VRChatOscPath + "_Renamed");
Directory.CreateDirectory(OscUtility.VRChatOscPath);

TestUtility.CreateConfigFileForTest(AvatarId, "Test Avatar", TestUtility.GetAvatarConfigDirectory());
_avatar = OscAvatarConfig.Create(AvatarId)!;

_server = new OscServer(OscUtility.SendPort);

OscParameter.Parameters.Clear();
}

[TearDown]
public void TearDown()
{
Directory.Delete(OscUtility.VRChatOscPath, true);
Directory.Move(OscUtility.VRChatOscPath + "_Renamed", OscUtility.VRChatOscPath);
_server.Dispose();
}

[OneTimeSetUp]
public void OneTimeSetUp()
{
}

[OneTimeTearDown]
public void OneTimeTearDown()
{

}

[Test]
public void TestCtor()
{
var physBone = new OscPhysBone(_avatar, PhysBoneParam);
Assert.AreSame(_avatar, physBone.Avatar);
Assert.AreEqual(PhysBoneParam, physBone.ParamName);
Assert.IsFalse(physBone.IsGrabbed);
Assert.Zero(physBone.Angle);
Assert.Zero(physBone.Stretch);

Assert.Throws<ArgumentException>(() => new OscPhysBone(_avatar, PhysBoneParam + "_"));
}

[Test]
public void TestIsGrabbed()
{
var physBone = new OscPhysBone(_avatar, PhysBoneParam);
Assert.IsFalse(physBone.IsGrabbed);

const string IsGrabbedParamName = PhysBoneParam + "_IsGrabbed";
var parameters = _avatar.Parameters;
var isGrabbedParam = parameters.Get(IsGrabbedParamName);
int passedCount = 0;

void Handler(OscAvatarParameter sender, ValueChangedEventArgs e)
{
Assert.AreEqual(isGrabbedParam, sender);
passedCount++;
}

physBone.ParameterChanged += Handler;

parameters[IsGrabbedParamName] = true;
Assert.IsTrue(physBone.IsGrabbed);
Assert.AreEqual(1, passedCount);

parameters[IsGrabbedParamName] = false;
Assert.IsFalse(physBone.IsGrabbed);
Assert.AreEqual(2, passedCount);

parameters[IsGrabbedParamName] = false;
Assert.IsFalse(physBone.IsGrabbed);
Assert.AreEqual(2, passedCount);

physBone.ParameterChanged -= Handler;
}

[Test]
public void TestAngle()
{
var physBone = new OscPhysBone(_avatar, PhysBoneParam);
Assert.AreEqual(0f, physBone.Angle);

const string AngleParamName = PhysBoneParam + "_Angle";
var parameters = _avatar.Parameters;
var angleParam = parameters.Get(AngleParamName);
int passedCount = 0;

void Handler(OscAvatarParameter sender, ValueChangedEventArgs e)
{
Assert.AreEqual(angleParam, sender);
passedCount++;
}

physBone.ParameterChanged += Handler;

parameters[AngleParamName] = 0.1f;
Assert.AreEqual(0.1f, physBone.Angle);
Assert.AreEqual(1, passedCount);

parameters[AngleParamName] = -1.2345f;
Assert.AreEqual(-1.2345f, physBone.Angle);
Assert.AreEqual(2, passedCount);

parameters[AngleParamName] = -1.2345f;
Assert.AreEqual(-1.2345f, physBone.Angle);
Assert.AreEqual(2, passedCount);

physBone.ParameterChanged -= Handler;
}

[Test]
public void TestStretch()
{
var physBone = new OscPhysBone(_avatar, PhysBoneParam);
Assert.AreEqual(0f, physBone.Stretch);

const string StretchParamName = PhysBoneParam + "_Stretch";
var parameters = _avatar.Parameters;
var stretchParam = parameters.Get(StretchParamName);
int passedCount = 0;

void Handler(OscAvatarParameter sender, ValueChangedEventArgs e)
{
Assert.AreEqual(stretchParam, sender);
passedCount++;
}

physBone.ParameterChanged += Handler;

parameters[StretchParamName] = 0.1f;
Assert.AreEqual(0.1f, physBone.Stretch);
Assert.AreEqual(1, passedCount);

parameters[StretchParamName] = -1.2345f;
Assert.AreEqual(-1.2345f, physBone.Stretch);
Assert.AreEqual(2, passedCount);

parameters[StretchParamName] = -1.2345f;
Assert.AreEqual(-1.2345f, physBone.Stretch);
Assert.AreEqual(2, passedCount);

physBone.ParameterChanged -= Handler;
}
}
33 changes: 33 additions & 0 deletions src/vrcosclib.Test/TestUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,39 @@ public static string CreateConfigFileForTest(string avatarId, string name, strin
""type"":""Int""
}}
}},
{{
""name"":""PhysBoneParam_IsGrabbed"",
""input"":{{
""address"":""/avatar/parameters/PhysBoneParam_IsGrabbed"",
""type"":""Bool""
}},
""output"":{{
""address"":""/avatar/parameters/PhysBoneParam_IsGrabbed"",
""type"":""Bool""
}}
}},
{{
""name"":""PhysBoneParam_Angle"",
""input"":{{
""address"":""/avatar/parameters/PhysBoneParam_Angle"",
""type"":""Float""
}},
""output"":{{
""address"":""/avatar/parameters/PhysBoneParam_Angle"",
""type"":""Float""
}}
}},
{{
""name"":""PhysBoneParam_Stretch"",
""input"":{{
""address"":""/avatar/parameters/PhysBoneParam_Stretch"",
""type"":""Float""
}},
""output"":{{
""address"":""/avatar/parameters/PhysBoneParam_Stretch"",
""type"":""Float""
}}
}},
{{
""name"":""VelocityZ"",
""output"":{{
Expand Down
78 changes: 78 additions & 0 deletions src/vrcosclib/Avatar/OscPhysBone.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace BuildSoft.VRChat.Osc.Avatar;

public class OscPhysBone
{
public OscAvatarConfig Avatar { get; }
public string ParamName { get; }

public bool IsGrabbed => GetParameterValue<bool>(nameof(IsGrabbed));
public float Angle => GetParameterValue<float>(nameof(Angle));
public float Stretch => GetParameterValue<float>(nameof(Stretch));

public event OscAvatarParameterChangedEventHandler? ParameterChanged;

public OscPhysBone(OscAvatarConfig avatar, string paramName)
{
string[] actualParamName = {
paramName + "_" + nameof(IsGrabbed),
paramName + "_" + nameof(Angle),
paramName + "_" + nameof(Stretch),
};
ThrowArgumentException_IfNotExistParameters(avatar, paramName, actualParamName);

Avatar = avatar;
ParamName = paramName;

var allParams = OscParameter.Parameters;
for (int i = 0; i < actualParamName.Length; i++)
{
var address = OscConst.AvatarParameterAddressSpace + actualParamName[i];
allParams.AddValueChangedEventByAddress(address, GetValueCallback);
}
}

private static void ThrowArgumentException_IfNotExistParameters(OscAvatarConfig avatar, string paramName, string[] actualParamName)
{
int count = 0;
foreach (var name in avatar.Parameters.Names)
{
for (int i = 0; i < actualParamName.Length; i++)
{
if (actualParamName[i] == name)
{
count++;
break;
}
}
if (count == actualParamName.Length)
{
break;
}
}
if (count != actualParamName.Length)
{
throw new ArgumentException($"The avatar don't have the parameter \"{paramName}\".", nameof(avatar));
}
}

private void GetValueCallback(IReadOnlyOscParameterCollection sender, ParameterChangedEventArgs e)
{
var name = e.Address[OscConst.AvatarParameterAddressSpace.Length..];
OnParameterChanged(Avatar.Parameters.Get(name), e);
}

protected internal void OnParameterChanged(OscAvatarParameter param, ValueChangedEventArgs e)
{
ParameterChanged?.Invoke(param, e);
}


private T? GetParameterValue<T>(string name) where T : notnull
{
return Avatar.Parameters.GetAs<T>(ParamName + "_" + name);
}
}

0 comments on commit 0f19b1d

Please sign in to comment.