/// <summary>
        /// Per Pixel Normals is a feature in U2018.3 with LWRP 4.0.1 or newer.
        /// It also requires Draw Instanced to be enabled on the terrain (new in U2018.3).
        /// If users want different per-pixel normals for different landscapes, they should set
        /// terrain type to custom, and use a custom material.
        /// </summary>
        /// <param name="lbLandscape"></param>
        /// <returns></returns>
        public static bool GetTerrainPerPixelNormals(LBLandscape lbLandscape)
        {
            bool isPerPixelNormalsEnabled = false;

            #if UNITY_2018_3_OR_NEWER
            if (lbLandscape != null)
            {
                LBLandscape.TerrainMaterialType _terrainMaterialType = lbLandscape.GetTerrainMaterialType();

                if (_terrainMaterialType == LBLandscape.TerrainMaterialType.URP)
                {
                    #if UNITY_2019_3_OR_NEWER
                    // Universal Render Pipeline requires U2019.3+
                    if (lbLandscape.GetLandscapeTerrainDrawInstanced())
                    {
                        // Check the custom material on the first terrain
                        Material _tCustomMat = lbLandscape.GetLandscapeTerrainCustomMaterial();

                        if (_tCustomMat != null && _tCustomMat.shader.name.Contains("Universal Render Pipeline/Terrain/Lit"))
                        {
                            if (_tCustomMat.HasProperty("_EnableInstancedPerPixelNormal"))
                            {
                                isPerPixelNormalsEnabled = (_tCustomMat.GetFloat("_EnableInstancedPerPixelNormal") > 0f);
                            }
                            else
                            {
                                Debug.LogWarning("URP material property _EnableInstancedPerPixelNormal is not available. Do you have URP 7.1.2 or newer in your project? Check Package Manager");
                            }
                        }
                    }
                    #endif
                }
                else if (_terrainMaterialType == LBLandscape.TerrainMaterialType.LWRP)
                {
                    if (lbLandscape.GetLandscapeTerrainDrawInstanced())
                    {
                        // Check the custom material on the first terrain
                        Material _tCustomMat = lbLandscape.GetLandscapeTerrainCustomMaterial();

                        // The shader path changed in LWRP 4.0 so make sure we have the correct version
                        if (_tCustomMat != null && _tCustomMat.shader.name.Contains("Lightweight Render Pipeline/Terrain/Lit"))
                        {
                            if (_tCustomMat.HasProperty("_TERRAIN_INSTANCED_PERPIXEL_NORMAL"))
                            {
                                isPerPixelNormalsEnabled = (_tCustomMat.GetFloat("_TERRAIN_INSTANCED_PERPIXEL_NORMAL") > 0f);
                            }
                            else
                            {
                                Debug.LogWarning("LWRP material property _TERRAIN_INSTANCED_PERPIXEL_NORMAL is not available. Do you have LWRP 4.0.1 or newer in your project? Check Package Manager");
                            }
                        }
                    }
                }
            }
            #endif

            return(isPerPixelNormalsEnabled);
        }
