예제 #1
0
 public static void MakeSharedChildrenMembers(SECTR_Sector sector, List<SECTR_Member.Child> sharedChildren, string undoName)
 {
     int numSharedChildren = sharedChildren.Count;
     for(int childIndex = 0; childIndex < numSharedChildren; ++childIndex)
     {
         SECTR_Member.Child child = sharedChildren[childIndex];
         bool hasMemberParent = false;
         Transform parent = child.gameObject.transform;
         while(parent != null)
         {
             if(parent.gameObject != sector.gameObject && parent.GetComponent<SECTR_Member>())
             {
                 hasMemberParent = true;
                 break;
             }
             else
             {
                 parent = parent.parent;
             }
         }
         if(!hasMemberParent)
         {
             SECTR_Member newMember = child.gameObject.AddComponent<SECTR_Member>();
             SECTR_Undo.Created(newMember, undoName);
         }
     }
     sector.ForceUpdate(true);
 }
예제 #2
0
    public static void MakeSharedChildrenMembers(SECTR_Sector sector, List <SECTR_Member.Child> sharedChildren, string undoName)
    {
        int numSharedChildren = sharedChildren.Count;

        for (int childIndex = 0; childIndex < numSharedChildren; ++childIndex)
        {
            SECTR_Member.Child child  = sharedChildren[childIndex];
            bool      hasMemberParent = false;
            Transform parent          = child.gameObject.transform;
            while (parent != null)
            {
                if (parent.gameObject != sector.gameObject && parent.GetComponent <SECTR_Member>())
                {
                    hasMemberParent = true;
                    break;
                }
                else
                {
                    parent = parent.parent;
                }
            }
            if (!hasMemberParent)
            {
                SECTR_Member newMember = child.gameObject.AddComponent <SECTR_Member>();
                SECTR_Undo.Created(newMember, undoName);
            }
        }
        sector.ForceUpdate(true);
    }
