Ejemplo n.º 1
0
        public static GPUInstancerShaderBindings GetDefaultGPUInstancerShaderBindings()
        {
            GPUInstancerShaderBindings shaderBindings = Resources.Load <GPUInstancerShaderBindings>(GPUInstancerConstants.SETTINGS_PATH + GPUInstancerConstants.SHADER_BINDINGS_DEFAULT_NAME);

            if (shaderBindings == null)
            {
                shaderBindings = ScriptableObject.CreateInstance <GPUInstancerShaderBindings>();
                shaderBindings.ResetShaderInstances();
#if UNITY_EDITOR
                if (!Application.isPlaying)
                {
                    if (!System.IO.Directory.Exists(GPUInstancerConstants.GetDefaultPath() + GPUInstancerConstants.RESOURCES_PATH + GPUInstancerConstants.SETTINGS_PATH))
                    {
                        System.IO.Directory.CreateDirectory(GPUInstancerConstants.GetDefaultPath() + GPUInstancerConstants.RESOURCES_PATH + GPUInstancerConstants.SETTINGS_PATH);
                    }

                    AssetDatabase.CreateAsset(shaderBindings, GPUInstancerConstants.GetDefaultPath() + GPUInstancerConstants.RESOURCES_PATH + GPUInstancerConstants.SETTINGS_PATH + GPUInstancerConstants.SHADER_BINDINGS_DEFAULT_NAME + ".asset");
                    AssetDatabase.SaveAssets();
                    AssetDatabase.Refresh();
                }
#endif
            }

            return(shaderBindings);
        }
