225 lines
9.9 KiB
C#
225 lines
9.9 KiB
C#
using NUnit.Framework;
|
|
using Streamingle.Gaze.Editor;
|
|
using UnityEngine;
|
|
|
|
namespace Streamingle.Gaze.Tests
|
|
{
|
|
public sealed class GazeCalibrationPreviewSessionTests
|
|
{
|
|
[Test]
|
|
public void Session_UsesRuntimeBlendSemantics_BlocksDriverAndRestoresWeights()
|
|
{
|
|
var root = new GameObject("CalibrationPreviewSessionTest");
|
|
var mesh = CreateMesh();
|
|
var profile = ScriptableObject.CreateInstance<BlendshapeGazeProfile>();
|
|
var session = new GazeCalibrationPreviewSession();
|
|
|
|
try
|
|
{
|
|
var renderer = root.AddComponent<SkinnedMeshRenderer>();
|
|
renderer.sharedMesh = mesh;
|
|
renderer.SetBlendShapeWeight(0, 25f);
|
|
renderer.SetBlendShapeWeight(1, 30f);
|
|
renderer.SetBlendShapeWeight(2, 45f);
|
|
renderer.SetBlendShapeWeight(3, 55f);
|
|
|
|
profile.ReplaceChannels(new[]
|
|
{
|
|
new GazeBlendShapeChannel("eyeLookUpLeft"),
|
|
new GazeBlendShapeChannel(
|
|
"eyeCorrectiveAdd",
|
|
GazeChannelUsage.Corrective,
|
|
GazeCorrectiveBlendMode.AdditiveFromCenter),
|
|
new GazeBlendShapeChannel(
|
|
"eyeCorrectiveOverride",
|
|
GazeChannelUsage.Corrective,
|
|
GazeCorrectiveBlendMode.Override)
|
|
});
|
|
for (var index = 0; index < 9; index++)
|
|
{
|
|
profile.SetSample(
|
|
(GazeCalibrationPoint)index,
|
|
new[]
|
|
{
|
|
index * 10f,
|
|
10f + index * 2f,
|
|
100f - index * 5f
|
|
});
|
|
}
|
|
|
|
var driver = root.AddComponent<BlendshapeGazeDriver>();
|
|
driver.TargetRenderer = renderer;
|
|
driver.Profile = profile;
|
|
var gazeOrigin = new GameObject("GazeOrigin").transform;
|
|
gazeOrigin.SetParent(root.transform, false);
|
|
driver.GazeOrigin = gazeOrigin;
|
|
|
|
Assert.That(session.Start(driver, out var error), Is.True, error);
|
|
Assert.That(session.GetOriginalWeightsCopy(), Is.EqualTo(new[] { 25f, 30f, 45f }));
|
|
|
|
session.PreviewSample(GazeCalibrationPoint.UpRight);
|
|
Assert.That(renderer.GetBlendShapeWeight(0), Is.EqualTo(80f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(1), Is.EqualTo(38f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(2), Is.EqualTo(60f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(3), Is.EqualTo(55f).Within(0.001f));
|
|
|
|
session.PreviewWeights(new[] { 67f, 23f, 72f }, GazeCalibrationPoint.CenterRight);
|
|
Assert.That(session.IsFreePreview, Is.False);
|
|
Assert.That(session.PreviewCoordinates, Is.EqualTo(Vector2.right));
|
|
Assert.That(renderer.GetBlendShapeWeight(0), Is.EqualTo(67f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(1), Is.EqualTo(35f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(2), Is.EqualTo(72f).Within(0.001f));
|
|
|
|
// The four surrounding samples are deliberately distinct. At
|
|
// (0.5, -0.5), indices 1/2/4/5 contribute equally.
|
|
session.PreviewNormalized(new Vector2(0.5f, -0.5f));
|
|
Assert.That(session.IsFreePreview, Is.True);
|
|
Assert.That(renderer.GetBlendShapeWeight(0), Is.EqualTo(30f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(1), Is.EqualTo(28f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(2), Is.EqualTo(85f).Within(0.001f));
|
|
|
|
driver.SetTimelineState(1234, new GazeTimelineState
|
|
{
|
|
TargetPosition = new Vector3(1f, 0f, 1f),
|
|
HasTargetPosition = true,
|
|
Influence = 1f,
|
|
EnableCorrectives = true
|
|
});
|
|
driver.EvaluateNow(true);
|
|
Assert.That(renderer.GetBlendShapeWeight(0), Is.EqualTo(30f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(1), Is.EqualTo(28f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(2), Is.EqualTo(85f).Within(0.001f));
|
|
|
|
session.Stop();
|
|
Assert.That(renderer.GetBlendShapeWeight(0), Is.EqualTo(25f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(1), Is.EqualTo(30f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(2), Is.EqualTo(45f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(3), Is.EqualTo(55f).Within(0.001f));
|
|
|
|
// Releasing the session also releases the driver override. A yaw
|
|
// beyond the profile limit resolves to CenterRight (sample 5).
|
|
driver.EvaluateNow(true);
|
|
Assert.That(renderer.GetBlendShapeWeight(0), Is.EqualTo(50f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(1), Is.EqualTo(32f).Within(0.001f));
|
|
Assert.That(renderer.GetBlendShapeWeight(2), Is.EqualTo(75f).Within(0.001f));
|
|
}
|
|
finally
|
|
{
|
|
session.Dispose();
|
|
Object.DestroyImmediate(root);
|
|
Object.DestroyImmediate(mesh);
|
|
Object.DestroyImmediate(profile);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Session_DoesNotRestoreIntoAnIndexWhoseResolvedNameChanged()
|
|
{
|
|
var root = new GameObject("CalibrationPreviewResolvedNameTest");
|
|
var mesh = CreateMesh();
|
|
var profile = ScriptableObject.CreateInstance<BlendshapeGazeProfile>();
|
|
var session = new GazeCalibrationPreviewSession();
|
|
|
|
try
|
|
{
|
|
var renderer = root.AddComponent<SkinnedMeshRenderer>();
|
|
renderer.sharedMesh = mesh;
|
|
renderer.SetBlendShapeWeight(0, 25f);
|
|
profile.ReplaceChannels(new[] { new GazeBlendShapeChannel("eyeLookUpLeft") });
|
|
for (var index = 0; index < 9; index++)
|
|
profile.SetSample((GazeCalibrationPoint)index, new[] { 80f });
|
|
|
|
var driver = root.AddComponent<BlendshapeGazeDriver>();
|
|
driver.TargetRenderer = renderer;
|
|
driver.Profile = profile;
|
|
|
|
Assert.That(session.Start(driver, out var error), Is.True, error);
|
|
session.PreviewSample(GazeCalibrationPoint.Center);
|
|
Assert.That(renderer.GetBlendShapeWeight(0), Is.EqualTo(80f).Within(0.001f));
|
|
|
|
mesh.ClearBlendShapes();
|
|
AddBlendShape(mesh, "replacementShape");
|
|
renderer.SetBlendShapeWeight(0, 66f);
|
|
|
|
session.Stop();
|
|
Assert.That(renderer.GetBlendShapeWeight(0), Is.EqualTo(66f).Within(0.001f));
|
|
}
|
|
finally
|
|
{
|
|
session.Dispose();
|
|
Object.DestroyImmediate(root);
|
|
Object.DestroyImmediate(mesh);
|
|
Object.DestroyImmediate(profile);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Session_AdditiveDraftUsesOriginalWeightUntilCenterIsCaptured()
|
|
{
|
|
var root = new GameObject("CalibrationPreviewDraftCorrectiveTest");
|
|
var mesh = CreateMesh();
|
|
var profile = ScriptableObject.CreateInstance<BlendshapeGazeProfile>();
|
|
var session = new GazeCalibrationPreviewSession();
|
|
|
|
try
|
|
{
|
|
var renderer = root.AddComponent<SkinnedMeshRenderer>();
|
|
renderer.sharedMesh = mesh;
|
|
renderer.SetBlendShapeWeight(1, 30f);
|
|
profile.ReplaceChannels(new[]
|
|
{
|
|
new GazeBlendShapeChannel(
|
|
"eyeCorrectiveAdd",
|
|
GazeChannelUsage.Corrective,
|
|
GazeCorrectiveBlendMode.AdditiveFromCenter)
|
|
});
|
|
|
|
var driver = root.AddComponent<BlendshapeGazeDriver>();
|
|
driver.TargetRenderer = renderer;
|
|
driver.Profile = profile;
|
|
|
|
Assert.That(session.Start(driver, out var error), Is.True, error);
|
|
session.PreviewWeights(new[] { 42f }, GazeCalibrationPoint.UpRight);
|
|
Assert.That(renderer.GetBlendShapeWeight(1), Is.EqualTo(42f).Within(0.001f));
|
|
|
|
session.Stop();
|
|
Assert.That(renderer.GetBlendShapeWeight(1), Is.EqualTo(30f).Within(0.001f));
|
|
}
|
|
finally
|
|
{
|
|
session.Dispose();
|
|
Object.DestroyImmediate(root);
|
|
Object.DestroyImmediate(mesh);
|
|
Object.DestroyImmediate(profile);
|
|
}
|
|
}
|
|
|
|
private static Mesh CreateMesh()
|
|
{
|
|
var mesh = new Mesh { name = "CalibrationPreviewSessionMesh" };
|
|
mesh.vertices = new[]
|
|
{
|
|
new Vector3(-0.1f, 0f, 0f),
|
|
new Vector3(0.1f, 0f, 0f),
|
|
new Vector3(0f, 0.1f, 0f)
|
|
};
|
|
mesh.triangles = new[] { 0, 1, 2 };
|
|
AddBlendShape(mesh, "Face.eyeLookUpLeft");
|
|
AddBlendShape(mesh, "Face.eyeCorrectiveAdd");
|
|
AddBlendShape(mesh, "Face.eyeCorrectiveOverride");
|
|
AddBlendShape(mesh, "unrelatedFaceShape");
|
|
return mesh;
|
|
}
|
|
|
|
private static void AddBlendShape(Mesh mesh, string name)
|
|
{
|
|
mesh.AddBlendShapeFrame(
|
|
name,
|
|
100f,
|
|
new[] { Vector3.up * 0.01f, Vector3.up * 0.01f, Vector3.up * 0.01f },
|
|
new Vector3[3],
|
|
new Vector3[3]);
|
|
}
|
|
}
|
|
}
|