You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			985 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			985 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
| namespace MagicaVoxelToolbox {
 | |
| 	using UnityEngine;
 | |
| 	using UnityEditor;
 | |
| 	using System.Collections;
 | |
| 	using System.Collections.Generic;
 | |
| 	using System.IO;
 | |
| 	using System.Text;
 | |
| 
 | |
| 	public struct Util {
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- File ---
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string Read (string path) {
 | |
| 			path = FixPath(path, false);
 | |
| 			StreamReader sr = File.OpenText(path);
 | |
| 			string data = sr.ReadToEnd();
 | |
| 			sr.Close();
 | |
| 			return data;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void Write (string data, string path) {
 | |
| 			path = FixPath(path, false);
 | |
| 			FileStream fs = new FileStream(path, FileMode.Create);
 | |
| 			StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
 | |
| 			sw.Write(data);
 | |
| 			sw.Close();
 | |
| 			fs.Close();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void CreateFolder (string path) {
 | |
| 			if (!string.IsNullOrEmpty(path) && !DirectoryExists(path)) {
 | |
| 				string pPath = GetParentPath(path);
 | |
| 				if (!DirectoryExists(pPath)) {
 | |
| 					CreateFolder(pPath);
 | |
| 				}
 | |
| 				path = FixPath(path, false);
 | |
| 				Directory.CreateDirectory(path);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static byte[] FileToByte (string path) {
 | |
| 			byte[] bytes = null;
 | |
| 			if (FileExists(path)) {
 | |
| 				path = FixPath(path, false);
 | |
| 				bytes = File.ReadAllBytes(path);
 | |
| 			}
 | |
| 			return bytes;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void ByteToFile (byte[] bytes, string path) {
 | |
| 			string parentPath = GetParentPath(path);
 | |
| 			CreateFolder(parentPath);
 | |
| 			path = FixPath(path, false);
 | |
| 			FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
 | |
| 			fs.Write(bytes, 0, bytes.Length);
 | |
| 			fs.Close();
 | |
| 			fs.Dispose();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool HasFileIn (string path, params string[] searchPattern) {
 | |
| 			if (PathIsDirectory(path)) {
 | |
| 				for (int i = 0; i < searchPattern.Length; i++) {
 | |
| 					path = FixPath(path, false);
 | |
| 					if (new DirectoryInfo(path).GetFiles(searchPattern[i], SearchOption.AllDirectories).Length > 0) {
 | |
| 						return true;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static FileInfo[] GetFilesIn (string path, params string[] searchPattern) {
 | |
| 			List<FileInfo> allFiles = new List<FileInfo>();
 | |
| 			path = FixPath(path, false);
 | |
| 			if (PathIsDirectory(path)) {
 | |
| 				if (searchPattern.Length > 0) {
 | |
| 					allFiles.AddRange(new DirectoryInfo(path).GetFiles("*.*", SearchOption.AllDirectories));
 | |
| 				} else {
 | |
| 					for (int i = 0; i < searchPattern.Length; i++) {
 | |
| 						allFiles.AddRange(new DirectoryInfo(path).GetFiles(searchPattern[i], SearchOption.AllDirectories));
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			return allFiles.ToArray();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void DeleteFile (string path) {
 | |
| 			if (FileExists(path)) {
 | |
| 				path = FixPath(path, false);
 | |
| 				File.Delete(path);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- Path ---
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private const string ROOT_NAME = "MagicaVoxel Toolbox";
 | |
| 
 | |
| 
 | |
| 		public static string GetRootPath (ScriptableObject scriptObj) {
 | |
| 			var rootPath = "";
 | |
| 			var script = MonoScript.FromScriptableObject(scriptObj);
 | |
| 			if (script) {
 | |
| 				var path = AssetDatabase.GetAssetPath(script);
 | |
| 				string rootName = ROOT_NAME;
 | |
| 				if (!string.IsNullOrEmpty(path)) {
 | |
| 					int index = path.LastIndexOf(rootName);
 | |
| 					if (index >= 0) {
 | |
| 						rootPath = path.Substring(0, index + rootName.Length);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			return rootPath;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string FixPath (string path, bool forUnity = true) {
 | |
| 			char dsChar = forUnity ? '/' : Path.DirectorySeparatorChar;
 | |
| 			char adsChar = forUnity ? '\\' : Path.AltDirectorySeparatorChar;
 | |
| 			path = path.Replace(adsChar, dsChar);
 | |
| 			path = path.Replace(new string(dsChar, 2), dsChar.ToString());
 | |
| 			while (path.Length > 0 && path[0] == dsChar) {
 | |
| 				path = path.Remove(0, 1);
 | |
| 			}
 | |
| 			while (path.Length > 0 && path[path.Length - 1] == dsChar) {
 | |
| 				path = path.Remove(path.Length - 1, 1);
 | |
| 			}
 | |
| 			return path;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string GetParentPath (string path) {
 | |
| 			path = FixPath(path, false);
 | |
| 			return FixedRelativePath(Directory.GetParent(path).FullName);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string FixedRelativePath (string path) {
 | |
| 			path = FixPath(path);
 | |
| 			if (path.StartsWith("Assets")) {
 | |
| 				return path;
 | |
| 			}
 | |
| 			var fixedDataPath = FixPath(Application.dataPath);
 | |
| 			if (path.StartsWith(fixedDataPath)) {
 | |
| 				return "Assets" + path.Substring(fixedDataPath.Length);
 | |
| 			} else {
 | |
| 				return "";
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string GetFullPath (string path) {
 | |
| 			path = FixPath(path, false);
 | |
| 			return new FileInfo(path).FullName;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string CombinePaths (params string[] paths) {
 | |
| 			string path = "";
 | |
| 			for (int i = 0; i < paths.Length; i++) {
 | |
| 				path = Path.Combine(path, paths[i]);
 | |
| 			}
 | |
| 			return path;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string GetExtension (string path) {
 | |
| 			return Path.GetExtension(path);//.txt
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string GetNameWithoutExtension (string path) {
 | |
| 			return Path.GetFileNameWithoutExtension(path);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static string GetNameWithExtension (string path) {
 | |
| 			return Path.GetFileName(path);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static string ChangeExtension (string path, string newEx) {
 | |
| 			return Path.ChangeExtension(path, newEx);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool DirectoryExists (string path) {
 | |
| 			path = FixPath(path, false);
 | |
| 			return Directory.Exists(path);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool FileExists (string path) {
 | |
| 			path = FixPath(path, false);
 | |
| 			return File.Exists(path);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool PathIsDirectory (string path) {
 | |
| 			if (!DirectoryExists(path)) { return false; }
 | |
| 			path = FixPath(path, false);
 | |
| 			FileAttributes attr = File.GetAttributes(path);
 | |
| 			if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
 | |
| 				return true;
 | |
| 			else
 | |
| 				return false;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool IsChildPath (string pathA, string pathB) {
 | |
| 			if (pathA.Length == pathB.Length) {
 | |
| 				return pathA == pathB;
 | |
| 			} else if (pathA.Length > pathB.Length) {
 | |
| 				return IsChildPathCompair(pathA, pathB);
 | |
| 			} else {
 | |
| 				return IsChildPathCompair(pathB, pathA);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool IsChildPathCompair (string longPath, string path) {
 | |
| 			if (longPath.Length <= path.Length || !PathIsDirectory(path) || !longPath.StartsWith(path)) {
 | |
| 				return false;
 | |
| 			}
 | |
| 			char c = longPath[path.Length];
 | |
| 			if (c != Path.DirectorySeparatorChar && c != Path.AltDirectorySeparatorChar) {
 | |
| 				return false;
 | |
| 			}
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- Message ---
 | |
| 
 | |
| 
 | |
| 		public static bool Dialog (string title, string msg, string ok, string cancel = "") {
 | |
| 			//EditorApplication.Beep();
 | |
| 			PauseWatch();
 | |
| 			if (string.IsNullOrEmpty(cancel)) {
 | |
| 				bool sure = EditorUtility.DisplayDialog(title, msg, ok);
 | |
| 				RestartWatch();
 | |
| 				return sure;
 | |
| 			} else {
 | |
| 				bool sure = EditorUtility.DisplayDialog(title, msg, ok, cancel);
 | |
| 				RestartWatch();
 | |
| 				return sure;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static int DialogComplex (string title, string msg, string ok, string cancel, string alt) {
 | |
| 			//EditorApplication.Beep();
 | |
| 			PauseWatch();
 | |
| 			int index = EditorUtility.DisplayDialogComplex(title, msg, ok, cancel, alt);
 | |
| 			RestartWatch();
 | |
| 			return index;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static void ProgressBar (string title, string msg, float value) {
 | |
| 			value = Mathf.Clamp01(value);
 | |
| 			EditorUtility.DisplayProgressBar(title, msg, value);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static void ClearProgressBar () {
 | |
| 			EditorUtility.ClearProgressBar();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- Watch ---
 | |
| 
 | |
| 
 | |
| 		private static System.Diagnostics.Stopwatch TheWatch;
 | |
| 
 | |
| 
 | |
| 		public static void StartWatch () {
 | |
| 			TheWatch = new System.Diagnostics.Stopwatch();
 | |
| 			TheWatch.Start();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static void PauseWatch () {
 | |
| 			if (TheWatch != null) {
 | |
| 				TheWatch.Stop();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static void RestartWatch () {
 | |
| 			if (TheWatch != null) {
 | |
| 				TheWatch.Start();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static double StopWatchAndGetTime () {
 | |
| 			if (TheWatch != null) {
 | |
| 				TheWatch.Stop();
 | |
| 				return TheWatch.Elapsed.TotalSeconds;
 | |
| 			}
 | |
| 			return 0f;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- Misc ---
 | |
| 
 | |
| 
 | |
| 		public static bool IsTypingInGUI () {
 | |
| 			return GUIUtility.keyboardControl != 0;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool NoFuncKeyPressing () {
 | |
| 			return !Event.current.alt && !Event.current.control && !Event.current.shift;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static Mesh CreateConeMesh (float radius, float height, int subdivisions = 12) {
 | |
| 			Mesh mesh = new Mesh();
 | |
| 
 | |
| 			Vector3[] vertices = new Vector3[subdivisions + 2];
 | |
| 			Vector2[] uv = new Vector2[vertices.Length];
 | |
| 			int[] triangles = new int[(subdivisions * 2) * 3];
 | |
| 
 | |
| 			vertices[0] = Vector3.zero;
 | |
| 			uv[0] = new Vector2(0.5f, 0f);
 | |
| 			for (int i = 0, n = subdivisions - 1; i < subdivisions; i++) {
 | |
| 				float ratio = (float)i / n;
 | |
| 				float r = ratio * (Mathf.PI * 2f);
 | |
| 				float x = Mathf.Cos(r) * radius;
 | |
| 				float z = Mathf.Sin(r) * radius;
 | |
| 				vertices[i + 1] = new Vector3(x, 0f, z);
 | |
| 				uv[i + 1] = new Vector2(ratio, 0f);
 | |
| 			}
 | |
| 			vertices[subdivisions + 1] = new Vector3(0f, height, 0f);
 | |
| 			uv[subdivisions + 1] = new Vector2(0.5f, 1f);
 | |
| 
 | |
| 			// construct bottom
 | |
| 
 | |
| 			for (int i = 0, n = subdivisions - 1; i < n; i++) {
 | |
| 				int offset = i * 3;
 | |
| 				triangles[offset] = 0;
 | |
| 				triangles[offset + 1] = i + 1;
 | |
| 				triangles[offset + 2] = i + 2;
 | |
| 			}
 | |
| 
 | |
| 			// construct sides
 | |
| 
 | |
| 			int bottomOffset = subdivisions * 3;
 | |
| 			for (int i = 0, n = subdivisions - 1; i < n; i++) {
 | |
| 				int offset = i * 3 + bottomOffset;
 | |
| 				triangles[offset] = i + 1;
 | |
| 				triangles[offset + 1] = subdivisions + 1;
 | |
| 				triangles[offset + 2] = i + 2;
 | |
| 			}
 | |
| 
 | |
| 			mesh.vertices = vertices;
 | |
| 			mesh.uv = uv;
 | |
| 			mesh.triangles = triangles;
 | |
| 			mesh.RecalculateBounds();
 | |
| 			mesh.RecalculateNormals();
 | |
| 
 | |
| 			return mesh;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static Mesh CreateSectorMesh (float radiusA, float radiusB, float height, float angle, int subDivisions = 12) {
 | |
| 
 | |
| 			subDivisions = Mathf.Clamp(subDivisions, 2, 128);
 | |
| 			angle = Mathf.Repeat(angle, 360f);
 | |
| 
 | |
| 			Mesh mesh = new Mesh();
 | |
| 
 | |
| 			int triLen = (8 * (subDivisions - 1) + 4) * 3;
 | |
| 			int vLen = subDivisions * 4;
 | |
| 			var vs = new Vector3[vLen];
 | |
| 			var uv = new Vector2[vLen];
 | |
| 			var tris = new int[triLen];
 | |
| 
 | |
| 			float currentAngle = -angle / 2f;
 | |
| 			for (int i = 0; i < subDivisions; i++, currentAngle += angle / (subDivisions - 1)) {
 | |
| 
 | |
| 				float rAngle = currentAngle * Mathf.Deg2Rad;
 | |
| 
 | |
| 				vs[i * 4 + 0] = new Vector3(Mathf.Sin(rAngle) * radiusA, -height * 0.5f, Mathf.Cos(rAngle) * radiusA);
 | |
| 				vs[i * 4 + 1] = new Vector3(Mathf.Sin(rAngle) * radiusB, -height * 0.5f, Mathf.Cos(rAngle) * radiusB);
 | |
| 				vs[i * 4 + 2] = new Vector3(Mathf.Sin(rAngle) * radiusA, height * 0.5f, Mathf.Cos(rAngle) * radiusA);
 | |
| 				vs[i * 4 + 3] = new Vector3(Mathf.Sin(rAngle) * radiusB, height * 0.5f, Mathf.Cos(rAngle) * radiusB);
 | |
| 
 | |
| 				uv[i * 4 + 0] = new Vector2(0, 0);
 | |
| 				uv[i * 4 + 1] = new Vector2(0, 1);
 | |
| 				uv[i * 4 + 2] = new Vector2(1, 0);
 | |
| 				uv[i * 4 + 3] = new Vector2(1, 1);
 | |
| 
 | |
| 				if (i < subDivisions - 1) {
 | |
| 
 | |
| 					tris[i * 24 + 0] = i * 4 + 2;
 | |
| 					tris[i * 24 + 1] = i * 4 + 3;
 | |
| 					tris[i * 24 + 2] = i * 4 + 7;
 | |
| 
 | |
| 					tris[i * 24 + 3] = i * 4 + 2;
 | |
| 					tris[i * 24 + 4] = i * 4 + 7;
 | |
| 					tris[i * 24 + 5] = i * 4 + 6;
 | |
| 
 | |
| 
 | |
| 					tris[i * 24 + 6] = i * 4 + 0;
 | |
| 					tris[i * 24 + 7] = i * 4 + 2;
 | |
| 					tris[i * 24 + 8] = i * 4 + 6;
 | |
| 
 | |
| 					tris[i * 24 + 9] = i * 4 + 0;
 | |
| 					tris[i * 24 + 10] = i * 4 + 6;
 | |
| 					tris[i * 24 + 11] = i * 4 + 4;
 | |
| 
 | |
| 
 | |
| 					tris[i * 24 + 12] = i * 4 + 0;
 | |
| 					tris[i * 24 + 13] = i * 4 + 5;
 | |
| 					tris[i * 24 + 14] = i * 4 + 1;
 | |
| 
 | |
| 					tris[i * 24 + 15] = i * 4 + 0;
 | |
| 					tris[i * 24 + 16] = i * 4 + 4;
 | |
| 					tris[i * 24 + 17] = i * 4 + 5;
 | |
| 
 | |
| 
 | |
| 					tris[i * 24 + 18] = i * 4 + 1;
 | |
| 					tris[i * 24 + 19] = i * 4 + 7;
 | |
| 					tris[i * 24 + 20] = i * 4 + 3;
 | |
| 
 | |
| 					tris[i * 24 + 21] = i * 4 + 1;
 | |
| 					tris[i * 24 + 22] = i * 4 + 5;
 | |
| 					tris[i * 24 + 23] = i * 4 + 7;
 | |
| 				}
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			tris[triLen - 12] = 0;
 | |
| 			tris[triLen - 11] = 1;
 | |
| 			tris[triLen - 10] = 3;
 | |
| 
 | |
| 			tris[triLen - 9] = 0;
 | |
| 			tris[triLen - 8] = 3;
 | |
| 			tris[triLen - 7] = 2;
 | |
| 
 | |
| 			tris[triLen - 6] = vLen - 4;
 | |
| 			tris[triLen - 5] = vLen - 2;
 | |
| 			tris[triLen - 4] = vLen - 1;
 | |
| 
 | |
| 			tris[triLen - 3] = vLen - 4;
 | |
| 			tris[triLen - 2] = vLen - 1;
 | |
| 			tris[triLen - 1] = vLen - 3;
 | |
| 
 | |
| 			mesh.vertices = vs;
 | |
| 			mesh.triangles = tris;
 | |
| 			mesh.RecalculateBounds();
 | |
| 			mesh.RecalculateNormals();
 | |
| 
 | |
| 			return mesh;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool InRange (int x, int y, int z, int sizeX, int sizeY, int sizeZ) {
 | |
| 			return x >= 0 && x < sizeX && y >= 0 && y < sizeY && z >= 0 && z < sizeZ;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static Vector2 VectorAbs (Vector2 v) {
 | |
| 			v.x = Mathf.Abs(v.x);
 | |
| 			v.y = Mathf.Abs(v.y);
 | |
| 			return v;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static Vector3 VectorAbs (Vector3 v) {
 | |
| 			v.x = Mathf.Abs(v.x);
 | |
| 			v.y = Mathf.Abs(v.y);
 | |
| 			v.z = Mathf.Abs(v.z);
 | |
| 			return v;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static Vector3 SwipYZ (Vector3 v) {
 | |
| 			float tempZ = v.z;
 | |
| 			v.z = v.y;
 | |
| 			v.y = tempZ;
 | |
| 			return v;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static float Remap (float l, float r, float newL, float newR, float t) {
 | |
| 			return l == r ? 0 : Mathf.LerpUnclamped(
 | |
| 				newL, newR,
 | |
| 				(t - l) / (r - l)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static int MaxAxis (Vector3 v) {
 | |
| 			if (Mathf.Abs(v.x) >= Mathf.Abs(v.y)) {
 | |
| 				return Mathf.Abs(v.x) >= Mathf.Abs(v.z) ? 0 : 2;
 | |
| 			} else {
 | |
| 				return Mathf.Abs(v.y) >= Mathf.Abs(v.z) ? 1 : 2;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static void CopyToClipboard (string containt) {
 | |
| 			TextEditor te = new TextEditor { text = containt };
 | |
| 			te.SelectAll();
 | |
| 			te.Copy();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string GetObj (Mesh m) {
 | |
| 
 | |
| 			StringBuilder sb = new StringBuilder();
 | |
| 
 | |
| 			sb.Append("g ").Append(m.name).Append("\n");
 | |
| 			foreach (Vector3 v in m.vertices) {
 | |
| 				sb.Append(string.Format("v {0} {1} {2}\n", v.x, v.y, v.z));
 | |
| 			}
 | |
| 			sb.Append("\n");
 | |
| 			foreach (Vector3 v in m.normals) {
 | |
| 				sb.Append(string.Format("vn {0} {1} {2}\n", v.x, v.y, v.z));
 | |
| 			}
 | |
| 			sb.Append("\n");
 | |
| 			foreach (Vector3 v in m.uv) {
 | |
| 				sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
 | |
| 			}
 | |
| 
 | |
| 			sb.Append("\n");
 | |
| 			sb.Append("usemtl ").Append(m.name).Append("\n");
 | |
| 			sb.Append("usemap ").Append(m.name).Append("\n");
 | |
| 
 | |
| 			int[] triangles = m.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)
 | |
| 				);
 | |
| 			}
 | |
| 
 | |
| 			return sb.ToString();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static Texture2D RenderTextureToTexture2D (Camera renderCamera) {
 | |
| 			var rTex = renderCamera.targetTexture;
 | |
| 			if (!rTex) { return null; }
 | |
| 			RenderTexture.active = rTex;
 | |
| 			Texture2D texture = new Texture2D(rTex.width, rTex.height, TextureFormat.ARGB32, false, false) {
 | |
| 				filterMode = FilterMode.Bilinear
 | |
| 			};
 | |
| 			var oldColor = renderCamera.backgroundColor;
 | |
| 			renderCamera.backgroundColor = Color.clear;
 | |
| 			renderCamera.Render();
 | |
| 			texture.ReadPixels(new Rect(0, 0, rTex.width, rTex.height), 0, 0, false);
 | |
| 			texture.Apply();
 | |
| 			renderCamera.backgroundColor = oldColor;
 | |
| 			RenderTexture.active = null;
 | |
| 			return texture;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static Texture2D TrimTexture (Texture2D texture, float alpha = 0.01f, int gap = 0) {
 | |
| 			int width = texture.width;
 | |
| 			int height = texture.height;
 | |
| 			var colors = texture.GetPixels();
 | |
| 			int minX = int.MaxValue;
 | |
| 			int minY = int.MaxValue;
 | |
| 			int maxX = int.MinValue;
 | |
| 			int maxY = int.MinValue;
 | |
| 
 | |
| 			for (int y = 0; y < height; y++) {
 | |
| 				for (int x = 0; x < width; x++) {
 | |
| 					var c = colors[y * width + x];
 | |
| 					if (c.a > alpha) {
 | |
| 						minX = Mathf.Min(minX, x);
 | |
| 						minY = Mathf.Min(minY, y);
 | |
| 						maxX = Mathf.Max(maxX, x);
 | |
| 						maxY = Mathf.Max(maxY, y);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// Gap
 | |
| 			minX = Mathf.Clamp(minX - gap, 0, width - 1);
 | |
| 			minY = Mathf.Clamp(minY - gap, 0, height - 1);
 | |
| 			maxX = Mathf.Clamp(maxX + gap, 0, width - 1);
 | |
| 			maxY = Mathf.Clamp(maxY + gap, 0, height - 1);
 | |
| 
 | |
| 			int newWidth = maxX - minX + 1;
 | |
| 			int newHeight = maxY - minY + 1;
 | |
| 			if (newWidth != width || newHeight != height) {
 | |
| 				texture.Resize(newWidth, newHeight);
 | |
| 				var newColors = new Color[newWidth * newHeight];
 | |
| 				for (int y = 0; y < newHeight; y++) {
 | |
| 					for (int x = 0; x < newWidth; x++) {
 | |
| 						newColors[y * newWidth + x] = colors[(y + minY) * width + (x + minX)];
 | |
| 					}
 | |
| 				}
 | |
| 				texture.SetPixels(newColors);
 | |
| 				texture.Apply();
 | |
| 			}
 | |
| 			return texture;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static bool GetBit (int value, int index) {
 | |
| 			if (index < 0 || index > 31) { return false; }
 | |
| 			var val = 1 << index;
 | |
| 			return (value & val) == val;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static int SetBitValue (int value, int index, bool bitValue) {
 | |
| 			if (index < 0 || index > 31) { return value; }
 | |
| 			var val = 1 << index;
 | |
| 			return bitValue ? (value | val) : (value & ~val);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void SetMaterialFloatOrColor (Material mat, string keyword, float value) {
 | |
| 			try {
 | |
| 				if (!string.IsNullOrEmpty(keyword)) {
 | |
| 					if (keyword[0] == '$' && keyword.Length > 2) {
 | |
| 						Color color = Color.white;
 | |
| 						switch (keyword[1]) {
 | |
| 							case 'r':
 | |
| 								color.r = value;
 | |
| 								break;
 | |
| 							case 'g':
 | |
| 								color.g = value;
 | |
| 								break;
 | |
| 							case 'b':
 | |
| 								color.b = value;
 | |
| 								break;
 | |
| 							case 'a':
 | |
| 								color.a = value;
 | |
| 								break;
 | |
| 							case 'c':
 | |
| 								color.r = value;
 | |
| 								color.g = value;
 | |
| 								color.b = value;
 | |
| 								break;
 | |
| 						}
 | |
| 						mat.SetColor(keyword.Substring(2, keyword.Length - 2), color);
 | |
| 					} else {
 | |
| 						mat.SetFloat(keyword, value);
 | |
| 					}
 | |
| 				}
 | |
| 			} catch { }
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static string GetPrefabAssetPath (GameObject g) {
 | |
| #if UNITY_4 || UNITY_5 || UNITY_2017 || UNITY_2018_1 || UNITY_2018_2
 | |
| 			return g ? AssetDatabase.GetAssetPath(PrefabUtility.GetPrefabParent(g)) : "";
 | |
| #else
 | |
| 			return g ? PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(g) : "";
 | |
| #endif
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void OverrideMesh (Mesh instance, Mesh data) {
 | |
| 
 | |
| 			instance.Clear();
 | |
| 
 | |
| 			instance.vertices = data.vertices;
 | |
| 			instance.uv = data.uv;
 | |
| 			instance.triangles = data.triangles;
 | |
| 			instance.colors = data.colors;
 | |
| 			instance.boneWeights = data.boneWeights;
 | |
| 			instance.bindposes = data.bindposes;
 | |
| 			instance.name = data.name;
 | |
| 
 | |
| 			instance.RecalculateNormals();
 | |
| 			instance.RecalculateTangents();
 | |
| 			instance.RecalculateBounds();
 | |
| 
 | |
| 			instance.UploadMeshData(false);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void OverrideTexture (Texture2D instance, Texture2D data) {
 | |
| 			instance.Resize(data.width, data.height, data.format, false);
 | |
| 			instance.SetPixels(data.GetPixels());
 | |
| 			instance.Apply();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void OverrideMaterial (Material instance, Material data, string mainTexKeyword) {
 | |
| 			instance.shader = data.shader;
 | |
| 			instance.SetTexture(mainTexKeyword, data.GetTexture(mainTexKeyword));
 | |
| 			instance.CopyPropertiesFromMaterial(data);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static AnimationClip CopyAnimation (AnimationClip source) {
 | |
| 			// Init
 | |
| 			var animation = new AnimationClip() {
 | |
| 				frameRate = source.frameRate,
 | |
| 				name = source.name,
 | |
| 				wrapMode = source.wrapMode,
 | |
| 				legacy = source.legacy,
 | |
| 				hideFlags = source.hideFlags,
 | |
| 				localBounds = source.localBounds,
 | |
| 			};
 | |
| 			// Data
 | |
| 			var bindings = AnimationUtility.GetCurveBindings(source);
 | |
| 			for (int i = 0; i < bindings.Length; i++) {
 | |
| 				var binding = bindings[i];
 | |
| 				var curve = AnimationUtility.GetEditorCurve(source, binding);
 | |
| 				var keys = new Keyframe[curve.length];
 | |
| 				for (int j = 0; j < keys.Length; j++) {
 | |
| 					keys[j] = curve.keys[j];
 | |
| 				}
 | |
| 				animation.SetCurve(binding.path, binding.type, binding.propertyName, new AnimationCurve(keys) {
 | |
| 					postWrapMode = curve.postWrapMode,
 | |
| 					preWrapMode = curve.preWrapMode,
 | |
| 				});
 | |
| 			}
 | |
| 			return animation;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void ClearChildrenImmediate (Transform tf) {
 | |
| 			if (!tf) { return; }
 | |
| 			int len = tf.childCount;
 | |
| 			for (int i = 0; i < len; i++) {
 | |
| 				Object.DestroyImmediate(tf.GetChild(0).gameObject, false);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static void CurveAllLiner (AnimationCurve curve) {
 | |
| 			for (int i = 0; i < curve.keys.Length; i++) {
 | |
| 				var key = curve.keys[i];
 | |
| 				key.inTangent = 0f;
 | |
| 				key.outTangent = 0f;
 | |
| #if UNITY_2018_3_6
 | |
| 				key.inWeight = 0f;
 | |
| 				key.outWeight = 0f;
 | |
| 				key.weightedMode = WeightedMode.Both;
 | |
| #endif
 | |
| 				curve.MoveKey(i, key);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- MagicaVoxel ---
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static readonly Dictionary<byte, Vector4> MAGIC_BYTE_TO_TRANSFORM_MAP = new Dictionary<byte, Vector4>() {
 | |
| 
 | |
| 			{ 40 , new Vector4(3,0,0,0)},
 | |
| 			{ 2  , new Vector4(3,3,0,0)},
 | |
| 			{ 24 , new Vector4(3,2,0,0)},
 | |
| 			{ 50 , new Vector4(3,1,0,0)},
 | |
| 			{ 120, new Vector4(1,0,2,0)},
 | |
| 			{ 98 , new Vector4(1,0,3,0)},
 | |
| 			{ 72 , new Vector4(1,0,0,0)},
 | |
| 			{ 82 , new Vector4(1,0,1,0)},
 | |
| 			{ 4  , new Vector4(0,0,0,0)},
 | |
| 			{ 22 , new Vector4(0,0,1,0)},
 | |
| 			{ 84 , new Vector4(0,0,2,0)},
 | |
| 			{ 70 , new Vector4(0,0,3,0)},
 | |
| 			{ 52 , new Vector4(0,2,0,0)},
 | |
| 			{ 118, new Vector4(0,2,3,0)},
 | |
| 			{ 100, new Vector4(0,2,2,0)},
 | |
| 			{ 38 , new Vector4(0,2,1,0)},
 | |
| 			{ 17 , new Vector4(0,3,0,0)},
 | |
| 			{ 89 , new Vector4(0,3,3,0)},
 | |
| 			{ 113, new Vector4(0,3,2,0)},
 | |
| 			{ 57 , new Vector4(0,3,1,0)},
 | |
| 			{ 33 , new Vector4(0,1,0,0)},
 | |
| 			{ 9  , new Vector4(0,1,1,0)},
 | |
| 			{ 65 , new Vector4(0,1,2,0)},
 | |
| 			{ 105, new Vector4(0,1,3,0)},
 | |
| 
 | |
| 			{ 56 , new Vector4(3,0,0,1)},
 | |
| 			{ 34 , new Vector4(3,3,0,1)},
 | |
| 			{ 8  , new Vector4(3,2,0,1)},
 | |
| 			{ 18 , new Vector4(3,1,0,1)},
 | |
| 			{ 104, new Vector4(1,0,2,1)},
 | |
| 			{ 66 , new Vector4(1,0,3,1)},
 | |
| 			{ 88 , new Vector4(1,0,0,1)},
 | |
| 			{ 114, new Vector4(1,0,1,1)},
 | |
| 			{ 20 , new Vector4(0,0,0,1)},
 | |
| 			{ 86 , new Vector4(0,0,1,1)},
 | |
| 			{ 68 , new Vector4(0,0,2,1)},
 | |
| 			{ 6  , new Vector4(0,0,3,1)},
 | |
| 			{ 36 , new Vector4(0,2,0,1)},
 | |
| 			{ 54 , new Vector4(0,2,3,1)},
 | |
| 			{ 116, new Vector4(0,2,2,1)},
 | |
| 			{ 102, new Vector4(0,2,1,1)},
 | |
| 			{ 49 , new Vector4(0,3,0,1)},
 | |
| 			{ 25 , new Vector4(0,3,3,1)},
 | |
| 			{ 81 , new Vector4(0,3,2,1)},
 | |
| 			{ 121, new Vector4(0,3,1,1)},
 | |
| 			{ 1  , new Vector4(0,1,0,1)},
 | |
| 			{ 73 , new Vector4(0,1,1,1)},
 | |
| 			{ 97 , new Vector4(0,1,2,1)},
 | |
| 			{ 41 , new Vector4(0,1,3,1)},
 | |
| 
 | |
| 
 | |
| 		};
 | |
| 
 | |
| 		private static readonly Dictionary<Vector4, byte> TRANSFORM_TO_MAGIC_BYTE_MAP = new Dictionary<Vector4, byte>() {
 | |
| 
 | |
| 			{new Vector4(3,0,0,0), 40 },
 | |
| 			{new Vector4(3,3,0,0), 2  },
 | |
| 			{new Vector4(3,2,0,0), 24 },
 | |
| 			{new Vector4(3,1,0,0), 50 },
 | |
| 			{new Vector4(1,0,2,0), 120},
 | |
| 			{new Vector4(1,0,3,0), 98 },
 | |
| 			{new Vector4(1,0,0,0), 72 },
 | |
| 			{new Vector4(1,0,1,0), 82 },
 | |
| 			{new Vector4(0,0,0,0), 4  },
 | |
| 			{new Vector4(0,0,1,0), 22 },
 | |
| 			{new Vector4(0,0,2,0), 84 },
 | |
| 			{new Vector4(0,0,3,0), 70 },
 | |
| 			{new Vector4(0,2,0,0), 52 },
 | |
| 			{new Vector4(0,2,3,0), 118},
 | |
| 			{new Vector4(0,2,2,0), 100},
 | |
| 			{new Vector4(0,2,1,0), 38 },
 | |
| 			{new Vector4(0,3,0,0), 17 },
 | |
| 			{new Vector4(0,3,3,0), 89 },
 | |
| 			{new Vector4(0,3,2,0), 113},
 | |
| 			{new Vector4(0,3,1,0), 57 },
 | |
| 			{new Vector4(0,1,0,0), 33 },
 | |
| 			{new Vector4(0,1,1,0), 9  },
 | |
| 			{new Vector4(0,1,2,0), 65 },
 | |
| 			{new Vector4(0,1,3,0), 105},
 | |
| 
 | |
| 			{new Vector4(3,0,0,1), 56 },
 | |
| 			{new Vector4(3,3,0,1), 34 },
 | |
| 			{new Vector4(3,2,0,1), 8  },
 | |
| 			{new Vector4(3,1,0,1), 18 },
 | |
| 			{new Vector4(1,0,2,1), 104},
 | |
| 			{new Vector4(1,0,3,1), 66 },
 | |
| 			{new Vector4(1,0,0,1), 88 },
 | |
| 			{new Vector4(1,0,1,1), 114},
 | |
| 			{new Vector4(0,0,0,1), 20 },
 | |
| 			{new Vector4(0,0,1,1), 86 },
 | |
| 			{new Vector4(0,0,2,1), 68 },
 | |
| 			{new Vector4(0,0,3,1), 6  },
 | |
| 			{new Vector4(0,2,0,1), 36 },
 | |
| 			{new Vector4(0,2,3,1), 54 },
 | |
| 			{new Vector4(0,2,2,1), 116},
 | |
| 			{new Vector4(0,2,1,1), 102},
 | |
| 			{new Vector4(0,3,0,1), 49 },
 | |
| 			{new Vector4(0,3,3,1), 25 },
 | |
| 			{new Vector4(0,3,2,1), 81 },
 | |
| 			{new Vector4(0,3,1,1), 121},
 | |
| 			{new Vector4(0,1,0,1), 1  },
 | |
| 			{new Vector4(0,1,1,1), 73 },
 | |
| 			{new Vector4(0,1,2,1), 97 },
 | |
| 			{new Vector4(0,1,3,1), 41 },
 | |
| 
 | |
| 		};
 | |
| 
 | |
| 
 | |
| 		public static void VoxMatrixByteToTransform (byte the_Byte_Which_Wasted_My_While_Afternoon, out Vector3 rotation, out Vector3 scale) {
 | |
| 			if (MAGIC_BYTE_TO_TRANSFORM_MAP.ContainsKey(the_Byte_Which_Wasted_My_While_Afternoon)) {
 | |
| 				var v4 = MAGIC_BYTE_TO_TRANSFORM_MAP[the_Byte_Which_Wasted_My_While_Afternoon];
 | |
| 				rotation = new Vector3(v4.x * 90f, v4.y * 90f, v4.z * 90f);
 | |
| 				scale = v4.w < 0.5f ? Vector3.one : new Vector3(-1, 1, 1);
 | |
| 			} else {
 | |
| 				rotation = Vector3.zero;
 | |
| 				scale = Vector3.one;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		public static byte TransformToVoxMatrixByte (Vector3 rot, Vector3 scale) {
 | |
| 			var v4 = new Vector4(
 | |
| 				 (byte)(Mathf.RoundToInt((Mathf.Repeat(rot.x, 360f) / 90f) % 4)),
 | |
| 				 (byte)(Mathf.RoundToInt((Mathf.Repeat(rot.y, 360f) / 90f) % 4)),
 | |
| 				 (byte)(Mathf.RoundToInt((Mathf.Repeat(rot.z, 360f) / 90f) % 4)),
 | |
| 				 (byte)(scale.x > 0f ? 0 : 1)
 | |
| 			);
 | |
| 			if (TRANSFORM_TO_MAGIC_BYTE_MAP.ContainsKey(v4)) {
 | |
| 				return TRANSFORM_TO_MAGIC_BYTE_MAP[v4];
 | |
| 			} else {
 | |
| 				return 4;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 
 | |
| }
 |