Skip to content

Instantly share code, notes, and snippets.

@zsoi
Last active July 8, 2025 21:27
Show Gist options
  • Select an option

  • Save zsoi/c965ff38938cd126f00d to your computer and use it in GitHub Desktop.

Select an option

Save zsoi/c965ff38938cd126f00d to your computer and use it in GitHub Desktop.
Helper to deep-copy a TerrainData object in Unity3D
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace ZS.Tools
{
/// <summary>
/// Provides means to deep-copy a TerrainData object because Unitys' built-in "Instantiate" method
/// will miss some things and the resulting copy still shares data with the original.
/// </summary>
public class TerrainDataCloner
{
/// <summary>
/// Creates a real deep-copy of a TerrainData
/// </summary>
/// <param name="original">TerrainData to duplicate</param>
/// <returns>New terrain data instance</returns>
public static TerrainData Clone(TerrainData original)
{
TerrainData dup = new TerrainData();
dup.alphamapResolution = original.alphamapResolution;
dup.baseMapResolution = original.baseMapResolution;
dup.detailPrototypes = CloneDetailPrototypes(original.detailPrototypes);
// The resolutionPerPatch is not publicly accessible so
// it can not be cloned properly, thus the recommendet default
// number of 16
dup.SetDetailResolution(original.detailResolution, 16);
dup.heightmapResolution = original.heightmapResolution;
dup.size = original.size;
dup.splatPrototypes = CloneSplatPrototypes(original.splatPrototypes);
dup.thickness = original.thickness;
dup.wavingGrassAmount = original.wavingGrassAmount;
dup.wavingGrassSpeed = original.wavingGrassSpeed;
dup.wavingGrassStrength = original.wavingGrassStrength;
dup.wavingGrassTint = original.wavingGrassTint;
dup.SetAlphamaps(0, 0, original.GetAlphamaps(0, 0, original.alphamapWidth, original.alphamapHeight));
dup.SetHeights(0, 0, original.GetHeights(0, 0, original.heightmapWidth, original.heightmapHeight));
for (int n = 0; n < original.detailPrototypes.Length; n++)
{
dup.SetDetailLayer(0, 0, n, original.GetDetailLayer(0, 0, original.detailWidth, original.detailHeight, n));
}
dup.treePrototypes = CloneTreePrototypes(original.treePrototypes);
dup.treeInstances = CloneTreeInstances(original.treeInstances);
return dup;
}
/// <summary>
/// Deep-copies an array of splat prototype instances
/// </summary>
/// <param name="original">Prototypes to clone</param>
/// <returns>Cloned array</returns>
static SplatPrototype[] CloneSplatPrototypes(SplatPrototype[] original)
{
SplatPrototype[] splatDup = new SplatPrototype[original.Length];
for (int n = 0; n < splatDup.Length; n++)
{
splatDup[n] = new SplatPrototype
{
metallic = original[n].metallic,
normalMap = original[n].normalMap,
smoothness = original[n].smoothness,
specular = original[n].specular,
texture = original[n].texture,
tileOffset = original[n].tileOffset,
tileSize = original[n].tileSize
};
}
return splatDup;
}
/// <summary>
/// Deep-copies an array of detail prototype instances
/// </summary>
/// <param name="original">Prototypes to clone</param>
/// <returns>Cloned array</returns>
static DetailPrototype[] CloneDetailPrototypes(DetailPrototype[] original)
{
DetailPrototype[] protoDuplicate = new DetailPrototype[original.Length];
for (int n = 0; n < original.Length; n++)
{
protoDuplicate[n] = new DetailPrototype
{
bendFactor = original[n].bendFactor,
dryColor = original[n].dryColor,
healthyColor = original[n].healthyColor,
maxHeight = original[n].maxHeight,
maxWidth = original[n].maxWidth,
minHeight = original[n].minHeight,
minWidth = original[n].minWidth,
noiseSpread = original[n].noiseSpread,
prototype = original[n].prototype,
prototypeTexture = original[n].prototypeTexture,
renderMode = original[n].renderMode,
usePrototypeMesh = original[n].usePrototypeMesh,
useInstancing = original[n].useInstancing,
};
}
return protoDuplicate;
}
/// <summary>
/// Deep-copies an array of tree prototype instances
/// </summary>
/// <param name="original">Prototypes to clone</param>
/// <returns>Cloned array</returns>
static TreePrototype[] CloneTreePrototypes(TreePrototype[] original)
{
TreePrototype[] protoDuplicate = new TreePrototype[original.Length];
for (int n = 0; n < original.Length; n++)
{
protoDuplicate[n] = new TreePrototype
{
bendFactor = original[n].bendFactor,
prefab = original[n].prefab,
};
}
return protoDuplicate;
}
/// <summary>
/// Deep-copies an array of tree instances
/// </summary>
/// <param name="original">Trees to clone</param>
/// <returns>Cloned array</returns>
static TreeInstance[] CloneTreeInstances(TreeInstance[] original)
{
TreeInstance[] treeInst = new TreeInstance[original.Length];
System.Array.Copy(original, treeInst, original.Length);
return treeInst;
}
}
}
@digitalbreed
Copy link
Copy Markdown

Please, no more "Depp-copies"! (Pun for German speakers.)

@Eldoir
Copy link
Copy Markdown

Eldoir commented Aug 16, 2017

This script is a truly lifesaver!!
Allowed me to fully rotate a terrain in runtime.
There is just a bug in line 53, about treePrototypes : it should clone original.treePrototypes and NOT dup.treePrototypes.

@Swahhillie
Copy link
Copy Markdown

Great script.
In unity 2018.3 you no longer need to clone the splatmaps. Instead, terrains can share terrainlayers.

dup.terrainLayers= original.terrainLayers;

@cyfinfaza
Copy link
Copy Markdown

cyfinfaza commented Feb 6, 2022

This script worked for me for sloped procedural infinite terrain stuff. Do fix the bug on line 53, though.
Just as a note for absolute beginners like me trying to use this: you have to create a script in the Unity GUI, then paste all this into it, and then add using ZS.Tools; to the top of whatever script you want to use it in in order to be able to access TerrainDataCloner.Clone

@ciyinei
Copy link
Copy Markdown

ciyinei commented Aug 31, 2023

One property is missing while cloning the detail prototypes.
You can add it pretty easily in the line 110.
useInstancing = original[n].useInstancing

@Jaakk0S
Copy link
Copy Markdown

Jaakk0S commented Jul 8, 2025

Useless. You clone a TerrainData with Object.Instantiate(terrainData)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment