Suite à un precedent sujet et grace au code de Kostiantyn Dvornik trouvé sur :
http://kostiantyn-dvornik.blogspot.fr/2 ... cript.html
J'ai modifié le code pour permettre la decoupe de 4 à 256 sous terrains (4 - 16 - 64 - 256 au choix).
Creer un fichier SplitTerrain.cs dans le dossier Editor :
Code : Tout sélectionner
/// <summary>
/// Dvornik
/// </summary>
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;
/// <summary>
/// Split terrain.
/// </summary>
/// UPDATE 02/2016 MARTIOL Bruno
public class SplitTerrain : EditorWindow {
int repeatProcess = 0; //!\\ IF repeatProcess = 0 : 4 terrains, if repeatProcess = 1 : 16 terrains, ....
string destFolder = "split_terrain"; // Folder in Assets/
string tagTerrain = "";
bool deleteGroupIfExists = false;
List<Terrain> terrainsToDelete;
GameObject emptyParent;
int totalTerrain;
// Add submenu
[MenuItem("Dvornik/Split Terrain")]
static void Init()
{
// Get existing open window or if none, make a new one:
SplitTerrain window = (SplitTerrain)EditorWindow.GetWindow(typeof(SplitTerrain));
window.minSize = new Vector2( 300f, 200f );
window.maxSize = new Vector2( 300f, 200f );
window.autoRepaintOnSceneChange = true;
window.title = "Split terrain";
window.Show();
}
void OnGUI()
{
GUILayout.Label ("Settings", EditorStyles.boldLabel);
destFolder = EditorGUILayout.TextField("Group name :", destFolder);
deleteGroupIfExists = EditorGUILayout.Toggle ("Delete group if exists :", deleteGroupIfExists);
tagTerrain = EditorGUILayout.TextField("Tag name :", tagTerrain);
repeatProcess = EditorGUILayout.IntField("Repeat operation (0 - 3) :", repeatProcess);
GUILayout.Label (" /!\\ WARNING");
GUILayout.Label (" Repeat operation = 0 - created 4 terrains ");
GUILayout.Label (" Repeat operation = 1 - created 16 terrains ");
GUILayout.Label (" Repeat operation = 2 - created 64 terrains ");
GUILayout.Label (" Repeat operation = 3 - created 256 terrains ");
if(GUILayout.Button("Split terrain"))
{
// repeatProcess too big ?
if(repeatProcess > 3) {
Debug.Log("Repeat operation too big !");
return;
}
// Folder
if(destFolder == null || "".Equals(destFolder)) {
Debug.Log("Group name missing !");
return;
}
// Delete Group ?
GameObject goToDelete = null;
if(deleteGroupIfExists) {
// Delete gameobject
goToDelete = GameObject.Find(destFolder);
// Delete Asset
if(AssetDatabase.IsValidFolder("Assets/" + destFolder)) {
string[] files = Directory.GetFiles("Assets/" + destFolder + "/");
foreach(string file in files) {
AssetDatabase.DeleteAsset(file);
}
}
}
// Empty parent
emptyParent = new GameObject();
emptyParent.name = destFolder;
// Progress
totalTerrain = (repeatProcess + 1) * 4;
// 1 terrain -> 4 terrains
List<Terrain> terrains = SplitIt(null);
// Terrains to delete
terrainsToDelete = new List<Terrain>();
// if repeatProcess = 1, 4 terrains -> 16 terrains
splitTerrains(terrains, repeatProcess);
// Delete terrains not finals
foreach (Terrain terrain in terrainsToDelete) {
AssetDatabase.DeleteAsset("Assets/" + destFolder + "/" + terrain.name + ".asset");
DestroyImmediate(terrain.gameObject);
}
if(goToDelete != null) {
DestroyImmediate(goToDelete);
}
}
}
void splitTerrains(List<Terrain> terrains, int lvl) {
if (lvl <= 0 || terrains == null) {
return;
}
lvl--;
foreach (Terrain terrain in terrains) {
List<Terrain> sub_terrains = SplitIt(terrain);
splitTerrains(sub_terrains, lvl);
if(sub_terrains != null && sub_terrains.Count > 0) {
terrainsToDelete.Add(terrain);
}
}
}
/// <summary>
/// Determines whether this instance is power of two the specified x.
/// </summary>
/// <returns>
/// <c>true</c> if this instance is power of two the specified x; otherwise, <c>false</c>.
/// </returns>
/// <param name='x'>
/// If set to <c>true</c> x.
/// </param>
bool IsPowerOfTwo(int x)
{
return (x & (x - 1)) == 0;
}
List<Terrain> SplitIt(Terrain terrain)
{
List<Terrain> listeTerrain = new List<Terrain>();
List<TerrainData> terrainData = new List<TerrainData>();
List<GameObject> terrainGo = new List<GameObject>();
Terrain parentTerrain;
if (terrain == null && Selection.activeGameObject == null )
{
Debug.Log("No terrain was selected");
return null;
}
if (terrain == null) {
parentTerrain = Selection.activeGameObject.GetComponent(typeof(Terrain)) as Terrain;
} else {
parentTerrain = terrain;
}
if ( parentTerrain == null )
{
Debug.Log("Current selection is not a terrain");
return null;
}
// Folder terrains_split
if(!AssetDatabase.IsValidFolder("Assets/" + destFolder)) {
AssetDatabase.CreateFolder("Assets", destFolder);
}
//Split terrain
for ( int i=0; i< 4; i++)
{
EditorUtility.DisplayProgressBar("Split terrain","Process " + i, (float) i / 4 );
TerrainData td = new TerrainData();
GameObject tgo = Terrain.CreateTerrainGameObject( td );
tgo.name = parentTerrain.name + "_" + i;
tgo.transform.SetParent(emptyParent.transform);
if (tagTerrain != null && tagTerrain != "") {
tgo.tag = tagTerrain;
}
terrainData.Add( td );
terrainGo.Add ( tgo );
Terrain genTer = tgo.GetComponent(typeof(Terrain)) as Terrain;
genTer.terrainData = td;
AssetDatabase.CreateAsset(td, "Assets/" + destFolder + "/" + genTer.name+ ".asset");
// Assign splatmaps
genTer.terrainData.splatPrototypes = parentTerrain.terrainData.splatPrototypes;
// Assign detail prototypes
genTer.terrainData.detailPrototypes = parentTerrain.terrainData.detailPrototypes;
// Assign tree information
genTer.terrainData.treePrototypes = parentTerrain.terrainData.treePrototypes;
// Copy parent terrain propeties
#region parent properties
genTer.basemapDistance = parentTerrain.basemapDistance;
genTer.castShadows = parentTerrain.castShadows;
genTer.detailObjectDensity = parentTerrain.detailObjectDensity;
genTer.detailObjectDistance = parentTerrain.detailObjectDistance;
genTer.heightmapMaximumLOD = parentTerrain.heightmapMaximumLOD;
genTer.heightmapPixelError = parentTerrain.heightmapPixelError;
genTer.treeBillboardDistance = parentTerrain.treeBillboardDistance;
genTer.treeCrossFadeLength = parentTerrain.treeCrossFadeLength;
genTer.treeDistance = parentTerrain.treeDistance;
genTer.treeMaximumFullLODCount = parentTerrain.treeMaximumFullLODCount;
#endregion
//Start processing it
// Translate peace to position
#region translate peace to right position
Vector3 parentPosition = parentTerrain.GetPosition();
int terraPeaces = (int) Mathf.Sqrt( 4 );
float spaceShiftX = parentTerrain.terrainData.size.z / terraPeaces;
float spaceShiftY = parentTerrain.terrainData.size.x / terraPeaces;
float xWShift = (i % terraPeaces ) * spaceShiftX;
float zWShift = ( i / terraPeaces ) * spaceShiftY;
tgo.transform.position = new Vector3( tgo.transform.position.x + zWShift,
tgo.transform.position.y,
tgo.transform.position.z + xWShift );
// Shift last position
tgo.transform.position = new Vector3( tgo.transform.position.x + parentPosition.x,
tgo.transform.position.y + parentPosition.y,
tgo.transform.position.z + parentPosition.z
);
#endregion
// Split height
#region split height
// Debug.Log ( "Split height" );
//Copy heightmap
td.heightmapResolution = parentTerrain.terrainData.heightmapResolution / terraPeaces;
//Keep y same
td.size = new Vector3( parentTerrain.terrainData.size.x / terraPeaces,
parentTerrain.terrainData.size.y,
parentTerrain.terrainData.size.z / terraPeaces
);
float[,] parentHeight = parentTerrain.terrainData.GetHeights(0,0, parentTerrain.terrainData.heightmapResolution, parentTerrain.terrainData.heightmapResolution );
float[,] peaceHeight = new float[ parentTerrain.terrainData.heightmapResolution / terraPeaces + 1,
parentTerrain.terrainData.heightmapResolution / terraPeaces + 1
];
// Shift calc
int heightShift = parentTerrain.terrainData.heightmapResolution / terraPeaces;
int startX = 0;
int startY = 0;
int endX = 0;
int endY = 0;
if ( i==0 )
{
startX = startY = 0;
endX = endY = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1;
}
if ( i==1 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1;
endY = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1;
}
if ( i==2 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1;
endY = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1;
}
if ( i==3 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1;
endY = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1;
}
// iterate
for ( int x=startX;x< endX;x++)
{
EditorUtility.DisplayProgressBar("Split terrain","Split height", (float) x / ( endX - startX ));
for ( int y=startY;y< endY;y++)
{
int xShift=0;
int yShift=0;
//
if ( i==0 )
{
xShift = 0;
yShift = 0;
}
//
if ( i==1 )
{
xShift = heightShift;
yShift = 0;
}
//
if ( i==2 )
{
xShift = 0;
yShift = heightShift;
}
if ( i==3 )
{
xShift = heightShift;
yShift = heightShift;
}
float ph = parentHeight[ x + xShift,y + yShift];
peaceHeight[x ,y ] = ph;
}
}
EditorUtility.ClearProgressBar();
// Set heightmap to child
genTer.terrainData.SetHeights( 0,0, peaceHeight );
#endregion
// Split splat map
#region split splat map
td.alphamapResolution = parentTerrain.terrainData.alphamapResolution / terraPeaces;
float[,,] parentSplat = parentTerrain.terrainData.GetAlphamaps(0,0, parentTerrain.terrainData.alphamapResolution, parentTerrain.terrainData.alphamapResolution );
float[,,] peaceSplat = new float[ parentTerrain.terrainData.alphamapResolution / terraPeaces ,
parentTerrain.terrainData.alphamapResolution / terraPeaces,
parentTerrain.terrainData.alphamapLayers
];
// Shift calc
int splatShift = parentTerrain.terrainData.alphamapResolution / terraPeaces;
if ( i==0 )
{
startX = startY = 0;
endX = endY = parentTerrain.terrainData.alphamapResolution / terraPeaces;
}
if ( i==1 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.alphamapResolution / terraPeaces;
endY = parentTerrain.terrainData.alphamapResolution / terraPeaces;
}
if ( i==2 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.alphamapResolution / terraPeaces;
endY = parentTerrain.terrainData.alphamapResolution / terraPeaces;
}
if ( i==3 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.alphamapResolution / terraPeaces;
endY = parentTerrain.terrainData.alphamapResolution / terraPeaces;
}
// iterate
for ( int s=0;s<parentTerrain.terrainData.alphamapLayers;s++)
{
for ( int x=startX;x< endX;x++)
{
EditorUtility.DisplayProgressBar("Split terrain","Split splat", (float) x / ( endX - startX ));
for ( int y=startY;y< endY;y++)
{
int xShift=0;
int yShift=0;
//
if ( i==0 )
{
xShift = 0;
yShift = 0;
}
//
if ( i==1 )
{
xShift = splatShift;
yShift = 0;
}
//
if ( i==2 )
{
xShift = 0;
yShift = splatShift;
}
if ( i==3 )
{
xShift = splatShift;
yShift = splatShift;
}
float ph = parentSplat[x + xShift,y + yShift, s];
peaceSplat[x ,y, s] = ph;
}
}
}
EditorUtility.ClearProgressBar();
// Set heightmap to child
genTer.terrainData.SetAlphamaps( 0,0, peaceSplat );
#endregion
// Split detail map
#region split detail map
td.SetDetailResolution( parentTerrain.terrainData.detailResolution / terraPeaces, 8 );
for ( int detLay=0; detLay< parentTerrain.terrainData.detailPrototypes.Length; detLay++)
{
int[,] parentDetail = parentTerrain.terrainData.GetDetailLayer(0,0, parentTerrain.terrainData.detailResolution, parentTerrain.terrainData.detailResolution, detLay );
int[,] peaceDetail = new int[ parentTerrain.terrainData.detailResolution / terraPeaces,
parentTerrain.terrainData.detailResolution / terraPeaces
];
// Shift calc
int detailShift = parentTerrain.terrainData.detailResolution / terraPeaces;
if ( i==0 )
{
startX = startY = 0;
endX = endY = parentTerrain.terrainData.detailResolution / terraPeaces;
}
if ( i==1 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.detailResolution / terraPeaces;
endY = parentTerrain.terrainData.detailResolution / terraPeaces;
}
if ( i==2 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.detailResolution / terraPeaces;
endY = parentTerrain.terrainData.detailResolution / terraPeaces;
}
if ( i==3 )
{
startX = startY = 0;
endX = parentTerrain.terrainData.detailResolution / terraPeaces;
endY = parentTerrain.terrainData.detailResolution / terraPeaces;
}
// iterate
for ( int x=startX;x< endX;x++)
{
EditorUtility.DisplayProgressBar("Split terrain","Split detail", (float) x / (endX - startX ));
for ( int y=startY;y< endY;y++)
{
int xShift=0;
int yShift=0;
//
if ( i==0 )
{
xShift = 0;
yShift = 0;
}
//
if ( i==1 )
{
xShift = detailShift;
yShift = 0;
}
//
if ( i==2 )
{
xShift = 0;
yShift = detailShift;
}
if ( i==3 )
{
xShift = detailShift;
yShift = detailShift;
}
int ph = parentDetail[x + xShift,y + yShift];
peaceDetail[x ,y] = ph;
}
}
EditorUtility.ClearProgressBar();
// Set heightmap to child
genTer.terrainData.SetDetailLayer( 0,0, detLay, peaceDetail );
}
#endregion
// Split tree data
#region split tree data
for( int t=0; t< parentTerrain.terrainData.treeInstances.Length;t++)
{
EditorUtility.DisplayProgressBar("Split terrain","Split trees " , (float) t / parentTerrain.terrainData.treeInstances.Length );
// Get tree instance
TreeInstance ti = parentTerrain.terrainData.treeInstances[t];
// First section
if ( i==0 &&
ti.position.x > 0f && ti.position.x < 0.5f &&
ti.position.z > 0f && ti.position.z < 0.5f
)
{
// Recalculate new tree position
ti.position = new Vector3( ti.position.x * 2f, ti.position.y, ti.position.z * 2f );
// Add tree instance
genTer.AddTreeInstance( ti );
}
// Second section
if ( i==1 &&
ti.position.x > 0.0f &&ti.position.x < 0.5f &&
ti.position.z >= 0.5f && ti.position.z <= 1.0f
)
{
// Recalculate new tree position
ti.position = new Vector3( (ti.position.x ) * 2f, ti.position.y, ( ti.position.z - 0.5f ) * 2f );
// Add tree instance
genTer.AddTreeInstance( ti );
}
// Third section
if ( i==2 &&
ti.position.x >= 0.5f && ti.position.x <= 1.0f &&
ti.position.z > 0.0f && ti.position.z < 0.5f
)
{
// Recalculate new tree position
ti.position = new Vector3( (ti.position.x - 0.5f ) * 2f, ti.position.y, ( ti.position.z ) * 2f );
// Add tree instance
genTer.AddTreeInstance( ti );
}
// Fourth section
if ( i==3 &&
ti.position.x >= 0.5f && ti.position.x <= 1.0f &&
ti.position.z >= 0.5f && ti.position.z <= 1.0f
)
{
// Recalculate new tree position
ti.position = new Vector3( (ti.position.x - 0.5f ) * 2f, ti.position.y, ( ti.position.z - 0.5f ) * 2f );
// Add tree instance
genTer.AddTreeInstance( ti );
}
}
#endregion
listeTerrain.Add(genTer);
AssetDatabase.SaveAssets();
}
EditorUtility.ClearProgressBar();
return listeTerrain;
}
}
Donc en fait, je n'ai quasiment pas touché a sa routine principale de division en 4, ce que je fais c'est que je la rappelle x fois pour chaque sous terrain cree, en supprimant a la fin les terrains intermediaires qui ne servent plus.
Je ne supprime biensur pas le terrain original.
J'en ai profité aussi pour regrouper les sous terrains crees dans un gameobject vide et je place aussi les assets SplatAlpha dans un folder.
Du coup, j'ai mis a jour la fenetre GUI parametrable, permettant ainsi de choisir le ratio de decoupe :
- 0 : 4 terrains
- 1 : 16 terrains
- 2 : 64 terrains
- 3 : 256 terrains
et le nom du groupe permettant de creer un dossier dans l'asset et un objet vide.
Voila
MAJ : Possibilite d'attribuer un tag a chaque terrain crée et possibilite d'effacer l'ancien groupe si on fait souvent cette operation.