public override void OnInspectorGUI() { SECTR_Sector mySector = (SECTR_Sector)target; base.OnInspectorGUI(); List <SECTR_Member.Child> sharedChildren = mySector.GetSharedChildren(); if (sharedChildren.Count > 0 && GUILayout.Button(new GUIContent("Fix Shared Children", "Adds Member components to any children that extend beyond this Sector and into other sectors."))) { MakeSharedChildrenMembers(mySector, sharedChildren, "Fix Shared Children"); } if (mySector.GetComponentInChildren <Terrain>()) { terrainFoldout = EditorGUILayout.Foldout(terrainFoldout, "Terrain Connections"); if (terrainFoldout) { serializedObject.Update(); DrawProperty("TopTerrain"); DrawProperty("BottomTerrain"); DrawProperty("LeftTerrain"); DrawProperty("RightTerrain"); if (GUILayout.Button("Reconnect Neighbors")) { mySector.ConnectTerrainNeighbors(); } serializedObject.ApplyModifiedProperties(); } } }
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); } }
private void _SetupChunk() { _FindChunkRoot(); if (chunkRoot) { // Activate the root if inactive (due to backwards compat or recentering if (!chunkRoot.activeSelf) { chunkRoot.SetActive(true); } // Recenter chunk under ourselves if (recenterChunk) { Transform chunkTransform = chunkRoot.transform; chunkTransform.localPosition = Vector3.zero; chunkTransform.localRotation = Quaternion.identity; chunkTransform.localScale = Vector3.one; } // Hook up the child proxy SECTR_Member rootMember = chunkSector.GetComponent <SECTR_Member>(); if (!rootMember) { rootMember = chunkSector.gameObject.AddComponent <SECTR_Member>(); rootMember.BoundsUpdateMode = SECTR_Member.BoundsUpdateModes.Static; rootMember.ForceUpdate(true); } else if (recenterChunk) { rootMember.ForceUpdate(true); } cachedSector.ChildProxy = rootMember; // Unfreeze our sector cachedSector.Frozen = false; if (cachedSector.TopTerrain || cachedSector.BottomTerrain || cachedSector.LeftTerrain || cachedSector.RightTerrain) { cachedSector.ConnectTerrainNeighbors(); if (cachedSector.TopTerrain) { cachedSector.TopTerrain.ConnectTerrainNeighbors(); } if (cachedSector.BottomTerrain) { cachedSector.BottomTerrain.ConnectTerrainNeighbors(); } if (cachedSector.LeftTerrain) { cachedSector.LeftTerrain.ConnectTerrainNeighbors(); } if (cachedSector.RightTerrain) { cachedSector.RightTerrain.ConnectTerrainNeighbors(); } } // Remove the proxy if there is one if (proxy) { GameObject.Destroy(proxy); } loadState = LoadState.Active; } }