Fixing Inflated TerrainData Files (Over 1Gb)

Hello, I am creating this post to help any future wanderers encountering unreasonably big TerrainData files (likely due to some untracked data or corruption). Here is a step by step guide on how to recreate the terrainData while keeping all settings.

TerrainData keeps track of terrain geometry and details/trees placement.

  1. Saving Heightmap/Splatmap
    Unity has a handy Import/Export tool for those in TerrainToolbox.
    (Window/Terrain/Terrain Toolbox/Terrain Utilities)

  2. Create new TerrainData file with same size (width, height…) as the previous one. You can do this manually by creating a new terrain or use TerrainTools again which is a little more convenient (Note we dont want to Duplicate the TerrainData from old terrain as that will also copy the problematic inflated data).
    You can locate the new TerrainData file through the TerrainCollider component, its usually Assets/Terrain/

  3. Update old Terrain Object with new TerrainData
    Turn on debug mode in Inspector (Three dots/Tick Debug). Now a new field in the terrain component will show up:
    image
    Place the new terrainData in it as well as in the TerrainCollider.

  4. Reimport Splats/Heightmaps
    This can be done in terrainToolbox again using Terrain Utilities/Import.

  5. Import Details/Trees
    I havent seen a way to do this with TerrainTools so here is an editor script which copies Coverage Grass + Trees placement data from old TerrainData to a new one:

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

public class TerrainCopyExtension : EditorWindow
{
    private TerrainData sourceTerrainData;
    private TerrainData destinationTerrainData;
    private bool copyTrees = true;
    private bool copyGrass = true;

    [MenuItem("Tools/Terrain Copy Extension")]
    public static void ShowWindow()
    {
        GetWindow<TerrainCopyExtension>("Terrain Copy");
    }

    private void OnGUI()
    {
        GUILayout.Label("Copy Terrain Data", EditorStyles.boldLabel);

        sourceTerrainData = (TerrainData)EditorGUILayout.ObjectField("Source Terrain Data", sourceTerrainData, typeof(TerrainData), false);
        destinationTerrainData = (TerrainData)EditorGUILayout.ObjectField("Destination Terrain Data", destinationTerrainData, typeof(TerrainData), false);

        copyTrees = EditorGUILayout.Toggle("Copy Trees", copyTrees);
        copyGrass = EditorGUILayout.Toggle("Copy Grass", copyGrass);

        if (GUILayout.Button("Copy Data"))
        {
            if (sourceTerrainData == null || destinationTerrainData == null)
            {
                EditorUtility.DisplayDialog("Error", "Please assign both source and destination Terrain Data.", "OK");
                return;
            }

            CopyTerrainData();
        }
    }

    private void CopyTerrainData()
    {
        if (copyTrees)
        {
            CopyTrees();
        }

        if (copyGrass)
        {
            CopyGrass();
        }

        EditorUtility.SetDirty(destinationTerrainData);
        AssetDatabase.SaveAssets();
        EditorUtility.DisplayDialog("Success", "Terrain data copied successfully!", "OK");
    }

    private void CopyTrees()
    {
        // Copy tree prototypes
        destinationTerrainData.treePrototypes = sourceTerrainData.treePrototypes;

        // Get the source tree instances
        TreeInstance[] sourceTreeInstances = sourceTerrainData.treeInstances;

        // Create a list to store the new tree instances
        List<TreeInstance> newTreeInstances = new List<TreeInstance>();

        // Calculate the scale factors
        Vector3 sourceSize = sourceTerrainData.size;
        Vector3 destSize = destinationTerrainData.size;
        Vector3 scaleFactor = new Vector3(destSize.x / sourceSize.x, destSize.y / sourceSize.y, destSize.z / sourceSize.z);

        // Copy and adjust each tree instance
        foreach (TreeInstance sourceTree in sourceTreeInstances)
        {
            TreeInstance newTree = sourceTree;

            // Adjust the position to fit the new terrain
            newTree.position = new Vector3(
                sourceTree.position.x * scaleFactor.x,
                sourceTree.position.y * scaleFactor.y,
                sourceTree.position.z * scaleFactor.z
            );

            // Optionally, you might want to adjust the scale of the trees as well
            newTree.widthScale *= scaleFactor.x;
            newTree.heightScale *= scaleFactor.y;

            newTreeInstances.Add(newTree);
        }

        // Set the new tree instances on the destination terrain
        destinationTerrainData.SetTreeInstances(newTreeInstances.ToArray(), true);
    }

    private void CopyGrass()
    {
        int layerCount = sourceTerrainData.detailPrototypes.Length;
        destinationTerrainData.detailPrototypes = sourceTerrainData.detailPrototypes;

        for (int layer = 0; layer < layerCount; layer++)
        {
            int[,] sourceDetailLayer = sourceTerrainData.GetDetailLayer(0, 0, sourceTerrainData.detailWidth, sourceTerrainData.detailHeight, layer);
            destinationTerrainData.SetDetailLayer(0, 0, layer, sourceDetailLayer);
        }
    }
}

And that should be all to get a 1:1 terrain copy onto new TerrainData. With these steps I’ve reduced TerrainData from 1.1Gb all the way down to 143mb. If there is a better way to do it. feel free to contribute to the thread!

Is there a good reason for not using an LTS version of Unity? That’s problem #1 right there if you’re using 2023.2 …it’s a recipe for disaster and kinda futile imo to to try and troubleshoot anything on an unstable release, much less the terrain system which has always been extremely slow compared to just using meshes. Like why would anybody do that unless they are getting paid to track down bugs is beyond me.

Hello, yes there is a couple reasons. First off some LTS versions don’t get some bug fixes backported or dont have some really necessary feature present in Previews. I personally use Unity 6 as of now and it works more or less well (but this is a work for a client). In terms of Terrian issues, I remember having some of similar nature even with 2021 LTSs but back then I didnt investigate it as much.

Got it. I think deleting the library folder and reloading the project could do the same. I think, Don’t hold me to it lol. I just read the new terrain system announcement. Pretty interesting stuff. They just need to put a mesh conversion modularally on top. It’s the safest way. It’s also possible to convert mesh to terrain. I really hope they swing at the obvious pitch over the center of the plate here and bask in the glory of easy af improvement that’ ps 99% already done for them.

Sadly it would not, as the .asset file in /Assets/ was already inflated. If you remove library it will just get used as is anyway. I can confirm this as I received the project thru git and both client’s version and my had the terrainData inflated.