Beast Helper

From Unify Community Wiki
Jump to: navigation, search


Author: Rubén Vallejo

Description

This wizard allows to save the state of your Beast Lightmaps (Tiling/Offset, GameObjects lightmap index etc), and restore it later.

You can merge several lightmapping sessions automatically with this.

Dual lightmaps are not supported for now.

Official unity forums thread: [1]

Usage

To install, just place the script in Assets/Editor folder. To use, just Go to Window > Beast Helper.

C# - BeastHelper.cs

using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
 
public class BeastHelper : EditorWindow
{
    int ArraySize = 10;
    Vector2 scrollpos1;
    static GameObject[] staticobjects; // Static Gameobjects Array
    static object[,,] arrayobjects = new object[255,50,3];  // array relative to baketex array, contains gamobject(s) and tiling info for each lightmap
    Texture2D[] baketex = new Texture2D[255]; //array of lightmaps
 
    //---------------------------------------------
 
    // Add menu named "My Window" to the Window menu
    [MenuItem("Window/Beast Helper")]
    static void Init()
    {
        // Get existing open window or if none, make a new one:
        BeastHelper window = (BeastHelper)EditorWindow.GetWindow(typeof(BeastHelper));
        window.autoRepaintOnSceneChange = true;
        window.title = "Beast Helper";
        window.Show();
 
        //-------------- Get Lightmapped geometry... ----------------
 
        GameObject[] gameobjects = (GameObject[])Resources.FindObjectsOfTypeAll(typeof(GameObject));
 
        int i1 = 0;
        foreach (GameObject g in gameobjects)
        {
            if (g.isStatic)
            {
                i1++;
            }
        }
 
        staticobjects = new GameObject[i1];
 
        int i2 = 0;
        foreach (GameObject g in gameobjects)
        {
            if (g.isStatic)
            {
                staticobjects.SetValue((GameObject)g, i2);
                i2++;
            }
        }
 
        Debug.Log("Static GameObjects count = " + i1);
 
        for (int i = 0; i < arrayobjects.GetLength(0); i++)
        {
            for (int i3 = 0; i3 < arrayobjects.GetLength(1); i3++)
            {
                arrayobjects[i, i3, 0] = null;
                arrayobjects[i, i3, 1] = (Vector4)new Vector4();
                arrayobjects[i, i3, 2] = (bool)false;
            }
        }
 
        //----------------------------------------------------------
 
    }
 
