476 lines
21 KiB
C#
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();
|
|
}
|
|
}
|
|
} |