194 lines
5.2 KiB
C#
194 lines
5.2 KiB
C#
using System.Text;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace UnityEngine.ProBuilder.Stl
|
|
{
|
|
/// <summary>
|
|
/// Describes the file format of an STL file.
|
|
/// </summary>
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public enum FileType
|
|
{
|
|
/// <summary>Use the [ASCII](https://en.wikipedia.org/wiki/ASCII) file format.</summary>
|
|
Ascii,
|
|
/// <summary>Use the [Binary](https://en.wikipedia.org/wiki/Binary_file) file format.</summary>
|
|
Binary
|
|
};
|
|
|
|
/**
|
|
* Export STL files from Unity mesh assets.
|
|
*/
|
|
static class pb_Stl
|
|
{
|
|
/**
|
|
* Write a mesh file to STL.
|
|
*/
|
|
public static bool WriteFile(string path, Mesh mesh, FileType type = FileType.Ascii, bool convertToRightHandedCoordinates = true)
|
|
{
|
|
return WriteFile(path, new Mesh[] { mesh }, type, convertToRightHandedCoordinates);
|
|
}
|
|
|
|
/**
|
|
* Write a collection of mesh assets to an STL file.
|
|
* No transformations are performed on meshes in this method.
|
|
* Eg, if you want to export a set of a meshes in a transform
|
|
* hierarchy the meshes should be transformed prior to this call.
|
|
*
|
|
* string path - Where to write the file.
|
|
* IList<Mesh> meshes - The mesh assets to write.
|
|
* FileType type - How to format the file (in ASCII or binary).
|
|
*/
|
|
public static bool WriteFile(string path, IList<Mesh> meshes, FileType type = FileType.Ascii, bool convertToRightHandedCoordinates = true)
|
|
{
|
|
try
|
|
{
|
|
switch(type)
|
|
{
|
|
case FileType.Binary:
|
|
{
|
|
// http://paulbourke.net/dataformats/stl/
|
|
// http://www.fabbers.com/tech/STL_Format
|
|
using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create), new ASCIIEncoding()))
|
|
{
|
|
// 80 byte header
|
|
writer.Write(new byte[80]);
|
|
|
|
uint totalTriangleCount = (uint) (meshes.Sum(x => x.triangles.Length) / 3);
|
|
|
|
// unsigned long facet count (4 bytes)
|
|
writer.Write( totalTriangleCount );
|
|
|
|
foreach(Mesh mesh in meshes)
|
|
{
|
|
Vector3[] v = convertToRightHandedCoordinates ? Left2Right(mesh.vertices) : mesh.vertices;
|
|
Vector3[] n = convertToRightHandedCoordinates ? Left2Right(mesh.normals) : mesh.normals;
|
|
int[] t = mesh.triangles;
|
|
int triangleCount = t.Length;
|
|
if(convertToRightHandedCoordinates)
|
|
System.Array.Reverse(t);
|
|
|
|
for(int i = 0; i < triangleCount; i += 3)
|
|
{
|
|
int a = t[i], b = t[i+1], c = t[i+2];
|
|
|
|
Vector3 avg = AvgNrm(n[a], n[b], n[c]);
|
|
|
|
writer.Write(avg.x);
|
|
writer.Write(avg.y);
|
|
writer.Write(avg.z);
|
|
|
|
writer.Write(v[a].x);
|
|
writer.Write(v[a].y);
|
|
writer.Write(v[a].z);
|
|
|
|
writer.Write(v[b].x);
|
|
writer.Write(v[b].y);
|
|
writer.Write(v[b].z);
|
|
|
|
writer.Write(v[c].x);
|
|
writer.Write(v[c].y);
|
|
writer.Write(v[c].z);
|
|
|
|
// specification says attribute byte count should be set to 0.
|
|
writer.Write( (ushort)0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
string model = WriteString(meshes);
|
|
File.WriteAllText(path, model);
|
|
break;
|
|
}
|
|
}
|
|
catch(System.Exception e)
|
|
{
|
|
UnityEngine.Debug.LogError(e.ToString());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Write a Unity mesh to an ASCII STL string.
|
|
*/
|
|
public static string WriteString(Mesh mesh, bool convertToRightHandedCoordinates = true)
|
|
{
|
|
return WriteString(new Mesh[] { mesh }, convertToRightHandedCoordinates);
|
|
}
|
|
|
|
/**
|
|
* Write a set of meshes to an ASCII string in STL format.
|
|
*/
|
|
public static string WriteString(IList<Mesh> meshes, bool convertToRightHandedCoordinates = true)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
string name = meshes.Count == 1 ? meshes[0].name : "Composite Mesh";
|
|
|
|
sb.AppendLine(string.Format("solid {0}", name));
|
|
|
|
foreach(Mesh mesh in meshes)
|
|
{
|
|
Vector3[] v = convertToRightHandedCoordinates ? Left2Right(mesh.vertices) : mesh.vertices;
|
|
Vector3[] n = convertToRightHandedCoordinates ? Left2Right(mesh.normals) : mesh.normals;
|
|
int[] t = mesh.triangles;
|
|
if(convertToRightHandedCoordinates) System.Array.Reverse(t);
|
|
int triLen = t.Length;
|
|
|
|
for(int i = 0; i < triLen; i+=3)
|
|
{
|
|
int a = t[i];
|
|
int b = t[i+1];
|
|
int c = t[i+2];
|
|
|
|
Vector3 nrm = AvgNrm(n[a], n[b], n[c]);
|
|
|
|
sb.AppendLine(string.Format("facet normal {0} {1} {2}", nrm.x, nrm.y, nrm.z));
|
|
|
|
sb.AppendLine("outer loop");
|
|
|
|
sb.AppendLine(string.Format("\tvertex {0} {1} {2}", v[a].x, v[a].y, v[a].z));
|
|
sb.AppendLine(string.Format("\tvertex {0} {1} {2}", v[b].x, v[b].y, v[b].z));
|
|
sb.AppendLine(string.Format("\tvertex {0} {1} {2}", v[c].x, v[c].y, v[c].z));
|
|
|
|
sb.AppendLine("endloop");
|
|
|
|
sb.AppendLine("endfacet");
|
|
}
|
|
}
|
|
|
|
sb.AppendLine(string.Format("endsolid {0}", name));
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static Vector3[] Left2Right(Vector3[] v)
|
|
{
|
|
Vector3[] r = new Vector3[v.Length];
|
|
|
|
for (int i = 0; i < v.Length; i++)
|
|
r[i] = new Vector3(v[i].z, -v[i].x, v[i].y);
|
|
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* Average of 3 vectors.
|
|
*/
|
|
private static Vector3 AvgNrm(Vector3 a, Vector3 b, Vector3 c)
|
|
{
|
|
return new Vector3(
|
|
(a.x + b.x + c.x) / 3f,
|
|
(a.y + b.y + c.y) / 3f,
|
|
(a.z + b.z + c.z) / 3f );
|
|
}
|
|
}
|
|
}
|