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 allFiles = new List(); 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 MAGIC_BYTE_TO_TRANSFORM_MAP = new Dictionary() { { 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 TRANSFORM_TO_MAGIC_BYTE_MAP = new Dictionary() { {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 } }