Skip to content

Instantly share code, notes, and snippets.

@Myvar
Created July 10, 2017 16:02
Show Gist options
  • Save Myvar/65ccf671de9389b8f475ff2d028dc03a to your computer and use it in GitHub Desktop.
Save Myvar/65ccf671de9389b8f475ff2d028dc03a to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Engine.Core;
using static Engine.Resources.Mesh;
namespace Engine.Resources
{
public class ObjFile
{
public List<Vector3f> Vertices { get; set; } = new List<Vector3f>();
public List<Vector2f> TextureCoordinates { get; set; } = new List<Vector2f>();
public List<Vector3f> Normals { get; set; } = new List<Vector3f>();
public List<MeshFace> Faces { get; set; } = new List<MeshFace>();
public Dictionary<string, Material> Materials { get; set; } = new Dictionary<string, Material>();
private string Directory { get; set; }
public ObjFile(string file)
{
Directory = new FileInfo(file).DirectoryName;
Parse(File.ReadLines(file).ToArray());
}
private void LoadMaterialFile(string file)
{
var mtl = File.ReadAllLines(Path.Combine(Directory, file));
var sb = new StringBuilder();
foreach (var i in mtl)
{
if (i.StartsWith("newmtl ") && sb.ToString() != "")
{
LoadMaterial(sb.ToString());
sb.Clear();
}
if (!i.StartsWith("#") && i.Trim() != "")
{
sb.AppendLine(i);
}
}
LoadMaterial(sb.ToString());
}
private void LoadMaterial(string matsrc)
{
var mat = new Material();
var name = "";
var fmt = new NumberFormatInfo();
fmt.NegativeSign = "-";
fmt.NumberDecimalSeparator = ".";
foreach (var s in matsrc.Replace("\r\n", "\n").Split('\n'))
{
if (s.StartsWith("newmtl "))
{
name = s.Split(' ').Last().Trim();
}
else if (s.StartsWith("Ns "))
{
mat.Ns = float.Parse(s.Split(' ').Last().Trim(), fmt);
}
else if (s.StartsWith("Ni "))
{
mat.Ni = float.Parse(s.Split(' ').Last().Trim(), fmt);
}
else if (s.StartsWith("d "))
{
mat.d = float.Parse(s.Split(' ').Last().Trim(), fmt);
}
else if (s.StartsWith("illum "))
{
mat.illum = int.Parse(s.Split(' ').Last().Trim());
}
else if (s.StartsWith("Ka "))
{
var segs = s.Remove(0, 3).Split(' ');
mat.Ka = new Vector3f(float.Parse(segs[0], fmt), float.Parse(segs[1], fmt), float.Parse(segs[2], fmt));
}
else if (s.StartsWith("Kd "))
{
var segs = s.Remove(0, 3).Split(' ');
mat.Kd = new Vector3f(float.Parse(segs[0], fmt), float.Parse(segs[1], fmt), float.Parse(segs[2], fmt));
}
else if (s.StartsWith("Ks "))
{
var segs = s.Remove(0, 3).Split(' ');
mat.Ks = new Vector3f(float.Parse(segs[0], fmt), float.Parse(segs[1], fmt), float.Parse(segs[2], fmt));
}
}
Materials.Add(name, mat);
}
private void Parse(string[] Lines)
{
foreach (var i in Lines)
{
Parse(i);
}
}
private string _tmpMaterialName { get; set; }
private void Parse(string line)
{
var s = line.Trim();
if (s.StartsWith("mtllib "))
{
LoadMaterialFile(s.Split(' ').Last().Trim());
}
else if (s.StartsWith("usemtl "))
{
_tmpMaterialName = s.Split(' ').Last().Trim();
}
else if (s.StartsWith("v "))
{
var ln = line.Remove(0, 2).Trim();
Vertices.Add(MeshFace.ParseVector3f(ln, ' '));
}
else if (s.StartsWith("vt "))
{
var ln = line.Remove(0, 3).Trim();
TextureCoordinates.Add(MeshFace.ParseVector2f(ln, ' '));
}
else if (s.StartsWith("vn "))
{
var ln = line.Remove(0, 3).Trim();
Normals.Add(MeshFace.ParseVector3f(ln, ' '));
}
else if (s.StartsWith("f "))
{
var ln = line.Remove(0, 2).Trim();
Faces.Add(new MeshFace(ln, _tmpMaterialName));
}
}
public void EdgeSplit()
{
if (!ModelHasUVOverlaping())
{
return;
}
int c = Faces.Count;
for (int i = 0; i < c; i++)
{
var mf = Faces[i];
if (UVOverlaps(mf))
{
SplitVertex(mf);
}
}
}
private void SplitVertex(MeshFace f)
{
for (int z = 0; z < Faces.Count; z++)
{
var mf = Faces[z];
if (mf != f)
{
if ((mf.A.Y == f.A.Y && mf.A.X == f.A.X) ||
(mf.B.Y == f.B.Y && mf.B.X == f.B.X) ||
(mf.C.Y == f.C.Y && mf.C.X == f.C.X))
{
{
var vert = Vertices[(int)f.A.X];
Vertices.Add(new Vector3f(vert.X, vert.Y, vert.Z));
f.A.X = Vertices.Count - 1;
}
{
var vert = Vertices[(int)f.B.X];
Vertices.Add(new Vector3f(vert.X, vert.Y, vert.Z));
f.B.X = Vertices.Count - 1;
}
{
var vert = Vertices[(int)f.C.X];
Vertices.Add(new Vector3f(vert.X, vert.Y, vert.Z));
f.C.X = Vertices.Count - 1;
}
}
}
}
}
private bool ModelHasUVOverlaping()
{
foreach (var i in Faces)
{
if (UVOverlaps(i))
{
return true;
}
}
return false;
}
private bool UVOverlaps(MeshFace f)
{
foreach (var i in Faces)
{
if (i != f)
{
if ((i.A.Y == f.A.Y && i.A.X == f.A.X) ||
(i.B.Y == f.B.Y && i.B.X == f.B.X) ||
(i.C.Y == f.C.Y && i.C.X == f.C.X))
{
return true;
}
}
}
return false;
}
public void Triangulate()
{
for (int i = 0; i < Faces.Count; i++)
{
var mf = Faces[i];
if (mf.IsQuad)
{
double dist1 = Vertices[(int)mf.A.X].DistanceTo(Vertices[(int)mf.C.X]);
double dist2 = Vertices[(int)mf.B.X].DistanceTo(Vertices[(int)mf.D.X]);
if (dist1 > dist2)
{
Faces.Add(new MeshFace(mf.A, mf.B, mf.D, mf.Material));
Faces.Add(new MeshFace(mf.B, mf.C, mf.D, mf.Material));
}
else
{
Faces.Add(new MeshFace(mf.A, mf.B, mf.C, mf.Material));
Faces.Add(new MeshFace(mf.A, mf.C, mf.D, mf.Material));
}
}
}
var newfaces = new List<MeshFace>();
foreach (var mf in Faces)
{
if (mf.IsTriangle) newfaces.Add(mf);
}
Faces.Clear();
Faces.AddRange(newfaces);
}
public void LoadModelObject(ref MeshObject mo, string mat)
{
mo.Material = Materials[mat];
var verticies = new List<Vertex>();
var faces = new List<MeshFace>();
foreach (var i in Faces)
{
if (i.Material == mat)
{
faces.Add(i);
}
}
foreach (var i in faces)
{
verticies.Add(new Vertex(Vertices[(int)i.A.X]));
i.A.X = verticies.Count - 1;
verticies.Add(new Vertex(Vertices[(int)i.B.X]));
i.B.X = verticies.Count - 1;
verticies.Add(new Vertex(Vertices[(int)i.C.X]));
i.C.X = verticies.Count - 1;
}
var indecies = new List<uint>();
foreach (var i in faces)
{
indecies.Add((uint)i.A.X);
indecies.Add((uint)i.B.X);
indecies.Add((uint)i.C.X);
}
mo.AddVertices(verticies.ToArray(), indecies.ToArray(), true, true);
}
// public Vertex[] GetVertexArray(ref MeshObject mo, string mat)
// {
// var re = new List<Vertex>();
// foreach (var v in Vertices)
// {
// re.Add(new Vertex(v));
// }
// if (TextureCoordinates.Count != 0)
// {
// foreach (var i in Faces)
// {
// re[(int)i.A.X].TexCoord = TextureCoordinates[(int)i.A.Y];
// re[(int)i.B.X].TexCoord = TextureCoordinates[(int)i.B.Y];
// re[(int)i.C.X].TexCoord = TextureCoordinates[(int)i.C.Y];
// }
// }
// return re.ToArray();
// }
// public uint[] GetIndices(string mat)
// {
// var re = new List<uint>();
// foreach (var i in Faces)
// {
// re.Add((uint)i.A.X);
// re.Add((uint)i.B.X);
// re.Add((uint)i.C.X);
// }
// return re.ToArray();
// }
}
public class MeshFace
{
public bool IsTriangle { get; set; }
public bool IsQuad { get; set; }
public string Material { get; set; }
public Vector3f A { get; set; }
public Vector3f B { get; set; }
public Vector3f C { get; set; }
public Vector3f D { get; set; }
public MeshFace(Vector3f a, Vector3f b, Vector3f c, string material)
{
A = a;
B = b;
C = c;
IsQuad = false;
IsTriangle = true;
Material = material;
}
public MeshFace(Vector3f a, Vector3f b, Vector3f c, Vector3f d, string material)
{
A = a;
B = b;
C = c;
D = d;
IsQuad = true;
IsTriangle = false;
Material = material;
}
public MeshFace(string raw, string material)
{
var s = raw.Trim();
if (s.StartsWith("f"))
{
s = s.Remove(0, 2).Trim();
}
var segs = s.Split(' ');
if (segs.Length < 4)
{
A = ParseVector3f(segs[0], '/');
B = ParseVector3f(segs[1], '/');
C = ParseVector3f(segs[2], '/');
IsQuad = false;
IsTriangle = true;
}
else
{
A = ParseVector3f(segs[0], '/');
B = ParseVector3f(segs[1], '/');
C = ParseVector3f(segs[2], '/');
D = ParseVector3f(segs[3], '/');
IsQuad = true;
IsTriangle = false;
}
Material = material;
}
public static Vector3f ParseVector3f(string raw, char spliter)
{
raw = raw.Trim();
float x = 0, y = 0, z = 0;
var fmt = new NumberFormatInfo();
fmt.NegativeSign = "-";
fmt.NumberDecimalSeparator = ".";
var segs = raw.Split(spliter);
if (!string.IsNullOrEmpty(segs[0].Trim()))
{
x = float.Parse(segs[0].Trim(), fmt);
}
else
{
x = 0;
}
if (!string.IsNullOrEmpty(segs[1].Trim()))
{
y = float.Parse(segs[1].Trim(), fmt);
}
else
{
y = 0;
}
if (!string.IsNullOrEmpty(segs[2].Trim()))
{
z = float.Parse(segs[2].Trim(), fmt);
}
else
{
z = 0;
}
return new Vector3f(x - 1, y - 1, z - 1);
}
public static Vector2f ParseVector2f(string raw, char spliter)
{
raw = raw.Trim();
float x = 0, y = 0;
var fmt = new NumberFormatInfo();
fmt.NegativeSign = "-";
fmt.NumberDecimalSeparator = ".";
var segs = raw.Split(spliter);
if (!string.IsNullOrEmpty(segs[0].Trim()))
{
x = float.Parse(segs[0].Trim(), fmt);
}
else
{
x = 0;
}
if (!string.IsNullOrEmpty(segs[1].Trim()))
{
y = float.Parse(segs[1].Trim(), fmt);
}
else
{
y = 0;
}
return new Vector2f(x - 1, y - 1);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment