-
Notifications
You must be signed in to change notification settings - Fork 72
Expand file tree
/
Copy pathMeshTests.cs
More file actions
130 lines (114 loc) · 5 KB
/
MeshTests.cs
File metadata and controls
130 lines (114 loc) · 5 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
// Visual Pinball Engine
// Copyright (C) 2023 freezy and VPE Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System;
using System.IO;
using System.Linq;
using System.Text;
using FluentAssertions;
using JeremyAnsel.Media.WavefrontObj;
using VisualPinball.Engine.Game;
using VisualPinball.Engine.Math;
using VisualPinball.Engine.VPT;
using VisualPinball.Engine.VPT.Table;
namespace VisualPinball.Engine.Test.Test
{
public abstract class MeshTests : BaseTests
{
private const float Threshold = 0.0001f;
protected static Mesh[] GetMeshes(Table table, IRenderable renderable, params string[] names)
{
return names.Select(n => renderable.GetMesh(n, table)).ToArray();
}
protected static ObjFile LoadObjFixture(string filePath)
{
var lines = File
.ReadAllLines(filePath)
.Where(l=> !l.StartsWith("usemtl")) // remove material references
.Select(l => l.StartsWith("o ") ? "g " + l.Substring(2) : l); // change object to group
// now, parse
using (var memStream = new MemoryStream(Encoding.ASCII.GetBytes(string.Join("\n", lines)))) {
return ObjFile.FromStream(memStream);
}
}
protected static void AssertObjMesh(Table table, ObjFile obj, IRenderable renderable, string[] meshIds,
Func<IRenderable, Mesh, string> getName = null, float threshold = Threshold)
{
var targetMeshes = GetMeshes(table, renderable, meshIds);
foreach (var mesh in targetMeshes) {
AssertObjMesh(obj, mesh, getName?.Invoke(renderable, mesh), threshold);
}
}
protected static void AssertObjMesh(Table table, ObjFile obj, IRenderable renderable,
Func<IRenderable, Mesh, string> getName = null, float threshold = Threshold)
{
var targetMeshes = GetMeshes(table, renderable, string.Empty); // empty for items that only have one mesh and id is ignored
foreach (var mesh in targetMeshes) {
AssertObjMesh(obj, mesh, getName?.Invoke(renderable, mesh), threshold);
}
}
protected static void AssertObjMesh(ObjFile objFile, string name, Mesh[] meshes, float threshold = Threshold)
{
var objGroup = objFile.Groups.First(g => g.Name == name);
// concat all vertices
var vertices = meshes.SelectMany(m => m.Vertices).ToArray();
var offset = 0;
// concat indices, but add additional offset for each new mesh
var indices = meshes.SelectMany((m, n) => {
var offset1 = offset;
var ii = m.Indices.Select(idx => idx + offset1);
offset += m.Vertices.Length;
return ii;
}).ToArray();
// compare concatenated mesh
var i = 0;
foreach (var face in objGroup.Faces) {
AssertVerticesEqual(objFile.Vertices[face.Vertices[2].Vertex - 1].Position, vertices[indices[i]], threshold);
AssertVerticesEqual(objFile.Vertices[face.Vertices[1].Vertex - 1].Position, vertices[indices[i + 1]], threshold);
AssertVerticesEqual(objFile.Vertices[face.Vertices[0].Vertex - 1].Position, vertices[indices[i + 2]], threshold);
i += 3;
}
}
protected static void AssertObjMesh(ObjFile objFile, Mesh mesh, string name = null, float threshold = Threshold, bool switchZ = false)
{
name = name ?? mesh.Name;
var objGroup = objFile.Groups.FirstOrDefault(g => g.Name == name);
if (objGroup == null) {
throw new Exception($"Cannot find group \"{name}\" in exported obj.");
}
var i = 0;
foreach (var face in objGroup.Faces) {
AssertVerticesEqual(objFile.Vertices[face.Vertices[2].Vertex - 1].Position, mesh.Vertices[mesh.Indices[i]], threshold, switchZ);
AssertVerticesEqual(objFile.Vertices[face.Vertices[1].Vertex - 1].Position, mesh.Vertices[mesh.Indices[i + 1]], threshold, switchZ);
AssertVerticesEqual(objFile.Vertices[face.Vertices[0].Vertex - 1].Position, mesh.Vertices[mesh.Indices[i + 2]], threshold, switchZ);
i += 3;
}
}
protected static void AssertNoObjMesh(ObjFile objFile, string name)
{
var objGroup = objFile.Groups.FirstOrDefault(g => g.Name == name);
if (objGroup != null) {
throw new Exception($"Group {name} must not exist, but does.");
}
}
private static void AssertVerticesEqual(ObjVector4 expected, Vertex3DNoTex2 actual, float threshold, bool switchZ = false)
{
var sign = switchZ ? -1 : 1;
actual.X.Should().BeApproximately(expected.X, threshold);
actual.Y.Should().BeApproximately(expected.Y, threshold);
actual.Z.Should().BeApproximately(sign * expected.Z, threshold);
}
}
}