Ejemplo n.º 2
0
        public virtual void SetDefaultGPUInstancerShaderBindings()
        {
            if (shaderBindings == null)
            {
#if UNITY_EDITOR
                if (!Application.isPlaying)
                {
                    Undo.RecordObject(this, "GPUInstancerShaderBindings instance generated");
                }
#endif
                shaderBindings = GetDefaultGPUInstancerShaderBindings();
            }
        }
        public static void DrawGPUInstancerPrototypeInfo(GPUInstancerPrototype selectedPrototype, UnityAction <string> DrawHelpText, Object component, UnityAction OnEditorDataChanged,
                                                         GPUInstancerShaderBindings shaderBindings, GPUInstancerEditorSimulator simulator, GPUInstancerTerrainSettings terrainSettings)
        {
            GPUInstancerTreePrototype treePrototype = (GPUInstancerTreePrototype)selectedPrototype;

            EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
            GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_treeSettings, GPUInstancerEditorConstants.Styles.boldLabel);

            treePrototype.isApplyRotation = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_useRandomTreeTotation, treePrototype.isApplyRotation);
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_useRandomTreeTotation);

            EditorGUILayout.EndVertical();
        }
        /// <summary>
        /// Generates instancing renderer data for a given GameObject, at the first LOD level.
        /// </summary>
        /// <param name="gameObject">GameObject</param>
        /// <param name="settings">GPU Instancer settings to find appropriate shader for materials</param>
        /// <param name="includeChildren">if true, renderers for all found children of this gameObject will be created as well</param>
        public bool CreateRenderersFromGameObject(GPUInstancerPrototype prototype, GPUInstancerShaderBindings shaderBindings)
        {
            if (prototype.prefabObject == null)
            {
                return(false);
            }

            if (prototype.prefabObject.GetComponent <LODGroup>() != null)
            {
                return(GenerateLODsFromLODGroup(prototype.prefabObject.GetComponent <LODGroup>(), shaderBindings, prototype.isShadowCasting));
            }
            else
            {
                if (instanceLODs == null || instanceLODs.Count == 0)
                {
                    AddLod();
                }
                return(CreateRenderersFromGameObject(0, prototype.prefabObject, shaderBindings, prototype.isShadowCasting));
            }
        }
        /// <summary>
        /// Generates instancing renderer data for a given GameObject, at the given LOD level.
        /// </summary>
        /// <param name="lod">Which LOD level to generate renderers in</param>
        /// <param name="gameObject">GameObject</param>
        /// <param name="settings">GPU Instancer settings to find appropriate shader for materials</param>
        /// <param name="includeChildren">if true, renderers for all found children of this gameObject will be created as well</param>
        private bool CreateRenderersFromGameObject(int lod, GameObject gameObject, GPUInstancerShaderBindings shaderBindings, bool createShadowMPB)
        {
            if (instanceLODs == null || instanceLODs.Count <= lod || instanceLODs[lod] == null)
            {
                Debug.LogError("Can't create renderer(s): Invalid LOD");
                return(false);
            }

            if (!gameObject)
            {
                Debug.LogError("Can't create renderer(s): reference GameObject is null");
                return(false);
            }

            List <MeshRenderer> meshRenderers = new List <MeshRenderer>();

            GetMeshRenderersOfTransform(gameObject.transform, meshRenderers);

            if (meshRenderers == null || meshRenderers.Count == 0)
            {
                Debug.LogError("Can't create renderer(s): no MeshRenderers found in the reference GameObject <" + gameObject.name +
                               "> or any of its children");
                return(false);
            }

            foreach (MeshRenderer meshRenderer in meshRenderers)
            {
                if (meshRenderer.GetComponent <MeshFilter>() == null)
                {
                    Debug.LogWarning("MeshRenderer with no MeshFilter found on GameObject <" + gameObject.name +
                                     "> (Child: <" + meshRenderer.gameObject + ">). Are you missing a component?");
                    continue;
                }

                List <Material> instanceMaterials = new List <Material>();

                for (int m = 0; m < meshRenderer.sharedMaterials.Length; m++)
                {
                    instanceMaterials.Add(shaderBindings.GetInstancedMaterial(meshRenderer.sharedMaterials[m]));
                }

                Matrix4x4 transformOffset  = Matrix4x4.identity;
                Transform currentTransform = meshRenderer.gameObject.transform;
                while (currentTransform != gameObject.transform)
                {
                    transformOffset  = Matrix4x4.TRS(currentTransform.localPosition, currentTransform.localRotation, currentTransform.localScale) * transformOffset;
                    currentTransform = currentTransform.parent;
                }

                MaterialPropertyBlock mpb = new MaterialPropertyBlock();
                meshRenderer.GetPropertyBlock(mpb);
                MaterialPropertyBlock shadowMPB = null;
                if (createShadowMPB)
                {
                    shadowMPB = new MaterialPropertyBlock();
                    meshRenderer.GetPropertyBlock(shadowMPB);
                }
                AddRenderer(lod, meshRenderer.GetComponent <MeshFilter>().sharedMesh, instanceMaterials, transformOffset, mpb, meshRenderer.shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off, meshRenderer.gameObject.layer, shadowMPB);
            }

            return(true);
        }
        /// <summary>
        /// Generates all LOD and render data from the supplied Unity LODGroup. Deletes all existing LOD data.
        /// </summary>
        /// <param name="lodGroup">Unity LODGroup</param>
        /// <param name="settings">GPU Instancer settings to find appropriate shader for materials</param>
        private bool GenerateLODsFromLODGroup(LODGroup lodGroup, GPUInstancerShaderBindings shaderBindings, bool createShadowMPB)
        {
            if (instanceLODs == null)
            {
                instanceLODs = new List <GPUInstancerPrototypeLOD>();
            }
            else
            {
                instanceLODs.Clear();
            }

            for (int lod = 0; lod < lodGroup.GetLODs().Length; lod++)
            {
                bool            hasBillboardRenderer = false;
                List <Renderer> lodRenderers         = new List <Renderer>();
                if (lodGroup.GetLODs()[lod].renderers != null)
                {
                    foreach (Renderer renderer in lodGroup.GetLODs()[lod].renderers)
                    {
                        if (renderer != null && renderer is MeshRenderer && renderer.GetComponent <MeshFilter>() != null)
                        {
                            lodRenderers.Add(renderer);
                        }
                        else if (renderer != null && renderer is BillboardRenderer)
                        {
                            hasBillboardRenderer = true;
                        }
                    }
                }

                if (!lodRenderers.Any())
                {
                    if (!hasBillboardRenderer)
                    {
                        Debug.LogWarning("LODGroup has no mesh renderers. Prefab: " + lodGroup.gameObject.name + " LODIndex: " + lod);
                    }
                    continue;
                }

                AddLod(lodGroup.GetLODs()[lod].screenRelativeTransitionHeight);

                for (int r = 0; r < lodRenderers.Count; r++)
                {
                    List <Material> instanceMaterials = new List <Material>();
                    for (int m = 0; m < lodRenderers[r].sharedMaterials.Length; m++)
                    {
                        instanceMaterials.Add(shaderBindings.GetInstancedMaterial(lodRenderers[r].sharedMaterials[m]));
                    }

                    Matrix4x4 transformOffset  = Matrix4x4.identity;
                    Transform currentTransform = lodRenderers[r].gameObject.transform;
                    while (currentTransform != lodGroup.gameObject.transform)
                    {
                        transformOffset  = Matrix4x4.TRS(currentTransform.localPosition, currentTransform.localRotation, currentTransform.localScale) * transformOffset;
                        currentTransform = currentTransform.parent;
                    }

                    MaterialPropertyBlock mpb = new MaterialPropertyBlock();
                    lodRenderers[r].GetPropertyBlock(mpb);
                    MaterialPropertyBlock shadowMPB = null;
                    if (createShadowMPB)
                    {
                        shadowMPB = new MaterialPropertyBlock();
                        lodRenderers[r].GetPropertyBlock(shadowMPB);
                    }
                    AddRenderer(lod, lodRenderers[r].GetComponent <MeshFilter>().sharedMesh, instanceMaterials, transformOffset, mpb, lodRenderers[r].shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off, lodRenderers[r].gameObject.layer, shadowMPB);
                }
            }
            return(true);
        }
        public static void DrawGPUInstancerPrototypeInfo(GPUInstancerPrototype selectedPrototype, UnityAction <string> DrawHelpText, UnityEngine.Object component, UnityAction OnEditorDataChanged,
                                                         GPUInstancerShaderBindings shaderBindings, GPUInstancerEditorSimulator simulator, GPUInstancerTerrainSettings terrainSettings, int detailLayer)
        {
            GPUInstancerDetailPrototype prototype = (GPUInstancerDetailPrototype)selectedPrototype;

            EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
            GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_detailProperties, GPUInstancerEditorConstants.Styles.boldLabel);

            EditorGUI.BeginChangeCheck();

            prototype.detailDensity = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_detailDensity, prototype.detailDensity, 0.0f, terrainSettings.detailDensity);
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_detailDensity);
            prototype.detailScale = EditorGUILayout.Vector4Field(GPUInstancerEditorConstants.TEXT_detailScale, prototype.detailScale);
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_detailScale);

            prototype.noiseSpread = EditorGUILayout.FloatField(GPUInstancerEditorConstants.TEXT_noiseSpread, prototype.noiseSpread);
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_noiseSpread);

            prototype.useCustomHealthyDryNoiseTexture = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_useCustomHealthyDryNoiseTexture, prototype.useCustomHealthyDryNoiseTexture);
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_useCustomHealthyDryNoiseTexture);
            if (prototype.useCustomHealthyDryNoiseTexture)
            {
                prototype.healthyDryNoiseTexture = (Texture2D)EditorGUILayout.ObjectField(GPUInstancerEditorConstants.TEXT_healthyDryNoiseTexture, prototype.healthyDryNoiseTexture, typeof(Texture2D), false);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_healthyDryNoiseTexture);
            }

            if (EditorGUI.EndChangeCheck())
            {
                Undo.RecordObject(component, "Editor data changed.");
                if (OnEditorDataChanged != null)
                {
                    OnEditorDataChanged();
                }
                EditorUtility.SetDirty(prototype);
            }

            EditorGUI.BeginChangeCheck();
            if (!prototype.usePrototypeMesh)
            {
                prototype.useCustomMaterialForTextureDetail = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_useCustomMaterialForTextureDetail, prototype.useCustomMaterialForTextureDetail);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_useCustomMaterialForTextureDetail);
                if (prototype.useCustomMaterialForTextureDetail)
                {
                    prototype.textureDetailCustomMaterial = (Material)EditorGUILayout.ObjectField(GPUInstancerEditorConstants.TEXT_textureDetailCustomMaterial, prototype.textureDetailCustomMaterial, typeof(Material), false);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_textureDetailCustomMaterial);
                    prototype.isBillboard = false;
                }
                else
                {
                    prototype.textureDetailCustomMaterial = null;
                }
            }

            EditorGUILayout.EndVertical();

            if (!prototype.usePrototypeMesh && !prototype.isBillboard)
            {
                EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
                GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_crossQuads, GPUInstancerEditorConstants.Styles.boldLabel);

                prototype.useCrossQuads = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_crossQuads, prototype.useCrossQuads);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_crossQuads);

                if (prototype.useCrossQuads)
                {
                    prototype.quadCount = EditorGUILayout.IntSlider(GPUInstancerEditorConstants.TEXT_quadCount, prototype.quadCount, 2, 4);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_quadCount);

                    if (!prototype.useCustomMaterialForTextureDetail)
                    {
                        prototype.billboardDistance = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_billboardDistance, prototype.billboardDistance, 0.5f, 1f);
                        DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_billboardDistance);
                        prototype.billboardDistanceDebug = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_billboardDistanceDebug, prototype.billboardDistanceDebug);
                        DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_billboardDistanceDebug);
                        if (prototype.billboardDistanceDebug)
                        {
                            prototype.billboardDistanceDebugColor = EditorGUILayout.ColorField(GPUInstancerEditorConstants.TEXT_billboardDistanceDebugColor, prototype.billboardDistanceDebugColor);
                            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_billboardDistanceDebugColor);
                        }
                        prototype.billboardFaceCamPos = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_CQBillboardFaceCamPos, prototype.billboardFaceCamPos);
                        DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_CQBillboardFaceCamPos);
                    }
                }
                else
                {
                    prototype.quadCount = 1;
                }

                EditorGUILayout.EndVertical();
            }
            else
            {
                prototype.useCrossQuads = false;
            }

            if (EditorGUI.EndChangeCheck())
            {
                if (!prototype.usePrototypeMesh && prototype.useCustomMaterialForTextureDetail && prototype.textureDetailCustomMaterial != null)
                {
                    if (!shaderBindings.IsShadersInstancedVersionExists(prototype.textureDetailCustomMaterial.shader.name))
                    {
                        Shader instancedShader;
                        if (GPUInstancerUtility.IsShaderInstanced(prototype.textureDetailCustomMaterial.shader))
                        {
                            instancedShader = prototype.textureDetailCustomMaterial.shader;
                        }
                        else
                        {
                            instancedShader = GPUInstancerUtility.CreateInstancedShader(prototype.textureDetailCustomMaterial.shader, shaderBindings);
                        }

                        if (instancedShader != null)
                        {
                            shaderBindings.AddShaderInstance(prototype.textureDetailCustomMaterial.shader.name, instancedShader);
                        }
                        else
                        {
                            Debug.LogWarning("Can not create instanced version for shader: " + prototype.textureDetailCustomMaterial.shader.name + ". Standard Shader will be used instead.");
                        }
                    }
                }
                EditorUtility.SetDirty(prototype);
            }

            if (!prototype.usePrototypeMesh && !prototype.useCustomMaterialForTextureDetail)
            {
                EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
                GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_foliageShaderProperties, GPUInstancerEditorConstants.Styles.boldLabel);

                EditorGUI.BeginChangeCheck();
                prototype.isBillboard = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_isBillboard, prototype.isBillboard);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_isBillboard);

                if (prototype.isBillboard)
                {
                    prototype.billboardFaceCamPos = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_billboardFaceCamPos, prototype.billboardFaceCamPos);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_billboardFaceCamPos);
                }

                if (EditorGUI.EndChangeCheck())
                {
                    EditorUtility.SetDirty(prototype);
                    if (simulator != null && simulator.simulateAtEditor && !simulator.initializingInstances)
                    {
                        GPUInstancerUtility.UpdateDetailInstanceRuntimeDataList(simulator.gpuiManager.runtimeDataList, terrainSettings, false, detailLayer);
                    }
                }

                EditorGUI.BeginChangeCheck();

                prototype.detailHealthyColor = EditorGUILayout.ColorField(GPUInstancerEditorConstants.TEXT_detailHealthyColor, prototype.detailHealthyColor);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_detailHealthyColor);
                prototype.detailDryColor = EditorGUILayout.ColorField(GPUInstancerEditorConstants.TEXT_detailDryColor, prototype.detailDryColor);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_detailDryColor);

                if (EditorGUI.EndChangeCheck())
                {
                    Undo.RecordObject(component, "Editor data changed.");
                    if (OnEditorDataChanged != null)
                    {
                        OnEditorDataChanged();
                    }
                    if (simulator != null && simulator.simulateAtEditor && !simulator.initializingInstances)
                    {
                        GPUInstancerUtility.UpdateDetailInstanceRuntimeDataList(simulator.gpuiManager.runtimeDataList, terrainSettings, false, detailLayer);
                    }
                    EditorUtility.SetDirty(prototype);
                }

                EditorGUI.BeginChangeCheck();

                prototype.ambientOcclusion = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_ambientOcclusion, prototype.ambientOcclusion, 0f, 1f);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_ambientOcclusion);
                prototype.gradientPower = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_gradientPower, prototype.gradientPower, 0f, 1f);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_gradientPower);

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

                prototype.windIdleSway = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_windIdleSway, prototype.windIdleSway, 0f, 1f);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_windIdleSway);
                prototype.windWavesOn = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_windWavesOn, prototype.windWavesOn);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_windWavesOn);
                if (prototype.windWavesOn)
                {
                    prototype.windWaveTintColor = EditorGUILayout.ColorField(GPUInstancerEditorConstants.TEXT_windWaveTintColor, prototype.windWaveTintColor);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_windWaveTintColor);
                    prototype.windWaveSize = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_windWaveSize, prototype.windWaveSize, 0f, 1f);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_windWaveSize);
                    prototype.windWaveTint = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_windWaveTint, prototype.windWaveTint, 0f, 1f);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_windWaveTint);
                    prototype.windWaveSway = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_windWaveSway, prototype.windWaveSway, 0f, 1f);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_windWaveSway);
                }
                if (EditorGUI.EndChangeCheck())
                {
                    EditorUtility.SetDirty(prototype);
                    if (simulator != null && simulator.simulateAtEditor && !simulator.initializingInstances)
                    {
                        GPUInstancerUtility.UpdateDetailInstanceRuntimeDataList(simulator.gpuiManager.runtimeDataList, terrainSettings, false, detailLayer);
                    }
                }
                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 virtual void DrawGPUInstancerPrototypeBox(GPUInstancerPrototype selectedPrototype, bool isFrustumCulling, bool isOcclusionCulling,
                                                         GPUInstancerShaderBindings shaderBindings, GPUInstancerBillboardAtlasBindings billboardAtlasBindings)
        {
            if (selectedPrototype == null)
            {
                return;
            }

            EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
            // title
            Rect foldoutRect = GUILayoutUtility.GetRect(0, 20, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(false));

            foldoutRect.x   += 12;
            showPrototypeBox = EditorGUI.Foldout(foldoutRect, showPrototypeBox, selectedPrototype.ToString(), true, GPUInstancerEditorConstants.Styles.foldout);

            if (!showPrototypeBox)
            {
                EditorGUILayout.EndVertical();
                return;
            }

            if (!string.IsNullOrEmpty(selectedPrototype.warningText))
            {
                EditorGUILayout.HelpBox(selectedPrototype.warningText, MessageType.Error);
                if (selectedPrototype.warningText.StartsWith("Can not create instanced version for shader"))
                {
                    GPUInstancerEditorConstants.DrawColoredButton(new GUIContent("Go to Unity Archive"),
                                                                  GPUInstancerEditorConstants.Colors.lightred, Color.white, FontStyle.Bold, Rect.zero,
                                                                  () =>
                    {
                        Application.OpenURL("https://unity3d.com/get-unity/download/archive");
                    });
                    GUILayout.Space(10);
                }
            }

            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.ObjectField(GPUInstancerEditorConstants.TEXT_prefabObject, selectedPrototype.prefabObject, typeof(GameObject), false);
            EditorGUILayout.ObjectField(GPUInstancerEditorConstants.TEXT_prototypeSO, selectedPrototype, typeof(GPUInstancerPrototype), false);
            EditorGUI.EndDisabledGroup();

            EditorGUI.BeginChangeCheck();

            #region Shadows
            EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
            GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_shadows, GPUInstancerEditorConstants.Styles.boldLabel);

            EditorGUI.BeginDisabledGroup(Application.isPlaying);
            selectedPrototype.isShadowCasting = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_isShadowCasting, selectedPrototype.isShadowCasting);
            DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_isShadowCasting);
            EditorGUI.EndDisabledGroup();
            if (selectedPrototype.isShadowCasting)
            {
                if (selectedPrototype is GPUInstancerPrefabPrototype)
                {
                    EditorGUI.BeginDisabledGroup(Application.isPlaying);
                    selectedPrototype.useOriginalShaderForShadow = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_useOriginalShaderForShadow, selectedPrototype.useOriginalShaderForShadow);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_useOriginalShaderForShadow);
                    EditorGUI.EndDisabledGroup();
                }

                if (!(selectedPrototype is GPUInstancerDetailPrototype))
                {
                    selectedPrototype.useCustomShadowDistance = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_useCustomShadowDistance, selectedPrototype.useCustomShadowDistance);
                    if (selectedPrototype.useCustomShadowDistance)
                    {
                        selectedPrototype.shadowDistance = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_shadowDistance, selectedPrototype.shadowDistance, 0.0f, QualitySettings.shadowDistance);
                    }
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_useCustomShadowDistance);
                    if (selectedPrototype.prefabObject != null && selectedPrototype.prefabObject.GetComponent <LODGroup>() != null)
                    {
                        LODGroup          lodGroup    = selectedPrototype.prefabObject.GetComponent <LODGroup>();
                        List <GUIContent> optionsList = GPUInstancerEditorConstants.Contents.LODs.GetRange(0, lodGroup.lodCount);
                        optionsList.Add(GPUInstancerEditorConstants.Contents.LODs[8]);
                        GUIContent[] options = optionsList.ToArray();
                        int          index   = 0;
                        for (int i = 0; i < lodGroup.lodCount; i++)
                        {
                            index = i * 4;
                            if (i >= 4)
                            {
                                index = (i - 4) * 4 + 1;
                            }
                            selectedPrototype.shadowLODMap[index] = EditorGUILayout.Popup(GPUInstancerEditorConstants.Contents.shadowLODs[i], selectedPrototype.shadowLODMap[index],
                                                                                          options);
                        }
                    }

                    selectedPrototype.cullShadows = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_cullShadows, selectedPrototype.cullShadows);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_cullShadows);
                }
            }

            EditorGUILayout.EndVertical();
            #endregion Shadows

            #region Culling
            EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
            GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_culling, GPUInstancerEditorConstants.Styles.boldLabel);

            selectedPrototype.maxDistance = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_maxDistance, selectedPrototype.maxDistance, 0.0f, GetMaxDistance(selectedPrototype));
            DrawHelpText(selectedPrototype is GPUInstancerDetailPrototype ? GPUInstancerEditorConstants.HELPTEXT_maxDistanceDetail : GPUInstancerEditorConstants.HELPTEXT_maxDistance);
            if (isFrustumCulling)
            {
                selectedPrototype.isFrustumCulling = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_isFrustumCulling, selectedPrototype.isFrustumCulling);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_isFrustumCulling);
                selectedPrototype.frustumOffset = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_frustumOffset, selectedPrototype.frustumOffset, 0.0f, 0.5f);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_frustumOffset);
            }

            if (isOcclusionCulling)
            {
                selectedPrototype.isOcclusionCulling = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_isOcclusionCulling, selectedPrototype.isOcclusionCulling);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_isOcclusionCulling);
            }

            if (isFrustumCulling || isOcclusionCulling)
            {
                selectedPrototype.minCullingDistance = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_minCullingDistance, selectedPrototype.minCullingDistance, 0, 100);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_minCullingDistance);
            }

            EditorGUILayout.EndVertical();
            #endregion Culling

            #region LOD
            if (selectedPrototype.prefabObject != null && (selectedPrototype.prefabObject.GetComponent <LODGroup>() != null || selectedPrototype.useGeneratedBillboard))
            {
                EditorGUILayout.BeginVertical(GPUInstancerEditorConstants.Styles.box);
                GPUInstancerEditorConstants.DrawCustomLabel(GPUInstancerEditorConstants.TEXT_LOD, GPUInstancerEditorConstants.Styles.boldLabel);

                EditorGUI.BeginDisabledGroup(Application.isPlaying);
                selectedPrototype.isLODCrossFade = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_isLODCrossFade, selectedPrototype.isLODCrossFade);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_isLODCrossFade);

                if (selectedPrototype.isLODCrossFade)
                {
                    selectedPrototype.isLODCrossFadeAnimate = EditorGUILayout.Toggle(GPUInstancerEditorConstants.TEXT_isLODCrossFadeAnimate, selectedPrototype.isLODCrossFadeAnimate);
                    DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_isLODCrossFadeAnimate);

                    if (!selectedPrototype.isLODCrossFadeAnimate)
                    {
                        selectedPrototype.lodFadeTransitionWidth = EditorGUILayout.Slider(GPUInstancerEditorConstants.TEXT_lodFadeTransitionWidth, selectedPrototype.lodFadeTransitionWidth, 0.0f, 1.0f);
                        DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_lodFadeTransitionWidth);
                    }
                }

                selectedPrototype.lodBiasAdjustment = EditorGUILayout.FloatField(GPUInstancerEditorConstants.TEXT_lodBiasAdjustment, selectedPrototype.lodBiasAdjustment);
                DrawHelpText(GPUInstancerEditorConstants.HELPTEXT_lodBiasAdjustment);
                EditorGUI.EndDisabledGroup();

                EditorGUILayout.EndVertical();
            }
            #endregion LOD

            EditorGUI.BeginDisabledGroup(Application.isPlaying);
            DrawGPUInstancerPrototypeInfo(selectedPrototype);

            DrawGPUInstancerPrototypeBillboardSettings(selectedPrototype, shaderBindings, billboardAtlasBindings);

            DrawGPUInstancerPrototypeActions();
            DrawGPUInstancerPrototypeAdvancedActions();

            if (EditorGUI.EndChangeCheck() && selectedPrototype != null)
            {
                EditorUtility.SetDirty(selectedPrototype);
            }
            EditorGUI.EndDisabledGroup();
            EditorGUILayout.EndVertical();
        }