public void SerializeTerrain(List <byte> bytes, Terrain terrain)
    {
        if (terrain == null)
        {
            return;
        }
        if (terrain.terrainData == null)
        {
            return;
        }

        R_SerializationHelper.SerializeString(bytes, terrain.name);
        R_SerializationHelper.SerializeVector3(bytes, terrain.transform.position);

        R_SerializationHelper.SerializeFloat(bytes, terrain.basemapDistance);
        #if UNITY_5 || UNITY_2017 || UNITY_2018_1 || UNITY_2018_2 || UNITY_2018_3 || UNITY_2018_4
        R_SerializationHelper.SerializeBool(bytes, terrain.castShadows);
        #else
        R_SerializationHelper.SerializeInt(bytes, (int)terrain.shadowCastingMode);
        #endif
        R_SerializationHelper.SerializeBool(bytes, terrain.collectDetailPatches);
        R_SerializationHelper.SerializeFloat(bytes, terrain.detailObjectDensity);
        R_SerializationHelper.SerializeFloat(bytes, terrain.detailObjectDistance);
        R_SerializationHelper.SerializeBool(bytes, terrain.drawHeightmap);
        R_SerializationHelper.SerializeBool(bytes, terrain.drawTreesAndFoliage);
        R_SerializationHelper.SerializeInt(bytes, terrain.heightmapMaximumLOD);
        R_SerializationHelper.SerializeFloat(bytes, terrain.heightmapPixelError);
        #if UNITY_5 || UNITY_2017 || UNITY_2018_1 || UNITY_2018_2 || UNITY_2018_3 || UNITY_2019_1
        R_SerializationHelper.SerializeFloat(bytes, terrain.legacyShininess);
        R_SerializationHelper.SerializeColor(bytes, terrain.legacySpecular);
        #endif
        R_SerializationHelper.SerializeInt(bytes, terrain.lightmapIndex);
        R_SerializationHelper.SerializeVector4(bytes, terrain.lightmapScaleOffset);

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

        #if UNITY_5 || UNITY_2017 || UNITY_2018_1 || UNITY_2018_2 || UNITY_2018_3 || UNITY_2019_1
        R_SerializationHelper.SerializeInt(bytes, (int)terrain.materialType);
        #endif
        R_SerializationHelper.SerializeInt(bytes, terrain.realtimeLightmapIndex);
        R_SerializationHelper.SerializeVector4(bytes, terrain.realtimeLightmapScaleOffset);
        R_SerializationHelper.SerializeInt(bytes, (int)terrain.reflectionProbeUsage);
        R_SerializationHelper.SerializeFloat(bytes, terrain.treeBillboardDistance);
        R_SerializationHelper.SerializeFloat(bytes, terrain.treeCrossFadeLength);
        R_SerializationHelper.SerializeFloat(bytes, terrain.treeDistance);
        R_SerializationHelper.SerializeInt(bytes, terrain.treeMaximumFullLODCount);

        SerializeTerrainData(bytes, terrain.terrainData);

        // Debug.Log(bytes.Count);
    }
    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);
        }
    }