/****************************************************************************
* Copyright 2019 Nreal Techonology Limited. All rights reserved.
*
* This file is part of NRSDK.
*
* https://www.nreal.ai/
*
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using UnityEngine;
namespace NRKernal.NRExamples
{
/// A mesh info processor to save unity mesh to obj file.
public class MeshSaver : MonoBehaviour, IMeshInfoProcessor
{
protected string SavePath
{
get
{
string folder;
#if UNITY_EDITOR
folder = Directory.GetCurrentDirectory();
#else
folder = Application.persistentDataPath;
#endif
return Path.Combine(folder, "MeshSave");
}
}
Dictionary m_MeshDict = new Dictionary();
int m_SubFolderIndex = 0;
Thread m_SaveThread;
void Awake()
{
DirectoryInfo directoryInfo = new DirectoryInfo(SavePath);
if (!directoryInfo.Exists)
{
directoryInfo.Create();
}
else
{
while (m_SubFolderIndex < int.MaxValue)
{
string subFolder = Path.Combine(SavePath, m_SubFolderIndex.ToString());
directoryInfo = new DirectoryInfo(subFolder);
if (directoryInfo.Exists)
m_SubFolderIndex++;
else
break;
}
}
}
public void Save()
{
if (m_SaveThread == null)
{
m_SaveThread = new Thread(SaveMeshThread);
m_SaveThread.Start();
}
}
void IMeshInfoProcessor.UpdateMeshInfo(ulong identifier, NRMeshingBlockState meshingBlockState, Mesh mesh)
{
NRDebugger.Debug("[MeshSaver] meshingBlockState: {0} identifier: {1}", meshingBlockState, identifier);
lock (m_MeshDict)
{
m_MeshDict[identifier] = mesh;
}
}
void SaveMeshThread()
{
Dictionary meshDictCopy;
lock (m_MeshDict)
{
meshDictCopy = new Dictionary(m_MeshDict);
}
DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(SavePath, m_SubFolderIndex.ToString()));
if (!directoryInfo.Exists)
{
directoryInfo.Create();
}
foreach (var item in meshDictCopy)
{
FileInfo fileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, item.Key.ToString() + ".obj"));
StreamWriter sw = new StreamWriter(fileInfo.FullName);
string str = MeshToString(item.Value);
sw.Write(str);
sw.Flush();
sw.Close();
}
m_SaveThread = null;
m_SubFolderIndex++;
}
void IMeshInfoProcessor.ClearMeshInfo()
{
NRDebugger.Debug("[MeshSaver] ClearMeshInfo.");
DirectoryInfo directoryInfo = new DirectoryInfo(SavePath);
if (directoryInfo.Exists)
{
foreach (var file in directoryInfo.EnumerateFiles("*.obj"))
{
file.Delete();
}
}
}
private static string MeshToString(Mesh mesh)
{
StringBuilder sb = new StringBuilder();
sb.Append("# \n");
Vector3[] vertices = mesh.vertices;
foreach (Vector3 v in vertices)
{
sb.Append(string.Format("v {0} {1} {2}\n", v.x, v.y, v.z));
}
sb.Append("\n");
Vector3[] normals = mesh.normals;
foreach (Vector3 vn in normals)
{
sb.Append(string.Format("vn {0} {1} {2}\n", vn.x, vn.y, vn.z));
}
sb.Append("\n");
int[] triangles = mesh.triangles;
for (int i = 0; i < triangles.Length; i += 3)
{
sb.Append(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n",
triangles[i] + 1, triangles[i + 1] + 1, triangles[i + 2] + 1));
}
sb.Append("\n");
return sb.ToString();
}
public static Mesh StringToMesh(string data)
{
var lines = data.Split('\n');
List verticeList = new List();
List normalList = new List();
List triangleList = new List();
foreach (var line in lines)
{
var nums = line.Split(' ', '/');
switch (nums[0])
{
case "v":
verticeList.Add(new Vector3(float.Parse(nums[1]), float.Parse(nums[2]), float.Parse(nums[3])));
break;
case "vn":
normalList.Add(new Vector3(float.Parse(nums[1]), float.Parse(nums[2]), float.Parse(nums[3])));
break;
case "f":
triangleList.Add(int.Parse(nums[1]) - 1);
triangleList.Add(int.Parse(nums[4]) - 1);
triangleList.Add(int.Parse(nums[7]) - 1);
break;
default:
break;
}
}
return new Mesh
{
vertices = verticeList.ToArray(),
normals = normalList.ToArray(),
triangles = triangleList.ToArray()
};
}
}
}