Created
July 10, 2017 16:02
-
-
Save Myvar/65ccf671de9389b8f475ff2d028dc03a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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