-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathObjIO.cs
205 lines (199 loc) · 6.56 KB
/
ObjIO.cs
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
using OpenTK;
using System;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
namespace MeshSimplify {
/// <summary>
/// Eine Klasse zum Laden und Speichern von Wavefront .obj Dateien.
/// </summary>
/// <remarks>
/// Es werden nur Dreiecksnetze unterstützt.
/// </remarks>
public static class ObjIO {
/// <summary>
/// Parsed die angegebene .obj Datei.
/// </summary>
/// <param name="path">
/// Der Name der Datei, welche geparsed werden soll.
/// </param>
/// <returns>
/// Eine Mesh Instanz, die aus den Daten der .obj Datei erzeugt wurde.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Die angegebene .obj Datei ist ungültig oder enthält ein nicht-unterstütztes
/// Meshformat.
/// </exception>
/// <exception cref="IOException">
/// Die angegebene Datei konnte nicht gelesen werden.
/// </exception>
public static Mesh Load(string path) {
var mesh = new Mesh();
using (var sr = File.OpenText(path)) {
string l = string.Empty;
while ((l = sr.ReadLine()) != null) {
if (l.StartsWith("v "))
mesh.Vertices.Add(ParseVertex(l));
else if (l.StartsWith("f "))
mesh.Faces.Add(ParseFace(l));
else if (l.StartsWith("#vsplit "))
mesh.Splits.Enqueue(ParseVertexSplit(l));
}
}
return mesh;
}
/// <summary>
/// Schreibt die angegebene Mesh in die angegebene Datei.
/// </summary>
/// <param name="mesh">
/// Die zu schreibende Mesh.
/// </param>
/// <param name="path">
/// Der Name der Datei, in die die Mesh geschrieben werden soll.
/// </param>
public static void Save(Mesh mesh, string path) {
using (var fs = File.Open(path, FileMode.Create)) {
using (var sw = new StreamWriter(fs)) {
sw.WriteLine("# {0}", DateTime.Now);
sw.WriteLine("# {0} Vertices", mesh.Vertices.Count);
foreach (var v in mesh.Vertices) {
sw.WriteLine("v {0} {1} {2}",
v.Position.X.ToString(CultureInfo.InvariantCulture),
v.Position.Y.ToString(CultureInfo.InvariantCulture),
v.Position.Z.ToString(CultureInfo.InvariantCulture));
}
sw.WriteLine();
sw.WriteLine("# {0} Faces", mesh.Faces.Count);
foreach (var f in mesh.Faces) {
sw.WriteLine("f {0} {1} {2}", f.Indices[0] + 1, f.Indices[1] + 1,
f.Indices[2] + 1);
}
if (mesh.Splits.Count > 0) {
sw.WriteLine();
sw.WriteLine("# {0} Split Records", mesh.Splits.Count);
foreach (var s in mesh.Splits) {
// vsplit als Kommentar schreiben, so daß die Datei auch weiterhin
// eine gültige .obj Datei bleibt.
sw.Write("#vsplit {0} {{{1} {2} {3}}} {{{4} {5} {6}}} {{ ", s.S + 1,
s.SPosition.X.ToString(CultureInfo.InvariantCulture),
s.SPosition.Y.ToString(CultureInfo.InvariantCulture),
s.SPosition.Z.ToString(CultureInfo.InvariantCulture),
s.TPosition.X.ToString(CultureInfo.InvariantCulture),
s.TPosition.Y.ToString(CultureInfo.InvariantCulture),
s.TPosition.Z.ToString(CultureInfo.InvariantCulture));
foreach (var f in s.Faces) {
sw.Write("({0} {1} {2}) ", f.Indices[0] + 1, f.Indices[1] + 1,
f.Indices[2] + 1);
}
sw.Write("}");
sw.WriteLine();
}
}
}
}
}
/// <summary>
/// Parsed eine Vertex Deklaration.
/// </summary>
/// <param name="l">
/// Die Zeile, die die Vertex Deklaration enthält.
/// </param>
/// <returns>
/// Der Vertex.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Die Vertexdelaration ist ungültig.
/// </exception>
static Vertex ParseVertex(string l) {
var p = l.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (p.Length != 4)
throw new InvalidOperationException("Invalid vertex format: " + l);
return new Vertex() {
Position = new Vector3d(
double.Parse(p[1], CultureInfo.InvariantCulture),
double.Parse(p[2], CultureInfo.InvariantCulture),
double.Parse(p[3], CultureInfo.InvariantCulture))
};
}
/// <summary>
/// Parsed eine Facetten Deklaration.
/// </summary>
/// <param name="l">
/// Die Zeile, die die Facetten Deklaration enthält.
/// </param>
/// <returns>
/// Die Facette.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Die Facettendeklaration ist ungültig.
/// </exception>
static Triangle ParseFace(string l) {
var p = l.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (p.Length != 4)
throw new InvalidOperationException("Invalid face: " + l);
var indices = new[] {
int.Parse(p[1]) - 1,
int.Parse(p[2]) - 1,
int.Parse(p[3]) - 1
};
return new Triangle(indices);
}
/// <summary>
/// Parsed einen dreidimensionalen Vektor.
/// </summary>
/// <param name="l">
/// Die Zeile, die den Vektor enthält.
/// </param>
/// <returns>
/// Der Vektor.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Die Vektordeklaration ist ungültig.
/// </exception>
static Vector3d ParseVector(string l) {
var p = l.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (p.Length != 3)
throw new InvalidOperationException("Invalid vector: " + l);
return new Vector3d(
double.Parse(p[0], CultureInfo.InvariantCulture),
double.Parse(p[1], CultureInfo.InvariantCulture),
double.Parse(p[2], CultureInfo.InvariantCulture));
}
/// <summary>
/// Parsed eine VertexSplit Deklaration.
/// </summary>
/// <param name="l">
/// Die Zeile, die die VertexSplit Deklaration enthält.
/// </param>
/// <returns>
/// Der Vertex-Split Eintrag.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Die VertexSplit Deklaration ist ungültig.
/// </exception>
static VertexSplit ParseVertexSplit(string l) {
var m = Regex.Match(l, @"^#vsplit\s+(\d+)\s+{(.*)}\s+{(.*)}\s+{(.*)}$");
if (!m.Success)
throw new InvalidOperationException("Invalid vsplit: " + l);
var s = new VertexSplit() {
S = int.Parse(m.Groups[1].Value) - 1,
SPosition = ParseVector(m.Groups[2].Value),
TPosition = ParseVector(m.Groups[3].Value)
};
var faceIndices = m.Groups[4].Value;
var matches = Regex.Matches(faceIndices, @"\((-?\d+)\s+(-?\d+)\s+(-?\d+)\)");
for (int i = 0; i < matches.Count; i++) {
var _m = matches[i];
if (!_m.Success)
throw new InvalidOperationException("Invalid face index entry in vsplit: " + l);
var indices = new[] {
int.Parse(_m.Groups[1].Value) - 1,
int.Parse(_m.Groups[2].Value) - 1,
int.Parse(_m.Groups[3].Value) - 1
};
s.Faces.Add(new Triangle(indices));
}
return s;
}
}
}