    void OnGUI()
    {
 
        GUILayout.Label("Stored Lightmaps", EditorStyles.boldLabel);
 
        ArraySize = EditorGUILayout.IntField("Array Size(" + ArraySize.ToString() + ")", ArraySize);
 
        GUILayoutOption[] layout1 = new GUILayoutOption[2];
 
        layout1[0] = (GUILayoutOption)GUILayout.Height(75);
        layout1[1] = (GUILayoutOption)GUILayout.Width(75);
 
 
        scrollpos1 = EditorGUILayout.BeginScrollView(scrollpos1);
        for (int i = 0; i < ArraySize; i++)
        {
 
            // Debug.Log("Loop1 i = " + i);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(i.ToString(),"", GUILayout.Width(20));
            baketex[i] = (Texture2D)EditorGUILayout.ObjectField((Texture2D)baketex.GetValue(i), typeof(Texture2D), layout1);
 
            //----------- Each GameObject whit [i] lightmap index go here -----------
 
            for (int i2 = 0; i2 < arrayobjects.GetLength(1); i2++)
            {
 
                    GUILayout.BeginVertical(GUILayout.Width(300));
                    EditorGUILayout.LabelField("GameObject: " + i2.ToString(), "", GUILayout.Width(300));
                    arrayobjects[i, i2, 0] = (GameObject)EditorGUILayout.ObjectField((GameObject)arrayobjects[i, i2, 0], typeof(GameObject), GUILayout.Width(300));
                    arrayobjects[i, i2, 1] = (Vector4)EditorGUILayout.Vector4Field("Tiling (X-Y) / Offset (X-Y)", (Vector4)arrayobjects[i, i2, 1], GUILayout.Width(300));
                    arrayobjects[i, i2, 2] = (bool)EditorGUILayout.Toggle("Use: ",(bool)arrayobjects[i, i2, 2], GUILayout.Width(300));
                    GUILayout.EndVertical();
 
            }
 
 
            //-------------------------------------------------------------------------
 
 
            EditorGUILayout.EndHorizontal();
        }
        EditorGUILayout.EndScrollView();
 
        Repaint();
 
        EditorGUILayout.BeginHorizontal();
        //-------------- Buttons -------------------
 
        if(GUILayout.Button("Read current maps"))
        {
            if (LightmapSettings.lightmaps.Length > ArraySize)
                ArraySize = LightmapSettings.lightmaps.Length;
 
            Repaint();
 
            for (int i = 0; i < LightmapSettings.lightmaps.Length; i++)
            {
                // Debug.Log("Loop2 i = " + i);
                baketex.SetValue(LightmapSettings.lightmaps[i].lightmapFar,i);
 
                //------------- Get objects with same lightmap index ---------
                if (baketex.GetValue(i) != null)
                {
                    var i2 = 0;
                    foreach (GameObject r in staticobjects)
                    {
                        if (r.renderer)
                        {
                            if (r.renderer.lightmapIndex == i)
                            {
                                arrayobjects[i, i2, 0] = (GameObject)r; // The real GameObject
                                arrayobjects[i, i2, 1] = (Vector4)r.renderer.lightmapTilingOffset; // Tiling Lightmap properties
                                arrayobjects[i, i2, 2] = (bool)true; // use it??
                                i2++;
                            }
                        }
 
                    }
                }
                //------------------------------------------------------------
            }
 
            Repaint();
        }
 
        if (GUILayout.Button("Load Maps info from Disk"))
        {
            string path = EditorUtility.OpenFilePanel("Select beast maps info file", Application.dataPath, "ini");
            if (path.Length > 0)
            {
                //-------------- Loading .ini file -------------
                cIni inifile = new cIni(path);
 
                string mapscountstr = inifile.ReadValue("MAPSCOUNT", "COUNT");
 
                int mapscount = 0;
                if (mapscountstr.Length > 0)
                {
                    mapscount = int.Parse(mapscountstr);
                }
 
                if (ArraySize < mapscount)
                {
                    ArraySize = mapscount;
                    Repaint();
                }
 
                int arrayofsset = 0;
 
                bool cancel = false;
 
                switch (EditorUtility.DisplayDialogComplex("Merge with current maps?", "If you choose merge, the loaded maps will be added after the current ones", "Merge", "Replace", "Cancel"))
                {
                    case 0:
                        {
                            for (int i = 0; i < ArraySize; i++)
                            {
                                if (baketex[i] != null)
                                    arrayofsset = i;
                            }
 
                            if (arrayofsset > 0)
                                arrayofsset++;
 
                            if ((arrayofsset + mapscount) > ArraySize)
                            {
                                ArraySize = arrayofsset + mapscount;
                            }
                        }
                        break;
                    case 1:
                        {
                            for (int i = 0; i < ArraySize; i++)
                            {
                                // Debug.Log("Loop2 i = " + i);
                                baketex.SetValue(null, i);
                                for (int i2 = 0; i2 < arrayobjects.GetLength(1); i2++)
                                {
                                    arrayobjects[i, i2, 0] = null;
                                    arrayobjects[i, i2, 1] = (Vector4)new Vector4();
                                    arrayobjects[i, i2, 2] = (bool)false;
                                }
 
                            }
                            Repaint(); 
                        }
                        break;
                    case 2:
                        {
                            cancel = true;
                        }
                        break;
                }
 
                if (cancel == false)
                {
                    int counter1 = 0;
                    for (int i = arrayofsset; i < ArraySize; i++)
                    {
                        string texpath = inifile.ReadValue("MAPS", counter1.ToString());
 
                        if (texpath.Length > 0)
                        {
                            baketex.SetValue((Texture2D)AssetDatabase.LoadAssetAtPath(texpath, typeof(Texture2D)), i);
                        }
                        counter1++;
                    }
 
 
                    for (int i2 = arrayofsset; i2 < ArraySize; i2++)
                    {
                        int counter2 = 0;
                        for (int i3 = 0; i3 < arrayobjects.GetLength(1); i3++)
                        {
                            string gpath = inifile.ReadValue("MAP" + counter2.ToString(), "GAMEOBJECT" + i3.ToString());
                            string gtiling = inifile.ReadValue("MAP" + counter2.ToString(), "TILING" + i3.ToString());
                            string guse = inifile.ReadValue("MAP" + counter2.ToString(), "USE" + i3.ToString());
 
                            if (gpath.Length > 0)
                            {
                                arrayobjects[i2, i3, 0] = (GameObject)GameObject.Find(gpath);
 
 
                                arrayobjects[i2, i3, 1] = (Vector4)vector4fromstring(gtiling);
                                arrayobjects[i2, i3, 2] = (bool)Convert.ToBoolean(guse);
                            }
                            counter2++;
                        }
 
                    }
                }
                //-----------------------------------------------
            }
        }
 
        if(GUILayout.Button("Store Maps info to Disk"))
        {
            EditorUtility.DisplayDialog("Important!", "If you want to start a new bake operation whitout lose current maps, move them to another folder in the project view! Beast will remove old maps otherwise!", "Ok");
 
            string path = EditorUtility.SaveFilePanel("Save beast maps info to file", Application.dataPath, "beast_info", "ini");
            if (path.Length > 0)
            {
                //-------------- Creating .ini file -------------
                if (File.Exists(path))
                    File.Delete(path); // Delete old ini file, otherwise files content will be merged.
 
                cIni inifile = new cIni(path);
 
                inifile.WriteValue("MAPSCOUNT", "COUNT", ArraySize.ToString());
 
                for (int i = 0; i < ArraySize; i++)
                {
                    inifile.WriteValue("MAPS",i.ToString(), AssetDatabase.GetAssetPath(baketex[i]));
                }
 
                for (int i2 = 0; i2 < ArraySize; i2++)
                {
                    for (int i3 = 0; i3 < arrayobjects.GetLength(1); i3++)
                    {
                        inifile.WriteValue("MAP" + i2.ToString(), "GAMEOBJECT" + i3.ToString(), GetHierarchy((GameObject)arrayobjects[i2,i3,0]));
                        Vector4 tiling = (Vector4)arrayobjects[i2, i3, 1];
                        inifile.WriteValue("MAP" + i2.ToString(), "TILING" + i3.ToString(), "(" + tiling.x.ToString() + "," + tiling.y.ToString() + "," + tiling.w.ToString() + "," + tiling.z.ToString() + ")");
                        inifile.WriteValue("MAP" + i2.ToString(), "USE" + i3.ToString(), arrayobjects[i2, i3, 2].ToString().ToLower());
                    }
 
                }
 
                //-----------------------------------------------
            }
 
 
 
        }
 
        if(GUILayout.Button("Set actual Maps in Beast"))
        {
            bool cancel = false;
 
 
 
            if(!cancel)
            {
                if (EditorUtility.DisplayDialog("Do you want to apply these maps??", "If you have unsaved maps in beast, you will lose them", "Apply", "Cancel"))
                {
 
                    LightmapData[] maps = LightmapSettings.lightmaps;
                    Array.Resize(ref maps, ArraySize);
 
 
                    for (int c2 = 0; c2 < maps.Length; c2++)
                    {
                        maps.SetValue(new LightmapData(), c2);
                    }
 
                    for (int c1 = 0; c1 < ArraySize; c1++)
                    {
                        maps[c1].lightmapFar = baketex[c1];
                    }
 
                    LightmapSettings.lightmaps = maps;
 
 
                        for (int i2 = 0; i2 < ArraySize; i2++)
                        {
                            for (int i3 = 0; i3 < arrayobjects.GetLength(1); i3++)
                            {
                                bool use = (bool)arrayobjects[i2, i3, 2];
 
                                if (use == true)
                                {
                                    GameObject g = (GameObject)arrayobjects[i2, i3, 0];
                                    g.renderer.lightmapIndex = i3;
                                    g.renderer.lightmapTilingOffset = (Vector4)arrayobjects[i2, i3, 1];
                                }
                            }
 
 
                        }
 
                }
            }
        }
 
        //------------------------------------------
        EditorGUILayout.EndHorizontal();
 
        //--------------- Down Buttons -------------
 
        EditorGUILayout.BeginHorizontal();
 
        if (GUILayout.Button("Clear All"))
        {
            for (int i = 0; i < ArraySize; i++)
            {
                // Debug.Log("Loop2 i = " + i);
                baketex.SetValue(null, i);
                for (int i2 = 0; i2 < arrayobjects.GetLength(1); i2++)
                {
                    arrayobjects[i, i2, 0] = null;
                    arrayobjects[i, i2, 1] = (Vector4)new Vector4();
                    arrayobjects[i, i2, 2] = (bool)false;
                }
 
            }
            Repaint();
        }
 
        //if (GUILayout.Button("Reload Gameobject List"))
        //{
 
        //}
 
        if (GUILayout.Button("Close"))
        {
            Close();
        }
 
        EditorGUILayout.EndHorizontal();
 
        if (GUI.changed)
            Repaint();
 
    }
 
