记录下,使用C#将obj模型转换成ply
完整代码如下
1、这个脚本定义了部分使用到的结构
using System;
using System.Collections.Generic;
namespace ModelProcess
{
class MathUtils
{
public class Vector3
{
public float x, y, z;
public Vector3() { }
public Vector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public static Vector3 Min
{
get
{
return new Vector3(float.MinValue, float.MinValue, float.MinValue);
}
}
public static Vector3 Max
{
get
{
return new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
}
}
public static Vector3 operator +(Vector3 a, Vector3 b)
{
return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
}
public static Vector3 operator -(Vector3 a, Vector3 b)
{
return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
}
public static Vector3 operator *(Vector3 v, float a)
{
return new Vector3(v.x * a, v.y * a, v.z * a);
}
public static Vector3 operator /(Vector3 v, float a)
{
if (a == 0) throw new Exception("除数不能为0!");
return new Vector3(v.x / a, v.y / a, v.z / a);
}
/// <summary>
/// 获取向量长度
/// </summary>
/// <returns></returns>
public float Length()
{
return (float)Math.Sqrt(x * x + y * y + z * z);
}
/// <summary>
/// 向量单位化
/// </summary>
/// <returns></returns>
public Vector3 Normalize()
{
float length = Length();
if (length == 0) return new Vector3(x, y, z);
return new Vector3(x / length, y / length, z / length);
}
/// <summary>
/// 获取两个向量中的更小元素,用于获取包围盒
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Vector3 GetMaxComponent(Vector3 a, Vector3 b)
{
return new Vector3(Math.Max(a.x, b.x), Math.Max(a.y, b.y), Math.Max(a.z, b.z));
}
/// <summary>
/// 获取两个向量中的更大元素,用于获取包围盒
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Vector3 GetMinComponent(Vector3 a, Vector3 b)
{
return new Vector3(Math.Min(a.x, b.x), Math.Min(a.y, b.y), Math.Min(a.z, b.z));
}
/// <summary>
/// 向量叉乘
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Vector3 Cross(Vector3 a, Vector3 b)
{
if (Equals(Dot(a, b), 1.0)) return new Vector3(0, 0, 0);
Vector3 res = new Vector3
{
x = a.y * b.z - a.z * b.y,
y = a.z * b.x - a.x * b.z,
z = a.x * b.y - a.y * b.x
};
return res;
}
public static float Dot(Vector3 a, Vector3 b)
{
return a.x * b.x + a.y * b.y + a.z * b.z;
}
public override string ToString()
{
return "x : " + x + " y : " + y + " z : " + z;
}
}
public class Vector2
{
public float x, y;
public Vector2() { }
public Vector2(float x,float y)
{
this.x = x;
this.y = y;
}
}
public class Triangle
{
// vertex
public int a;
public int b;
public int c;
// normal
public int an;
public int bn;
public int cn;
// uv
public int au;
public int bu;
public int cu;
public bool hasNormal;
public bool hasUv;
}
public class Group
{
public string groupName;
public string mtlName;
public List<Triangle> triangles;
}
public class ColladaMaterial
{
public Vector3 diffuse;
public Vector3 emissive;
public string diffuseMap;
public ColladaMaterial()
{
diffuse = new Vector3(1, 1, 1);
emissive = new Vector3(0, 0, 0);
diffuseMap = "";
}
}
}
}
2、这个脚本定义了将obj转换成ply的函数,调用方式为ExportPlyFromObj.Export(objPath,outputPath),记得引入命名空间
using System;
using System.Collections.Generic;
using System.IO;
using static ModelProcess.MathUtils;
namespace ModelProcess
{
class ExportPlyFromObj
{
/// <summary>
/// 将obj转换成ply格式
/// </summary>
/// <param name="objPath">obj模型的完整路径</param>
/// <param name="outputPath">导出的ply的完整路径,带后缀</param>
public static void Export(string objPath, string outputPath)
{
List<Vector3> vertices = new List<Vector3>();
List<Vector3> normals = new List<Vector3>();
Dictionary<string, Vector3> mtl = new Dictionary<string, Vector3>();
string mtlPath = Utils.GetFirstModelPath(Path.GetDirectoryName(objPath), new string[] { ".mtl" });
if (!string.IsNullOrEmpty(mtlPath))
{
FileStream fs = new FileStream(mtlPath, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs);
string line = "";
Vector3 currentColor = new Vector3();
while (!sr.EndOfStream)
{
line = Utils.GetStrFields(sr.ReadLine()).Trim();
if (line.StartsWith("newmtl "))
{
string name = line.Substring(7);
name = Utils.ReplaceAll(name, " ", "_");
currentColor = new Vector3(1, 1, 1);
if (!mtl.ContainsKey(name))
{
mtl.Add(name, currentColor);
}
}
else
{
if (line.StartsWith("Kd "))
{
string[] strs = line.Split(' ');
currentColor.x = float.Parse(strs[1]);
currentColor.y = float.Parse(strs[2]);
currentColor.z = float.Parse(strs[3]);
}
}
}
sr.Close();
fs.Close();
}
int triangleCount = 0;
{
FileStream fs = new FileStream(objPath, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs);
string line = "";
while (!sr.EndOfStream)
{
line = Utils.GetStrFields(sr.ReadLine()).Trim();
if (line.StartsWith("v "))
{
string[] strs = line.Split(' ');
if (strs.Length >= 4)
{
vertices.Add(new Vector3(float.Parse(strs[1]), float.Parse(strs[2]), float.Parse(strs[3])));
}
}
else if (line.StartsWith("vn "))
{
string[] strs = line.Split(' ');
if (strs.Length >= 4)
{
normals.Add(new Vector3(float.Parse(strs[1]), float.Parse(strs[2]), float.Parse(strs[3])));
}
}
else if (line.StartsWith("f "))
{
string[] strs = line.Split(' ');
triangleCount += strs.Length - 3;
}
}
sr.Close();
fs.Close();
}
{
FileStream outFs = new FileStream(outputPath, FileMode.Create, FileAccess.Write);
StreamWriter sw = new StreamWriter(outFs);
sw.WriteLine("ply");
sw.WriteLine("format ascii 1.0");
sw.WriteLine("element vertex " + triangleCount * 3);
sw.WriteLine("property float x");
sw.WriteLine("property float y");
sw.WriteLine("property float z");
sw.WriteLine("property float nx");
sw.WriteLine("property float ny");
sw.WriteLine("property float nz");
sw.WriteLine("property uchar red");
sw.WriteLine("property uchar green");
sw.WriteLine("property uchar blue");
sw.WriteLine("element face " + triangleCount);
sw.WriteLine("property list uchar int vertex_index");
sw.WriteLine("end_header");
FileStream fs = new FileStream(objPath, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs);
string line = "";
int avi, ani, bvi, bni, cvi, cni;
Vector3 a, b, c, n, an, bn, cn;
Vector3 currenColor = new Vector3(1, 1, 1);
while (!sr.EndOfStream)
{
line = Utils.GetStrFields(sr.ReadLine()).Trim();
if (line.StartsWith("usemtl "))
{
string name = line.Substring(7);
name = Utils.ReplaceAll(name, " ", "_");
if (mtl.TryGetValue(name, out Vector3 v))
{
currenColor = v;
}
}
if (line.StartsWith("f "))
{
string[] strs = line.Split(' ');
string[] first = strs[1].Split('/');
avi = int.Parse(first[0]);
if (avi < 0) avi += vertices.Count;
else avi -= 1;
ani = 0;
if (first.Length >= 3 && !string.IsNullOrEmpty(first[2]))
{
ani = int.Parse(first[2]);
}
for (int k = 2; k < strs.Length - 1; k++)
{
string[] second = strs[k].Split('/');
string[] third = strs[k + 1].Split('/');
bvi = int.Parse(second[0]);
if (bvi < 0) bvi += vertices.Count;
else bvi -= 1;
cvi = int.Parse(third[0]);
if (cvi < 0) cvi += vertices.Count;
else cvi -= 1;
a = vertices[avi];
b = vertices[bvi];
c = vertices[cvi];
if (ani != 0)
{
if (ani < 0) ani += normals.Count;
else ani -= 1;
bni = int.Parse(second[2]);
if (bni < 0) bni += normals.Count;
else bni -= 1;
cni = int.Parse(third[2]);
if (cni < 0) cni += normals.Count;
else cni -= 1;
an = normals[ani];
bn = normals[bni];
cn = normals[cni];
}
else
{
Vector3 ab = b - a;
Vector3 ac = c - a;
n = Vector3.Cross(ab, ac).Normalize();
an = n;
bn = n;
cn = n;
}
sw.WriteLine(a.x + " " + a.y + " " + a.z + " " + an.x + " " + an.y + " " + an.z + " " + (uint)Math.Floor(currenColor.x * 255) + " " + (uint)Math.Floor(currenColor.y * 255) + " " + (uint)Math.Floor(currenColor.z * 255));
sw.WriteLine(b.x + " " + b.y + " " + b.z + " " + bn.x + " " + bn.y + " " + bn.z + " " + (uint)Math.Floor(currenColor.x * 255) + " " + (uint)Math.Floor(currenColor.y * 255) + " " + (uint)Math.Floor(currenColor.z * 255));
sw.WriteLine(c.x + " " + c.y + " " + c.z + " " + cn.x + " " + cn.y + " " + cn.z + " " + (uint)Math.Floor(currenColor.x * 255) + " " + (uint)Math.Floor(currenColor.y * 255) + " " + (uint)Math.Floor(currenColor.z * 255));
}
}
}
sr.Close();
fs.Close();
for (int i = 0; i < triangleCount; i++)
{
sw.WriteLine("3 " + (i * 3) + " " + (i * 3 + 1) + " " + (i * 3 + 2));
}
sw.Close();
outFs.Close();
}
vertices.Clear();
normals.Clear();
mtl.Clear();
}
}
}