예제 #3
0
    /// Re-adds the data from the specified Sector to the current scene. Safe to call from command line.
    /// <param name="sector">The Sector to import.</param>
    /// <returns>Returns true if Sector was successfully imported, false otherwise.</returns>
    public static bool ImportFromChunk(SECTR_Sector sector)
    {
        if (sector == null)
        {
            Debug.LogError("Cannot import invalid Sector.");
            return(false);
        }

        if (!sector.Frozen)
        {
            Debug.Log("Skipping import of unfrozen Sector");
            return(true);
        }

        if (!sector.gameObject.isStatic)
        {
            Debug.Log("Skipping import of dynamic Sector " + sector.name + ".");
            return(true);
        }

        SECTR_Chunk chunk = sector.GetComponent <SECTR_Chunk>();

        if (chunk)
        {
            EditorApplication.OpenSceneAdditive(chunk.NodeName);
            GameObject newNode = GameObject.Find(chunk.NodeName);
            if (newNode == null)
            {
                Debug.LogError("Exported data does not match scene. Skipping import of " + sector.name + ".");
                return(false);
            }

            SECTR_ChunkRef chunkRef = newNode.GetComponent <SECTR_ChunkRef>();
            if (chunkRef && chunkRef.RealSector)
            {
                newNode = chunkRef.RealSector.gameObject;
                if (chunkRef.Recentered)
                {
                    newNode.transform.parent        = sector.transform;
                    newNode.transform.localPosition = Vector3.zero;
                    newNode.transform.localRotation = Quaternion.identity;
                    newNode.transform.localScale    = Vector3.one;
                }
                newNode.transform.parent = null;
                GameObject.DestroyImmediate(chunkRef.gameObject);
            }

            while (newNode.transform.childCount > 0)
            {
                newNode.transform.GetChild(0).parent = sector.transform;
            }


            // Merge lightmaps into the scene
#if !UNITY_STREAM_ENLIGHTEN
            SECTR_LightmapRef newRef = newNode.GetComponent <SECTR_LightmapRef>();
            if (newRef)
            {
                int            numLightmaps = LightmapSettings.lightmaps.Length;
                LightmapData[] newLightmaps = new LightmapData[numLightmaps];
                for (int lightmapIndex = 0; lightmapIndex < numLightmaps; ++lightmapIndex)
                {
                    newLightmaps[lightmapIndex] = LightmapSettings.lightmaps[lightmapIndex];
                }
                foreach (SECTR_LightmapRef.RefData refData in newRef.LightmapRefs)
                {
                    if (refData.index >= 0 && refData.index < numLightmaps)
                    {
                        LightmapData newData = new LightmapData();
                        newData.lightmapNear        = refData.NearLightmap;
                        newData.lightmapFar         = refData.FarLightmap;
                        newLightmaps[refData.index] = newData;
                    }
                }
                LightmapSettings.lightmaps = newLightmaps;

#if !UNITY_4
                foreach (SECTR_LightmapRef.RenderData indexData in newRef.LightmapRenderers)
                {
                    if (indexData.renderer)
                    {
                        indexData.renderer.lightmapIndex       = indexData.rendererLightmapIndex;
                        indexData.renderer.lightmapScaleOffset = indexData.rendererLightmapScaleOffset;
                        GameObjectUtility.SetStaticEditorFlags(indexData.renderer.gameObject, GameObjectUtility.GetStaticEditorFlags(indexData.renderer.gameObject) | StaticEditorFlags.BatchingStatic);
                    }
                    if (indexData.terrain)
                    {
                        indexData.terrain.lightmapIndex = indexData.terrainLightmapIndex;
                    }
                }
#endif

                GameObject.DestroyImmediate(newRef);
            }
#endif


            // Copy terrain component specially because the generic routine doesn't work for some reason.
            Terrain terrain = newNode.GetComponent <Terrain>();
            if (terrain)
            {
                Terrain terrainClone = sector.gameObject.AddComponent <Terrain>();
                terrainClone.terrainData             = terrain.terrainData;
                terrainClone.basemapDistance         = terrain.basemapDistance;
                terrainClone.castShadows             = terrain.castShadows;
                terrainClone.detailObjectDensity     = terrain.detailObjectDensity;
                terrainClone.detailObjectDistance    = terrain.detailObjectDistance;
                terrainClone.heightmapMaximumLOD     = terrain.heightmapMaximumLOD;
                terrainClone.heightmapPixelError     = terrain.heightmapPixelError;
                terrainClone.lightmapIndex           = terrain.lightmapIndex;
                terrainClone.treeBillboardDistance   = terrain.treeBillboardDistance;
                terrainClone.treeCrossFadeLength     = terrain.treeCrossFadeLength;
                terrainClone.treeDistance            = terrain.treeDistance;
                terrainClone.treeMaximumFullLODCount = terrain.treeMaximumFullLODCount;
                terrainClone.Flush();
            }

            // Destroy the placeholder Member if there is one.
            // It's theoretically possible to have multiple members, so remove them all.
            SECTR_Member[] oldMembers    = newNode.GetComponents <SECTR_Member>();
            int            numOldMembers = oldMembers.Length;
            for (int oldIndex = 0; oldIndex < numOldMembers; ++oldIndex)
            {
                GameObject.DestroyImmediate(oldMembers[oldIndex]);
            }

            // Copy all remaining components over
            Component[] remainingComponents = newNode.GetComponents <Component>();
            int         numRemaining        = remainingComponents.Length;
            for (int componentIndex = 0; componentIndex < numRemaining; ++componentIndex)
            {
                Component component = remainingComponents[componentIndex];
                if (component != newNode.transform && component.GetType() != typeof(Terrain))
                {
                    Component componentClone = sector.gameObject.AddComponent(component.GetType());
                    EditorUtility.CopySerialized(component, componentClone);
                }
            }

            // Enable a TerrainComposer node if there is one.
            MonoBehaviour terrainNeighbors = sector.GetComponent("TerrainNeighbors") as MonoBehaviour;
            if (terrainNeighbors)
            {
                terrainNeighbors.enabled = true;
            }

            GameObject.DestroyImmediate(newNode);
            sector.Frozen = false;
            sector.ForceUpdate(true);
            chunk.enabled = false;
            return(true);
        }
        return(false);
    }