    // Class for handling .ini files...
 
    public class cIni
    {
 
        [DllImport("kernel32", SetLastError = true)]
        private static extern int WritePrivateProfileString(string pSection, string pKey, string pValue, string pFile);
        [DllImport("kernel32", SetLastError = true)]
        private static extern int WritePrivateProfileStruct(string pSection, string pKey, string pValue, int pValueLen, string pFile);
        [DllImport("kernel32", SetLastError = true)]
        private static extern int GetPrivateProfileString(string pSection, string pKey, string pDefault, byte[] prReturn, int pBufferLen, string pFile);
        [DllImport("kernel32", SetLastError = true)]
        private static extern int GetPrivateProfileStruct(string pSection, string pKey, byte[] prReturn, int pBufferLen, string pFile);
 
        private string ls_IniFilename;
        private int li_BufferLen = 256;
 
        /// <summary>
        /// cINI Constructor
        /// </summary>
        public cIni(string pIniFilename)
        {
            ls_IniFilename = pIniFilename;
        }
 
        /// <summary>
        /// INI filename (If no path is specifyed the function will look with-in the windows directory for the file)
        /// </summary>
        public string IniFile
        {
            get { return (ls_IniFilename); }
            set { ls_IniFilename = value; }
        }
 
        /// <summary>
        /// Max return length when reading data (Max: 32767)
        /// </summary>
        public int BufferLen
        {
            get { return li_BufferLen; }
            set
            {
                if (value > 32767) { li_BufferLen = 32767; }
                else if (value < 1) { li_BufferLen = 1; }
                else { li_BufferLen = value; }
            }
        }
 
