namespace MagicaVoxelToolbox { using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using MagicaVoxelToolbox; using MagicaVoxelToolbox.Saving; public class VoxelToUnityWindow : MoenenEditorWindow { #region --- SUB --- public struct PathData { public string Path; public string Extension; public string Root; } public enum Task { Prefab = 0, Lod = 1, Material = 2, Obj = 3, ToJson = 4, ToVox = 5, ToQb = 6, } public enum ExportMod { Specified = 0, OriginalPath = 1, AskEverytime = 2, } public enum PivotMod { Specified = 0, MagicaVoxelWorldMod = 1, } public enum _25DSpriteNum { _1 = 1, _4 = 4, _8 = 8, _16 = 16, } public enum FacingOption { PositiveZInUnity = 0, NegativeZInUnity = 1, } #endregion #region --- VAR --- // Global private static Vector2[] SHADER_VALUE_REMAP_SOURCE = new Vector2[SHADER_PROPERTY_NUM] { new Vector2(0, 100), new Vector2(0, 100), new Vector2(0, 100), new Vector2(0, 100), new Vector2(0, 100), new Vector2(0, 100), new Vector2(0, 100), new Vector2(0, 100), new Vector2(1, 2), new Vector2(0, 100), new Vector2(0, 100), new Vector2(1, 5), new Vector2(0, 100), }; private const int SHADER_NUM = VoxelData.MaterialData.SHADER_NUM; private const int SHADER_PROPERTY_NUM = VoxelData.MaterialData.SHADER_PROPERTY_NUM; private static readonly string[] SHADER_NAMES = new string[SHADER_NUM] { "Diffuse", "Metal", "Plastic", "Glass", "Emiss", }; // Short private static ExportMod TheExportMod { get { return (ExportMod)ExportModIndex.Value; } set { ExportModIndex.Value = (int)value; } } private static Core_Voxel.LightMapSupportType LightMapSupportMode { get { return (Core_Voxel.LightMapSupportType)LightMapSupportTypeIndex.Value; } set { LightMapSupportTypeIndex.Value = (int)value; } } private static Shader TheDiffuseShader { get { return Shader.Find(Shader_Paths[0]); } set { Shader_Paths[0].Value = value ? value.name : "Mobile/Diffuse"; } } private static Shader TheMetalShader { get { return Shader.Find(Shader_Paths[1]); } set { Shader_Paths[1].Value = value ? value.name : "Mobile/Diffuse"; } } private static Shader ThePlasticShader { get { return Shader.Find(Shader_Paths[2]); } set { Shader_Paths[2].Value = value ? value.name : "Mobile/Diffuse"; } } private static Shader TheGlassShader { get { return Shader.Find(Shader_Paths[3]); } set { Shader_Paths[3].Value = value ? value.name : "Mobile/Diffuse"; } } private static Shader TheEmissionShader { get { return Shader.Find(Shader_Paths[4]); } set { Shader_Paths[4].Value = value ? value.name : "Mobile/Diffuse"; } } private static Shader[] TheShaders { get { return new Shader[SHADER_NUM] { Shader.Find(Shader_Paths[0]), Shader.Find(Shader_Paths[1]), Shader.Find(Shader_Paths[2]), Shader.Find(Shader_Paths[3]), Shader.Find(Shader_Paths[4]), }; } } private static string[] ShaderKeywords { get { return new string[SHADER_PROPERTY_NUM] { Shader_Keywords[0], Shader_Keywords[1], Shader_Keywords[2], Shader_Keywords[3], Shader_Keywords[4], Shader_Keywords[5], Shader_Keywords[6], Shader_Keywords[7], Shader_Keywords[8], Shader_Keywords[9], Shader_Keywords[10], Shader_Keywords[11], Shader_Keywords[12], }; } } private static Vector2[] ShaderValueRemaps { get { return new Vector2[SHADER_PROPERTY_NUM] { Shader_ValueRemaps[0], Shader_ValueRemaps[1], Shader_ValueRemaps[2], Shader_ValueRemaps[3], Shader_ValueRemaps[4], Shader_ValueRemaps[5], Shader_ValueRemaps[6], Shader_ValueRemaps[7], Shader_ValueRemaps[8], Shader_ValueRemaps[9], Shader_ValueRemaps[10], Shader_ValueRemaps[11], Shader_ValueRemaps[12], }; } } // Data private Vector2 MasterScrollPosition; private static bool AboutFold_Contact = false; private static bool AboutFold_Ad = false; // Selection private static Dictionary TaskMap = new Dictionary(); private static int VoxNum = 0; private static int QbNum = 0; private static int FolderNum = 0; private static int ObjNum = 0; private static int JsonNum = 0; private static Texture2D VoxFileIcon = null; private static Texture2D QbFileIcon = null; private static Texture2D JsonFileIcon = null; // Saving private static EditorSavingBool ViewPanelOpen = new EditorSavingBool("V2U.ViewPanelOpen", true); private static EditorSavingBool CreatePanelOpen = new EditorSavingBool("V2U.CreatePanelOpen", true); private static EditorSavingBool SettingPanelOpen = new EditorSavingBool("V2U.SettingPanelOpen", false); private static EditorSavingBool AboutPanelOpen = new EditorSavingBool("V2U.AboutPanelOpen", false); private static EditorSavingBool ModelGenerationSettingPanelOpen = new EditorSavingBool("V2U.ModelGenerationSettingPanelOpen", false); private static EditorSavingBool OptimizationSettingPanelOpen = new EditorSavingBool("V2U.OptimizationSettingPanelOpen", false); private static EditorSavingBool ShaderSettingPanelOpen = new EditorSavingBool("V2U.ShaderSettingPanelOpen", false); private static EditorSavingBool SpriteGenerationSettingPanelOpen = new EditorSavingBool("V2U.SpriteGenerationSettingPanelOpen", false); private static EditorSavingBool SystemSettingPanelOpen = new EditorSavingBool("V2U.SystemSettingPanelOpen", false); private static EditorSavingBool ToolPanelOpen = new EditorSavingBool("V2U.ToolPanelOpen", true); private static EditorSavingBool ColorfulTitle = new EditorSavingBool("V2U.ColorfulTitle", true); private static EditorSavingString ExportPath = new EditorSavingString("V2U.ExportPath", "Assets"); private static EditorSavingInt LightMapSupportTypeIndex = new EditorSavingInt("V2U.LightMapSupportTypeIndex", 0); private static EditorSavingInt ExportModIndex = new EditorSavingInt("V2U.ExportModIndex", 2); private static EditorSavingFloat ModelScale = new EditorSavingFloat("V2U.ModelScale", 0.1f); private static EditorSavingInt LodNum = new EditorSavingInt("V2U.LodNum", 2); private static EditorSavingBool LogMessage = new EditorSavingBool("V2U.LogMessage", true); private static EditorSavingBool ShowDialog = new EditorSavingBool("V2U.ShowDialog", true); private static EditorSavingBool EditorDockToScene = new EditorSavingBool("V2U.EditorDockToScene", true); private static EditorSavingVector3 ModelPivot = new EditorSavingVector3("V2U.ModelPivot", Vector3.one * 0.5f); private static EditorSavingBool OptimizeFront = new EditorSavingBool("V2U.OptimizeFront", true); private static EditorSavingBool OptimizeBack = new EditorSavingBool("V2U.OptimizeBack", true); private static EditorSavingBool OptimizeUp = new EditorSavingBool("V2U.OptimizeUp", true); private static EditorSavingBool OptimizeDown = new EditorSavingBool("V2U.OptimizeDown", true); private static EditorSavingBool OptimizeLeft = new EditorSavingBool("V2U.OptimizeLeft", true); private static EditorSavingBool OptimizeRight = new EditorSavingBool("V2U.OptimizeRight", true); private static EditorSavingBool FacingYPlusInMV = new EditorSavingBool("V2U.FacingYPlusInMV", true); private static EditorSavingInt CurrentShowingShaderIndex = new EditorSavingInt("V2U.CurrentShowingShaderIndex", 0); private static EditorSavingInt ReplacementModeIndex = new EditorSavingInt("V2U.ReplacementModeIndex", 0); private static EditorSavingString ShaderMainTextureKeyword = new EditorSavingString("V2U.ShaderMainTextureKeyword", "_MainTex");// Keyword Using in VEditor private static EditorSavingString[] Shader_Paths = new EditorSavingString[5] { new EditorSavingString("V2U.ShaderPath_Diffuse", "Mobile/Diffuse"), new EditorSavingString("V2U.ShaderPath_Matel", "Standard"), new EditorSavingString("V2U.ShaderPath_Plastic", "Standard"), new EditorSavingString("V2U.ShaderPath_Glass", "Sprites/Default"), new EditorSavingString("V2U.ShaderPath_Emission", "Sprites/Default"), }; private static EditorSavingString[] Shader_Keywords = new EditorSavingString[SHADER_PROPERTY_NUM] { new EditorSavingString("V2U.ShaderKeyword_MetalWeight", "$c_Color"), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_MetalRough", "_Metallic"), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_MetalSpecular",""), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_PlasticWeight","$c_Color"), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_PlasticRough","_Metallic"), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_PlasticSpecular",""), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_GlassWeight","$a_Color"), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_GlassRough",""), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_GlassRefract",""), // 1-2 0-1 new EditorSavingString("V2U.ShaderKeyword_GlassAttenuate",""), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_EmitWeight","$c_Color"), // 0-100 0-1 new EditorSavingString("V2U.ShaderKeyword_EmitPower",""), // 1-5 0-4 new EditorSavingString("V2U.ShaderKeyword_EmitLdr",""), // 0-100 0-1 }; private static EditorSavingVector2[] Shader_ValueRemaps = new EditorSavingVector2[SHADER_PROPERTY_NUM] { new EditorSavingVector2("V2U.ShaderValueRemap_MetalWeight", new Vector2(0.5f, 1f)), new EditorSavingVector2("V2U.ShaderValueRemap_MetalRough", new Vector2(0f, 0.8f)), new EditorSavingVector2("V2U.ShaderValueRemap_MetalSpecular", new Vector2(0f, 0.8f)), new EditorSavingVector2("V2U.ShaderValueRemap_PlasticWeight", new Vector2(0.5f, 1f)), new EditorSavingVector2("V2U.ShaderValueRemap_PlasticRough", new Vector2(0f, 0.2f)), new EditorSavingVector2("V2U.ShaderValueRemap_PlasticSpecular", new Vector2(0f, 0.2f)), new EditorSavingVector2("V2U.ShaderValueRemap_GlassWeight", new Vector2(0.8f, 0.3f)), new EditorSavingVector2("V2U.ShaderValueRemap_GlassRough", new Vector2(0f, 1f)), new EditorSavingVector2("V2U.ShaderValueRemap_GlassRefract", new Vector2(0f, 1f)), new EditorSavingVector2("V2U.ShaderValueRemap_GlassAttenuate", new Vector2(0f, 1f)), new EditorSavingVector2("V2U.ShaderValueRemap_EmitWeight", new Vector2(0.5f, 1f)), new EditorSavingVector2("V2U.ShaderValueRemap_EmitPower", new Vector2(0f, 1f)), new EditorSavingVector2("V2U.ShaderValueRemap_EmitLdr", new Vector2(0f, 1f)), }; #endregion #region --- MSG --- [MenuItem("Tools/MagicaVoxel Toolbox/Toolbox")] public static void OpenWindow() { var inspector = typeof(Editor).Assembly.GetType("UnityEditor.InspectorWindow"); VoxelToUnityWindow window = inspector != null ? GetWindow("Voxel To Unity", true, inspector) : GetWindow("Voxel To Unity", true); window.minSize = new Vector2(275, 400); window.maxSize = new Vector2(600, 1000); } private void OnEnable() { Window_Enable(); } private void OnFocus() { RefreshSelection(); Repaint(); } private void OnSelectionChange() { RefreshSelection(); Repaint(); } private void OnGUI() { MasterScrollPosition = GUILayout.BeginScrollView(MasterScrollPosition, GUI.skin.scrollView); TitleGUI(); Window_Main(); GUILayout.EndScrollView(); if (Event.current.type == EventType.MouseDown) { GUI.FocusControl(null); Repaint(); } } private void TitleGUI() { const string MAIN_TITLE = "Voxel to Unity"; const string MAIN_TITLE_RICH = "Voxel to Unity"; Space(6); LayoutV(() => { GUIStyle style = new GUIStyle() { alignment = TextAnchor.LowerCenter, fontSize = 12, fontStyle = FontStyle.Bold }; style.normal.textColor = Color.white; style.richText = true; Rect rect = GUIRect(0, 18); GUIStyle shadowStyle = new GUIStyle(style) { richText = false }; EditorGUI.DropShadowLabel(rect, MAIN_TITLE, shadowStyle); GUI.Label(rect, ColorfulTitle ? MAIN_TITLE_RICH : MAIN_TITLE, style); }); Space(6); } #endregion #region --- API --- public static void Window_Enable() { LoadSetting(); RefreshSelection(); RefreshMergeSetting(); } public static void Window_Main() { ViewGUI(); CreateGUI(); // ToolGUI(); SettingGUI(); // AboutGUI(); if (GUI.changed) { SaveSetting(); } } #endregion #region --- GUI --- private static void ViewGUI() { LayoutF(() => { bool addSpaceFlag = true; Space(2); int iconSize = 26; LayoutH(() => { // Init GUIStyle labelStyle = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleLeft, fontSize = 10 }; // Icons if (FolderNum > 0) { if (VoxNum + QbNum + JsonNum <= 0) { // None With Folder EditorGUI.HelpBox(GUIRect(0, iconSize + 14), "There are NO .vox or .qb file in selecting folder.", MessageType.Warning); addSpaceFlag = false; } } else if (VoxNum + QbNum + JsonNum <= 0) { if (ObjNum > 0) { // Selecting Not Voxel File EditorGUI.HelpBox(GUIRect(0, iconSize + 14), "The file selecting is NOT .vox or .qb file.", MessageType.Warning); addSpaceFlag = false; } else { // None EditorGUI.HelpBox(GUIRect(0, iconSize + 14), "Select *.vox, *.qb, *.json or folder in Project View.", MessageType.Info); addSpaceFlag = false; } } Space(4); if (VoxNum > 0) { // Vox LayoutH(() => { if (VoxFileIcon) { GUI.DrawTexture(GUIRect(iconSize, iconSize), VoxFileIcon); } }, true); GUI.Label(GUIRect(0, iconSize), ".vox\n× " + VoxNum.ToString(), labelStyle); Space(4); } if (QbNum > 0) { // Qb LayoutH(() => { if (QbFileIcon) { GUI.DrawTexture(GUIRect(iconSize, iconSize), QbFileIcon); } }, true); GUI.Label(GUIRect(0, iconSize), ".qb\n× " + QbNum.ToString(), labelStyle); } if (JsonNum > 0) { // Json LayoutH(() => { if (JsonFileIcon) { GUI.DrawTexture(GUIRect(iconSize, iconSize), JsonFileIcon); } }, true); GUI.Label(GUIRect(0, iconSize), ".json\n× " + JsonNum.ToString(), labelStyle); } }); Space(addSpaceFlag ? 16 : 6); // Scale Too Small Warning if (ModelScale == 0) { EditorGUI.HelpBox(GUIRect(0, iconSize + 14), "Model scale has been set to 0. Your model will be invisible.", MessageType.Error); Space(6); } else if (ModelScale <= 0.0001f) { EditorGUI.HelpBox(GUIRect(0, iconSize + 14), "Model scale is too small. Your may not able to see them.", MessageType.Warning); Space(6); } // Combine Warning if (!OptimizeFront || !OptimizeBack || !OptimizeLeft || !OptimizeRight || !OptimizeUp || !OptimizeDown) { EditorGUI.HelpBox(GUIRect(0, iconSize + 14), "Faces in some direction will NOT be combine.\nSee \"Setting\" > \"Optimization\".", MessageType.Info); Space(6); } }, "Selecting Files", ViewPanelOpen, true); } private static void CreateGUI() { LayoutF(() => { bool oldEnable = GUI.enabled; GUI.enabled = VoxNum > 0 || QbNum > 0; int buttonHeight = 34; string s = VoxNum + QbNum > 1 ? "s" : ""; var dotStyle = new GUIStyle(GUI.skin.label) { richText = true, alignment = TextAnchor.MiddleLeft, }; Space(6); Rect rect = new Rect(); if (GUI.Button(rect = GUIRect(0, buttonHeight), "Create Prefab" + s)) { // Create Prefab DoTask(Task.Prefab); } GUI.Label(rect, GUI.enabled ? " ●" : "", dotStyle); // Space(4); // if (GUI.Button(rect = GUIRect(0, buttonHeight), "Create LOD Prefab" + s)) { // // Create LOD Prefab // DoTask(Task.Lod); // } // GUI.Label(rect, GUI.enabled ? " ●" : "", dotStyle); // Space(4); // bool oldE = GUI.enabled; // GUI.enabled = oldE && VoxNum > 0; // if (GUI.Button(rect = GUIRect(0, buttonHeight), "Create Material Prefab" + s)) { // // Create Material Prefab // DoTask(Task.Material); // } // GUI.Label(rect, GUI.enabled ? " ●" : "", dotStyle); // GUI.enabled = oldE; // Space(4); // if (GUI.Button(rect = GUIRect(0, buttonHeight), "Create Obj File" + s)) { // // Create Obj File // DoTask(Task.Obj); // } // GUI.Label(rect, GUI.enabled ? " ●" : "", dotStyle); // Space(4); // LayoutH(() => { // GUI.enabled = VoxNum > 0 || QbNum > 0; // if (GUI.Button(rect = GUIRect(0, buttonHeight), " To Json")) { // // To Json // DoTask(Task.ToJson); // } // GUI.Label(rect, GUI.enabled ? " ●" : "", dotStyle); // Space(2); // GUI.enabled = JsonNum > 0 || QbNum > 0; // if (GUI.Button(rect = GUIRect(0, buttonHeight), " To Vox")) { // // To Vox // DoTask(Task.ToVox); // } // GUI.Label(rect, GUI.enabled ? " ●" : "", dotStyle); // Space(2); // GUI.enabled = JsonNum > 0 || VoxNum > 0; // if (GUI.Button(rect = GUIRect(0, buttonHeight), " To Qb")) { // // To Qb // DoTask(Task.ToQb); // } // GUI.Label(rect, GUI.enabled ? " ●" : "", dotStyle); // }); // Space(6); // // Export To // LayoutV(() => { // GUI.enabled = true; // Space(4); LayoutH(() => { GUI.Label(GUIRect(0, 18), "Export To:"); TheExportMod = (ExportMod)EditorGUI.EnumPopup(GUIRect(110, 18), TheExportMod); }); if (TheExportMod == ExportMod.Specified) { Space(4); LayoutH(() => { Space(6); EditorGUI.SelectableLabel(GUIRect(0, 18), ExportPath, GUI.skin.textField); if (GUI.Button(GUIRect(60, 18), "Browse", EditorStyles.miniButtonMid)) { BrowseExportPath(); } }); Space(2); } // Space(2); // GUI.enabled = oldEnable; // }, true); // Space(4); // GUI.enabled = oldEnable; }, "Create", CreatePanelOpen, true); } private static void ToolGUI() { LayoutF(() => { var dotStyle = new GUIStyle(GUI.skin.label) { fontSize = 9, richText = true, alignment = TextAnchor.MiddleLeft, }; // Rigging Editor LayoutH(() => { GUIRect(16, 26); Rect rect = new Rect(); if (GUI.Button(rect = GUIRect(0, 28), " Rigging Editor")) { VoxelEditorWindow.OpenWindow(VoxelEditorWindow.EditorMode.Rigging, EditorDockToScene); } GUI.Label(rect, " ●", dotStyle); GUIRect(16, 26); }); Space(4); // Skeletal Animation Editor LayoutH(() => { GUIRect(16, 26); Rect rect = new Rect(); if (GUI.Button(rect = GUIRect(0, 28), " Skeletal Animation Editor")) { var window = VoxelEditorWindow.OpenWindow(VoxelEditorWindow.EditorMode.Skeletal, EditorDockToScene); if (window) { window.OpenSkeletal(); } } GUI.Label(rect, " ●", dotStyle); GUIRect(16, 26); }); Space(4); // Sprite Editor LayoutH(() => { GUIRect(16, 26); Rect rect = new Rect(); if (GUI.Button(rect = GUIRect(0, 28), " Sprite Editor")) { VoxelEditorWindow.OpenWindow(VoxelEditorWindow.EditorMode.Sprite, EditorDockToScene); } GUI.Label(rect, " ●", dotStyle); GUIRect(16, 26); }); Space(4); // Map Generator LayoutH(() => { GUIRect(16, 26); Rect rect = new Rect(); if (GUI.Button(rect = GUIRect(0, 28), " Map Generator")) { var window = VoxelEditorWindow.OpenWindow(VoxelEditorWindow.EditorMode.MapGenerator, EditorDockToScene); if (window) { window.OpenGenerator(); } } GUI.Label(rect, " ●", dotStyle); GUIRect(16, 26); }); Space(4); // Character Generator LayoutH(() => { GUIRect(16, 26); Rect rect = new Rect(); if (GUI.Button(rect = GUIRect(0, 28), " Character Generator")) { var window = VoxelEditorWindow.OpenWindow(VoxelEditorWindow.EditorMode.CharacterGenerator, EditorDockToScene); if (window) { window.OpenGenerator(); } } GUI.Label(rect, " ●", dotStyle); GUIRect(16, 26); }); Space(4); // Prefab Combiner LayoutH(() => { GUIRect(16, 26); Rect rect = new Rect(); if (GUI.Button(rect = GUIRect(0, 28), " Prefab Combiner")) { var window = VoxelEditorWindow.OpenWindow(VoxelEditorWindow.EditorMode.PrefabCombiner, EditorDockToScene); if (window) { window.OpenCombiner(); } } GUI.Label(rect, " ●", dotStyle); GUIRect(16, 26); }); Space(4); Space(4); }, "Tools", ToolPanelOpen, true); } private static void SettingGUI() { LayoutF(() => { const int HEIGHT = 16; // Model Generation LayoutF(() => { Space(2); // Pivot LayoutH(() => { EditorGUI.LabelField(GUIRect(48, 18), "Pivot"); ModelPivot.Value = EditorGUI.Vector3Field(GUIRect(0, 18), "", ModelPivot); }); Space(2); // Scale ModelScale.Value = Mathf.Max(EditorGUI.FloatField(GUIRect(0, HEIGHT), "Scale (unit/voxel)", ModelScale), 0f); Space(4); // LOD LodNum.Value = Mathf.Clamp(EditorGUI.IntField(GUIRect(0, HEIGHT), "LOD Num", LodNum), 2, 4); Space(4); // Facing Y+ FacingYPlusInMV.Value = (FacingOption)EditorGUI.EnumPopup(GUIRect(0, HEIGHT), "Y+ in MagicaVoxel is", (FacingOption)(FacingYPlusInMV.Value ? 0 : 1)) == 0; Space(4); // LightMapSupportType LightMapSupportMode = (Core_Voxel.LightMapSupportType)EditorGUI.EnumPopup(GUIRect(0, HEIGHT), "Lightmap", LightMapSupportMode); Space(4); // Replacement Mode ReplacementModeIndex.Value = (int)(Core_File.ReplacementMode)EditorGUI.EnumPopup(GUIRect(0, HEIGHT), new GUIContent("Object Replacement", "Only for sub objects inside prefab"), (Core_File.ReplacementMode)ReplacementModeIndex.Value); Space(4); }, "Model Generation", ModelGenerationSettingPanelOpen, true); // Optimization LayoutF(() => { LayoutH(() => { OptimizeLeft.Value = EditorGUI.ToggleLeft(GUIRect(0, HEIGHT), "Combine X-", OptimizeLeft); Space(2); OptimizeRight.Value = EditorGUI.ToggleLeft(GUIRect(0, HEIGHT), "Combine X+", OptimizeRight); }); Space(2); LayoutH(() => { OptimizeDown.Value = EditorGUI.ToggleLeft(GUIRect(0, HEIGHT), "Combine Y-", OptimizeDown); Space(2); OptimizeUp.Value = EditorGUI.ToggleLeft(GUIRect(0, HEIGHT), "Combine Y+", OptimizeUp); }); Space(2); LayoutH(() => { OptimizeBack.Value = EditorGUI.ToggleLeft(GUIRect(0, HEIGHT), "Combine Z-", OptimizeBack); Space(2); OptimizeFront.Value = EditorGUI.ToggleLeft(GUIRect(0, HEIGHT), "Combine Z+", OptimizeFront); }); Space(6); EditorGUI.HelpBox( GUIRect(0, 32), "The check boxes above will make faces only combines on specified directions.", MessageType.Info ); Space(6); }, "Optimization", OptimizationSettingPanelOpen, true); // Shaders LayoutF(() => { // Bar LayoutH(() => { for (int i = 0; i < SHADER_NUM; i++) { var style = i == 0 ? EditorStyles.miniButtonLeft : i == SHADER_NUM - 1 ? EditorStyles.miniButtonRight : EditorStyles.miniButtonMid; if (CurrentShowingShaderIndex == i) { style = new GUIStyle(style) { normal = style.active, }; } if (GUI.Button(GUIRect(0, 16), SHADER_NAMES[i], style)) { CurrentShowingShaderIndex.Value = i; GUI.FocusControl(null); } } }); Space(4); switch (CurrentShowingShaderIndex) { default: case 0: // Diffuse LayoutV(() => { Space(2); TheDiffuseShader = (Shader)EditorGUI.ObjectField(GUIRect(0, 16), "Diffuse Shader", TheDiffuseShader, typeof(Shader), false); Space(2); ShaderMainTextureKeyword.Value = EditorGUI.TextField(GUIRect(0, 16), "MainText Keyword", ShaderMainTextureKeyword.Value); Space(2); }, true); Space(2); break; case 1: // Metal LayoutV(() => { Space(2); TheMetalShader = (Shader)EditorGUI.ObjectField(GUIRect(0, 16), "Metal Shader", TheMetalShader, typeof(Shader), false); // Prop ShaderPropertyGUI(-1, ""); Space(2); ShaderPropertyGUI(0, "Weight"); Space(6); ShaderPropertyGUI(1, "Rough"); Space(6); ShaderPropertyGUI(2, "Specular"); Space(6); }, true); Space(2); break; case 2: // Plastic LayoutV(() => { Space(2); ThePlasticShader = (Shader)EditorGUI.ObjectField(GUIRect(0, 16), "Plastic Shader", ThePlasticShader, typeof(Shader), false); // Prop ShaderPropertyGUI(-1, ""); Space(2); ShaderPropertyGUI(3, "Weight"); Space(6); ShaderPropertyGUI(4, "Rough"); Space(6); ShaderPropertyGUI(5, "Specular"); Space(6); }, true); Space(2); break; case 3: // Glass LayoutV(() => { Space(2); TheGlassShader = (Shader)EditorGUI.ObjectField(GUIRect(0, 16), "Glass Shader", TheGlassShader, typeof(Shader), false); // Prop ShaderPropertyGUI(-1, ""); Space(2); ShaderPropertyGUI(6, "Weight"); Space(6); ShaderPropertyGUI(7, "Rough"); Space(6); ShaderPropertyGUI(8, "Refract"); Space(6); ShaderPropertyGUI(9, "Attenuate"); Space(6); }, true); Space(2); break; case 4: // Emission LayoutV(() => { Space(2); TheEmissionShader = (Shader)EditorGUI.ObjectField(GUIRect(0, 16), "Emission Shader", TheEmissionShader, typeof(Shader), false); // Prop ShaderPropertyGUI(-1, ""); Space(2); ShaderPropertyGUI(10, "Weight"); Space(6); ShaderPropertyGUI(11, "Power"); Space(6); ShaderPropertyGUI(12, "LDR"); Space(6); }, true); Space(2); break; } if (CurrentShowingShaderIndex != 0) { Space(2); EditorGUI.HelpBox( GUIRect(0, 72), "Add tag for keyword will make it represent color. No tag means float.\n" + "\"$a\" = alpha \"$r\" = r \"$g\" = g\n\"$b\" = b \"$c\" = rgb\n eg. \"$aTint\", \"$r_Color\", \"FloatValue\"", MessageType.Info ); Space(6); } }, "Shader", ShaderSettingPanelOpen, true); // System LayoutF(() => { Space(2); LayoutH(() => { LogMessage.Value = EditorGUI.Toggle(GUIRect(HEIGHT, HEIGHT), LogMessage); GUI.Label(GUIRect(0, 18), "Log To Console"); Space(2); ShowDialog.Value = EditorGUI.Toggle(GUIRect(HEIGHT, HEIGHT), ShowDialog); GUI.Label(GUIRect(0, 18), "Dialog Window"); }); Space(2); LayoutH(() => { ColorfulTitle.Value = EditorGUI.Toggle(GUIRect(HEIGHT, HEIGHT), ColorfulTitle); GUI.Label(GUIRect(0, 18), "Colorful Title"); EditorDockToScene.Value = EditorGUI.Toggle(GUIRect(HEIGHT, HEIGHT), EditorDockToScene); GUI.Label(GUIRect(0, 18), "Dock Editor To Scene"); }); Space(2); }, "System", SystemSettingPanelOpen, true); }, "Setting", SettingPanelOpen, true); } #endregion #region --- TSK --- public static void DoTask(Task task) { if (TaskMap.Count == 0) { return; } if (TheExportMod == ExportMod.AskEverytime && !BrowseExportPath()) { return; } RefreshMergeSetting(); string failMessage = "[Voxel] Failed to create model for {0} model{1}."; int successedNum = 0; int failedNum = 0; int taskCount = TaskMap.Count; bool useLOD = task == Task.Lod; bool useMaterial = task == Task.Material; var resultList = new List(); Util.ProgressBar("Creating", "Starting task...", 0f); Util.StartWatch(); ForAllSelection((pathData) => { try { string fileName = Util.GetNameWithoutExtension(pathData.Path); Util.ProgressBar("Creating", string.Format("[{1}/{2}] Creating {0}", fileName, successedNum + failedNum + 1, taskCount), (float)(successedNum + failedNum + 1) / (taskCount + 1)); VoxelData voxelData = null; switch (task) { case Task.Prefab: // Model if (pathData.Extension == ".vox" || pathData.Extension == ".qb") { voxelData = VoxelFile.GetVoxelData( Util.FileToByte(pathData.Path), pathData.Extension == ".vox" ); // Rotate 180 in Y-Axis (No Rig Data) if (!FacingYPlusInMV) { voxelData.Rotate180InY(); } // Create Result var result = Core_Voxel.CreateLodModel( voxelData, ModelScale, useLOD ? LodNum : 1, useMaterial, LightMapSupportMode, ModelPivot ); if (TheExportMod == ExportMod.OriginalPath) { result.ExportRoot = Util.GetParentPath(pathData.Path); result.ExportSubRoot = ""; } else { result.ExportRoot = ExportPath; result.ExportSubRoot = pathData.Root; } result.FileName = fileName; result.Extension = task == Task.Obj ? ".obj" : ".prefab"; result.IsRigged = false; result.WithAvatar = false; resultList.Add(result); } break; } successedNum++; } catch (System.Exception ex) { failMessage += "\n[path] " + pathData.Path + "\n" + ex.Message; failedNum++; } }); // File try { Core_File.CreateFileForResult( resultList, useMaterial ? TheShaders : new Shader[1] { TheDiffuseShader }, ShaderKeywords, ShaderValueRemaps, ModelScale, ModelPivot, (Core_File.ReplacementMode)ReplacementModeIndex.Value, ShaderMainTextureKeyword.Value ); double taskTime = Util.StopWatchAndGetTime(); // Log Messages if (successedNum > 0) { string msg = string.Format("[Voxel] {0} model{1} created in {2}sec.", successedNum, (successedNum > 1 ? "s" : ""), taskTime.ToString("0.00")); if (LogMessage) { Debug.Log(msg); } if (ShowDialog) { Util.Dialog("Success", msg, "OK"); } } if (failedNum > 0) { string msg = string.Format(failMessage, failedNum.ToString(), (failedNum > 1 ? "s" : "")); if (LogMessage) { Debug.LogWarning(msg); } if (ShowDialog) { Util.Dialog("Warning", msg, "OK"); } } } catch (System.Exception ex) { Debug.LogError(ex.Message); } Util.ClearProgressBar(); } #endregion #region --- LGC --- public static void LoadSetting() { ViewPanelOpen.Load(); CreatePanelOpen.Load(); SettingPanelOpen.Load(); AboutPanelOpen.Load(); ModelGenerationSettingPanelOpen.Load(); SpriteGenerationSettingPanelOpen.Load(); SystemSettingPanelOpen.Load(); ToolPanelOpen.Load(); ColorfulTitle.Load(); ExportPath.Load(); ExportModIndex.Load(); ModelScale.Load(); LodNum.Load(); LogMessage.Load(); ShowDialog.Load(); LightMapSupportTypeIndex.Load(); OptimizationSettingPanelOpen.Load(); ShaderSettingPanelOpen.Load(); OptimizeFront.Load(); OptimizeBack.Load(); OptimizeUp.Load(); OptimizeDown.Load(); OptimizeLeft.Load(); OptimizeRight.Load(); EditorDockToScene.Load(); ModelPivot.Load(); FacingYPlusInMV.Load(); CurrentShowingShaderIndex.Load(); ReplacementModeIndex.Load(); ShaderMainTextureKeyword.Load(); for (int i = 0; i < Shader_Paths.Length; i++) { Shader_Paths[i].Load(); } for (int i = 0; i < Shader_Keywords.Length; i++) { Shader_Keywords[i].Load(); } for (int i = 0; i < Shader_ValueRemaps.Length; i++) { Shader_ValueRemaps[i].Load(); } } private static void SaveSetting() { ViewPanelOpen.TrySave(); CreatePanelOpen.TrySave(); SettingPanelOpen.TrySave(); AboutPanelOpen.TrySave(); ModelGenerationSettingPanelOpen.TrySave(); SpriteGenerationSettingPanelOpen.TrySave(); SystemSettingPanelOpen.TrySave(); ToolPanelOpen.TrySave(); ColorfulTitle.TrySave(); ExportPath.TrySave(); ExportModIndex.TrySave(); ModelScale.TrySave(); LodNum.TrySave(); LogMessage.TrySave(); ShowDialog.TrySave(); LightMapSupportTypeIndex.TrySave(); OptimizationSettingPanelOpen.TrySave(); ShaderSettingPanelOpen.TrySave(); OptimizeFront.TrySave(); OptimizeBack.TrySave(); OptimizeUp.TrySave(); OptimizeDown.TrySave(); OptimizeLeft.TrySave(); OptimizeRight.TrySave(); EditorDockToScene.TrySave(); ModelPivot.TrySave(); FacingYPlusInMV.TrySave(); CurrentShowingShaderIndex.TrySave(); ReplacementModeIndex.TrySave(); ShaderMainTextureKeyword.TrySave(); for (int i = 0; i < Shader_Paths.Length; i++) { Shader_Paths[i].TrySave(); } for (int i = 0; i < Shader_Keywords.Length; i++) { Shader_Keywords[i].TrySave(); } for (int i = 0; i < Shader_ValueRemaps.Length; i++) { Shader_ValueRemaps[i].TrySave(); } } private static void RefreshSelection() { VoxNum = 0; QbNum = 0; FolderNum = 0; JsonNum = 0; // Fix Selection var fixedSelection = new List>(); for (int i = 0; i < Selection.objects.Length; i++) { fixedSelection.Add(new KeyValuePair( Selection.objects[i], AssetDatabase.GetAssetPath(Selection.objects[i])) ); } for (int i = 0; i < fixedSelection.Count; i++) { if (!fixedSelection[i].Key) { continue; } var pathI = fixedSelection[i].Value; for (int j = 0; j < fixedSelection.Count; j++) { if (i == j || !fixedSelection[j].Key) { continue; } var pathJ = fixedSelection[j].Value; if (Util.IsChildPathCompair(pathJ, pathI)) { fixedSelection[j] = new KeyValuePair(null, null); } } } UpdateTaskMap(fixedSelection); } public static void UpdateTaskMap(List> fixedSelection) { // Get Task Map TaskMap.Clear(); for (int i = 0; i < fixedSelection.Count; i++) { if (ReferenceEquals(fixedSelection[i].Key, null)) { continue; } var obj = fixedSelection[i].Key; var path = fixedSelection[i].Value; path = Util.FixPath(path); var ex = Util.GetExtension(path); if (AssetDatabase.IsValidFolder(path)) { FolderNum++; var files = Util.GetFilesIn(path, "*.vox", "*.qb", "*.json"); for (int j = 0; j < files.Length; j++) { var filePath = Util.FixedRelativePath(files[j].FullName); var fileEx = Util.GetExtension(filePath); if (fileEx == ".vox" || fileEx == ".qb" || fileEx == ".json") { var fileObj = AssetDatabase.LoadAssetAtPath(filePath); if (fileObj && !TaskMap.ContainsKey(fileObj)) { TaskMap.Add(fileObj, new PathData() { Path = filePath, Extension = fileEx, Root = Util.FixPath(filePath.Substring( path.Length, filePath.Length - path.Length - Util.GetNameWithExtension(filePath).Length )), }); if (fileEx == ".vox") { VoxNum++; FixVoxIcon(fileObj); } else if (fileEx == ".qb") { QbNum++; FixQbIcon(fileObj); } else if (fileEx == ".json") { JsonNum++; FixJsonIcon(fileObj); } } } } } else if (ex == ".vox" || ex == ".qb" || ex == ".json") { if (!TaskMap.ContainsKey(obj)) { TaskMap.Add(obj, new PathData() { Path = path, Extension = ex, Root = "", }); if (ex == ".vox") { VoxNum++; FixVoxIcon(obj); } else if (ex == ".qb") { QbNum++; FixQbIcon(obj); } else if (ex == ".json") { JsonNum++; FixJsonIcon(obj); } } } } ObjNum = Selection.objects.Length; } private static void ForAllSelection(System.Action action) { foreach (var key_Value in TaskMap) { action(key_Value.Value); } } private static void FixVoxIcon(Object vox) { if (!VoxFileIcon) { VoxFileIcon = AssetPreview.GetMiniThumbnail(vox); } } private static void FixQbIcon(Object qb) { if (!QbFileIcon) { QbFileIcon = AssetPreview.GetMiniThumbnail(qb); } } private static void FixJsonIcon(Object json) { if (!JsonFileIcon) { JsonFileIcon = AssetPreview.GetMiniThumbnail(json); } } private static bool BrowseExportPath() { string newPath = Util.FixPath(EditorUtility.OpenFolderPanel("Select Export Path", ExportPath, "")); if (!string.IsNullOrEmpty(newPath)) { newPath = Util.FixedRelativePath(newPath); if (!string.IsNullOrEmpty(newPath)) { ExportPath.Value = newPath; return true; } else { Util.Dialog("Warning", "Export path must in Assets folder.", "OK"); } } return false; } #endregion #region --- UTL --- public static void RefreshMergeSetting() { Core_Voxel.SetMergeInDirection(OptimizeFront, OptimizeBack, OptimizeUp, OptimizeDown, OptimizeLeft, OptimizeRight); } private static void ShaderPropertyGUI(int index, string name) { const int GAP = 12; if (index < 0) { // Title LayoutH(() => { EditorGUI.LabelField(GUIRect(0, 16), ""); EditorGUI.LabelField(GUIRect(0, 16), "Keyword", EditorStyles.centeredGreyMiniLabel); Space(GAP); EditorGUI.LabelField(GUIRect(18 * 4 + 30 + 2 + 2, 16), "Remap", EditorStyles.centeredGreyMiniLabel); }); } else { // Content LayoutH(() => { EditorGUI.LabelField(GUIRect(0, 16), name); Shader_Keywords[index].Value = EditorGUI.TextField(GUIRect(0, 16), Shader_Keywords[index]); Space(GAP); bool oldE = GUI.enabled; GUI.enabled = false; EditorGUI.FloatField(GUIRect(18, 16), SHADER_VALUE_REMAP_SOURCE[index].x); Space(2); EditorGUI.FloatField(GUIRect(30, 16), SHADER_VALUE_REMAP_SOURCE[index].y); GUI.enabled = oldE; EditorGUI.LabelField(GUIRect(18, 16), "→", EditorStyles.centeredGreyMiniLabel); Vector2 newRemap = Vector2.zero; newRemap.x = EditorGUI.FloatField(GUIRect(18, 16), Shader_ValueRemaps[index].Value.x); Space(2); newRemap.y = EditorGUI.FloatField(GUIRect(18, 16), Shader_ValueRemaps[index].Value.y); Shader_ValueRemaps[index].Value = newRemap; }); } } #endregion } [CustomEditor(typeof(DefaultAsset)), CanEditMultipleObjects] public class VoxelInspector : Editor { private void OnEnable() { if (HasVoxelTarget()) { VoxelToUnityWindow.Window_Enable(); } } public override void OnInspectorGUI() { base.OnInspectorGUI(); if (HasVoxelTarget()) { bool oldE = GUI.enabled; GUI.enabled = true; VoxelToUnityWindow.Window_Main(); GUI.enabled = oldE; } } private bool HasVoxelTarget() { for (int i = 0; i < targets.Length; i++) { string path = AssetDatabase.GetAssetPath(targets[i]); string ex = Util.GetExtension(path); if (ex == ".vox" || ex == ".qb" || Util.HasFileIn(path, "*.vox", "*.qb")) { return true; } } return false; } } }