public void SerializeTerrainData(List <byte> bytes, TerrainData terrainData)
    {
        R_SerializationHelper.SerializeString(bytes, terrainData.name);

        int heightmapResolution = terrainData.heightmapResolution;
        int detailResolution    = terrainData.detailResolution;

        R_SerializationHelper.SerializeInt(bytes, heightmapResolution);
        R_SerializationHelper.SerializeInt(bytes, terrainData.baseMapResolution);
        R_SerializationHelper.SerializeInt(bytes, terrainData.alphamapResolution);
        R_SerializationHelper.SerializeInt(bytes, detailResolution);
        R_SerializationHelper.SerializeVector3(bytes, terrainData.size);

        R_SerializationHelper.SerializeFloat(bytes, terrainData.thickness);
        R_SerializationHelper.SerializeFloat(bytes, terrainData.wavingGrassAmount);
        R_SerializationHelper.SerializeFloat(bytes, terrainData.wavingGrassSpeed);
        R_SerializationHelper.SerializeFloat(bytes, terrainData.wavingGrassStrength);
        R_SerializationHelper.SerializeColor(bytes, terrainData.wavingGrassTint);


        // Splat textures
        SplatPrototype[] splatPrototypes = terrainData.splatPrototypes;
        R_SerializationHelper.SerializeInt(bytes, splatPrototypes.Length);
        for (int i = 0; i < splatPrototypes.Length; i++)
        {
            SplatPrototype splat = splatPrototypes[i];
            R_SerializationHelper.SerializeFloat(bytes, splat.metallic);
            if (splat.normalMap != null)
            {
                bytes.Add(1);
                R_SerializationHelper.SerializeString(bytes, splat.normalMap.name);
            }
            else
            {
                bytes.Add(0);
            }

            R_SerializationHelper.SerializeFloat(bytes, splat.smoothness);
            R_SerializationHelper.SerializeString(bytes, splat.texture.name);
            R_SerializationHelper.SerializeVector2(bytes, splat.tileOffset);
            R_SerializationHelper.SerializeVector2(bytes, splat.tileSize);
        }

        // Tree Prototypes
        TreePrototype[] treePrototypes = terrainData.treePrototypes;
        R_SerializationHelper.SerializeInt(bytes, treePrototypes.Length);
        for (int i = 0; i < treePrototypes.Length; i++)
        {
            TreePrototype tree = treePrototypes[i];
            R_SerializationHelper.SerializeFloat(bytes, tree.bendFactor);
            R_SerializationHelper.SerializeString(bytes, tree.prefab.name);
        }

        // Grass
        DetailPrototype[] detailPrototypes = terrainData.detailPrototypes;
        int detailPrototypesLength         = detailPrototypes.Length;

        R_SerializationHelper.SerializeInt(bytes, detailPrototypes.Length);
        for (int i = 0; i < detailPrototypes.Length; i++)
        {
            DetailPrototype detail = detailPrototypes[i];
            R_SerializationHelper.SerializeFloat(bytes, detail.bendFactor);
            R_SerializationHelper.SerializeColor(bytes, detail.dryColor);
            R_SerializationHelper.SerializeColor(bytes, detail.healthyColor);
            R_SerializationHelper.SerializeFloat(bytes, detail.maxHeight);
            R_SerializationHelper.SerializeFloat(bytes, detail.maxWidth);
            R_SerializationHelper.SerializeFloat(bytes, detail.minHeight);
            R_SerializationHelper.SerializeFloat(bytes, detail.minWidth);
            R_SerializationHelper.SerializeFloat(bytes, detail.noiseSpread);
            if (detail.prototype != null)
            {
                bytes.Add(1);
                R_SerializationHelper.SerializeString(bytes, detail.prototype.name);
            }
            else
            {
                bytes.Add(0);
            }

            if (detail.prototypeTexture != null)
            {
                bytes.Add(1);
                R_SerializationHelper.SerializeString(bytes, detail.prototypeTexture.name);
            }
            else
            {
                bytes.Add(0);
            }

            R_SerializationHelper.SerializeInt(bytes, (int)detail.renderMode);
        }

        // Heights
        float[,] heights = terrainData.GetHeights(0, 0, heightmapResolution, heightmapResolution);
        R_SerializationHelper.Serialize2DFloatArray(bytes, heights);

        // Splat maps
        R_SerializationHelper.SerializeInt(bytes, terrainData.alphamapTextures.Length);
        for (int i = 0; i < terrainData.alphamapTextures.Length; i++)
        {
            Texture2D tex      = terrainData.alphamapTextures[i];
            byte[]    texBytes = tex.EncodeToPNG();
            R_SerializationHelper.SerializeInt(bytes, texBytes.Length);
            bytes.AddRange(texBytes);
        }

        // Trees
        TreeInstance[] treeInstances = terrainData.treeInstances;
        R_SerializationHelper.SerializeInt(bytes, treeInstances.Length);
        for (int i = 0; i < treeInstances.Length; i++)
        {
            TreeInstance tree = treeInstances[i];
            R_SerializationHelper.SerializeColor(bytes, tree.color);
            R_SerializationHelper.SerializeFloat(bytes, tree.heightScale);
            R_SerializationHelper.SerializeColor(bytes, tree.lightmapColor);
            R_SerializationHelper.SerializeVector3(bytes, tree.position);
            R_SerializationHelper.SerializeInt(bytes, tree.prototypeIndex);
            R_SerializationHelper.SerializeFloat(bytes, tree.rotation);
            R_SerializationHelper.SerializeFloat(bytes, tree.widthScale);
        }

        // Grass
        for (int i = 0; i < detailPrototypesLength; i++)
        {
            int[,] detailMap = terrainData.GetDetailLayer(0, 0, detailResolution, detailResolution, i);
            R_SerializationHelper.Serialize2DIntArrayToBytes(bytes, detailMap);
        }
    }