        /// <summary>
        /// Read value from INI File
        /// </summary>
        public string ReadValue(string pSection, string pKey, string pDefault)
        {
 
            return (z_GetString(pSection, pKey, pDefault));
 
        }
 
        /// <summary>
        /// Read value from INI File, default = ""
        /// </summary>
        public string ReadValue(string pSection, string pKey)
        {
 
            return (z_GetString(pSection, pKey, ""));
 
        }
 
        /// <summary>
        /// Write value to INI File
        /// </summary>
        public void WriteValue(string pSection, string pKey, string pValue)
        {
 
            WritePrivateProfileString(pSection, pKey, pValue, this.ls_IniFilename);
 
        }
 
        /// <summary>
        /// Remove value from INI File
        /// </summary>
        public void RemoveValue(string pSection, string pKey)
        {
 
            WritePrivateProfileString(pSection, pKey, null, this.ls_IniFilename);
 
        }
 
        /// <summary>
        /// Read values in a section from INI File
        /// </summary>
        public void ReadValues(string pSection, ref Array pValues)
        {
 
            pValues = z_GetString(pSection, null, null).Split((char)0);
 
        }
 
        /// <summary>
        /// Read sections from INI File
        /// </summary>
        public void ReadSections(ref Array pSections)
        {
 
            pSections = z_GetString(null, null, null).Split((char)0);
 
        }
 
        /// <summary>
        /// Remove section from INI File
        /// </summary>
        public void RemoveSection(string pSection)
        {
 
            WritePrivateProfileString(pSection, null, null, this.ls_IniFilename);
 
        }
 
        /// <summary>
        /// Call GetPrivateProfileString / GetPrivateProfileStruct API
        /// </summary>
        private string z_GetString(string pSection, string pKey, string pDefault)
        {
 
            string sRet = pDefault; byte[] bRet = new byte[li_BufferLen];
            int i = GetPrivateProfileString(pSection, pKey, pDefault, bRet, li_BufferLen, ls_IniFilename);
            sRet = System.Text.Encoding.GetEncoding(1252).GetString(bRet, 0, i).TrimEnd((char)0);
            return (sRet);
 
        }
 
    }
 
    //Return the full hierarchy string of a gameObject, ready for use in GameObject.Find method later...
    string GetHierarchy(GameObject g)
    {
        if (!g || g == null)
        {
            return "";
        }
 
        string hierarchy = null;
        string gname = g.name;
 
        Transform parent = g.transform.parent;
 
        while (parent)
        {
            hierarchy += "/" + parent.gameObject.name;
            parent = parent.parent;
        }
        string inverse = gname + hierarchy;
 
        string[] arrayh = (string[])inverse.Split(new string[] { "/" }, StringSplitOptions.None);
        Array.Reverse(arrayh);
 
        return "/" + string.Join("/", arrayh);
 
    }
 
    Vector4 vector4fromstring(string vectorstring)
    {
        if (vectorstring == null || vectorstring.Length == 0)
            return new Vector4();
 
        vectorstring = vectorstring.Substring(1, vectorstring.Length - 2);
 
        string[] arrayvector = (string[])vectorstring.Split(new string[] { "," }, StringSplitOptions.None);
 
        Vector4 vectorfinal = new Vector4(float.Parse(arrayvector[0]), float.Parse(arrayvector[1]), float.Parse(arrayvector[2]), float.Parse(arrayvector[3]));
 
        return vectorfinal;
    }
 
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Tools