Exemple #2
0
        /// <summary>
        /// Load a template but delay short period of time after updating
        /// the infoPanel text to force it to be displayed
        /// For URP/LWRP/HDRP all trees will be forced to be billboarded
        /// as currently we are only using built-in SRP trees.
        /// </summary>
        /// <param name="templateNumber"></param>
        /// <returns></returns>
        IEnumerator LoadTemplateWithUIDelay(int templateNumber)
        {
            bool showSRPWarning = false;

            if (lbTemplates != null)
            {
                int numTemplates = lbTemplates.Length;

                if (templateNumber < numTemplates)
                {
                    LBTemplate lbTemplate = lbTemplates[templateNumber];

                    if (lbTemplate != null)
                    {
                        // Disable buttons
                        EnableButton(quality1Button, false);
                        EnableButton(quality2Button, false);
                        EnableButton(quality3Button, false);
                        EnableButton(quality4Button, false);
                        EnableButton(infoButton, false);
                        EnableButton(gpuButton, false);
                        DisplayBackgroundPanel(true);

                        currentTemplateName = lbTemplate.name;

                        SetInfoTitle("Loading Template... PLEASE WAIT");

                        string infoText = "Applying template... ";

                        SetInfoText(infoText);
                        DisplayInfoPanel(true);
                        yield return(new WaitForSeconds(0.1f));

                        // Remove any camera animators
                        LBCameraAnimator.RemoveCameraAnimatorsFromScene(lbLandscape, false);
                        yield return(new WaitForSeconds(0.1f));

                        // if LBLighting is in the scene, remove all references to terrains to
                        // prevent errors when the current landscape is deleted.
                        LBLighting lbLighting = GameObject.FindObjectOfType <LBLighting>();
                        if (lbLighting != null)
                        {
                            lbLighting.ClearAllTerrains();
                        }

                        if (lbLandscape != null)
                        {
                            DestroyImmediate(lbLandscape.gameObject);
                        }
                        lbLandscape = lbTemplate.CreateLandscapeFromTemplate("DemoLandscape");
                        Camera mainCamera = Camera.main;

                        if (mainCamera != null)
                        {
                            mainCamera.transform.position = new Vector3(2000f, 2000f, 0f);
                            mainCamera.transform.LookAt(lbLandscape.transform);
                        }

                        // Currently, when applying template ignore water if LWRP, URP or HDRP are enabled
                        // This could be updated if we had a LWRP/HDRP-compatible water asset
                        if (lbTemplate.ApplyTemplateToLandscape(lbLandscape, ignoreStartPosition, true, (isURP || isLWRP || isHDRP) ? null : waterPrefab, true))
                        {
                            infoText += "DONE\n\n";
                            SetInfoText(infoText);
                            yield return(new WaitForSeconds(0.1f));

                            lbLandscape.showTiming = false;
                            lbLandscape.SetLandscapeTerrains(true);

                            // Build the landscape
                            int numTerrains = (lbLandscape.landscapeTerrains == null ? 0 : lbLandscape.landscapeTerrains.Length);
                            if (numTerrains > 0)
                            {
                                // Our older templates don't use GPU for Topography and Path. If it is fully capable
                                // of using the GPU, list it here.
                                bool isModernTemplate = lbTemplate.name.StartsWith("DemoStoneCottage");

                                // Override the template GPU settings
                                lbLandscape.useGPUTopography = isModernTemplate ? enableGPU : false;
                                lbLandscape.useGPUTexturing  = enableGPU;
                                lbLandscape.useGPUGrass      = enableGPU;
                                lbLandscape.useGPUPath       = isModernTemplate ? enableGPU : false;

                                // Check for URP/LWRP/HDRP or do we need to create a default material for U2019.2.0 or newer
                                if (isURP || isLWRP || isHDRP || is201920Plus)
                                {
                                    float   pixelError = 0f;
                                    Terrain terrain    = null;
                                    LBLandscape.TerrainMaterialType terrainMaterialType = isURP ? LBLandscape.TerrainMaterialType.URP : (isLWRP ? LBLandscape.TerrainMaterialType.LWRP : (terrainMaterialType = isHDRP ? LBLandscape.TerrainMaterialType.HDRP : LBLandscape.TerrainMaterialType.BuiltInStandard));

                                    for (int tIdx = 0; tIdx < numTerrains; tIdx++)
                                    {
                                        terrain = lbLandscape.landscapeTerrains[tIdx];
                                        lbLandscape.SetTerrainMaterial(terrain, tIdx, (tIdx == numTerrains - 1), terrain.terrainData.size.x, ref pixelError, terrainMaterialType);

                                        // For URP/LWRP/HDRP all trees will be forced to be billboarded
                                        // as currently we are only using built-in SRP trees.
                                        if (isURP || isLWRP || isHDRP)
                                        {
                                            // Force all trees to be billboards
                                            terrain.treeBillboardDistance   = 0f;
                                            terrain.treeMaximumFullLODCount = 0;
                                            showSRPWarning = true;
                                        }
                                    }
                                }

                                // These are the cut-down versions without progress bars and minimal validation
                                infoText += "Building Topography" + (lbLandscape.useGPUTopography ? " (GPU)..." : "...");
                                SetInfoText(infoText);
                                yield return(new WaitForSeconds(0.1f));

                                lbLandscape.ApplyTopography(true, true);
                                infoText += "DONE\n\n";
                                infoText += "Adding Textures" + (lbLandscape.useGPUTexturing ? " (GPU)..." : "...");
                                SetInfoText(infoText);
                                yield return(new WaitForSeconds(0.1f));

                                lbLandscape.ApplyTextures(true, true);
                                infoText += "DONE\n\n";
                                infoText += "Placing Trees... ";
                                SetInfoText(infoText);
                                yield return(new WaitForSeconds(0.1f));

                                lbLandscape.ApplyTrees(true, true);
                                infoText += "DONE\n\n";
                                infoText += "Placing Grass" + (lbLandscape.useGPUGrass ? " (GPU)..." : "...");
                                SetInfoText(infoText);
                                yield return(new WaitForSeconds(0.1f));

                                lbLandscape.ApplyGrass(true, true);
                                infoText += "DONE\n\n";
                                SetInfoText(infoText);
                                yield return(new WaitForSeconds(0.1f));

                                if (lbLandscape.lbGroupList != null && lbLandscape.lbGroupList.Count > 0)
                                {
                                    infoText += "Placing Groups" + (lbLandscape.useGPUTopography ? " (GPU)..." : "...");
                                    SetInfoText(infoText);
                                    yield return(new WaitForSeconds(0.1f));

                                    lbLandscape.ApplyGroups(false, false);
                                }

                                // Apply lighting last as it needs the Camera Paths (and Animator camera to set up WeatherFX)
                                if (lbTemplate.isLBLightingIncluded)
                                {
                                    infoText += "Applying lighting... ";
                                    SetInfoText(infoText);
                                    yield return(new WaitForSeconds(0.1f));

                                    //if (lbLighting != null) { DestroyImmediate(lbLighting.gameObject); }

                                    if (lbLighting == null)
                                    {
                                        // If LBLighting isn't already in the scene, add it.
                                        lbLighting = LBLighting.AddLightingToScene(true);
                                    }

                                    if (lbLighting != null)
                                    {
                                        // Restore the lighting settings from the template
                                        lbTemplate.ApplyLBLightingSettings(lbLandscape, ref lbLighting, mainCamera, true);
                                    }
                                    infoText += "DONE\n\n";
                                    SetInfoText(infoText);
                                    yield return(new WaitForSeconds(0.1f));

                                    // Prior to Unity 5.4, ImageFX and/or camera clear flags are not updated correctly
                                    // when the WeatherFX with celestials is added to the scene after a configuration without
                                    // celestials. The workaround, is to force the GameView to repaint or be re-initalised.
                                    // To be effective, a "reasonable" delay is required between switching away from Game view
                                    // and back. The exact cause of the issue is unknown...
                                    #if !UNITY_5_4_OR_NEWER && UNITY_EDITOR
                                    if (lbLighting.useCelestials)
                                    {
                                        bool wasMaximized = LBEditorHelper.GameViewMaximize(this.GetType(), false);
                                        LBEditorHelper.ShowSceneView(this.GetType(), true);
                                        yield return(new WaitForSeconds(0.5f));

                                        LBEditorHelper.GameViewMaximize(this.GetType(), wasMaximized);
                                    }
                                    #endif
                                }

                                // Find the first animation and start it (there should be only one in a demo template)
                                LBCameraAnimator lbCameraAnimator = LBCameraAnimator.GetFirstCameraAnimatorInLandscape(lbLandscape);
                                if (lbCameraAnimator != null)
                                {
                                    if (lbTemplate.name.StartsWith("FPS Forest Demo"))
                                    {
                                        // This uses the old Unity 4 trees which don't work so well with anti-aliasing
                                        QualitySettings.antiAliasing = 0;

                                        if (lbCameraAnimator.animatorCamera != null)
                                        {
                                            lbCameraAnimator.SetMoveSpeed(2f);
                                            lbCameraAnimator.animatorCamera.renderingPath = RenderingPath.Forward;
                                            lbCameraAnimator.animateSpeed = false;
                                        }
                                    }
                                    else if (lbTemplate.name.StartsWith("DemoStoneCottage"))
                                    {
                                        lbCameraAnimator.minMoveSpeed = 1f;
                                        lbCameraAnimator.maxMoveSpeed = 3f;
                                        lbCameraAnimator.animateSpeed = true;
                                    }
                                    else
                                    {
                                        lbCameraAnimator.animateSpeed = true;
                                    }

                                    // Reset LBImageFX timing so clouds always appear the same
                                    if (lbCameraAnimator.animatorCamera != null)
                                    {
                                        LBImageFX lbImageFX = lbCameraAnimator.animatorCamera.GetComponent <LBImageFX>();
                                        if (lbImageFX != null)
                                        {
                                            //Debug.Log("LBDemoLoader.LoadTemplateWithUIDelay - Resetting LBImageFX cloud positions");
                                            lbImageFX.ResetCloudPosition();
                                        }
                                    }

                                    //Debug.Log("LBDemoLoader " + lbTemplate.name + " renderpath: " + lbCameraAnimator.animatorCamera.renderingPath);

                                    lbCameraAnimator.pauseAtEndDuration = 999f;
                                    lbCameraAnimator.BeginAnimation(true, 0f);
                                }
                                else
                                {
                                    Debug.LogWarning("LBDemoLoader.LoadTemplateWithUIDelay - Couldn't find a camera animator in the demo template");
                                }
                            }
                        }

                        // Re-enable buttons
                        EnableButton(quality1Button, true);
                        EnableButton(quality2Button, true);
                        EnableButton(quality3Button, true);
                        EnableButton(quality4Button, true);
                        EnableButton(infoButton, true);
                        EnableButton(gpuButton, true);
                        UpdateInfoButtonText("Show Info");
                    }
                }
            }

            if (showSRPWarning)
            {
                SetInfoTitle("SRP WARNING");
                SetInfoText("This demo scene is designed for the built-in RP.\n\nSee SRP folder for into on URP and HDRP");
            }
            else
            {
                DisplayInfoPanel(false);
            }

            DisplayBackgroundPanel(false);
        }
    // Use this for initialization
    void Awake()
    {
        #region Initialise
        // This line just gets the starting time of the generation so that the total generation time
        // can be recorded and displayed
        float generationStartTime = Time.realtimeSinceStartup;

        RuntimeSampleHelper.RemoveDefaultCamera();
        RuntimeSampleHelper.RemoveDefaultLight();

        // Get a link to the LBLandscape script
        landscape = this.GetComponent <LBLandscape>();

        if (landscape == null)
        {
            Debug.Log("Cannot find LBLandscape script attached to Runtime gameobject");
            return;
        }

        // Check to see if Universal Render Pipeline is installed in the project
        bool isURP = LBLandscape.IsURP(false);

        // Check to see if Light Weight Render Pipeline is installed in this project
        bool isLWRP = !isURP && LBLandscape.IsLWRP(false);

        // Check to see if High Definition Render Pipeline is installed in this project
        bool isHDRP = !isURP && !isLWRP && LBLandscape.IsHDRP(false);

        #if UNITY_2019_2_OR_NEWER
        bool is201920Plus = true;
        #else
        bool is201920Plus = false;
        #endif

        #endregion

        #region Create the terrains and store references to them
        terrainsList = new List <Terrain>();
        int terrainNumber = 0;

        for (float tx = 0f; tx < landscapeSize.x - 1f; tx += 2000f)
        {
            for (float ty = 0f; ty < landscapeSize.y - 1f; ty += 2000f)
            {
                // Create a new gameobject
                GameObject terrainObj = new GameObject("Runtime Terrain " + (terrainNumber++).ToString("000"));

                // Correctly parent and position the terrain
                terrainObj.transform.parent        = this.transform;
                terrainObj.transform.localPosition = new Vector3(tx, 0f, ty);

                // Add a terrain component
                Terrain newTerrain = terrainObj.AddComponent <Terrain>();

                // Set terrain settings (depending on your situtation, you may need to set more or less than I have in this example)
                newTerrain.heightmapPixelError   = 1;
                newTerrain.basemapDistance       = 5000f;
                newTerrain.treeDistance          = 5000f;
                newTerrain.treeBillboardDistance = 100f;
                newTerrain.detailObjectDistance  = 150f;
                newTerrain.treeCrossFadeLength   = 25f;

                // Set terrain data settings (same as above comment)
                TerrainData newTerrainData = new TerrainData();

                // One thing to note here is that modfiying the heightmap resolution not only clears all terrai height data,
                // it also scales up or down the size of the terrain. So you should always set the heightmap resolution
                // BEFORE you set the terrain size
                newTerrainData.heightmapResolution = 513;
                newTerrainData.size = Vector3.one * 2000f;
                newTerrainData.SetDetailResolution(1024, 16);
                newTerrain.terrainData = newTerrainData;

                // Set up the terrain collider
                TerrainCollider newTerrainCol = terrainObj.AddComponent <TerrainCollider>();
                newTerrainCol.terrainData = newTerrainData;

                // Add the terrain to the list of terrains
                terrainsList.Add(newTerrain);
            }
        }

        #endregion

        landscape.SetLandscapeTerrains(true);
        landscape.SetTerrainNeighbours(false);

        #region Set the terrain material
        int numTerrains = landscape.landscapeTerrains == null ? 0 : landscape.landscapeTerrains.Length;

        // Check for URP/LWRP/HDRP or do we need to create a default material for U2019.2.0 or newer
        if (isURP || isLWRP || isHDRP || is201920Plus)
        {
            float   pixelError = 0f;
            Terrain terrain    = null;
            LBLandscape.TerrainMaterialType terrainMaterialType = isURP ? LBLandscape.TerrainMaterialType.URP : (isLWRP ? LBLandscape.TerrainMaterialType.LWRP : (terrainMaterialType = isHDRP ? LBLandscape.TerrainMaterialType.HDRP : LBLandscape.TerrainMaterialType.BuiltInStandard));

            for (int tIdx = 0; tIdx < numTerrains; tIdx++)
            {
                terrain = landscape.landscapeTerrains[tIdx];
                landscape.SetTerrainMaterial(terrain, tIdx, (tIdx == numTerrains - 1), terrain.terrainData.size.x, ref pixelError, terrainMaterialType);
            }
        }
        #endregion

        // Set the topography noise variables
        float maskWarpAmount    = 0f;
        float maskNoiseTileSize = 10000f;
        float maskNoiseOffsetX  = 0f;
        float maskNoiseOffsetY  = 0f;

        AnimationCurve distanceToCentreMask = new AnimationCurve();
        int            keyInt = distanceToCentreMask.AddKey(0f, 1f);
        keyInt = distanceToCentreMask.AddKey(0.529f, 0.959f);
        keyInt = distanceToCentreMask.AddKey(1f, 0f);
        Keyframe[] curveKeys = distanceToCentreMask.keys;
        curveKeys[0].inTangent  = 0f;
        curveKeys[0].outTangent = 0f;
        curveKeys[1].inTangent  = -0.25f;
        curveKeys[1].outTangent = -0.25f;
        curveKeys[2].inTangent  = 0f;
        curveKeys[2].outTangent = 0f;
        distanceToCentreMask    = new AnimationCurve(curveKeys);

        AnimationCurve maskNoiseCurveModifier = AnimationCurve.Linear(0f, 0.5f, 1f, 1f);

        // Avoid warning of keyInt not being used.
        if (keyInt == 0)
        {
        }

        // Create the Topography Layers
        // You can mix and match Perlin and Image layers
        landscape.topographyLayersList = new List <LBLayer>();
        if (landscape.topographyLayersList != null)
        {
            // Add one or more Base layers
            LBLayer lbBaseLayer1 = new LBLayer();
            if (lbBaseLayer1 != null)
            {
                lbBaseLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.DesertFloorBase);
                landscape.topographyLayersList.Add(lbBaseLayer1);
            }

            // Add one or more Additive layers
            LBLayer lbAdditiveLayer1 = new LBLayer();
            if (lbAdditiveLayer1 != null)
            {
                // You can manually configure a layer, or use a preset then modify it.
                lbAdditiveLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.MountainRangeComplexBase);
                // If using using a different type of preset, must set the type after applying preset
                lbAdditiveLayer1.type = LBLayer.LayerType.PerlinAdditive;
                // Optionally override the preset settings
                lbAdditiveLayer1.noiseTileSize   = 5000f;
                lbAdditiveLayer1.octaves         = 8;
                lbAdditiveLayer1.lacunarity      = 1.92f;
                lbAdditiveLayer1.gain            = 0.45f;
                lbAdditiveLayer1.warpAmount      = 0;
                lbAdditiveLayer1.removeBaseNoise = true;
                lbAdditiveLayer1.heightScale     = 1f;
                lbAdditiveLayer1.additiveAmount  = 0.75f;
                lbAdditiveLayer1.additiveCurve   = LBLayer.CreateAdditiveCurve(lbAdditiveLayer1.additiveAmount);
                lbAdditiveLayer1.perOctaveCurveModifierPresets = new List <LBCurve.CurvePreset>();
                lbAdditiveLayer1.perOctaveCurveModifierPresets.Add(LBCurve.CurvePreset.DoubleRidged);
                landscape.topographyLayersList.Add(lbAdditiveLayer1);
            }

            // Add a detail layer
            LBLayer lbDetailLayer1 = new LBLayer();
            if (lbDetailLayer1 != null)
            {
                lbDetailLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.HillsDetail);
                landscape.topographyLayersList.Add(lbDetailLayer1);
            }
        }

        // Create the terrain topographies
        for (int t = 0; t < terrainsList.Count && landscape.topographyLayersList != null; t++)
        {
            // Add the topography layers
            terrainsList[t].terrainData = LBLandscapeTerrain.HeightmapFromLayers(landscape, terrainsList[t].terrainData,
                                                                                 terrainsList[t].transform.position, landscapeSize, landscape.transform.position, landscape.topographyLayersList);


            if (IsMaskingOn)
            {
                // Example of applying a mask to the terrain topography
                terrainsList[t].terrainData = LBLandscapeTerrain.MaskedHeightmap(terrainsList[t].terrainData,
                                                                                 terrainsList[t].transform.position, landscapeSize, transform.position,
                                                                                 1, distanceToCentreMask, maskWarpAmount, maskNoiseTileSize,
                                                                                 new Vector2(maskNoiseOffsetX, maskNoiseOffsetY), maskNoiseCurveModifier);
            }
        }

        // Create a list of LBTerrainTexture objects
        // These contain the textures and normal maps but also the rules for applying them to the terrain

        landscape.terrainTexturesList = new List <LBTerrainTexture>();

        // Populate the list by creating temporary LBTerrainTexture objects and adjusting their settings,
        // then adding each one into the list

        // Grass Hill texture
        LBTerrainTexture tempTerrainTexture = new LBTerrainTexture();
        tempTerrainTexture.texture   = grassHillTexture;
        tempTerrainTexture.normalMap = grassHillNormalMap;

        tempTerrainTexture.tileSize       = Vector2.one * 25f;
        tempTerrainTexture.minInclination = 0f;
        tempTerrainTexture.maxInclination = 45f;
        tempTerrainTexture.useNoise       = true;
        tempTerrainTexture.noiseTileSize  = 100f;
        tempTerrainTexture.texturingMode  = LBTerrainTexture.TexturingMode.Inclination;
        landscape.terrainTexturesList.Add(tempTerrainTexture);

        // Rock Layered texture
        tempTerrainTexture                = new LBTerrainTexture();
        tempTerrainTexture.texture        = rockLayeredTexture;
        tempTerrainTexture.normalMap      = rockLayeredNormalMap;
        tempTerrainTexture.tileSize       = Vector2.one * 100f;
        tempTerrainTexture.minInclination = 30f;
        tempTerrainTexture.maxInclination = 90f;
        tempTerrainTexture.useNoise       = true;
        tempTerrainTexture.noiseTileSize  = 100f;
        tempTerrainTexture.texturingMode  = LBTerrainTexture.TexturingMode.Inclination;
        landscape.terrainTexturesList.Add(tempTerrainTexture);

        // Texture the terrains
        for (int t = 0; t < terrainsList.Count; t++)
        {
            // Use the LBLandscapeTerrain.TextureTerrain function for texturing the terrain
            terrainsList[t].terrainData = LBLandscapeTerrain.TextureTerrain(terrainsList[t].terrainData, landscape.terrainTexturesList,
                                                                            terrainsList[t].transform.position, landscapeSize, this.transform.position, false, landscape);
        }

        // Display the total time taken to generate the landscape (usually for debugging purposes)
        Debug.Log("Time taken to generate landscape: " + (Time.realtimeSinceStartup - generationStartTime).ToString("00.00") + " seconds.");
    }
        /// <summary>
        /// Copy LB Texturing tab Textures to MicroSplat.
        /// Typically called from within the Texturing tab of LB Editor window.
        /// </summary>
        /// <param name="landscape"></param>
        /// <param name="terrainMaterialType"></param>
        /// <param name="terrainMat"></param>
        /// <param name="showErrors"></param>
        public static void MicroSplatCopyTextures(LBLandscape landscape, LBLandscape.TerrainMaterialType terrainMaterialType, Material terrainMat, bool showErrors)
        {
            string methodName = "LBEditorIntegration.MicroSplatCopyTextures";

            // Basic validation
            if (landscape == null)
            {
                if (showErrors)
                {
                    Debug.LogWarning(methodName + " - landscape cannot be null");
                }
            }
            else if (terrainMaterialType != LBLandscape.TerrainMaterialType.MicroSplat)
            {
                if (showErrors)
                {
                    Debug.LogWarning(methodName + " - The Landscape Terrain Settings needs to have a Material Type of MicroSplat. Please set and try again.");
                }
            }
            else if (terrainMat == null)
            {
                if (showErrors)
                {
                    Debug.LogWarning(methodName + " - the landscape does not appear to have a MicroSplat material. Has it been initialised in the Landscape Terrain Settings?");
                }
            }
            else
            {
                // Find the Texture Array Config file
                //JBooth.MicroSplat.TextureArrayConfig textureArrayConfig = LBEditorHelper.GetAsset<JBooth.MicroSplat.TextureArrayConfig>("MicroSplatData", terrainMat.name.Replace("MicroSplat","MicroSplatConfig") + ".asset");
                JBooth.MicroSplat.TextureArrayConfig textureArrayConfig = GetTextureArrayConfig(terrainMat);
                if (textureArrayConfig == null)
                {
                    if (showErrors)
                    {
                        Debug.LogWarning("ERROR: " + methodName + " could not find TextureArrayConfig [MicroSplatData/" + terrainMat.name.Replace("MicroSplat", "MicroSplatConfig") + ".asset]");
                    }
                }
                else
                {
                    // Get existing list of Textures from MicroSplat
                    List <JBooth.MicroSplat.TextureArrayConfig.TextureEntry> textureOldEntryList = textureArrayConfig.sourceTextures;
                    //int numMSTextures = (textureOldEntryList == null ? 0 : textureOldEntryList.Count);
                    int numLBTextures         = (landscape.terrainTexturesList == null ? 0 : landscape.terrainTexturesList.Count);
                    LBTerrainTexture lbTrnTex = null;
                    JBooth.MicroSplat.TextureArrayConfig.TextureEntry textureEntry = null;

                    //Debug.Log("[DEBUG] numMSTextures: " + numMSTextures + " numLBTextures:" + numLBTextures);

                    // Create a new list
                    List <JBooth.MicroSplat.TextureArrayConfig.TextureEntry> textureNewEntryList = new List <JBooth.MicroSplat.TextureArrayConfig.TextureEntry>();

                    // Loop through all the Landscape Builder Textures
                    for (int lbTexIdx = 0; lbTexIdx < numLBTextures; lbTexIdx++)
                    {
                        lbTrnTex = landscape.terrainTexturesList[lbTexIdx];

                        if (lbTrnTex != null && !lbTrnTex.isDisabled)
                        {
                            // Find matching MicroSplat texture entry
                            textureEntry = textureOldEntryList.Find(te => (lbTrnTex.texture != null && te.diffuse != null && te.diffuse.name == lbTrnTex.texture.name) &&
                                                                    (lbTrnTex.normalMap == null && te.normal == null || (lbTrnTex.normalMap != null && te.normal != null && te.normal.name == lbTrnTex.normalMap.name)) &&
                                                                    (lbTrnTex.heightMap == null && te.height == null || (lbTrnTex.heightMap != null && te.height != null && te.height.name == lbTrnTex.heightMap.name))
                                                                    );
                            //textureEntry = textureOldEntryList.Find(te => lbTrnTex.texture != null && te.diffuse != null && te.diffuse.name == lbTrnTex.texture.name);

                            if (textureEntry != null)
                            {
                                // update tinted texture if required
                                textureEntry.diffuse = lbTrnTex.isTinted ? (lbTrnTex.tintedTexture == null ? lbTrnTex.texture : lbTrnTex.tintedTexture) : lbTrnTex.texture;

                                textureNewEntryList.Add(textureEntry);
                                //Debug.Log("[DEBUG] Added matching entry: " + textureEntry.diffuse.name);
                            }
                            else
                            {
                                // Add missing textures
                                textureEntry = new JBooth.MicroSplat.TextureArrayConfig.TextureEntry();
                                if (textureEntry != null)
                                {
                                    textureEntry.diffuse = lbTrnTex.isTinted ? (lbTrnTex.tintedTexture == null ? lbTrnTex.texture : lbTrnTex.tintedTexture) : lbTrnTex.texture;
                                    textureEntry.normal  = lbTrnTex.normalMap;
                                    textureEntry.height  = lbTrnTex.heightMap;
                                    textureNewEntryList.Add(textureEntry);

                                    //Debug.Log("[DEBUG] Adding new entry: " + lbTrnTex.texture.name);
                                }
                            }

                            // In MicroSplat the first splatprototype sets the default tilesize
                            if (lbTexIdx == 0)
                            {
                                // Convert UVs to MicroSplat tiling
                                Vector3 terrainSize = landscape.GetLandscapeTerrainSize();
                                // scale, offset (default offset to 0,0)
                                terrainMat.SetVector("_UVScale", new Vector4(1.0f / (lbTrnTex.tileSize.x / terrainSize.x), 1.0f / (lbTrnTex.tileSize.y / terrainSize.z), 0f, 0f));
                            }
                            //Texture2DArray diff = textureArrayConfig.GetTexture("_Diffuse") as Texture2DArray;
                        }
                    }

                    // Update the list of source textures in MicroSplat
                    textureArrayConfig.sourceTextures = textureNewEntryList;
                    staticMSTextureArrayConfig        = textureArrayConfig;
                    // Gets called once only, after all the Inspectors have been updated
                    EditorApplication.delayCall += MicroSplatDelayedCompileConfig;
                }
            }
        }
        /// <summary>
        /// Copy the Landscape textures from LB to MegaSplat
        /// EDITOR-ONLY
        /// </summary>
        /// <param name="landscape"></param>
        /// <param name="terrainMaterialType"></param>
        /// <param name="showErrors"></param>
        public static void MegaSplatCopyTextures(LBLandscape landscape, LBLandscape.TerrainMaterialType terrainMaterialType, bool showErrors)
        {
            string methodName = "LBEditorIntegration.MegaSplatCopyTextures";

            // Basic validation
            if (landscape == null)
            {
                if (showErrors)
                {
                    Debug.LogWarning(methodName + " - landscape cannot be null");
                }
            }
            else if (terrainMaterialType != LBLandscape.TerrainMaterialType.MegaSplat)
            {
                if (showErrors)
                {
                    Debug.LogWarning(methodName + " - The Landscape Terrain Settings needs to have a Material Type of Mega Splat. Please set and try again.");
                }
            }
            else if (landscape.terrainCustomMaterial == null)
            {
                if (showErrors)
                {
                    Debug.LogWarning(methodName + " - the landscape does not appear to have a MegaSplat material. Has it been initialised in the Landscape Terrain Settings?");
                }
            }
            else
            {
                UnityEngine.Object obj = UnityEditor.Selection.activeObject;

                if (obj != null)
                {
                    System.Type cfgType                      = obj.GetType();
                    System.Type cfgEditorType                = null;
                    System.Type TerrainPainterWindowType     = null;
                    System.Type ITerrainPainterUtilityType   = null;
                    System.Type TerrainToMegaSplatConfigType = null;

                    if (cfgType.FullName == "JBooth.MegaSplat.TextureArrayConfig")
                    {
                        //Debug.Log("MegaSplatCopyTextures cfgType found: " + cfgType.AssemblyQualifiedName);

                        UnityEngine.Object objTerrainToMegaSplatConfig = LBIntegration.MegaSplatCreateTerrainToMegaSplatConfig(landscape, terrainMaterialType, obj, showErrors);

                        // Get the textures from the landscape
                        List <LBTerrainTexture> terrainTextureList = landscape.TerrainTexturesList();

                        if (terrainTextureList != null)
                        {
                            if (terrainTextureList.Count > 0)
                            {
                                // Add all the non-disabled textures to a list

                                // Previously MegaSplat uses an array of Texture2Ds. Now it has a TextureEntry class
                                // which can hold multiple textures (diffuse, normalmap, heightmap etc)
                                List <JBooth.MegaSplat.TextureArrayConfig.TextureEntry> textureEntryList = new List <JBooth.MegaSplat.TextureArrayConfig.TextureEntry>();
                                JBooth.MegaSplat.TextureArrayConfig.TextureEntry        textureEntry     = null;

                                for (int t = 0; t < terrainTextureList.Count; t++)
                                {
                                    LBTerrainTexture lbTerrainTexture = terrainTextureList[t];
                                    if (!lbTerrainTexture.isDisabled)
                                    {
                                        Texture2D texture2D = lbTerrainTexture.texture;
                                        if (texture2D != null)
                                        {
                                            textureEntry = new JBooth.MegaSplat.TextureArrayConfig.TextureEntry();
                                            if (textureEntry != null)
                                            {
                                                textureEntry.diffuse = texture2D;
                                                textureEntry.normal  = lbTerrainTexture.normalMap;
                                                textureEntry.height  = lbTerrainTexture.heightMap;
                                                textureEntryList.Add(textureEntry);
                                            }
                                        }
                                    }
                                }
                                try
                                {
                                    if (textureEntryList.Count > 0)
                                    {
                                        cfgEditorType                = System.Type.GetType("JBooth.MegaSplat.TextureArrayConfigEditor, Assembly-CSharp-Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", true, true);
                                        TerrainPainterWindowType     = System.Type.GetType("JBooth.TerrainPainter.TerrainPainterWindow, Assembly-CSharp-Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", true, true);
                                        ITerrainPainterUtilityType   = System.Type.GetType("JBooth.TerrainPainter.ITerrainPainterUtility, Assembly-CSharp-Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", true, true);
                                        TerrainToMegaSplatConfigType = objTerrainToMegaSplatConfig.GetType();

                                        var cfgEditorWindow = UnityEditor.EditorWindow.GetWindow <UnityEditor.EditorWindow>("Inspector", false);

                                        if (cfgEditorType == null)
                                        {
                                            if (showErrors)
                                            {
                                                Debug.LogWarning(methodName + " - could not find TextureArrayConfigEditor editor class. Please Report.");
                                            }
                                        }
                                        else if (TerrainPainterWindowType == null)
                                        {
                                            if (showErrors)
                                            {
                                                Debug.LogWarning(methodName + " - could not find TerrainPainterWindowType editor class. Please Report.");
                                            }
                                        }
                                        else if (ITerrainPainterUtilityType == null)
                                        {
                                            if (showErrors)
                                            {
                                                Debug.LogWarning(methodName + " - could not find ITerrainPainterUtilityType editor class. Please Report.");
                                            }
                                        }
                                        else if (TerrainToMegaSplatConfigType == null)
                                        {
                                            if (showErrors)
                                            {
                                                Debug.LogWarning(methodName + " - could not find TerrainToMegaSplatConfigType editor class. Please Report.");
                                            }
                                        }
                                        else
                                        {
                                            // LB 2.0.6 Beta 7j add compile step back in (was previously broken and had to be done manually in the editor)
                                            //string[] shaderFeatures = { "_TERRAIN" };
                                            //LBIntegration.MegaSplatCompileShader(landscape, shaderFeatures, false, true);


                                            // Previously MegaSplat uses an array of Texture2Ds. Now it has a TextureEntry class
                                            cfgType.GetField("sourceTextures").SetValue(obj, textureEntryList);

                                            if (cfgEditorWindow != null)
                                            {
                                                cfgEditorType.InvokeMember("CompileConfig", System.Reflection.BindingFlags.InvokeMethod, null, null, new object[] { obj });

                                                // Select the landscape in the Hierarchy and open the Terrain Painter window
                                                UnityEditor.Selection.activeGameObject = landscape.gameObject;
                                                var windowTerrainPainter = UnityEditor.EditorWindow.GetWindow(TerrainPainterWindowType, false);
                                                if (windowTerrainPainter == null)
                                                {
                                                    Debug.LogWarning("LBIntegration.MegaSplatCopyTextures - Could not open TerrainPainter window. Please Report.");
                                                }
                                                else
                                                {
                                                    // Add the TextureArrayConfig to the TerrainToMegaSplatConfig file
                                                    TerrainToMegaSplatConfigType.InvokeMember("config", System.Reflection.BindingFlags.SetField | System.Reflection.BindingFlags.Public, null, objTerrainToMegaSplatConfig, new object[] { obj });

                                                    // Updates the Paint tab in Terrain Painter
                                                    TerrainPainterWindowType.InvokeMember("config", System.Reflection.BindingFlags.SetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public, null, windowTerrainPainter, new object[] { obj });

                                                    // Initialise Utilities (else results in a timing issue where Utilities not found first time)
                                                    TerrainPainterWindowType.InvokeMember("InitPluginUtilities", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public, null, windowTerrainPainter, new object[] { });

                                                    // We need to give time for MegaSplat to configure the Texture Array
                                                    Debug.Log("INFO: Setting up MegaSplat Texture Array...");

                                                    // Update "config" field of the Terrain Converter on the Utilities tab
                                                    IList utilitiesList = (IList)TerrainPainterWindowType.InvokeMember("utilities", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public, null, windowTerrainPainter, new object[] { });
                                                    if (utilitiesList == null)
                                                    {
                                                        if (showErrors)
                                                        {
                                                            Debug.LogWarning("LBIntegration.MegaSplatCopyTextures - Could not find Utilities. Please Report.");
                                                        }
                                                    }
                                                    else if (utilitiesList != null)
                                                    {
                                                        if (utilitiesList.Count == 0)
                                                        {
                                                            if (showErrors)
                                                            {
                                                                Debug.LogWarning("LBIntegration.MegaSplatCopyTextures - No Utilities found. Please Report.");
                                                            }
                                                        }

                                                        // Located the Terrain To Splat Converter utility
                                                        foreach (var tPainterInterface in utilitiesList)
                                                        {
                                                            if (tPainterInterface.ToString() == "JBooth.MegaSplat.TerrainToSplatConverter")
                                                            {
                                                                //Debug.Log("Interface: " + tPainterInterface.GetType().Name);
                                                                tPainterInterface.GetType().InvokeMember("config", System.Reflection.BindingFlags.SetField | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic, null, tPainterInterface, new object[] { objTerrainToMegaSplatConfig });

                                                                int tabIndex = LBIntegration.MegaSplatGetTerrainPainterTabIndex("Utility", true);

                                                                // Show the Utilities tab
                                                                TerrainPainterWindowType.InvokeMember("tab", System.Reflection.BindingFlags.SetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public, null, windowTerrainPainter, new object[] { tabIndex });

                                                                //var terrainJobs = TerrainPainterWindowType.InvokeMember("terrains", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public, null, windowTerrainPainter, new object[] { });

                                                                ////Debug.Log("Calling OnGUI");
                                                                //tPainterInterface.GetType().InvokeMember("OnGUI", System.Reflection.BindingFlags.InvokeMethod, null, tPainterInterface, new object[] { terrainJobs });
                                                                break;
                                                            }
                                                        }
                                                    }

                                                    LBIntegration.MegaSplatUpdateShaderSplat(landscape, "Albedo", false, obj, true);
                                                }
                                            }
                                            else
                                            {
                                                Debug.LogWarning(methodName + " - Could not open TextureArrayConfigEditor window. Please Report.");
                                            }
                                        }
                                    }
                                }
                                catch (System.Exception ex)
                                {
                                    if (showErrors)
                                    {
                                        Debug.LogWarning(methodName + " something has gone wrong (MegaSplat 1.73+ required). Please report. " + ex.Message);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        Debug.LogWarning("MegaSplatCopyTextures - Please select the Texture Array Config in the Project window and try again.");
                    }
                }
                else
                {
                    Debug.LogWarning("MegaSplatCopyTextures - Please select the Texture Array Config in the Project window and try again.");
                }
            }
        }