背景:客户想在UG中打开编辑好的模型,UG不支持obj格式。mesh转曲面不太现实,折中取了个STL。
mesh转OBJ格式
设置导出模型的零点,例如设置底面中心为导出模型的零点。
float offsetX = 0;
float offsetY = 0;
float offsetZ = 0;
float minX = float.MaxValue;
float maxX = float.MinValue;
float minY = float.MaxValue;
float maxY = float.MinValue;
float minZ = float.MaxValue;
float maxZ = float.MinValue;
foreach (var item in vertices )
{
Vector3 worldPos = trans.TransformPoint(item);
if (minX > worldPos.x) minX = worldPos.x;
if (minY > worldPos.y) minY = worldPos.y;
if (minZ > worldPos.z) minZ = worldPos.z;
if (maxX < worldPos.x) maxX = worldPos.x;
if (maxY < worldPos.y) maxY = worldPos.y;
if (maxZ < worldPos.z) maxZ = worldPos.z;
}
offsetX = (minX + manX) / 2;
offsetZ = (maxZ + minZ) / 2;
offsetY = minY;
校验传入路径名称是否带.obj后缀。
if (!name.Contains(".obj"))
{
name = Path.Combine(name + ".obj");
}
验证是否导出成功
if (!File.Exists(name ))
{
UnityEngine.Debug.LogError("导出失败!");
}
完整代码
public static void Export(Transform trans, Mesh mesh, string name)
{
if (mesh == null) return;
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
StringBuilder sb = new StringBuilder();
float offsetX = 0;
float offsetY = 0;
float offsetZ = 0;
float minX = float.MaxValue;
float maxX = float.MinValue;
float minY = float.MaxValue;
float maxY = float.MinValue;
float minZ = float.MaxValue;
float maxZ = float.MinValue;
foreach (var item in vertices )
{
Vector3 worldPos = trans.TransformPoint(item);
if (minX > worldPos.x) minX = worldPos.x;
if (minY > worldPos.y) minY = worldPos.y;
if (minZ > worldPos.z) minZ = worldPos.z;
if (maxX < worldPos.x) maxX = worldPos.x;
if (maxY < worldPos.y) maxY = worldPos.y;
if (maxZ < worldPos.z) maxZ = worldPos.z;
}
offsetX = minX;
offsetZ = (maxZ + minZ) / 2;
offsetY = (minY + maxY) / 2;
//sb.Append("#为模型导出的obj,不带材质\n\n");
for (int i = 0; i < vertices.Length; i++)
{
Vector3 worldVertices = trans.TransformPoint(vertices[i]);
sb.Append("v " + (worldVertices.x - offsetX) + " " + (worldVertices.y - offsetY) + " " + (worldVertices.z - offsetZ) + "\n");
}
sb.Append("\n");
for (int i = 0; i < normals.Length; i++)
{
//反转法线过后
sb.Append("vn " + -(normals[i].y) + " " + (normals[i].x) + " " + (normals[i].z) + "\n");
}
sb.Append("\n");
for (int i = 0; i < triangles.Length; i += 3)
{
sb.Append("f " + (triangles[i] + 1) + "//" + (triangles[i] + 1) + " " + (triangles[i+1] + 1) + "//" + (triangles[i+1] + 1) + " " + (triangles[i + 2] + 1) + "//" + (triangles[i + 2] + 1) + "\n");
}
//校验导出名称
if (!name.Contains(".obj"))
{
name = Path.Combine(name + ".obj");
}
File.WriteAllText(name, sb.ToString());
if (!File.Exists(name ))
{
UnityEngine.Debug.LogError("导出失败!");
}
}
mesh转STL格式
二进制格式(UG打开正常)
public static void STLExportBinary(Transform trans,, Mesh mesh, string name)
{
if (mesh == null) return;
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
StringBuilder sb = new StringBuilder();
float offsetX = 0;
float offsetY = 0;
float offsetZ = 0;
float minX = float.MaxValue;
float maxX = float.MinValue;
float minY = float.MaxValue;
float maxY = float.MinValue;
float minZ = float.MaxValue;
float maxZ = float.MinValue;
foreach (var item in vertices )
{
Vector3 worldPos = trans.TransformPoint(item);
if (minX > worldPos.x) minX = worldPos.x;
if (minY > worldPos.y) minY = worldPos.y;
if (minZ > worldPos.z) minZ = worldPos.z;
if (maxX < worldPos.x) maxX = worldPos.x;
if (maxY < worldPos.y) maxY = worldPos.y;
if (maxZ < worldPos.z) maxZ = worldPos.z;
}
offsetX = minX;
offsetZ = (maxZ + minZ) / 2;
offsetY = (minY + maxY) / 2;
int trianglesCount = triangles.Length;
//计算出stl的大小
int stlSize = 80 + 4 + (trianglesCount + 360) * (4 * 12 + 2);
byte[] stl = new byte[stlSize];
//创建一个文件流用于将数据写入到本地
MemoryStream ms = new MemoryStream(stl);
BinaryWriter writer = new BinaryWriter(ms);
byte[] header = new byte[80];
short _short = 0;
header[0] = 73;
header[1] = 99;
header[2] = 101;
header[3] = 109;
header[4] = 97;
header[5] = 110;
writer.Write(header);
writer.Write(trianglesCount);
//循环遍历顶点,并将数据写入到文件中
for (int i = 0; i < trianglesCount; i += 3)
{
writer.Write(0.0f);
writer.Write(0.0f);
writer.Write(0.0f);
Vector3 worldPos1 = (vertices[triangles[i]]) ;
Vector3 worldPos2 = (vertices[triangles[i + 2]]) ;
Vector3 worldPos3 = (vertices[triangles[i + 1]]);
writer.Write(worldPos1.x-offsetX );
writer.Write(worldPos1.z-offsetZ );
writer.Write(worldPos1.y-offsetY );
writer.Write(worldPos2.x-offsetX );
writer.Write(worldPos2.z-offsetZ );
writer.Write(worldPos2.y-offsetY );
writer.Write(worldPos3.x-offsetX );
writer.Write(worldPos3.z-offsetZ );
writer.Write(worldPos3.y-offsetY );
writer.Write(_short);
}
writer.Close();
ms.Close();
ms.Dispose();
//将数据保存到本地
File.WriteAllBytes(name , stl);
if (!File.Exists(name))
{
UnityEngine.Debug.LogError("导出失败!");
}
else
{
UnityEngine.Debug.Log("导出完成");
}
ASCII格式(UG打开报错)
基础框架和导出obj一致,区别在于写入了大量的标志信息。
public static void STLExportASCII(Transform trans, Mesh mesh, string name)
{
if (mesh == null) return;
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
StringBuilder sb = new StringBuilder();
float offsetX = 0;
float offsetY = 0;
float offsetZ = 0;
float minX = float.MaxValue;
float maxX = float.MinValue;
float minY = float.MaxValue;
float maxY = float.MinValue;
float minZ = float.MaxValue;
float maxZ = float.MinValue;
foreach (var item in vertices )
{
Vector3 worldPos = trans.TransformPoint(item);
if (minX > worldPos.x) minX = worldPos.x;
if (minY > worldPos.y) minY = worldPos.y;
if (minZ > worldPos.z) minZ = worldPos.z;
if (maxX < worldPos.x) maxX = worldPos.x;
if (maxY < worldPos.y) maxY = worldPos.y;
if (maxZ < worldPos.z) maxZ = worldPos.z;
}
offsetX = minX;
offsetZ = (maxZ + minZ) / 2;
offsetY = (minY + maxY) / 2;
sb.Append("\nsolid " + "导出工件" + "\n");
for (int i = 0; i < triangles.Length / 3; i++)
{
try
{
Vector3 curPos1 = vertices[triangles[i * 3]];
Vector3 curPos2 = vertices[triangles[i * 3 + 1]];
Vector3 curPos3 = vertices[triangles[i * 3 + 2]];
Vector3 nor1 = trans.TransformDirection(normals[triangles[i * 3]]);
Vector3 nor2 = trans.TransformDirection(normals[triangles[i * 3 + 1]]);
Vector3 nor3 = trans.TransformDirection(normals[triangles[i * 3 + 2]]);
//顶点变换到世界空间
Vector3 worldPos1 = trans.TransformPoint(curPos1);
Vector3 worldPos2 = trans.TransformPoint(curPos2);
Vector3 worldPos3 = trans.TransformPoint(curPos3);
Vector3 normal = (nor1 + nor2 + nor3) / 3;
sb.Append("facet normal " + normal.x + " " + normal.y + " " + normal.z);
sb.Append("outer loop\n");
sb.Append("\tvertex " + worldPos1.x-offsetX + " " + worldPos1.y-offsetY + " " + worldPos1.z-offsetZ + "\n");
sb.Append("\tvertex " + worldPos2.x-offsetX + " " + worldPos2.y-offsetY + " " + worldPos2.z-offsetZ + "\n");
sb.Append("\tvertex " + worldPos3.x -offsetX + " " + worldPos3.y-offsetY + " " + worldPos3.z-offsetZ + "\n");
sb.Append("\tendloop\n");
sb.Append("\tendfacet\n");
}
catch (Exception e)
{
UnityEngine.Debug.Log("缺少顶点重用信息,索引为:" + triangles[i * 3] + "\n" + e);
}
}
sb.Append("endsolid " + name);
File.WriteAllText(name, sb.ToString());
if (!File.Exists(name))
{
UnityEngine.Debug.LogError("导出失败!");
}
else
{
UnityEngine.Debug.Log("导出完成");
}
调用win窗口
调用dll及基础参数定义,直接拷贝就行。
public class OpenFileName
{
public int structSize = 0;
public IntPtr dlgOwner = IntPtr.Zero;
public IntPtr instance = IntPtr.Zero;
public String filter = null;
public String customFilter = null;
public int maxCustFilter = 0;
public int filterIndex = 0;
public String file = null;
public int maxFile = 0;
public String fileTitle = null;
public int maxFileTitle = 0;
public String initialDir = null;
public String title = null;
public int flags = 0;
public short fileOffset = 0;
public short fileExtension = 0;
public String defExt = null;
public IntPtr custData = IntPtr.Zero;
public IntPtr hook = IntPtr.Zero;
public String templateName = null;
public IntPtr reservedPtr = IntPtr.Zero;
public int reservedInt = 0;
public int flagsEx = 0;
}
///2.系统函数调用类, 如下:
public class LocalDialog
{
//链接指定系统函数 打开文件对话框
[DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
public static extern bool GetOpenFileName([In, Out] OpenFileName ofn);
public static bool GetOFN([In, Out] OpenFileName ofn)
{
return GetOpenFileName(ofn);
}
//链接指定系统函数 另存为对话框
[DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
public static extern bool GetSaveFileName([In, Out] OpenFileName ofn);
public static bool GetSFN([In, Out] OpenFileName ofn)
{
return GetSaveFileName(ofn);
}
}
导入
导入参考图
/// <summary>
/// 选择导入路径
/// </summary>
public void InputFile()
{
file = "";
OpenFileName openFileName = new OpenFileName();
openFileName.structSize = Marshal.SizeOf(openFileName);
openFileName.filter = "模型文件(*.obj)\0*.obj";
openFileName.file = new string(new char[256]);
openFileName.maxFile = openFileName.file.Length;
openFileName.fileTitle = new string(new char[64]);
openFileName.maxFileTitle = openFileName.fileTitle.Length;
openFileName.initialDir = Application.streamingAssetsPath.Replace('/', '\\');//默认路径
openFileName.title = "导入";
openFileName.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000008;
if (LocalDialog.GetOpenFileName(openFileName))
{
file = openFileName.file;
}
}
导出
设置格式 \0*.obj\0 和 \0*.stl\0,这种形式是分开显示格式,下图所示; \0*.obj *.stl\0 这种显示在一起,导入参考图所示。
文章来源:https://www.toymoban.com/news/detail-648992.html
/// <summary>
/// 选择导出路径
/// </summary>
void OutputFile()
{
if (Manager3.instance.cylinder==null )
{
textTips.text = "未加载工件!";
return;
}
file = "";
OpenFileName openFileName = new OpenFileName();
openFileName.structSize = Marshal.SizeOf(openFileName);
openFileName.filter = "模型文件(*.obj)\0*.obj\0模型文件(*.stl)\0*.stl\0";
openFileName.file = new string(new char[256]);
openFileName.maxFile = openFileName.file.Length;
openFileName.fileTitle = new string(new char[64]);
openFileName.maxFileTitle = openFileName.fileTitle.Length;
openFileName.initialDir = Application.streamingAssetsPath.Replace('/', '\\');//默认路径
openFileName.title = "导出";
openFileName.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000008;
openFileName.defExt = ".obj";
if (LocalDialog.GetSaveFileName(openFileName))
{
file = openFileName.file;
}
}
注意事项
1.不建议在编辑器中频繁调用win。可能引起Library错误,导致资源全部丢失,需要删除Library,重新加载场景。
2. openFileName.defExt = “.obj”; 这个重点,选择格式后,自动在输入框中追加后缀。不设置defExt 值时,需要自己追加。
文章来源地址https://www.toymoban.com/news/detail-648992.html
到了这里,关于mesh导出OBJ、STL格式和win窗口调用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!