예제 #4
0
    private static void _SectorizeConnected(Terrain terrain, bool createPortalGeo, bool includeStatic, bool includeDynamic, Dictionary <Terrain, Terrain> processedTerrains, List <Transform> rootTransforms, List <Bounds> rootBounds)
    {
        if (terrain && !processedTerrains.ContainsKey(terrain))
        {
            string undoString = "Sectorize Connected";
            processedTerrains[terrain]  = terrain;
            terrain.gameObject.isStatic = true;
            GameObject newSectorObject = new GameObject(terrain.name + " Sector");
            newSectorObject.isStatic                = true;
            newSectorObject.transform.parent        = terrain.transform.parent;
            newSectorObject.transform.localPosition = terrain.transform.localPosition;
            newSectorObject.transform.localRotation = terrain.transform.localRotation;
            newSectorObject.transform.localScale    = terrain.transform.localScale;
            terrain.transform.parent                = newSectorObject.transform;
            SECTR_Sector newSector = newSectorObject.AddComponent <SECTR_Sector>();
            newSector.ForceUpdate(true);
            SECTR_Undo.Created(newSectorObject, undoString);
            _Encapsulate(newSector, rootTransforms, rootBounds, undoString);

            Component terrainNeighbors = terrain.GetComponent("TerrainNeighbors");
            if (terrainNeighbors)
            {
                System.Type neighborsType = terrainNeighbors.GetType();
                Terrain     topTerrain    = neighborsType.GetField("top").GetValue(terrainNeighbors) as Terrain;
                if (topTerrain)
                {
                    SECTR_Sector neighborSector = topTerrain.transform.parent ? topTerrain.transform.parent.GetComponent <SECTR_Sector>() : null;
                    if (neighborSector)
                    {
                        newSector.TopTerrain         = neighborSector;
                        neighborSector.BottomTerrain = newSector;
                        _CreatePortal(createPortalGeo, newSector, neighborSector, newSectorObject.transform.parent, undoString);
                    }
                    _SectorizeConnected(topTerrain, createPortalGeo, includeStatic, includeDynamic, processedTerrains, rootTransforms, rootBounds);
                }
                Terrain bottomTerrain = neighborsType.GetField("bottom").GetValue(terrainNeighbors) as Terrain;
                if (bottomTerrain)
                {
                    SECTR_Sector neighborSector = bottomTerrain.transform.parent ? bottomTerrain.transform.parent.GetComponent <SECTR_Sector>() : null;
                    if (neighborSector)
                    {
                        newSector.BottomTerrain   = neighborSector;
                        neighborSector.TopTerrain = newSector;
                        _CreatePortal(createPortalGeo, newSector, neighborSector, newSectorObject.transform.parent, undoString);
                    }
                    _SectorizeConnected(bottomTerrain, createPortalGeo, includeStatic, includeDynamic, processedTerrains, rootTransforms, rootBounds);
                }
                Terrain leftTerrain = neighborsType.GetField("left").GetValue(terrainNeighbors) as Terrain;
                if (leftTerrain)
                {
                    SECTR_Sector neighborSector = leftTerrain.transform.parent ? leftTerrain.transform.parent.GetComponent <SECTR_Sector>() : null;
                    if (neighborSector)
                    {
                        newSector.LeftTerrain       = neighborSector;
                        neighborSector.RightTerrain = newSector;
                        _CreatePortal(createPortalGeo, newSector, neighborSector, newSectorObject.transform.parent, undoString);
                    }
                    _SectorizeConnected(leftTerrain, createPortalGeo, includeStatic, includeDynamic, processedTerrains, rootTransforms, rootBounds);
                }
                Terrain rightTerrain = neighborsType.GetField("right").GetValue(terrainNeighbors) as Terrain;
                if (rightTerrain)
                {
                    SECTR_Sector neighborSector = rightTerrain.transform.parent ? rightTerrain.transform.parent.GetComponent <SECTR_Sector>() : null;
                    if (neighborSector)
                    {
                        newSector.RightTerrain     = neighborSector;
                        neighborSector.LeftTerrain = newSector;
                        _CreatePortal(createPortalGeo, newSector, neighborSector, newSectorObject.transform.parent, undoString);
                    }
                    _SectorizeConnected(rightTerrain, createPortalGeo, includeStatic, includeDynamic, processedTerrains, rootTransforms, rootBounds);
                }
            }
        }
    }
예제 #5
0
    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);
        }
    }