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); }
public static List <SECTR_Occluder> GetOccludersInSector(SECTR_Sector sector) { List <SECTR_Occluder> occluders = null; occluderTable.TryGetValue(sector, out occluders); return(occluders); }
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(); } } }
private void ChunkChanged(SECTR_Chunk source, bool loaded) { int numSectors = Sectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = Sectors[sectorIndex]; if (sector) { SECTR_Chunk chunk = sector.GetComponent <SECTR_Chunk>(); if (chunk && chunk != source) { // We need to temporarily remove our callback so // that we don't get infinite loops. chunk.ReferenceChange -= ChunkChanged; if (loaded) { chunk.AddReference(); } else { chunk.RemoveReference(); } chunk.ReferenceChange += ChunkChanged; } } } }
// This needs to be guaranteed happen before other SECTR updates to prevent // one frame of falling. void FixedUpdate() { bool allSectorsFrozen = true; int numSectors = cachedMember.Sectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = cachedMember.Sectors[sectorIndex]; if (!sector.Frozen) { allSectorsFrozen = false; break; } } if (allSectorsFrozen && !hibernating) { _Hibernate(); } else if (!allSectorsFrozen && hibernating) { _WakeUp(); } if (hibernating && HibernateUpdate != null) { HibernateUpdate(); } }
void _PickSector(SECTR_Portal myPortal) { HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive)); if (Event.current.type == EventType.MouseMove) { _ComputeCursorVert(); } else if (Event.current.type == EventType.MouseUp && Event.current.button == 0 && !Event.current.alt && !Event.current.control) { SECTR_Sector sector = _GetSectorFromSelection(); if (sector) { SECTR_Undo.Record(myPortal, "Assign Sector to Portal."); if (pickBack) { myPortal.BackSector = sector; } else { myPortal.FrontSector = sector; } EditorUtility.SetDirty(myPortal); pickFront = false; pickBack = false; _EndSelection(); } } else if (Event.current.type == EventType.KeyUp && Event.current.keyCode == KeyCode.Escape) { pickBack = false; pickFront = false; _EndSelection(); } }
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); }
private void ExportUpdate() { SECTR_Chunk myChunk = (SECTR_Chunk)target; if (myChunk) { SECTR_Sector mySector = myChunk.GetComponent <SECTR_Sector>(); if (mySector) { switch (modiferMode) { case ModifierMode.Export: SECTR_StreamExport.ExportToChunk(mySector); break; case ModifierMode.Import: SECTR_StreamExport.ImportFromChunk(mySector); break; case ModifierMode.Revert: SECTR_StreamExport.DeleteExportedSector(mySector); myChunk.enabled = true; break; case ModifierMode.None: default: break; } } modiferMode = ModifierMode.None; } EditorApplication.update -= ExportUpdate; }
void OnEnable() { cachedSector = GetComponent <SECTR_Sector>(); if (cachedSector.Frozen) { _CreateProxy(); } }
private static void _CreatePortal(bool createGeo, SECTR_Sector front, SECTR_Sector back, Transform parent, string undoString) { if (front && back) { string portalName = "SECTR Terrain Portal"; GameObject newPortalObject; SECTR_Portal newPortal; #if !UNITY_4_0 && !UNITY_4_1 if (createGeo) { newPortalObject = GameObject.CreatePrimitive(PrimitiveType.Quad); newPortalObject.name = portalName; Mesh quadResource = newPortalObject.GetComponent <MeshFilter>().sharedMesh; GameObject.DestroyImmediate(newPortalObject.GetComponent <MeshFilter>()); GameObject.DestroyImmediate(newPortalObject.GetComponent <MeshRenderer>()); GameObject.DestroyImmediate(newPortalObject.GetComponent <Collider>()); newPortal = newPortalObject.AddComponent <SECTR_Portal>(); newPortal.HullMesh = quadResource; } else #endif { newPortalObject = new GameObject(portalName); newPortal = newPortalObject.AddComponent <SECTR_Portal>(); } newPortal.SetFlag(SECTR_Portal.PortalFlags.PassThrough, true); newPortal.FrontSector = front; newPortal.BackSector = back; newPortal.transform.parent = parent; newPortal.transform.position = (front.TotalBounds.center + back.TotalBounds.center) * 0.5f; if (createGeo) { newPortal.transform.LookAt(back.TotalBounds.center); Vector3 orientation = newPortal.transform.forward; if (Mathf.Abs(orientation.x) >= Mathf.Abs(orientation.y) && Mathf.Abs(orientation.x) >= Mathf.Abs(orientation.z)) { newPortal.transform.localScale = new Vector3(front.TotalBounds.size.z, front.TotalBounds.size.y, 1f); } else if (Mathf.Abs(orientation.y) >= Mathf.Abs(orientation.x) && Mathf.Abs(orientation.y) >= Mathf.Abs(orientation.z)) { newPortal.transform.localScale = new Vector3(front.TotalBounds.size.x, front.TotalBounds.size.z, 1f); } else if (Mathf.Abs(orientation.z) >= Mathf.Abs(orientation.x) && Mathf.Abs(orientation.z) >= Mathf.Abs(orientation.y)) { newPortal.transform.localScale = new Vector3(front.TotalBounds.size.x, front.TotalBounds.size.y, 1f); } } else { newPortal.transform.LookAt(front.TotalBounds.center); } SECTR_Undo.Created(newPortalObject, undoString); } }
void _SwapSectors(SECTR_Portal myPortal) { SECTR_Undo.Record(myPortal, "Swap Portal Sectors"); SECTR_Sector oldFront = myPortal.FrontSector; SECTR_Sector oldBack = myPortal.BackSector; myPortal.FrontSector = null; myPortal.BackSector = null; myPortal.FrontSector = oldBack; myPortal.BackSector = oldFront; EditorUtility.SetDirty(myPortal); }
SECTR_Sector _GetSectorFromSelection() { GameObject selected = lastSelectedObject; SECTR_Sector sector = null; while (selected != null && sector == null) { sector = selected.GetComponent <SECTR_Sector>(); selected = selected.transform.parent ? selected.transform.parent.gameObject : null; } return(sector); }
private bool _IsSectorLoaded(SECTR_Sector sector) { if (sector && sector.Frozen) { SECTR_Chunk chunk = sector.GetComponent <SECTR_Chunk>(); if (chunk && !chunk.IsLoaded()) { return(false); } } return(true); }
private SECTR_Chunk _GetOppositeChunk(Vector3 position) { if (Portal) { SECTR_Sector oppositeSector = SECTR_Geometry.IsPointInFrontOfPlane(position, Portal.Center, Portal.Normal) ? Portal.BackSector : Portal.FrontSector; if (oppositeSector) { return(oppositeSector.GetComponent <SECTR_Chunk>()); } } return(null); }
protected static Terrain GetTerrain(SECTR_Sector sector) { if (sector) { SECTR_Member realSector = sector.childProxy ? sector.childProxy : sector; return(realSector.GetComponentInChildren <Terrain>()); } else { return(null); } }
// Add and removes references to current and neighboring sectors // as this component moves around the world. private void _MembershipChanged(List <SECTR_Sector> left, List <SECTR_Sector> joined) { // Add ref to all of the new objects first so that we don't unload and then immeditately load again. if (joined != null) { int numJoined = joined.Count; for (int sectorIndex = 0; sectorIndex < numJoined; ++sectorIndex) { SECTR_Sector sector = joined[sectorIndex]; if (sector && !currentSectors.Contains(sector)) { SECTR_Graph.BreadthWalk(ref neighbors, sector, 0, MaxDepth); int numNeighbors = neighbors.Count; for (int neighborIndex = 0; neighborIndex < numNeighbors; ++neighborIndex) { SECTR_Chunk neighborChunk = neighbors[neighborIndex].Sector.GetComponent <SECTR_Chunk>(); if (neighborChunk) { neighborChunk.AddReference(); } } currentSectors.Add(sector); } } } // Dec ref any sectors we're no longer in. if (left != null) { int numLeft = left.Count; for (int sectorIndex = 0; sectorIndex < numLeft; ++sectorIndex) { SECTR_Sector sector = left[sectorIndex]; // We have to be careful about double-removing on shutdown b/c we don't control // order of destruction. if (sector && currentSectors.Contains(sector)) { SECTR_Graph.BreadthWalk(ref neighbors, sector, 0, MaxDepth); int numNeighbors = neighbors.Count; for (int neighborIndex = 0; neighborIndex < numNeighbors; ++neighborIndex) { SECTR_Chunk neighborChunk = neighbors[neighborIndex].Sector.GetComponent <SECTR_Chunk>(); if (neighborChunk) { neighborChunk.RemoveReference(); } } currentSectors.Remove(sector); } } } }
/// Called when a Sector is being destroyed public void SectorDisabled(SECTR_Sector sector) { if (sector) { sectors.Remove(sector); if (Changed != null) { leftSectors.Clear(); leftSectors.Add(sector); Changed(leftSectors, null); } } }
/// Returns the list of Sectors that intersect an AABB. /// Sectors may overlap and are not exclusive, hence the list. /// <param name="sectors"> List of sectors to write into.</param> /// <param name="bounds">The world space bounding box for which to search.</param> /// <returns>The List of Sectors overlapping bounds</returns> public static void GetContaining(ref List <SECTR_Sector> sectors, Bounds bounds) { sectors.Clear(); int numSectors = allSectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = allSectors[sectorIndex]; if (sector.TotalBounds.Intersects(bounds)) { sectors.Add(sector); } } }
void OnDrawGizmos() { Gizmos.color = SECTR_Sector.SectorColor; int numSectors = Sectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = Sectors[sectorIndex]; if (sector) { Gizmos.DrawLine(transform.position, sector.TotalBounds.center); } } }
private static SECTR_Sector _FindSectorByName(string name) { int numSectors = SECTR_Sector.All.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = SECTR_Sector.All[sectorIndex]; if (sector.name == name) { return(sector); } } return(null); }
void Update() { if (waypoints.Count == 0 && SECTR_Sector.All.Count > 0 && MovementSpeed > 0f) { SECTR_Sector goal = SECTR_Sector.All[Random.Range(0, SECTR_Sector.All.Count)]; SECTR_Graph.FindShortestPath(ref path, transform.position, goal.transform.position, SECTR_Portal.PortalFlags.Locked); Vector3 height = Vector3.zero; Collider myCollider = GetComponent <Collider>(); if (myCollider) { height.y += myCollider.bounds.extents.y; } waypoints.Clear(); int numNodes = path.Count; for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { SECTR_Graph.Node node = path[nodeIndex]; waypoints.Add(node.Sector.transform.position + height); if (node.Portal) { waypoints.Add(node.Portal.transform.position); } } waypoints.Add(goal.transform.position + height); currentWaypointIndex = 0; } if (waypoints.Count > 0 && MovementSpeed > 0) { Vector3 nextWaypoint = waypoints[currentWaypointIndex]; Vector3 vecToGoal = nextWaypoint - transform.position; float sqrGoalDistance = vecToGoal.sqrMagnitude; if (sqrGoalDistance > SECTR_Geometry.kVERTEX_EPSILON) { float distanceToGoal = Mathf.Sqrt(sqrGoalDistance); vecToGoal /= distanceToGoal; vecToGoal *= Mathf.Min(MovementSpeed * Time.deltaTime, distanceToGoal); transform.position += vecToGoal; } else { ++currentWaypointIndex; if (currentWaypointIndex >= waypoints.Count) { waypoints.Clear(); } } } }
private static void _Encapsulate(SECTR_Sector newSector, List <Transform> rootTransforms, List <Bounds> rootBounds, string undoString) { int numRoots = rootTransforms.Count; for (int rootIndex = numRoots - 1; rootIndex >= 0; --rootIndex) { Transform rootTransform = rootTransforms[rootIndex]; if (rootTransform != newSector.transform && SECTR_Geometry.BoundsContainsBounds(newSector.TotalBounds, rootBounds[rootIndex])) { SECTR_Undo.Parent(newSector.gameObject, rootTransform.gameObject, undoString); rootTransforms.RemoveAt(rootIndex); rootBounds.RemoveAt(rootIndex); } } }
void Start() { cachedMember.ForceUpdate(true); int numSectors = cachedMember.Sectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = cachedMember.Sectors[sectorIndex]; SECTR_Chunk chunk = sector.GetComponent <SECTR_Chunk>(); if (chunk) { chunk.AddReference(); } } LockSelf(true); }
void OnDisable() { int numSectors = Sectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = Sectors[sectorIndex]; if (sector) { SECTR_Chunk chunk = sector.GetComponent <SECTR_Chunk>(); if (chunk) { chunk.ReferenceChange -= ChunkChanged; } } } }
void OnDisable() { if (!quitting && asyncLoadOp != null && !asyncLoadOp.isDone) { Debug.LogError("Chunk unloaded with async operation active. " + "Do not disable chunks until async operations are complete or Unity will likely crash."); } if (loadState != LoadState.Unloaded) { _FindChunkRoot(); if (chunkRoot) { _DestoryChunk(false, true); } } cachedSector = null; }
protected virtual void OnDisable() { if (Changed != null && sectors.Count > 0) { Changed(sectors, null); } if (!isSector && !neverJoin) { int numSectors = sectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = sectors[sectorIndex]; if (sector) { sector.Deregister(this); } } sectors.Clear(); } int numChildren = children.Count; for (int childIndex = 0; childIndex < numChildren; ++childIndex) { Child child = children[childIndex]; child.processed = false; childPool.Push(child); } children.Clear(); childTable.Clear(); renderers.Clear(); lights.Clear(); terrains.Clear(); if (SECTR_Modules.VIS) { shadowLights.Clear(); shadowCasters.Clear(); } bakedOnlyTable = null; allMembers.Remove(this); allMemberTable.Remove(transform); }
private void _MembershipChanged(List <SECTR_Sector> left, List <SECTR_Sector> joined) { // Add ref to all of the new objects first so that we don't unload and then immeditately load again. if (joined != null) { int numJoined = joined.Count; for (int sectorIndex = 0; sectorIndex < numJoined; ++sectorIndex) { SECTR_Sector sector = joined[sectorIndex]; if (sector) { List <SECTR_Occluder> occluders; if (!occluderTable.TryGetValue(sector, out occluders)) { occluders = new List <SECTR_Occluder>(4); occluderTable[sector] = occluders; } occluders.Add(this); currentSectors.Add(sector); } } } // Dec ref any sectors we're no longer in. if (left != null) { int numLeft = left.Count; for (int sectorIndex = 0; sectorIndex < numLeft; ++sectorIndex) { SECTR_Sector sector = left[sectorIndex]; // We have to be careful about double-removing on shutdown b/c we don't control // order of destruction. if (sector && currentSectors.Contains(sector)) { List <SECTR_Occluder> occluders; if (occluderTable.TryGetValue(sector, out occluders)) { occluders.Remove(this); } currentSectors.Remove(sector); } } } }
protected virtual void OnDrawGizmosSelected() { // Render a cube of the aggregate bounds. if (enabled && !isSector) { // Draw the bounds Gizmos.color = MemberColor; Gizmos.DrawWireCube(totalBounds.center, totalBounds.size); // Render links to neighbor Sectors. Gizmos.color = SECTR_Sector.SectorColor; int numSectors = sectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = sectors[sectorIndex]; Gizmos.DrawLine(totalBounds.center, sector.TotalBounds.center); } } }
void OnDisable() { int numSectors = sectors.Count; for (int sectorIndex = 0; sectorIndex < numSectors; ++sectorIndex) { SECTR_Sector sector = sectors[sectorIndex]; if (sector) { SECTR_Chunk chunk = sector.GetComponent <SECTR_Chunk>(); if (chunk) { chunk.RemoveReference(); } } } sectors.Clear(); updated = false; }
public override void OnInspectorGUI() { SECTR_Portal myPortal = (SECTR_Portal)target; SECTR_Sector newFront = ObjectField <SECTR_Sector>("Front Sector", "Reference to the Sector on the front side of this Portal", myPortal.FrontSector, true); SECTR_Sector newBack = ObjectField <SECTR_Sector>("Back Sector", "Reference to the Sector on the back side of this Portal", myPortal.BackSector, true); // Only apply changes if things are actually different. // Note that the code below duplicates some functionality from SECTR_Portal's // accessors, but I can'f figure out any other way to get the SerializedProperty // multi-select compatable Undo to work... if (myPortal.FrontSector != newFront || myPortal.BackSector != newBack) { serializedObject.Update(); if (myPortal.FrontSector != newFront) { if (myPortal.FrontSector) { myPortal.FrontSector.Deregister(myPortal); } frontProp.objectReferenceValue = newFront; if (myPortal.FrontSector) { myPortal.FrontSector.Register(myPortal); } } if (myPortal.BackSector != newBack) { if (myPortal.BackSector) { myPortal.BackSector.Deregister(myPortal); } backProp.objectReferenceValue = newBack; if (myPortal.BackSector) { myPortal.BackSector.Register(myPortal); } } serializedObject.ApplyModifiedProperties(); } base.OnInspectorGUI(); }
private void _RefChunks() { if (!chunksReferenced) { int numChunks = Sectors.Count; for (int chunkIndex = 0; chunkIndex < numChunks; ++chunkIndex) { SECTR_Sector sector = Sectors[chunkIndex]; if (sector) { SECTR_Chunk chunk = sector.GetComponent <SECTR_Chunk>(); if (chunk) { chunk.AddReference(); } } } chunksReferenced = true; } }
private SECTR_Portal _CrossedPortal(SECTR_Sector sector) { if(sector) { Vector3 rayDirection = transform.position - lastPosition; int numPortals = sector.Portals.Count; for(int portalIndex = 0; portalIndex < numPortals; ++portalIndex) { SECTR_Portal portal = sector.Portals[portalIndex]; if(portal) { bool forwardTraversal = (portal.FrontSector == sector); Plane portalPlane = forwardTraversal ? portal.HullPlane : portal.ReverseHullPlane; SECTR_Sector oppositeSector = forwardTraversal ? portal.BackSector : portal.FrontSector; if(oppositeSector && Vector3.Dot(rayDirection, portalPlane.normal) < 0f && portalPlane.GetSide(transform.position) != portalPlane.GetSide(lastPosition) && portal.IsPointInHull(transform.position, rayDirection.magnitude)) { return portal; } } } } return null; }
/// Called when a Sector is being destroyed public void SectorDisabled(SECTR_Sector sector) { if(sector) { sectors.Remove(sector); if(Changed != null) { leftSectors.Clear(); leftSectors.Add(sector); Changed(leftSectors, null); } } }
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; } 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 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; 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.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; // 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)); // 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 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 for(int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for(int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { for(int heightIndex = 0; heightIndex < sectorsHeight; ++heightIndex) { 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); } else if(splitTerrain) { 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(); // 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]; // 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.Apply(false); } } } } } // destroy original terrain if(splitTerrain) { SECTR_Undo.Destroy(terrain.gameObject, undoString); } }
private static void _CreatePortal(bool createGeo, SECTR_Sector front, SECTR_Sector back, Transform parent, string undoString) { if(front && back) { string portalName = "SECTR Terrain Portal"; GameObject newPortalObject; SECTR_Portal newPortal; #if !UNITY_4_0 && !UNITY_4_1 if(createGeo) { newPortalObject = GameObject.CreatePrimitive(PrimitiveType.Quad); newPortalObject.name = portalName; Mesh quadResource = newPortalObject.GetComponent<MeshFilter>().sharedMesh; GameObject.DestroyImmediate(newPortalObject.GetComponent<MeshFilter>()); GameObject.DestroyImmediate(newPortalObject.GetComponent<MeshRenderer>()); GameObject.DestroyImmediate(newPortalObject.GetComponent<Collider>()); newPortal = newPortalObject.AddComponent<SECTR_Portal>(); newPortal.HullMesh = quadResource; } else #endif { newPortalObject = new GameObject(portalName); newPortal = newPortalObject.AddComponent<SECTR_Portal>(); } newPortal.SetFlag(SECTR_Portal.PortalFlags.PassThrough, true); newPortal.FrontSector = front; newPortal.BackSector = back; newPortal.transform.parent = parent; newPortal.transform.position = (front.TotalBounds.center + back.TotalBounds.center) * 0.5f; if(createGeo) { newPortal.transform.LookAt(back.TotalBounds.center); Vector3 orientation = newPortal.transform.forward; if(Mathf.Abs(orientation.x) >= Mathf.Abs(orientation.y) && Mathf.Abs(orientation.x) >= Mathf.Abs(orientation.z)) { newPortal.transform.localScale = new Vector3(front.TotalBounds.size.z, front.TotalBounds.size.y, 1f); } else if(Mathf.Abs(orientation.y) >= Mathf.Abs(orientation.x) && Mathf.Abs(orientation.y) >= Mathf.Abs(orientation.z)) { newPortal.transform.localScale = new Vector3(front.TotalBounds.size.x, front.TotalBounds.size.z, 1f); } else if(Mathf.Abs(orientation.z) >= Mathf.Abs(orientation.x) && Mathf.Abs(orientation.z) >= Mathf.Abs(orientation.y)) { newPortal.transform.localScale = new Vector3(front.TotalBounds.size.x, front.TotalBounds.size.y, 1f); } } else { newPortal.transform.LookAt(front.TotalBounds.center); } SECTR_Undo.Created(newPortalObject, undoString); } }
private static void _Encapsulate(SECTR_Sector newSector, List<Transform> rootTransforms, List<Bounds> rootBounds, string undoString) { int numRoots = rootTransforms.Count; for(int rootIndex = numRoots - 1; rootIndex >= 0; --rootIndex) { Transform rootTransform = rootTransforms[rootIndex]; if(rootTransform != newSector.transform && SECTR_Geometry.BoundsContainsBounds(newSector.TotalBounds, rootBounds[rootIndex])) { SECTR_Undo.Parent(newSector.gameObject, rootTransform.gameObject, undoString); rootTransforms.RemoveAt(rootIndex); rootBounds.RemoveAt(rootIndex); } } }