private static int _ShowBackupPrompt() { int dialogValue = EditorUtility.DisplayDialogComplex("Make Backup?", "This operation will significantly modify your scene. Would you like to make a backup first?", "Yes", "Cancel", "No"); if (dialogValue == 0) { string sceneDir; string sceneName; SECTR_Asset.SplitPath(EditorApplication.currentScene, out sceneDir, out sceneName); sceneName = sceneName.Replace(".unity", ""); AssetDatabase.CopyAsset(EditorApplication.currentScene, sceneDir + sceneName + "_Backup.unity"); AssetDatabase.Refresh(); SECTR_VC.WaitForVC(); } return(dialogValue); }
public static string MakeExportFolder(string subFolder, bool clearSubfolder, out string sceneDir, out string sceneName) { if (GetCurrentSceneParts(out sceneDir, out sceneName)) { string folderPath = sceneDir; string baseFolder = UnityToOSPath(folderPath) + sceneName; if (!System.IO.Directory.Exists(baseFolder)) { // Create new folder. Use substring b/c Unity dislikes the trailing / AssetDatabase.CreateFolder(folderPath.Substring(0, folderPath.Length - 1), sceneName); SECTR_VC.WaitForVC(); } folderPath += sceneName + "/"; // Inside that "standard" folder, make a Chunks subfolder, so that we can manage the contents, // but first, remove previous export, if there was one. string osPath = UnityToOSPath(folderPath) + subFolder; bool createSubFolder = !string.IsNullOrEmpty(subFolder) && !System.IO.Directory.Exists(osPath); if (clearSubfolder && !createSubFolder) { AssetDatabase.DeleteAsset(folderPath + subFolder); createSubFolder = true; } if (createSubFolder) { // Create new folder. Use substring b/c Unity dislikes the trailing / AssetDatabase.CreateFolder(folderPath.Substring(0, folderPath.Length - 1), subFolder); SECTR_VC.WaitForVC(); } if (!string.IsNullOrEmpty(subFolder)) { folderPath += subFolder + "/"; } return(folderPath); } else { sceneDir = null; sceneName = null; return(null); } }
/// Imports all exported Sectors into the scene. Safe to call from the command line. public static void ImportSceneChunks() { int numSectors = SECTR_Sector.All.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = SECTR_Sector.All[sectorIndex]; if (sector.Frozen) { EditorUtility.DisplayProgressBar("Importing Scene Chunks", "Importing " + sector.name, (float)sectorIndex / (float)numSectors); ImportFromChunk(sector); } } if (SECTR_VC.CheckOut(EditorApplication.currentScene)) { EditorApplication.SaveScene(); SECTR_VC.WaitForVC(); } EditorUtility.ClearProgressBar(); }
/// Reverts all imported Sectors into the scene. Safe to call from the command line. public static void RevertSceneChunks() { int numSectors = SECTR_Sector.All.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = SECTR_Sector.All[sectorIndex]; SECTR_Chunk chunk = sector.GetComponent <SECTR_Chunk>(); if (!sector.Frozen && chunk && System.IO.File.Exists(SECTR_Asset.UnityToOSPath(chunk.NodeName))) { EditorUtility.DisplayProgressBar("Reverting Scene Chunks", "Reverting " + sector.name, (float)sectorIndex / (float)numSectors); DeleteExportedSector(sector); } } if (SECTR_VC.CheckOut(EditorApplication.currentScene)) { EditorApplication.SaveScene(); SECTR_VC.WaitForVC(); } EditorUtility.ClearProgressBar(); }
public override void OnInspectorGUI() { SECTR_Chunk myChunk = (SECTR_Chunk)target; SECTR_Sector mySector = myChunk.GetComponent <SECTR_Sector>(); EditorGUILayout.BeginHorizontal(); bool editMode = !EditorApplication.isPlaying && !EditorApplication.isPaused; bool alreadyExported = myChunk && System.IO.File.Exists(SECTR_Asset.UnityToOSPath(myChunk.NodeName)); GUI.enabled = editMode; if (mySector.Frozen) { // Import if (alreadyExported && GUILayout.Button(new GUIContent("Import", "Imports this Sector into the scene."))) { modiferMode = ModifierMode.Import; } // Export GUI.enabled = false; GUILayout.Button(new GUIContent("Export", "Exports this Sector into a Chunk scene.")); GUI.enabled = editMode; } else { // Revert if (alreadyExported && GUILayout.Button(new GUIContent("Revert", "Discards changes to this Sector."))) { modiferMode = ModifierMode.Revert; } // Export if (GUILayout.Button(new GUIContent("Export", "Exports this Sector into a Chunk scene."))) { modiferMode = ModifierMode.Export; } } EditorGUILayout.EndHorizontal(); base.OnInspectorGUI(); if (!mySector.Frozen) { proxyFoldout = EditorGUILayout.Foldout(proxyFoldout, "Proxy Mesh Tool"); if (proxyFoldout) { EditorGUILayout.BeginVertical(); _BuildChildControls(myChunk.transform, true); if (GUILayout.Button("Create Proxy Mesh")) { Dictionary <Material, List <CombineInstance> > meshHash = new Dictionary <Material, List <CombineInstance> >(); Matrix4x4 chunkWorldToLocal = myChunk.transform.worldToLocalMatrix; foreach (Renderer renderer in checkState.Keys) { if (checkState[renderer]) { MeshFilter meshFilter = renderer.GetComponent <MeshFilter>(); int numSubMeshes = meshFilter.sharedMesh.subMeshCount; for (int submeshIndex = 0; submeshIndex < numSubMeshes; ++submeshIndex) { Material material = renderer.sharedMaterials[submeshIndex]; List <CombineInstance> materialMeshes = null; if (!meshHash.TryGetValue(material, out materialMeshes)) { materialMeshes = new List <CombineInstance>(); meshHash[material] = materialMeshes; } CombineInstance instance = new CombineInstance(); instance.transform = chunkWorldToLocal * renderer.transform.localToWorldMatrix; instance.mesh = renderer.GetComponent <MeshFilter>().sharedMesh; instance.subMeshIndex = submeshIndex; materialMeshes.Add(instance); } } } if (meshHash.Count > 0) { List <CombineInstance> combinedMeshes = new List <CombineInstance>(); List <Material> combinedMaterials = new List <Material>(); foreach (Material material in meshHash.Keys) { CombineInstance instance = new CombineInstance(); instance.mesh = new Mesh(); instance.mesh.CombineMeshes(meshHash[material].ToArray(), true, true); combinedMeshes.Add(instance); combinedMaterials.Add(material); } string sceneDir; string sceneName; string exportFolder = SECTR_Asset.MakeExportFolder("Proxies", false, out sceneDir, out sceneName); myChunk.ProxyMesh = SECTR_Asset.Create <Mesh>(exportFolder, myChunk.name + "_Proxy", new Mesh()); myChunk.ProxyMesh.CombineMeshes(combinedMeshes.ToArray(), false, false); myChunk.ProxyMaterials = combinedMaterials.ToArray(); int numCombined = combinedMeshes.Count; for (int combinedIndex = 0; combinedIndex < numCombined; ++combinedIndex) { Mesh.DestroyImmediate(combinedMeshes[combinedIndex].mesh); } SECTR_VC.WaitForVC(); } else { EditorUtility.DisplayDialog("Proxy Error", "Must have at least one mesh selected to create a proxy.", "Ok"); } } EditorGUILayout.EndVertical(); } } GUI.enabled = true; if (modiferMode != ModifierMode.None) { EditorApplication.update += ExportUpdate; } }
/// Exports all Sectors in the scene. Safe to call from the command line. public static void ExportSceneChunks() { // Create a progress bar, because we're friendly like that. string progressTitle = "Chunking Level For Streaming"; EditorUtility.DisplayProgressBar(progressTitle, "Preparing", 0); List <EditorBuildSettingsScene> sceneSettings = new List <EditorBuildSettingsScene>(EditorBuildSettings.scenes); EditorBuildSettingsScene rootSceneSettings = new EditorBuildSettingsScene(EditorApplication.currentScene, true); bool sceneExists = false; foreach (EditorBuildSettingsScene oldScene in sceneSettings) { if (oldScene.path == EditorApplication.currentScene) { sceneExists = true; oldScene.enabled = true; break; } } if (!sceneExists) { sceneSettings.Add(rootSceneSettings); EditorBuildSettings.scenes = sceneSettings.ToArray(); } // Export each sector to an individual file. // Inner loop reloads the scene, and Sector creation order is not deterministic, // so it requires multiple passes through the list. int numSectors = SECTR_Sector.All.Count; int progress = 0; int unfrozenSectors = 0; // Figure out how many sectors we should be exporting. for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = SECTR_Sector.All[sectorIndex]; if (!sector.Frozen) { ++unfrozenSectors; } } while (progress < unfrozenSectors) { for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = SECTR_Sector.All[sectorIndex]; if (!sector.Frozen) { EditorUtility.DisplayProgressBar(progressTitle, "Exporting " + sector.name, (float)progress / (float)unfrozenSectors); ExportToChunk(sector); ++progress; } } } EditorApplication.SaveScene(); SECTR_VC.WaitForVC(); // Cleanup EditorUtility.ClearProgressBar(); }
/// Exports the specific Sector into an external level file, deleting the current scene copy in the process. Safe to call from command line. /// <param name="sector">The Sector to export.</param> /// <returns>Returns true if Sector was successfully exported, false otherwise.</returns> public static bool ExportToChunk(SECTR_Sector sector) { if (string.IsNullOrEmpty(EditorApplication.currentScene)) { Debug.LogError("Scene must be saved befor export."); return(false); } if (sector == null) { Debug.LogError("Cannot export null Sector."); return(false); } if (!sector.gameObject.activeInHierarchy) { Debug.LogError("Cannot export inactive Sectors."); return(false); } if (!sector.gameObject.isStatic) { Debug.Log("Skipping export of dynamic sector" + sector.name + "."); return(true); } if (sector.Frozen) { // Already exported Debug.Log("Skipping frozen sector " + sector.name); return(true); } string sceneDir; string sceneName; string exportDir = SECTR_Asset.MakeExportFolder("Chunks", false, out sceneDir, out sceneName); if (string.IsNullOrEmpty(exportDir)) { Debug.LogError("Could not create Chunks folder."); return(false); } // Delete the previous export, if there is one. // Prevents duplicate names piling up. SECTR_Chunk oldChunk = sector.GetComponent <SECTR_Chunk>(); if (oldChunk) { AssetDatabase.DeleteAsset(oldChunk.NodeName); SECTR_VC.WaitForVC(); } // Sectors are not guaranteed to be uniquely named, so always generate a unique name. string originalSectorName = sector.name; string newAssetPath = AssetDatabase.GenerateUniqueAssetPath(exportDir + sceneName + "_" + originalSectorName + ".unity"); sector.name = newAssetPath; // Make sure the current scene is saved, preserving all changes. EditorApplication.SaveScene(); SECTR_VC.WaitForVC(); string originalScene = EditorApplication.currentScene; List <EditorBuildSettingsScene> sceneSettings = new List <EditorBuildSettingsScene>(EditorBuildSettings.scenes); // SaveScene can cause crashes w/ version control, so we work around it with a copy. AssetDatabase.CopyAsset(originalScene, newAssetPath); SECTR_VC.WaitForVC(); EditorApplication.OpenScene(newAssetPath); SECTR_VC.WaitForVC(); sector = _FindSectorByName(newAssetPath); // Make sure to force update all members so that membership info is correct. List <SECTR_Member> allMembers = FindAllOfType <SECTR_Member>(); for (int memberIndex = 0; memberIndex < allMembers.Count; ++memberIndex) { allMembers[memberIndex].ForceUpdate(true); } // Multi-sector members need to stay in the master scene. foreach (SECTR_Member member in allMembers) { if (member.Sectors.Count > 1 && member.transform.IsChildOf(sector.transform)) { bool unparentMember = true; // Only affect the first member in the hierarchy below the sector Transform parent = member.transform.parent; while (parent != sector.transform) { if (parent.GetComponent <SECTR_Member>() != null) { unparentMember = false; break; } parent = parent.parent; } if (unparentMember) { if (PrefabUtility.GetPrefabType(sector.gameObject) != PrefabType.None) { Debug.LogWarning("Export is unparenting shared member " + member.name + " from prefab Sector " + sector.name + ". This will break the prefab."); } member.transform.parent = null; } } } // Unparent the sector from anything sector.transform.parent = null; // Any children of this sector should be exported. // The rest should be destroyed. List <Transform> allXforms = FindAllOfType <Transform>(); #if !UNITY_STREAM_ENLIGHTEN List <int> referencedLightmaps = new List <int>(LightmapSettings.lightmaps.Length); #endif foreach (Transform transform in allXforms) { if (transform && transform.IsChildOf(sector.transform)) { #if !UNITY_STREAM_ENLIGHTEN Renderer childRenderer = transform.GetComponent <Renderer>(); if (childRenderer && childRenderer.lightmapIndex >= 0 && !referencedLightmaps.Contains(childRenderer.lightmapIndex)) { referencedLightmaps.Add(childRenderer.lightmapIndex); } Terrain childTerrain = transform.GetComponent <Terrain>();; if (childTerrain && childTerrain.lightmapIndex >= 0 && !referencedLightmaps.Contains(childTerrain.lightmapIndex)) { referencedLightmaps.Add(childTerrain.lightmapIndex); } #endif } else if (transform) { GameObject.DestroyImmediate(transform.gameObject); } } #if !UNITY_STREAM_ENLIGHTEN if (referencedLightmaps.Count > 0) { SECTR_LightmapRef newRef = sector.GetComponent <SECTR_LightmapRef>(); if (!newRef) { newRef = sector.gameObject.AddComponent <SECTR_LightmapRef>(); } newRef.ReferenceLightmaps(referencedLightmaps); } // Nuke global data like nav meshes and lightmaps // Lightmap indexes will be preserved on export. NavMeshBuilder.ClearAllNavMeshes(); #if !UNITY_4 SerializedObject serialObj = new SerializedObject(GameObject.FindObjectOfType <LightmapSettings>()); SerializedProperty snapshotProp = serialObj.FindProperty("m_LightmapSnapshot"); snapshotProp.objectReferenceValue = null; serialObj.ApplyModifiedProperties(); #endif LightmapSettings.lightmaps = new LightmapData[0]; LightmapSettings.lightProbes = new LightProbes(); #endif GameObject dummyParent = new GameObject(newAssetPath); SECTR_ChunkRef chunkRef = dummyParent.AddComponent <SECTR_ChunkRef>(); chunkRef.RealSector = sector.transform; sector.transform.parent = dummyParent.transform; // If the sector has a chunk marked for re-use, perform some special work. SECTR_Chunk originalChunk = sector.GetComponent <SECTR_Chunk>(); if (originalChunk && originalChunk.ExportForReuse) { chunkRef.Recentered = true; sector.transform.localPosition = Vector3.zero; sector.transform.localRotation = Quaternion.identity; sector.transform.localScale = Vector3.one; sector.gameObject.SetActive(false); } // Rename the real chunk root with a clear name. sector.name = originalSectorName + "_Chunk"; // Strip off any functional objects that will be preserved in the root scene. // Destroy the chunk first because it has dependencies on Sector. GameObject.DestroyImmediate(originalChunk); Component[] components = sector.GetComponents <Component>(); foreach (Component component in components) { if (component.GetType().IsSubclassOf(typeof(MonoBehaviour)) && component.GetType() != typeof(Terrain) && component.GetType() != typeof(SECTR_LightmapRef)) { GameObject.DestroyImmediate(component); } } // Re-add a member that will persist all of the references and save us work post load. SECTR_Member refMember = chunkRef.RealSector.gameObject.AddComponent <SECTR_Member>(); refMember.NeverJoin = true; refMember.BoundsUpdateMode = SECTR_Member.BoundsUpdateModes.Static; refMember.ForceUpdate(true); // Save scene and append it to the build settings. EditorApplication.SaveScene(); SECTR_VC.WaitForVC(); EditorBuildSettingsScene sectorSceneSettings = new EditorBuildSettingsScene(newAssetPath, true); bool sceneExists = false; foreach (EditorBuildSettingsScene oldScene in sceneSettings) { if (oldScene.path == newAssetPath) { sceneExists = true; oldScene.enabled = true; break; } } if (!sceneExists) { sceneSettings.Add(sectorSceneSettings); } string[] pathParts = newAssetPath.Split('/'); string sectorPath = pathParts[pathParts.Length - 1].Replace(".unity", ""); // Update the master scene with exported info. EditorApplication.OpenScene(originalScene); SECTR_VC.WaitForVC(); sector = _FindSectorByName(newAssetPath); sector.name = originalSectorName; DeleteExportedSector(sector); // Make sure Sectors has a Chunk SECTR_Chunk newChunk = sector.GetComponent <SECTR_Chunk>(); if (!newChunk) { newChunk = sector.gameObject.AddComponent <SECTR_Chunk>(); } newChunk.ScenePath = sectorPath; newChunk.NodeName = newAssetPath; newChunk.enabled = true; // Disable a TerrainComposer node if there is one. MonoBehaviour terrainNeighbors = sector.GetComponent("TerrainNeighbors") as MonoBehaviour; if (terrainNeighbors) { terrainNeighbors.enabled = false; } // Save off the accumulated build settings EditorBuildSettings.scenes = sceneSettings.ToArray(); AssetDatabase.Refresh(); EditorApplication.SaveScene(); SECTR_VC.WaitForVC(); return(true); }
public static void SectorizeTerrain(Terrain terrain, int sectorsWidth, int sectorsLength, int sectorsHeight, bool splitTerrain, bool createPortalGeo, bool includeStatic, bool includeDynamic) { if (!terrain) { Debug.LogWarning("Cannot sectorize null terrain."); return; } if (terrain.transform.root.GetComponentsInChildren <SECTR_Sector>().Length > 0) { Debug.LogWarning("Cannot sectorize terrain that is already part of a Sector."); } string undoString = "Sectorized " + terrain.name; if (sectorsWidth == 1 && sectorsLength == 1) { SECTR_Sector newSector = terrain.gameObject.AddComponent <SECTR_Sector>(); SECTR_Undo.Created(newSector, undoString); newSector.ForceUpdate(true); return; } if (splitTerrain && (!Mathf.IsPowerOfTwo(sectorsWidth) || !Mathf.IsPowerOfTwo(sectorsLength))) { Debug.LogWarning("Splitting terrain requires power of two sectors in width and length."); splitTerrain = false; } else if (splitTerrain && sectorsWidth != sectorsLength) { Debug.LogWarning("Splitting terrain requires same number of sectors in width and length."); splitTerrain = false; } int terrainLayer = terrain.gameObject.layer; Vector3 terrainSize = terrain.terrainData.size; float sectorWidth = terrainSize.x / sectorsWidth; float sectorHeight = terrainSize.y / sectorsHeight; float sectorLength = terrainSize.z / sectorsLength; int heightmapWidth = (terrain.terrainData.heightmapWidth / sectorsWidth); int heightmapLength = (terrain.terrainData.heightmapHeight / sectorsLength); int alphaWidth = terrain.terrainData.alphamapWidth / sectorsWidth; int alphaLength = terrain.terrainData.alphamapHeight / sectorsLength; int detailWidth = terrain.terrainData.detailWidth / sectorsWidth; int detailLength = terrain.terrainData.detailHeight / sectorsLength; string sceneDir = ""; string sceneName = ""; string exportFolder = splitTerrain ? SECTR_Asset.MakeExportFolder("TerrainSplits", false, out sceneDir, out sceneName) : ""; Transform baseTransform = null; if (splitTerrain) { GameObject baseObject = new GameObject(terrain.name); baseTransform = baseObject.transform; SECTR_Undo.Created(baseObject, undoString); } List <Transform> rootTransforms = new List <Transform>(); List <Bounds> rootBounds = new List <Bounds>(); _GetRoots(includeStatic, includeDynamic, rootTransforms, rootBounds); // Create Sectors string progressTitle = "Sectorizing Terrain"; int progressCounter = 0; EditorUtility.DisplayProgressBar(progressTitle, "Preparing", 0); SECTR_Sector[,,] newSectors = new SECTR_Sector[sectorsWidth, sectorsLength, sectorsHeight]; Terrain[,] newTerrains = splitTerrain ? new Terrain[sectorsWidth, sectorsLength] : null; for (int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for (int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { for (int heightIndex = 0; heightIndex < sectorsHeight; ++heightIndex) { string newName = terrain.name + " " + widthIndex + "-" + lengthIndex + "-" + heightIndex; EditorUtility.DisplayProgressBar(progressTitle, "Creating sector " + newName, progressCounter++ / (float)(sectorsWidth * sectorsLength * sectorsHeight)); GameObject newSectorObject = new GameObject("SECTR " + newName + " Sector"); newSectorObject.transform.parent = baseTransform; Vector3 sectorCorner = new Vector3(widthIndex * sectorWidth, heightIndex * sectorHeight, lengthIndex * sectorLength) + terrain.transform.position; newSectorObject.transform.position = sectorCorner; newSectorObject.isStatic = true; SECTR_Sector newSector = newSectorObject.AddComponent <SECTR_Sector>(); newSector.OverrideBounds = !splitTerrain && (sectorsWidth > 1 || sectorsLength > 1); newSector.BoundsOverride = new Bounds(sectorCorner + new Vector3(sectorWidth * 0.5f, sectorHeight * 0.5f, sectorLength * 0.5f), new Vector3(sectorWidth, sectorHeight, sectorLength)); newSectors[widthIndex, lengthIndex, heightIndex] = newSector; if (splitTerrain && heightIndex == 0) { GameObject newTerrainObject = new GameObject(newName + " Terrain"); newTerrainObject.layer = terrainLayer; newTerrainObject.tag = terrain.tag; newTerrainObject.transform.parent = newSectorObject.transform; newTerrainObject.transform.localPosition = Vector3.zero; newTerrainObject.transform.localRotation = Quaternion.identity; newTerrainObject.transform.localScale = Vector3.one; newTerrainObject.isStatic = true; Terrain newTerrain = newTerrainObject.AddComponent <Terrain>(); newTerrain.terrainData = SECTR_Asset.Create <TerrainData>(exportFolder, newName, new TerrainData()); EditorUtility.SetDirty(newTerrain.terrainData); SECTR_VC.WaitForVC(); // Copy properties // Basic terrain properties newTerrain.editorRenderFlags = terrain.editorRenderFlags; newTerrain.castShadows = terrain.castShadows; newTerrain.heightmapMaximumLOD = terrain.heightmapMaximumLOD; newTerrain.heightmapPixelError = terrain.heightmapPixelError; newTerrain.lightmapIndex = -1; // Can't set lightmap UVs on terrain. newTerrain.materialTemplate = terrain.materialTemplate; #if !UNITY_4 newTerrain.bakeLightProbesForTrees = terrain.bakeLightProbesForTrees; newTerrain.legacyShininess = terrain.legacyShininess; newTerrain.legacySpecular = terrain.legacySpecular; #endif // Copy geometric data int heightmapBaseX = widthIndex * heightmapWidth; int heightmapBaseY = lengthIndex * heightmapLength; int heightmapWidthX = heightmapWidth + (sectorsWidth > 1 ? 1 : 0); int heightmapWidthY = heightmapLength + (sectorsLength > 1 ? 1 : 0); newTerrain.terrainData.heightmapResolution = terrain.terrainData.heightmapResolution / sectorsWidth; newTerrain.terrainData.size = new Vector3(sectorWidth, terrainSize.y, sectorLength); newTerrain.terrainData.SetHeights(0, 0, terrain.terrainData.GetHeights(heightmapBaseX, heightmapBaseY, heightmapWidthX, heightmapWidthY)); #if !UNITY_4 newTerrain.terrainData.thickness = terrain.terrainData.thickness; #endif // Copy alpha maps int alphaBaseX = alphaWidth * widthIndex; int alphaBaseY = alphaLength * lengthIndex; newTerrain.terrainData.splatPrototypes = terrain.terrainData.splatPrototypes; newTerrain.basemapDistance = terrain.basemapDistance; newTerrain.terrainData.baseMapResolution = terrain.terrainData.baseMapResolution / sectorsWidth; newTerrain.terrainData.alphamapResolution = terrain.terrainData.alphamapResolution / sectorsWidth; newTerrain.terrainData.SetAlphamaps(0, 0, terrain.terrainData.GetAlphamaps(alphaBaseX, alphaBaseY, alphaWidth, alphaLength)); // Copy detail info newTerrain.detailObjectDensity = terrain.detailObjectDensity; newTerrain.detailObjectDistance = terrain.detailObjectDistance; newTerrain.terrainData.detailPrototypes = terrain.terrainData.detailPrototypes; newTerrain.terrainData.SetDetailResolution(terrain.terrainData.detailResolution / sectorsWidth, 8); // TODO: extract detailResolutionPerPatch #if !UNITY_4 newTerrain.collectDetailPatches = terrain.collectDetailPatches; #endif int detailBaseX = detailWidth * widthIndex; int detailBaseY = detailLength * lengthIndex; int numLayers = terrain.terrainData.detailPrototypes.Length; for (int layer = 0; layer < numLayers; ++layer) { newTerrain.terrainData.SetDetailLayer(0, 0, layer, terrain.terrainData.GetDetailLayer(detailBaseX, detailBaseY, detailWidth, detailLength, layer)); } // Copy grass and trees newTerrain.terrainData.wavingGrassAmount = terrain.terrainData.wavingGrassAmount; newTerrain.terrainData.wavingGrassSpeed = terrain.terrainData.wavingGrassSpeed; newTerrain.terrainData.wavingGrassStrength = terrain.terrainData.wavingGrassStrength; newTerrain.terrainData.wavingGrassTint = terrain.terrainData.wavingGrassTint; newTerrain.treeBillboardDistance = terrain.treeBillboardDistance; newTerrain.treeCrossFadeLength = terrain.treeCrossFadeLength; newTerrain.treeDistance = terrain.treeDistance; newTerrain.treeMaximumFullLODCount = terrain.treeMaximumFullLODCount; newTerrain.terrainData.treePrototypes = terrain.terrainData.treePrototypes; newTerrain.terrainData.RefreshPrototypes(); foreach (TreeInstance treeInstace in terrain.terrainData.treeInstances) { if (treeInstace.prototypeIndex >= 0 && treeInstace.prototypeIndex < newTerrain.terrainData.treePrototypes.Length && newTerrain.terrainData.treePrototypes[treeInstace.prototypeIndex].prefab) { Vector3 worldSpaceTreePos = Vector3.Scale(treeInstace.position, terrainSize) + terrain.transform.position; if (newSector.BoundsOverride.Contains(worldSpaceTreePos)) { Vector3 localSpaceTreePos = new Vector3((worldSpaceTreePos.x - newTerrain.transform.position.x) / sectorWidth, treeInstace.position.y, (worldSpaceTreePos.z - newTerrain.transform.position.z) / sectorLength); TreeInstance newInstance = treeInstace; newInstance.position = localSpaceTreePos; newTerrain.AddTreeInstance(newInstance); } } } // Copy physics #if UNITY_4_LATE newTerrain.terrainData.physicMaterial = terrain.terrainData.physicMaterial; #endif // Force terrain to rebuild newTerrain.Flush(); UnityEditor.EditorUtility.SetDirty(newTerrain.terrainData); SECTR_VC.WaitForVC(); newTerrain.enabled = false; newTerrain.enabled = true; TerrainCollider terrainCollider = terrain.GetComponent <TerrainCollider>(); if (terrainCollider) { TerrainCollider newCollider = newTerrainObject.AddComponent <TerrainCollider>(); #if !UNITY_4_LATE newCollider.sharedMaterial = terrainCollider.sharedMaterial; #endif newCollider.terrainData = newTerrain.terrainData; } newTerrains[widthIndex, lengthIndex] = newTerrain; SECTR_Undo.Created(newTerrainObject, undoString); } newSector.ForceUpdate(true); SECTR_Undo.Created(newSectorObject, undoString); _Encapsulate(newSector, rootTransforms, rootBounds, undoString); } } } // Create portals and neighbors progressCounter = 0; for (int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for (int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { for (int heightIndex = 0; heightIndex < sectorsHeight; ++heightIndex) { EditorUtility.DisplayProgressBar(progressTitle, "Creating portals...", progressCounter++ / (float)(sectorsWidth * sectorsLength * sectorsHeight)); if (widthIndex < sectorsWidth - 1) { _CreatePortal(createPortalGeo, newSectors[widthIndex + 1, lengthIndex, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex], baseTransform, undoString); } if (lengthIndex < sectorsLength - 1) { _CreatePortal(createPortalGeo, newSectors[widthIndex, lengthIndex + 1, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex], baseTransform, undoString); } if (heightIndex > 0) { _CreatePortal(createPortalGeo, newSectors[widthIndex, lengthIndex, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex - 1], baseTransform, undoString); } } } } if (splitTerrain) { progressCounter = 0; for (int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for (int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { EditorUtility.DisplayProgressBar(progressTitle, "Smoothing split terrain...", progressCounter++ / (float)(sectorsWidth * sectorsLength * sectorsHeight)); // Blend together the seams of the alpha maps, which requires // going through all of the mip maps of all of the layer textures. // We have to blend here rather than when we set the alpha data (above) // because Unity computes mips and we need to blend all of the mips. Terrain newTerrain = newTerrains[widthIndex, lengthIndex]; SECTR_Sector terrainSector = newSectors[widthIndex, lengthIndex, 0]; terrainSector.LeftTerrain = widthIndex > 0 ? newSectors[widthIndex - 1, lengthIndex, 0] : null; terrainSector.RightTerrain = widthIndex < sectorsWidth - 1 ? newSectors[widthIndex + 1, lengthIndex, 0] : null; terrainSector.BottomTerrain = lengthIndex > 0 ? newSectors[widthIndex, lengthIndex - 1, 0] : null; terrainSector.TopTerrain = lengthIndex < sectorsLength - 1 ? newSectors[widthIndex, lengthIndex + 1, 0] : null; terrainSector.ConnectTerrainNeighbors(); // Use reflection trickery to get at the raw texture values. System.Reflection.PropertyInfo alphamapProperty = newTerrain.terrainData.GetType().GetProperty("alphamapTextures", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); // Get the texture we'll write into Texture2D[] alphaTextures = (Texture2D[])alphamapProperty.GetValue(newTerrain.terrainData, null); int numTextures = alphaTextures.Length; // Get the textures we'll read from Texture2D[] leftNeighborTextures = terrainSector.LeftTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex - 1, lengthIndex].terrainData, null) : null; Texture2D[] rightNeighborTextures = terrainSector.RightTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex + 1, lengthIndex].terrainData, null) : null; Texture2D[] topNeighborTextures = terrainSector.TopTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex, lengthIndex + 1].terrainData, null) : null; Texture2D[] bottomNeighborTextures = terrainSector.BottomTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex, lengthIndex - 1].terrainData, null) : null; for (int textureIndex = 0; textureIndex < numTextures; ++textureIndex) { Texture2D alphaTexture = alphaTextures[textureIndex]; Texture2D leftTexture = leftNeighborTextures != null ? leftNeighborTextures[textureIndex] : null; Texture2D rightTexture = rightNeighborTextures != null ? rightNeighborTextures[textureIndex] : null; Texture2D topTexture = topNeighborTextures != null ? topNeighborTextures[textureIndex] : null; Texture2D bottomTexture = bottomNeighborTextures != null ? bottomNeighborTextures[textureIndex] : null; int numMips = alphaTexture.mipmapCount; for (int mipIndex = 0; mipIndex < numMips; ++mipIndex) { Color[] alphaTexels = alphaTexture.GetPixels(mipIndex); int width = (int)Mathf.Sqrt(alphaTexels.Length); int height = width; for (int texelWidthIndex = 0; texelWidthIndex < width; ++texelWidthIndex) { for (int texelHeightIndex = 0; texelHeightIndex < height; ++texelHeightIndex) { // We can take advantage of the build order to average on the leading edges (right and top) // and then copy form the trailing edges (left and bottom) if (texelWidthIndex == 0 && leftTexture) { Color[] neighborTexels = leftTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] = neighborTexels[(width - 1) + (texelHeightIndex * width)]; } else if (texelWidthIndex == width - 1 && rightTexture) { Color[] neighborTexels = rightTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] += neighborTexels[0 + (texelHeightIndex * width)]; alphaTexels[texelWidthIndex + texelHeightIndex * width] *= 0.5f; } else if (texelHeightIndex == 0 && bottomTexture) { Color[] neighborTexels = bottomTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] = neighborTexels[texelWidthIndex + ((height - 1) * width)]; } else if (texelHeightIndex == height - 1 && topTexture) { Color[] neighborTexels = topTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] += neighborTexels[texelWidthIndex + (0 * width)]; alphaTexels[texelWidthIndex + texelHeightIndex * width] *= 0.5f; } } } alphaTexture.SetPixels(alphaTexels, mipIndex); } alphaTexture.wrapMode = TextureWrapMode.Clamp; alphaTexture.Apply(false); } newTerrain.Flush(); } } } EditorUtility.ClearProgressBar(); // destroy original terrain if (splitTerrain) { SECTR_Undo.Destroy(terrain.gameObject, undoString); } }