public override void DrawPrototypeBoxButtons()
        {
            if (!Application.isPlaying)
            {
                GPUInstancerEditorConstants.DrawColoredButton(GPUInstancerEditorConstants.Contents.generatePrototypes, GPUInstancerEditorConstants.Colors.darkBlue, Color.white, FontStyle.Bold, Rect.zero,
                                                              () =>
                {
                    if (EditorUtility.DisplayDialog(GPUInstancerEditorConstants.TEXT_generatePrototypesConfirmation, GPUInstancerEditorConstants.TEXT_generatePrototypeAreYouSure, GPUInstancerEditorConstants.TEXT_generatePrototypes, GPUInstancerEditorConstants.TEXT_cancel))
                    {
                        _treeManager.GeneratePrototypes(true);
                    }
                });
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_generatePrototypesTree);

                GPUInstancerEditorConstants.DrawColoredButton(GPUInstancerEditorConstants.Contents.regenerateBillboards, GPUInstancerEditorConstants.Colors.darkBlue, Color.white, FontStyle.Bold, Rect.zero,
                                                              () =>
                {
                    if (EditorUtility.DisplayDialog(GPUInstancerEditorConstants.TEXT_regenerateBillboardsConfirmation, GPUInstancerEditorConstants.TEXT_regenerateBillboardsAreYouSure, GPUInstancerEditorConstants.TEXT_regenerateBillboards, GPUInstancerEditorConstants.TEXT_cancel))
                    {
                        foreach (GPUInstancerPrototype prototype in _treeManager.prototypeList)
                        {
                            if (prototype.useGeneratedBillboard)
                            {
                                GPUInstancerUtility.GeneratePrototypeBillboard(prototype, true);
                            }
                        }
                    }
                });
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_regenerateBillboards);
            }
        }
        public void DrawGPUInstancerManagerGUILayout()
        {
            int prototypeRowCount = Mathf.FloorToInt((EditorGUIUtility.currentViewWidth - 30f) / PROTOTYPE_RECT_SIZE);

            EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
            GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_prototypes, GPUInstancerEditorConstants.Styles.boldLabel);
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_prototypes);

            if (!Application.isPlaying)
            {
                GPUInstancerEditorConstants.DrawColoredButton(GPUInstancerEditorConstants.Contents.generatePrototypes, GPUInstancerEditorConstants.Colors.darkBlue, Color.white, FontStyle.Bold, Rect.zero,
                                                              () =>
                {
                    if (EditorUtility.DisplayDialog(GPUInstancerEditorConstants.TEXT_generatePrototypesConfirmation, GPUInstancerEditorConstants.TEXT_generatePrototypeAreYouSure, GPUInstancerEditorConstants.TEXT_generatePrototypes, GPUInstancerEditorConstants.TEXT_cancel))
                    {
                        _treeManager.GeneratePrototypes(true);
                    }
                });
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_generatePrototypesTree);

                GPUInstancerEditorConstants.DrawColoredButton(GPUInstancerEditorConstants.Contents.regenerateBillboards, GPUInstancerEditorConstants.Colors.darkBlue, Color.white, FontStyle.Bold, Rect.zero,
                                                              () =>
                {
                    if (EditorUtility.DisplayDialog(GPUInstancerEditorConstants.TEXT_regenerateBillboardsConfirmation, GPUInstancerEditorConstants.TEXT_regenerateBillboardsAreYouSure, GPUInstancerEditorConstants.TEXT_regenerateBillboards, GPUInstancerEditorConstants.TEXT_cancel))
                    {
                        foreach (GPUInstancerPrototype prototype in _treeManager.prototypeList)
                        {
                            if (prototype.useGeneratedBillboard)
                            {
                                GPUInstancerUtility.GeneratePrototypeBillboard(prototype, _treeManager.billboardAtlasBindings, true);
                            }
                        }
                    }
                });
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_regenerateBillboards);
            }

            if (prototypeContents == null || prototypeContents.Length != _treeManager.prototypeList.Count)
            {
                GeneratePrototypeContents();
            }

            int i = 0;

            EditorGUILayout.BeginHorizontal();
            foreach (GPUInstancerPrototype prototype in _treeManager.prototypeList)
            {
                if (prototype == null)
                {
                    continue;
                }
                if (i != 0 && i % prototypeRowCount == 0)
                {
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.BeginHorizontal();
                }

                DrawGPUInstancerPrototypeButton(prototype, prototypeContents[i]);
                i++;
            }

            if (i != 0 && i % prototypeRowCount == 0)
            {
                EditorGUILayout.EndHorizontal();
                EditorGUILayout.BeginHorizontal();
            }
            if (!Application.isPlaying)
            {
                DrawGPUInstancerPrototypeAddButton();
            }

            EditorGUILayout.EndHorizontal();
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_addprototypetree);

            DrawGPUInstancerPrototypeBox(_treeManager.selectedPrototype, prop_isManagerFrustumCulling.boolValue, prop_isManagerOcclusionCulling.boolValue,
                                         _treeManager.shaderBindings, _treeManager.billboardAtlasBindings);

            EditorGUILayout.EndVertical();
        }
        public virtual void DrawGPUInstancerPrototypeBillboardSettings(GPUInstancerPrototype selectedPrototype,
                                                                       GPUInstancerShaderBindings shaderBindings, GPUInstancerBillboardAtlasBindings billboardAtlasBindings)
        {
            if (selectedPrototype.isBillboardDisabled || (selectedPrototype is GPUInstancerDetailPrototype && !((GPUInstancerDetailPrototype)selectedPrototype).usePrototypeMesh))
            {
                if (selectedPrototype.useGeneratedBillboard)
                {
                    selectedPrototype.useGeneratedBillboard = false;
                }
                if (selectedPrototype.billboard != null)
                {
                    selectedPrototype.billboard = null;
                }
                return;
            }

            if (Event.current.type == EventType.Repaint && !selectedPrototype.checkedForBillboardExtentions)
            {
                selectedPrototype.checkedForBillboardExtentions = true;
                if (CheckForBillboardExtentions(selectedPrototype, billboardAtlasBindings))
                {
                    return;
                }
            }

            EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);

            GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_billboardSettings, GPUInstancerEditorConstants.Styles.boldLabel);

            selectedPrototype.useGeneratedBillboard = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_useGeneratedBillboard, selectedPrototype.useGeneratedBillboard);
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_useGeneratedBillboard);

            if (selectedPrototype.useGeneratedBillboard && selectedPrototype.billboard == null)
            {
                selectedPrototype.billboard = new GPUInstancerBillboard();
            }
            else if (!selectedPrototype.useGeneratedBillboard && selectedPrototype.billboard != null)
            {
                if (selectedPrototype.billboard.albedoAtlasTexture != null)
                {
                    billboardAtlasBindings.DeleteBillboardTextures(selectedPrototype);
                }
                if (!selectedPrototype.billboard.useCustomBillboard)
                {
                    selectedPrototype.billboard = null;
                }
            }

            if (selectedPrototype.useGeneratedBillboard)
            {
                if (selectedPrototype.treeType != GPUInstancerTreeType.SpeedTree && selectedPrototype.treeType != GPUInstancerTreeType.TreeCreatorTree && selectedPrototype.treeType != GPUInstancerTreeType.SoftOcclusionTree &&
                    !selectedPrototype.billboard.useCustomBillboard)
                {
                    EditorGUILayout.HelpBox(GPUInstancerEditorConstants.HELPTEXT_unsupportedBillboardWarning, MessageType.Warning);
                }

                bool previousUseCustomBillboard = selectedPrototype.billboard.useCustomBillboard;
                selectedPrototype.billboard.useCustomBillboard = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_useCustomBillboard, selectedPrototype.billboard.useCustomBillboard);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_useCustomBillboard);

                if (selectedPrototype.billboard.useCustomBillboard)
                {
                    selectedPrototype.billboard.customBillboardMesh = (Mesh)EditorGUILayout.ObjectField(GPUInstancerEditorConstants.TEXT_customBillboardMesh,
                                                                                                        selectedPrototype.billboard.customBillboardMesh, typeof(Mesh), false);
                    selectedPrototype.billboard.customBillboardMaterial = (Material)EditorGUILayout.ObjectField(GPUInstancerEditorConstants.TEXT_customBillboardMaterial,
                                                                                                                selectedPrototype.billboard.customBillboardMaterial, typeof(Material), false);
                    selectedPrototype.billboard.isBillboardShadowCasting = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_isBillboardShadowCasting,
                                                                                                  selectedPrototype.billboard.isBillboardShadowCasting);

                    if (!previousUseCustomBillboard && selectedPrototype.billboard.albedoAtlasTexture != null)
                    {
                        billboardAtlasBindings.DeleteBillboardTextures(selectedPrototype);
                    }


                    if (shaderBindings != null && selectedPrototype.billboard.customBillboardMaterial != null)
                    {
                        if (!shaderBindings.IsShadersInstancedVersionExists(selectedPrototype.billboard.customBillboardMaterial.shader.name))
                        {
                            Shader instancedShader = GPUInstancerUtility.CreateInstancedShader(selectedPrototype.billboard.customBillboardMaterial.shader, shaderBindings);
                            if (instancedShader != null)
                            {
                                shaderBindings.AddShaderInstance(selectedPrototype.billboard.customBillboardMaterial.shader.name, instancedShader);
                            }
                        }
                    }
                }
                else
                {
                    if (selectedPrototype.billboard.customBillboardInLODGroup)
                    {
                        selectedPrototype.billboard.customBillboardInLODGroup = false;
                    }

                    selectedPrototype.billboard.billboardQuality = (BillboardQuality)EditorGUILayout.Popup(GPUInstancerEditorConstants.TEXT_billboardQuality,
                                                                                                           (int)selectedPrototype.billboard.billboardQuality, GPUInstancerEditorConstants.TEXT_BillboardQualityOptions);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_billboardQuality);

                    switch (selectedPrototype.billboard.billboardQuality)
                    {
                    case BillboardQuality.Low:
                        selectedPrototype.billboard.atlasResolution = 1024;
                        break;

                    case BillboardQuality.Mid:
                        selectedPrototype.billboard.atlasResolution = 2048;
                        break;

                    case BillboardQuality.High:
                        selectedPrototype.billboard.atlasResolution = 4096;
                        break;

                    case BillboardQuality.VeryHigh:
                        selectedPrototype.billboard.atlasResolution = 8192;
                        break;
                    }

                    selectedPrototype.billboard.frameCount = EditorGUILayout.IntSlider(GPUInstancerEditorConstants.TEXT_billboardFrameCount, selectedPrototype.billboard.frameCount, 8, 32);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_billboardFrameCount);
                    selectedPrototype.billboard.frameCount = Mathf.NextPowerOfTwo(selectedPrototype.billboard.frameCount);

                    selectedPrototype.billboard.billboardBrightness = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_billboardBrightness, selectedPrototype.billboard.billboardBrightness, 0.0f, 1.0f);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_billboardBrightness);

                    selectedPrototype.billboard.isOverridingOriginalCutoff = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_overrideOriginalCutoff, selectedPrototype.billboard.isOverridingOriginalCutoff);
                    if (selectedPrototype.billboard.isOverridingOriginalCutoff)
                    {
                        selectedPrototype.billboard.cutoffOverride = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_overrideCutoffAmount, selectedPrototype.billboard.cutoffOverride, 0.01f, 1.0f);
                    }
                    else
                    {
                        selectedPrototype.billboard.cutoffOverride = -1f;
                    }
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_overrideOriginalCutoff);
                }

                if (!selectedPrototype.billboard.customBillboardInLODGroup)
                {
                    bool hasLODGroup        = selectedPrototype.prefabObject.GetComponent <LODGroup>() != null;
                    bool speedTreeBillboard = selectedPrototype.treeType == GPUInstancerTreeType.SpeedTree && hasLODGroup &&
                                              selectedPrototype.prefabObject.GetComponentInChildren <BillboardRenderer>() != null;
                    if (hasLODGroup && !speedTreeBillboard)
                    {
                        selectedPrototype.billboard.replaceLODCullWithBillboard = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_replaceLODCull, selectedPrototype.billboard.replaceLODCullWithBillboard);
                        DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_replaceLODCull);
                    }
                    if ((!hasLODGroup || !selectedPrototype.billboard.replaceLODCullWithBillboard) && !speedTreeBillboard)
                    {
                        selectedPrototype.billboard.billboardDistance = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_generatedBillboardDistance, selectedPrototype.billboard.billboardDistance, 0.01f, 1f);
                        DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_generatedBillboardDistance);
                    }
                }

                if (!selectedPrototype.billboard.useCustomBillboard)
                {
                    selectedPrototype.billboard.billboardFaceCamPos = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_billboardFaceCamPos, selectedPrototype.billboard.billboardFaceCamPos);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_billboardFaceCamPos);

                    if (selectedPrototype.billboard.albedoAtlasTexture == null)
                    {
                        GPUInstancerUtility.AssignBillboardBinding(selectedPrototype, billboardAtlasBindings);
                    }

                    EditorGUI.BeginDisabledGroup(true);
                    EditorGUILayout.ObjectField(GPUInstancerEditorConstants.TEXT_billboardAlbedo, selectedPrototype.billboard.albedoAtlasTexture, typeof(GameObject), false);
                    EditorGUILayout.ObjectField(GPUInstancerEditorConstants.TEXT_billboardNormal, selectedPrototype.billboard.normalAtlasTexture, typeof(GameObject), false);
                    EditorGUI.EndDisabledGroup();
                }

                GUILayout.Space(10);

                EditorGUILayout.BeginHorizontal();

                if (!selectedPrototype.billboard.useCustomBillboard)
                {
                    GPUInstancerEditorConstants.DrawColoredButton(selectedPrototype.billboard.albedoAtlasTexture == null ?
                                                                  GPUInstancerEditorConstants.Contents.generateBillboard : GPUInstancerEditorConstants.Contents.regenerateBillboard,
                                                                  GPUInstancerEditorConstants.Colors.green, Color.white, FontStyle.Bold, Rect.zero,
                                                                  () =>
                    {
                        GPUInstancerUtility.GeneratePrototypeBillboard(selectedPrototype, billboardAtlasBindings, selectedPrototype.billboard.albedoAtlasTexture != null);
                    });
                }

                if ((!selectedPrototype.billboard.useCustomBillboard && selectedPrototype.billboard.albedoAtlasTexture != null) ||
                    (selectedPrototype.billboard.useCustomBillboard &&
                     selectedPrototype.billboard.customBillboardMesh != null &&
                     selectedPrototype.billboard.customBillboardMaterial != null))
                {
                    GPUInstancerEditorConstants.DrawColoredButton(GPUInstancerEditorConstants.Contents.showBillboard, GPUInstancerEditorConstants.Colors.lightBlue, Color.white, FontStyle.Bold, Rect.zero,
                                                                  () =>
                    {
                        GPUInstancerUtility.ShowBillboardQuad(selectedPrototype, Vector3.zero);
                    });
                }

                EditorGUILayout.EndHorizontal();

                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_regenerateBillboard);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_showBillboard);
            }

            if (selectedPrototype.useGeneratedBillboard && selectedPrototype.billboard != null && selectedPrototype.billboard.useCustomBillboard && GPUInstancerDefines.billboardExtentions != null && GPUInstancerDefines.billboardExtentions.Count > 0)
            {
                GUILayout.Space(10);

                EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);

                GPUInstancerEditorConstants.DrawCustomLabel("External Billboard Generators", GPUInstancerEditorConstants.Styles.boldLabel);

                GUILayout.Space(5);

                foreach (Extention.GPUInstancerBillboardExtention billboardExtention in GPUInstancerDefines.billboardExtentions)
                {
                    try
                    {
                        EditorGUILayout.BeginHorizontal();
                        GUILayout.Label(billboardExtention.GetTitle(), GPUInstancerEditorConstants.Styles.label);

                        GPUInstancerEditorConstants.DrawColoredButton(new GUIContent(billboardExtention.GetButtonText()), GPUInstancerEditorConstants.Colors.green, Color.white, FontStyle.Bold, Rect.zero,
                                                                      () =>
                        {
                            _redirectObject = billboardExtention.GenerateBillboard(selectedPrototype.prefabObject);
                            selectedPrototype.checkedForBillboardExtentions = false;
                        });
                        EditorGUILayout.EndHorizontal();

                        GUILayout.Space(5);
                    }
                    catch (System.Exception e)
                    {
                        EditorUtility.ClearProgressBar();
                        Debug.LogError("Error generating billboard: " + e.Message + " StackTrace:" + e.StackTrace);
                    }
                }

                EditorGUILayout.EndVertical();
            }

            EditorGUILayout.EndVertical();
        }
        public override void AddPickerObject(UnityEngine.Object pickerObject, GPUInstancerPrototype overridePrototype = null)
        {
            base.AddPickerObject(pickerObject, overridePrototype);

            if (pickerObject == null)
            {
                return;
            }

            if (!(pickerObject is GameObject))
            {
                EditorUtility.DisplayDialog(GPUInstancerConstants.TEXT_PREFAB_TYPE_WARNING_TITLE, GPUInstancerConstants.TEXT_TREE_PREFAB_TYPE_WARNING, GPUInstancerConstants.TEXT_OK);
                return;
            }

            GameObject prefabObject = (GameObject)pickerObject;

#if UNITY_2018_3_OR_NEWER
            PrefabAssetType prefabType = PrefabUtility.GetPrefabAssetType(pickerObject);

            if (prefabType == PrefabAssetType.Regular || prefabType == PrefabAssetType.Variant || prefabType == PrefabAssetType.Model)
            {
                GameObject newPrefabObject = (GameObject)PrefabUtility.GetCorrespondingObjectFromSource(prefabObject);
                if (newPrefabObject != null)
                {
                    while (newPrefabObject.transform.parent != null)
                    {
                        newPrefabObject = newPrefabObject.transform.parent.gameObject;
                    }
                    prefabObject = newPrefabObject;
                }
            }
            else
            {
                EditorUtility.DisplayDialog(GPUInstancerConstants.TEXT_PREFAB_TYPE_WARNING_TITLE, GPUInstancerConstants.TEXT_TREE_PREFAB_TYPE_WARNING, GPUInstancerConstants.TEXT_OK);
                return;
            }
#else
            PrefabType prefabType = PrefabUtility.GetPrefabType(pickerObject);

            if (prefabType != PrefabType.Prefab && prefabType != PrefabType.ModelPrefab)
            {
                bool instanceFound = false;
                if (prefabType == PrefabType.PrefabInstance || prefabType == PrefabType.ModelPrefabInstance)
                {
#if UNITY_2018_2_OR_NEWER
                    GameObject newPrefabObject = (GameObject)PrefabUtility.GetCorrespondingObjectFromSource(prefabObject);
#else
                    GameObject newPrefabObject = (GameObject)PrefabUtility.GetPrefabParent(prefabObject);
#endif
                    if (PrefabUtility.GetPrefabType(newPrefabObject) == PrefabType.Prefab || PrefabUtility.GetPrefabType(newPrefabObject) == PrefabType.ModelPrefab)
                    {
                        while (newPrefabObject.transform.parent != null)
                        {
                            newPrefabObject = newPrefabObject.transform.parent.gameObject;
                        }
                        prefabObject  = newPrefabObject;
                        instanceFound = true;
                    }
                }
                if (!instanceFound)
                {
                    EditorUtility.DisplayDialog(GPUInstancerConstants.TEXT_PREFAB_TYPE_WARNING_TITLE, GPUInstancerConstants.TEXT_TREE_PREFAB_TYPE_WARNING, GPUInstancerConstants.TEXT_OK);
                    return;
                }
            }
#endif

            Undo.RecordObject(this, "Add tree prototype");

            if (_treeManager.terrainSettings != null && _treeManager.terrain != null && _treeManager.terrain.terrainData != null)
            {
                if (overridePrototype != null)
                {
                    int prototypeIndex = prototypeList.IndexOf(overridePrototype);
                    if (prototypeIndex >= 0 && prototypeIndex < _treeManager.terrain.terrainData.treePrototypes.Length)
                    {
                        TreePrototype[] treePrototypes = _treeManager.terrain.terrainData.treePrototypes;

                        treePrototypes[prototypeIndex].prefab           = prefabObject;
                        overridePrototype.prefabObject                  = prefabObject;
                        _treeManager.terrain.terrainData.treePrototypes = treePrototypes;
                        _treeManager.terrain.terrainData.RefreshPrototypes();

                        GPUInstancerUtility.DetermineTreePrototypeType(overridePrototype);

                        if (overridePrototype.billboard != null && overridePrototype.useGeneratedBillboard)
                        {
                            GPUInstancerUtility.GeneratePrototypeBillboard(overridePrototype, true);
                        }
                    }
                }
                else
                {
                    List <TreePrototype> newTreePrototypes = new List <TreePrototype>(_treeManager.terrain.terrainData.treePrototypes);

                    TreePrototype terrainTreePrototype = new TreePrototype()
                    {
                        prefab     = prefabObject,
                        bendFactor = 0
                    };
                    newTreePrototypes.Add(terrainTreePrototype);

                    _treeManager.terrain.terrainData.treePrototypes = newTreePrototypes.ToArray();
                    _treeManager.terrain.terrainData.RefreshPrototypes();
                    GPUInstancerUtility.AddTreeInstancePrototypeFromTerrainPrototype(_treeManager.gameObject, prototypeList, terrainTreePrototype,
                                                                                     newTreePrototypes.Count - 1, _treeManager.terrainSettings);
                }
            }
        }