using UnityEngine;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using UnityEngine.ProBuilder;
namespace UnityEditor.ProBuilder
{
///
/// Export options for Ply format.
///
sealed class PlyOptions
{
// Should the mesh be exported with a right handed coordinate system?
public bool isRightHanded = true;
// Should n-gon faces be allowed?
public bool ngons = false;
// Should quad faces be allowed?
public bool quads = true;
// Should object transforms be applied to mesh attributes before writing to PLY?
public bool applyTransforms = true;
}
///
/// Import and export of Ply files in Unity.
///
static class PlyExporter
{
///
/// Export a ply file.
///
///
///
///
///
public static bool Export(IEnumerable models, out string contents, PlyOptions options = null)
{
if (options == null)
options = new PlyOptions();
foreach (ProBuilderMesh pb in models)
{
pb.ToMesh();
pb.Refresh();
}
int modelCount = models.Count();
Vector3[] positions = models.SelectMany(x => x.positionsInternal).ToArray();
Vector3[] normals = models.SelectMany(x => x.GetNormals()).ToArray();
Color[] colors = models.SelectMany(x => x.GetColors()).ToArray();
List faces = new List(modelCount);
int vertexOffset = 0;
foreach (ProBuilderMesh pb in models)
{
List indexes = null;
if (options.ngons)
{
indexes = pb.facesInternal.Select(y => options.quads ? (y.IsQuad() ? y.ToQuad() : y.indexesInternal) : y.indexesInternal).ToList();
}
else
{
indexes = new List();
foreach (Face face in pb.facesInternal)
{
if (options.quads && face.IsQuad())
{
indexes.Add(face.ToQuad());
continue;
}
for (int i = 0; i < face.indexesInternal.Length; i += 3)
indexes.Add(new int[] {
face.indexesInternal[i + 0],
face.indexesInternal[i + 1],
face.indexesInternal[i + 2]
});
}
}
foreach (int[] face in indexes)
for (int y = 0; y < face.Length; y++)
face[y] += vertexOffset;
vertexOffset += pb.vertexCount;
if (options.applyTransforms)
{
Transform trs = pb.transform;
for (int i = 0; positions != null && i < positions.Length; i++)
positions[i] = trs.TransformPoint(positions[i]);
for (int i = 0; normals != null && i < normals.Length; i++)
normals[i] = trs.TransformDirection(normals[i]);
}
faces.AddRange(indexes);
}
bool res = Export(positions, faces.ToArray(), out contents, normals, colors, options.isRightHanded);
foreach (ProBuilderMesh pb in models)
pb.Optimize();
return res;
}
///
/// Create the contents of an ASCII formatted PLY file.
///
///
///
///
///
///
///
///
public static bool Export(
Vector3[] positions,
int[][] faces, out string contents,
Vector3[] normals = null,
Color[] colors = null,
bool flipHandedness = true)
{
int faceCount = faces != null ? faces.Length : 0;
int vertexCount = positions != null ? positions.Length : 0;
if (vertexCount < 1 || faceCount < 1)
{
contents = null;
return false;
}
bool hasNormals = normals != null && normals.Length == vertexCount;
bool hasColors = colors != null && colors.Length == vertexCount;
StringBuilder sb = new StringBuilder();
WriteHeader(vertexCount, faceCount, hasNormals, hasColors, ref sb);
for (int i = 0; i < vertexCount; i++)
{
sb.Append(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", flipHandedness ? -positions[i].x : positions[i].x, positions[i].y, positions[i].z));
if (hasNormals)
sb.Append(string.Format(CultureInfo.InvariantCulture, " {0} {1} {2}", flipHandedness ? -normals[i].x : -normals[i].x, normals[i].y, normals[i].z));
if (hasColors)
sb.Append(string.Format(CultureInfo.InvariantCulture, " {0} {1} {2} {3}",
System.Math.Min(System.Math.Max(0, (int)(colors[i].r * 255)), 255),
System.Math.Min(System.Math.Max(0, (int)(colors[i].g * 255)), 255),
System.Math.Min(System.Math.Max(0, (int)(colors[i].b * 255)), 255),
System.Math.Min(System.Math.Max(0, (int)(colors[i].a * 255)), 255)));
sb.AppendLine();
}
for (int i = 0; i < faceCount; i++)
{
int faceLength = faces[i] != null ? faces[i].Length : 0;
sb.Append(faceLength.ToString(CultureInfo.InvariantCulture));
for (int n = 0; n < faceLength; n++)
sb.Append(string.Format(CultureInfo.InvariantCulture, " {0}", faces[i][flipHandedness ? faceLength - n - 1 : n]));
sb.AppendLine();
}
contents = sb.ToString();
return true;
}
static void WriteHeader(int vertexCount, int faceCount, bool hasNormals, bool hasColors, ref StringBuilder sb)
{
sb.AppendLine("ply");
sb.AppendLine("format ascii 1.0");
sb.AppendLine("comment Exported by [ProBuilder](http://www.procore3d.com/probuilder)");
sb.AppendLine("comment " + System.DateTime.Now);
sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "element vertex {0}", vertexCount));
sb.AppendLine("property float32 x");
sb.AppendLine("property float32 y");
sb.AppendLine("property float32 z");
if (hasNormals)
{
sb.AppendLine("property float32 nx");
sb.AppendLine("property float32 ny");
sb.AppendLine("property float32 nz");
}
if (hasColors)
{
sb.AppendLine("property uint8 red");
sb.AppendLine("property uint8 green");
sb.AppendLine("property uint8 blue");
sb.AppendLine("property uint8 alpha");
}
sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "element face {0}", faceCount));
sb.AppendLine("property list uint8 int32 vertex_index");
sb.AppendLine("end_header");
}
}
}