private static (List <Vertex> vertices, List <VertexNormal> normals, List <TextureUv> uvs, List <Group> groups) ExtractOBJData(string[] lines) { var enumerable = from line in lines let split = line.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(l => l.Trim()).ToArray() where split.Any() && !split.First().StartsWith("#") select split; var vertices = new List <Vertex>(); var normals = new List <VertexNormal>(); var uvs = new List <TextureUv>(); var currentGroup = new Group("unnamed"); var groups = new List <Group>(); var elementComparer = new ElementComparer(vertices, uvs, normals); foreach (var line in enumerable) { switch (line[0]) { case "v": Debug.Assert(line.Length == 4); var vertex = new Vertex { X = float.Parse(line[1]), Y = float.Parse(line[2]), Z = float.Parse(line[3]) }; vertices.Add(vertex); break; case "vn": Debug.Assert(line.Length == 4); normals.Add(new VertexNormal { X = float.Parse(line[1]), Y = float.Parse(line[2]), Z = float.Parse(line[3]) }); break; case "vt": uvs.Add(new TextureUv { U = float.Parse(line[1]), V = float.Parse(line[2]) //Ignore w }); break; case "g": case "o": //For the sake of bedrock, treat groups and objects as the same thing. currentGroup = new Group(line[1]); groups.Add(currentGroup); break; case "f": Debug.Assert(line.Length >= 4 && line.Length <= 5); var polygon = new Polygon(); for (var i = 1; i < line.Length; ++i) { var elements = line[i].Split("/"); var vertexIndex = int.Parse(elements[0]); var textureUvIndex = int.Parse(elements[1]); if (elements.Length > 2) { var normalIndex = int.Parse(elements[2]); polygon.AddElement(new Element(vertexIndex, textureUvIndex, normalIndex)); } else { polygon.AddElement(new Element(vertexIndex, textureUvIndex)); } } if (currentGroup.TryGetPreviousPolygon(out var lastPolygon) && lastPolygon.Vertices.Count == 3) { var uniqueElements = lastPolygon.Vertices.Except(polygon.Vertices, elementComparer).ToArray(); var missingElements = polygon.Vertices.Except(lastPolygon.Vertices, elementComparer).ToArray(); if (uniqueElements.Length == 1 && missingElements.Length == 1) { lastPolygon.RepairToQuad(missingElements.Single()); } else { currentGroup.AddPolygon(polygon); } } else { currentGroup.AddPolygon(polygon); } break; } }