forked from StereoKit/StereoKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTests.cs
More file actions
308 lines (272 loc) · 9.39 KB
/
Copy pathTests.cs
File metadata and controls
308 lines (272 loc) · 9.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
using StereoKit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
public enum SceneType
{
Demos,
Tests,
Docs,
MAX
}
public static class Tests
{
struct SceneItem
{
public Type type;
public string name;
}
static Type[] allScenes;
static SceneItem[][] sceneTypes;
static ITest activeScene;
static ITest nextScene;
static int sceneIndex = 0;
static int runFrames = 2;
static float runSeconds = 0;
static int sceneFrame = 0;
static float sceneTime = 0;
static int failures = 0;
static int shutdownFrames = 0;
static HashSet<string> screenshots = new HashSet<string>();
private static Type ActiveScene { set { nextScene = (ITest)Activator.CreateInstance(value); } }
public static bool IsTesting { get; set; }
public static bool TestSingle { get; set; }
public static int SceneFrame => sceneFrame;
public static string ScreenshotRoot { get; set; } = null;
public static bool MakeScreenshots { get; set; } = true;
public static string GltfFolders { get; set; } = null;
public static string GltfScreenshotRoot { get; set; } = null;
public static void FindTests()
{
allScenes = Assembly.GetExecutingAssembly()
.GetTypes()
.Where (a => a != typeof(ITest) && typeof(ITest).IsAssignableFrom(a) )
.OrderBy (a => a.Name )
.ToArray ();
sceneTypes = new SceneItem[(int)SceneType.MAX][];
sceneTypes[(int)SceneType.Demos] = allScenes
.Where (t => t.Name.StartsWith("Demo"))
.Select (t => new SceneItem { type = t, name = t.Name.Substring("Demo".Length) })
.ToArray();
sceneTypes[(int)SceneType.Tests] = allScenes
.Where (t => t.Name.StartsWith("Test"))
.Select (t => new SceneItem { type = t, name = t.Name.Substring("Test".Length) })
.ToArray();
sceneTypes[(int)SceneType.Docs] = allScenes
.Where (t => t.Name.StartsWith("Doc"))
.Select (t => new SceneItem { type = t, name = t.Name.Substring("Doc".Length) })
.ToArray();
}
public static int Count (SceneType category) => sceneTypes[(int)category].Length;
public static bool IsActive(SceneType category, int i) => sceneTypes[(int)category][i].type == activeScene.GetType();
public static void Initialize()
{
if (IsTesting && !TestSingle) {
nextScene = null;
}
if (nextScene == null)
nextScene = (ITest)Activator.CreateInstance(allScenes[sceneIndex]);
}
public static void Update()
{
if (IsTesting && runSeconds != 0)
Time.SetTime(Time.Total+(1/90.0), 1/90.0);
// Run a few more frames after we're finished testing to flush screenshots
if (shutdownFrames > 0)
{
shutdownFrames--;
if (shutdownFrames == 0)
SK.Quit();
return;
}
if (nextScene != null)
{
activeScene?.Shutdown();
GC.Collect(int.MaxValue, GCCollectionMode.Forced);
if (IsTesting)
{
Time .SetTime (0);
Input.HandVisible (Handed.Max, false);
Input.HandClearOverride(Handed.Left);
Input.HandClearOverride(Handed.Right);
}
nextScene.Initialize();
if (IsTesting)
Assets.BlockForPriority(int.MaxValue);
sceneTime = Time.Totalf;
activeScene = nextScene;
nextScene = null;
}
// If we're testing, catch and log exceptions instead of crashing
if (IsTesting)
{
try { activeScene.Step(); }
catch ( Exception e )
{
Log.Err(e.ToString());
failures++;
runFrames = sceneFrame + 1; // Ditch out of this test
}
} else {
activeScene.Step();
}
sceneFrame++;
if (IsTesting && FinishedWithTest())
{
sceneIndex += 1;
if (sceneIndex >= allScenes.Length || TestSingle)
shutdownFrames = 4; // run a few more frames so the last screenshot flushes
else
SetTestActive(allScenes[sceneIndex].Name);
}
}
public static void Shutdown()
{
activeScene?.Shutdown();
activeScene = null;
GC.Collect(int.MaxValue, GCCollectionMode.Forced);
if (IsTesting) {
if (failures != 0)
{
Log.Warn($"Testing <~RED>FAILED<~clr>: {failures} failures encountered!");
Environment.Exit(-1);
} else {
Log.Info($"Testing <~GRN>passed!<~clr>");
}
}
Log.Info($"Quit reason: <~WHT>{SK.QuitReason}<~clr>");
}
public static string GetTestName(SceneType category, int index)
{
return sceneTypes[(int)category][index].name;
}
public static void SetTestActive(SceneType category, int index)
{
Log.Info($"Starting Scene: {sceneTypes[(int)category][index].name}");
ActiveScene = sceneTypes[(int)category][index].type;
}
public static void SetTestActive(string name)
{
name = name.ToLower();
Type result = allScenes.OrderBy( a => {
string str = a.Name.ToLower();
if (str == name) return 0;
else if (str.Contains(name)) return str.Length - name.Length;
else return 1000 + string.Compare(str, name);
}).First();
Log.Info($"Starting Scene: {result.Name}");
sceneFrame = 0;
runFrames = 2;
runSeconds = 0;
ActiveScene = result;
}
public static void Test(Func<bool> testFunction)
{
try {
if (!testFunction())
{
Log.Err($"Test failed for {testFunction.Method.Name}!");
failures += 1;
}
} catch (Exception e) {
Log.Err($"Test CRASHED for {testFunction.Method.Name}!\n{e.ToString()}");
failures += 1;
}
}
// Report a failure from within a running test scene. Unlike a bare
// Log.Err, this actually fails the test run.
public static void Fail(string message)
{
Log.Err(message);
failures += 1;
}
private static bool FinishedWithTest()
{
if (runSeconds != 0) return Time.Totalf-sceneTime > runSeconds;
if (runFrames != -1) return sceneFrame == runFrames;
return true;
}
public static void RunForFrames (int frames) => runFrames = frames;
public static void RunForSeconds(float seconds) => runSeconds = seconds;
public static void RunContinue () => runFrames += 1;
public static void RunStop () => runFrames = -1;
public static void Screenshot(string name, int width, int height, float fov, Vec3 from, Vec3 at)
=> Screenshot(name, 0, width, height, fov, from, at);
public static void Screenshot(string name, int width, int height, Vec3 from, Vec3 at)
=> Screenshot(name, 0, width, height, 90, from, at);
public static void Screenshot(string name, int frame, int width, int height, float fov, Vec3 from, Vec3 at)
{
if (!IsTesting || frame != sceneFrame || screenshots.Contains(name) || !MakeScreenshots)
return;
screenshots.Add(name);
string file = Path.Combine(ScreenshotRoot, name);
string dir = Path.GetDirectoryName(file);
if (Directory.Exists(dir) == false)
Directory.CreateDirectory(dir);
Renderer.Screenshot(file, from, at, width, height, fov);
}
public static void ScreenshotGltf(string name, int width, int height, Vec3 from, Vec3 at)
{
if (!IsTesting || screenshots.Contains(name) || !MakeScreenshots || GltfScreenshotRoot == null)
return;
screenshots.Add(name);
string file = Path.Combine(GltfScreenshotRoot, name);
string dir = Path.GetDirectoryName(file);
if (Directory.Exists(dir) == false)
Directory.CreateDirectory(dir);
Renderer.Screenshot(file, from, at, width, height, 45);
}
public static void Hand(in HandJoint[] joints) => Hand(Handed.Right, joints);
public static void Hand(Handed hand, in HandJoint[] joints)
{
if (!IsTesting)
return;
Input.HandVisible (hand, true);
Input.HandOverride(hand, joints);
}
public static void DrawInteractorRay(Interactor actor, ref float refVisibleAmt, ref float refActiveAmt, float skip = 0.07f, bool hideInactive = true)
{
// This is a port of the internal `interactor_show_ray` (interactor_modes.cpp)
// built only from the public Interactor API
if (!actor.Tracked.IsActive()) return;
bool actorVisible = hideInactive == false || actor.Focused != IdHash.None;
refVisibleAmt = SKMath.Lerp(refVisibleAmt, actorVisible ? 1f : 0f, 16.0f * Time.StepUnscaledf);
float visibility = refVisibleAmt;
if (visibility < 0.001f) return;
refActiveAmt = SKMath.Lerp(refActiveAmt, actor.Active != IdHash.None ? 1f : 0f, 16.0f * Time.StepUnscaledf);
float active = refActiveAmt;
Vec3 motionPos = actor.Motion.position;
float length = 0.35f;
Vec3 uncenteredDir = (actor.End - motionPos).Normalized;
Vec3 centeredDir = uncenteredDir;
if (actor.Focused != IdHash.None && actor.TryGetFocusBounds(out Pose poseWorld, out Bounds boundsLocal, out Vec3 atLocal))
{
Vec3 pt = poseWorld.ToMatrix().Transform(boundsLocal.center + atLocal);
length = Vec3.Distance(pt, motionPos);
centeredDir = (pt - motionPos).Normalized;
}
length = SKMath.Lerp(0.35f, length, visibility);
length = Math.Max(0, length - skip);
float alpha = 0.35f + active * 0.65f;
if (hideInactive) alpha *= visibility;
const int ct = 20;
const float raySnap = 1.0f;
LinePoint[] pts = new LinePoint[ct];
for (int i = 0; i < ct; i++)
{
float pct = (float)i / (ct - 1);
float blend = pct * pct * pct * raySnap;
float d = skip + pct * length;
float pctI = 1 - pct;
float curve = SKMath.Lerp(
MathF.Sin(pctI * pctI * MathF.PI),
MathF.Min(1f, MathF.Sin(pct * pct * MathF.PI) * 1.5f), active);
float width = (0.002f + curve * 0.003f) * visibility;
Vec3 at = motionPos + Vec3.Lerp(uncenteredDir * d, centeredDir * d, blend);
pts[i] = new LinePoint(at, new Color32(255, 255, 255, (byte)(curve * alpha * 255)), width);
}
Lines.Add(pts);
}
}