Files
Pilz/Pilz.Simple3DFileParser/FileParser/Obj.cs
2020-09-24 11:21:53 +02:00

476 lines
21 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using global::System.Globalization;
using global::System.IO;
using System.Linq;
using global::System.Threading;
using Microsoft.VisualBasic.CompilerServices;
using global::Pilz.S3DFileParser.Exceptions;
namespace Pilz.S3DFileParser.ObjModule
{
public class ObjFile
{
public static Object3D FromFile(string FileName, bool LoadMaterials, UpAxis UpAxis)
{
var curThread = Thread.CurrentThread;
var curCultInfo = curThread.CurrentCulture;
var newCultInfo = new CultureInfo(curCultInfo.Name);
newCultInfo.NumberFormat.NumberDecimalSeparator = ".";
newCultInfo.NumberFormat.PercentDecimalSeparator = ".";
newCultInfo.NumberFormat.CurrencyDecimalSeparator = ".";
newCultInfo.NumberFormat.NumberGroupSeparator = ",";
newCultInfo.NumberFormat.PercentGroupSeparator = ",";
newCultInfo.NumberFormat.CurrencyGroupSeparator = ",";
curThread.CurrentCulture = newCultInfo;
var newObj = new Object3D();
var newMesh = new Mesh();
string curObjPath = Path.GetDirectoryName(FileName);
var mtllibs = new Dictionary<string, MaterialLib>();
MaterialLib curMaterialLib = null;
Material curMaterial = null;
var srObj = new StreamReader(FileName, System.Text.Encoding.ASCII);
string line = "";
while (!srObj.EndOfStream)
{
line = srObj.ReadLine().Trim();
if (!string.IsNullOrEmpty(line))
{
switch (true)
{
case object _ when line.StartsWith("mtllib "):
{
string name = line.Substring(7);
if (!mtllibs.ContainsKey(name))
{
string mtlfile = Path.Combine(curObjPath, name);
if (!File.Exists(mtlfile))
throw new MaterialException("Material Library not found!");
var newmtl = new MaterialLib();
newmtl.FromFile(mtlfile, LoadMaterials);
mtllibs.Add(name, newmtl);
curMaterialLib = newmtl;
foreach (KeyValuePair<string, Material> kvp in curMaterialLib.Materials)
{
if (!newObj.Materials.ContainsKey(kvp.Key))
newObj.Materials.Add(kvp.Key, kvp.Value);
}
}
else
{
curMaterialLib = mtllibs[name];
}
break;
}
case object _ when line.StartsWith("usemtl "):
{
curMaterial = curMaterialLib.Materials[line.Substring(7)];
break;
}
case object _ when line.StartsWith("v "):
{
if (line.Contains("nan"))
line = line.Replace("nan", "0");
var splitXYZ = line.Substring(2).Split(' ');
double tX = Convert.ToDouble(splitXYZ[0]);
double tY = Convert.ToDouble(splitXYZ[1]);
double tZ = Convert.ToDouble(splitXYZ[2]);
var v = new Vertex();
switch (UpAxis)
{
case UpAxis.Y:
{
v.X = tX;
v.Y = tY;
v.Z = tZ;
break;
}
case UpAxis.Z:
{
v.X = tY;
v.Y = tZ;
v.Z = tX;
break;
}
}
newMesh.Vertices.Add(v);
break;
}
case object _ when line.StartsWith("vt "):
{
var uvstr = line.Substring(3).Split(' ');
var uv = new UV()
{
U = Convert.ToSingle(uvstr[0]),
V = Convert.ToSingle(uvstr[1])
};
newMesh.UVs.Add(uv);
break;
}
case object _ when line.StartsWith("vn "):
{
var splitXYZ = line.Substring(3).Split(' ');
float tX = Convert.ToSingle(splitXYZ[0]);
float tY = Convert.ToSingle(splitXYZ[1]);
float tZ = Convert.ToSingle(splitXYZ[2]);
var n = new Normal();
switch (UpAxis)
{
case UpAxis.Y:
{
n.X = tX;
n.Y = tY;
n.Z = tZ;
break;
}
case UpAxis.Z:
{
n.X = tZ;
n.Y = tY;
n.Z = tX;
break;
}
}
newMesh.Normals.Add(n);
break;
}
case object _ when line.StartsWith("vc "):
{
var splitRGB = line.Substring(3).Split(' ');
float tX = Convert.ToSingle(splitRGB[0]);
float tY = Convert.ToSingle(splitRGB[1]);
float tZ = Convert.ToSingle(splitRGB[2]);
var vc = new VertexColor();
switch (UpAxis)
{
case UpAxis.Y:
{
vc.R = tX;
vc.G = tY;
vc.B = tZ;
break;
}
case UpAxis.Z:
{
vc.R = tY;
vc.G = tZ;
vc.B = tX;
break;
}
}
newMesh.VertexColors.Add(vc);
break;
}
case object _ when line.StartsWith("f "):
{
var tri = new Face() { Material = curMaterial };
foreach (string xyz1 in line.Substring(2).Split(' '))
{
var xyz = xyz1.Trim();
if (string.IsNullOrEmpty(xyz))
continue;
string[] splitsub = null;
var p = new Point();
switch (true)
{
case object _ when xyz.Contains("/"):
{
splitsub = xyz.Split('/');
break;
}
case object _ when xyz.Contains(@"\"):
{
splitsub = xyz.Split('\\');
break;
}
default:
{
splitsub = new string[] { 0.ToString(), 0.ToString(), 0.ToString() };
break;
}
}
string v1 = splitsub[0];
string v2 = splitsub[1];
string v3 = splitsub[2];
if (!string.IsNullOrEmpty(v1))
{
p.Vertex = newMesh.Vertices[Convert.ToInt32(v1) - 1];
}
if (!string.IsNullOrEmpty(v2))
{
p.UV = newMesh.UVs[Convert.ToInt32(v2) - 1];
}
else
{
var newUV = new UV() { U = 0f, V = 0f };
p.UV = newUV;
newMesh.UVs.Add(newUV);
}
if (!string.IsNullOrEmpty(v3))
{
p.Normal = newMesh.Normals[Convert.ToInt32(v3) - 1];
}
if (splitsub.Count() > 3)
{
string v4 = splitsub[3];
if (!string.IsNullOrEmpty(v4))
p.VertexColor = newMesh.VertexColors[Convert.ToInt32(v4) - 1];
}
tri.Points.Add(p);
}
newMesh.Faces.Add(tri);
break;
}
}
}
}
newObj.Meshes.Add(newMesh);
curThread.CurrentCulture = curCultInfo;
srObj.Close();
return newObj;
}
public static void ToFile(string FileName, Object3D obj)
{
var fs = new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite);
var sw = new StreamWriter(fs, System.Text.Encoding.ASCII);
if (obj.Materials.Count > 0)
{
string mtlName = Path.GetFileNameWithoutExtension(FileName) + ".mtl";
string mtlFile = Path.Combine(Path.GetDirectoryName(FileName), mtlName);
sw.WriteLine($"mtllib {mtlName}");
MaterialLib.ToFile(mtlFile, obj);
}
int curVertCount = 1;
int curUVCount = 1;
int curNormCount = 1;
int curVertColCount = 1;
foreach (Mesh m in obj.Meshes)
{
foreach (Vertex vert in m.Vertices)
sw.WriteLine($"v {vert.X.ToString().Replace(",", ".")} {vert.Y.ToString().Replace(",", ".")} {vert.Z.ToString().Replace(",", ".")}");
foreach (UV uv in m.UVs)
sw.WriteLine($"vt {uv.U.ToString().Replace(",", ".")} {uv.V.ToString().Replace(",", ".")}");
foreach (Normal norm in m.Normals)
sw.WriteLine($"vn {norm.X.ToString().Replace(",", ".")} {norm.Y.ToString().Replace(",", ".")} {norm.Z.ToString().Replace(",", ".")}");
foreach (VertexColor vertcol in m.VertexColors)
sw.WriteLine($"vc {vertcol.R.ToString().Replace(",", ".")} {vertcol.G.ToString().Replace(",", ".")} {vertcol.B.ToString().Replace(",", ".")}");
Material curMtl = null;
foreach (Face f in m.Faces)
{
if (!ReferenceEquals(curMtl, f.Material))
{
curMtl = f.Material;
sw.WriteLine($"usemtl _{GetIndexOfMaterialInList(obj, curMtl)}");
}
sw.Write("f");
foreach (Point p in f.Points)
{
sw.Write(" ");
sw.Write(curVertCount + m.Vertices.IndexOf(p.Vertex));
sw.Write("/");
if (p.UV is object)
sw.Write(curUVCount + m.UVs.IndexOf(p.UV));
sw.Write("/");
if (p.Normal is object)
sw.Write(curNormCount + m.Normals.IndexOf(p.Normal));
if (m.VertexColors.Count > 0)
{
sw.Write("/");
if (p.VertexColor is object)
sw.Write(curVertColCount + m.VertexColors.IndexOf(p.VertexColor));
}
}
sw.WriteLine();
}
curVertCount += m.Vertices.Count;
curUVCount += m.UVs.Count;
curNormCount += m.Normals.Count;
curVertColCount += m.VertexColors.Count;
}
sw.Flush();
fs.Close();
}
public static int GetIndexOfMaterialInList(Object3D obj, Material matToFind)
{
for (int Index = 0, loopTo = obj.Materials.Count - 1; Index <= loopTo; Index++)
{
if (obj.Materials.ElementAt(Index).Value.Equals(matToFind))
{
return Index;
}
}
return -1;
}
}
public class MaterialLib
{
public Dictionary<string, Material> Materials { get; private set; } = new Dictionary<string, Material>();
private readonly Dictionary<string, Image> LoadedImages = new Dictionary<string, Image>();
public void FromFile(string fileName, bool LoadMaterials)
{
LoadedImages.Clear();
string curMatLibPath = Path.GetDirectoryName(fileName);
Material curMat = null;
string curName = "";
var srMtl = new StreamReader(fileName, System.Text.Encoding.ASCII);
string line = "";
while (!srMtl.EndOfStream)
{
line = srMtl.ReadLine();
switch (true)
{
case object _ when line.StartsWith("newmtl "):
{
curMat = new Material();
curName = line.Substring(7);
Materials.Add(curName, curMat);
break;
}
case object _ when line.ToLower().StartsWith("kd "):
{
var splitColor = line.Substring(3).Split(' ');
var col = Color.FromArgb((int)Convert.ToSingle(Math.Round(255d * Conversions.ToDouble(splitColor[0]))), (int)Convert.ToSingle(Math.Round(255d * Conversions.ToDouble(splitColor[1]))), (int)Convert.ToSingle(Math.Round(255d * Conversions.ToDouble(splitColor[2]))));
curMat.Color = col;
break;
}
case object _ when line.ToLower().StartsWith("d "):
{
curMat.Opacity = Convert.ToSingle(line.Substring(2));
break;
}
case object _ when line.ToLower().StartsWith("tr "):
{
curMat.Opacity = 1f - Convert.ToSingle(line.Substring(2));
break;
}
case object _ when line.ToLower().StartsWith("map_kd "):
{
if (LoadMaterials)
{
string mtlpath = line.Substring(7);
string combipath = Path.Combine(curMatLibPath, line.Substring(7));
string imgfile;
if (File.Exists(combipath))
{
imgfile = combipath;
}
else if (File.Exists(line.Substring(7)))
{
imgfile = mtlpath;
}
else
{
imgfile = "";
}
if (!string.IsNullOrEmpty(imgfile))
{
if (LoadedImages.ContainsKey(imgfile))
{
curMat.Image = LoadedImages[imgfile];
}
else
{
var fs = new FileStream(imgfile, FileMode.Open, FileAccess.Read);
curMat.Image = Image.FromStream(fs);
fs.Close();
bool imgExists = false;
foreach (var kvp in LoadedImages)
{
if (!imgExists && ((Bitmap)kvp.Value).IsTheSameAs((Bitmap)curMat.Image))
{
curMat.Image = kvp.Value;
imgExists = true;
}
}
if (!imgExists)
{
LoadedImages.Add(imgfile, curMat.Image);
}
}
}
}
break;
}
}
}
srMtl.Close();
}
public static void ToFile(string fileName, Object3D obj)
{
var fs = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);
var sw = new StreamWriter(fs, System.Text.Encoding.ASCII);
string imgDirName = Path.GetFileNameWithoutExtension(fileName);
string imgDirFull = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName));
foreach (KeyValuePair<string, Material> kvp in obj.Materials)
{
var mat = kvp.Value;
string name = "_" + ObjFile.GetIndexOfMaterialInList(obj, mat);
sw.WriteLine($"newmtl {name}");
if (mat.Color is object)
{
sw.WriteLine($"kd {(mat.Color.Value.R / 255d).ToString().Replace(",", ".")} {(mat.Color.Value.G / 255d).ToString().Replace(",", ".")} {(mat.Color.Value.B / 255d).ToString().Replace(",", ".")}");
}
if (mat.Opacity is object)
{
sw.WriteLine($"d {mat.Opacity.Value.ToString().Replace(",", ".")}");
}
if (mat.Image is object)
{
string imgFile = name + ".png";
if (!Directory.Exists(imgDirFull))
Directory.CreateDirectory(imgDirFull);
mat.Image.Save(Path.Combine(imgDirFull, imgFile), System.Drawing.Imaging.ImageFormat.Png);
sw.WriteLine($"map_kd {Path.Combine(imgDirName, imgFile)}");
}
}
sw.Flush();
fs.Close();
}
}
}