public static void CreateCullingCameraCamera() { string newObjectName = "SECTR Camera"; string undoString = "Create " + newObjectName; if (Selection.activeGameObject && Selection.activeGameObject.GetComponent <Camera>()) { if (Selection.activeGameObject.GetComponent <SECTR_CullingCamera>()) { Debug.LogWarning("Selected Camera already has a SECTR CullingCamera."); } else { SECTR_CullingCamera newCullingCamera = Selection.activeGameObject.AddComponent <SECTR_CullingCamera>(); SECTR_Undo.Created(newCullingCamera, undoString); } } else { GameObject newObject = CreateGameObject(newObjectName); newObject.AddComponent <SECTR_CullingCamera>(); SECTR_Undo.Created(newObject, undoString); Selection.activeGameObject = newObject; } }
public static void CreateTriggerLoader() { string newObjectName = "SECTR Trigger Loader"; string undoName = "Created " + newObjectName; GameObject newGameObject; SECTR_Portal selectedPortal = Selection.activeGameObject ? Selection.activeGameObject.GetComponent <SECTR_Portal>() : null; if (selectedPortal) { newGameObject = CreateTriggerFromPortal(selectedPortal, newObjectName); SECTR_TriggerLoader newLoader = newGameObject.AddComponent <SECTR_TriggerLoader>(); if (selectedPortal.FrontSector) { newLoader.Sectors.Add(selectedPortal.FrontSector); } if (selectedPortal.BackSector) { newLoader.Sectors.Add(selectedPortal.BackSector); } } else { newGameObject = CreateGameObject(newObjectName); BoxCollider newCollider = newGameObject.AddComponent <BoxCollider>(); newCollider.isTrigger = true; newGameObject.AddComponent <SECTR_TriggerLoader>(); } SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; }
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); }
public static void CreateMember() { string newName = "SECTR Member"; string undoName = "Create " + newName; GameObject newGameObject = CreateGameObject(newName); newGameObject.AddComponent <SECTR_Member>(); SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; }
public static void CreateStartLoader() { string newObjectName = "SECTR Start Loader"; string undoName = "Created " + newObjectName; GameObject newGameObject = CreateGameObject(newObjectName); newGameObject.AddComponent <SECTR_StartLoader>(); SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; }
public static void CreateHibernator() { string newObjectName = "SECTR Hibernator"; string undoName = "Created " + newObjectName; GameObject newGameObject = CreateGameObject(newObjectName); newGameObject.AddComponent <SECTR_Hibernator>(); SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; }
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); } }
public static void CreatePortal() { string newName = "SECTR Portal"; string undoName = "Create " + newName; GameObject newGameObject = CreateGameObject(newName); SECTR_Portal newPortal = newGameObject.AddComponent <SECTR_Portal>(); newPortal.ForceEditHull = true; newPortal.CenterOnEdit = true; SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; }
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); }
public static void CreateOccluder() { string newObjectName = "SECTR Occluder"; string undoString = "Create " + newObjectName; GameObject newObject = CreateGameObject(newObjectName); SECTR_Occluder newOccluder = newObject.AddComponent <SECTR_Occluder>(); newOccluder.ForceEditHull = true; newOccluder.CenterOnEdit = true; SECTR_Undo.Created(newObject, undoString); Selection.activeGameObject = newObject; }
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); } } }
public static void CreateLOD() { string newObjectName = "SECTR LOD"; string undoString = "Create " + newObjectName; GameObject newObject = CreateGameObject(newObjectName); newObject.AddComponent <SECTR_LOD>(); int numSelected = Selection.gameObjects.Length; SECTR_Undo.Created(newObject, undoString); for (int selectedIndex = 0; selectedIndex < numSelected; ++selectedIndex) { SECTR_Undo.Parent(newObject, Selection.gameObjects[selectedIndex], undoString); } Selection.activeGameObject = newObject; }
protected static GameObject CreateDoor <T>(string newName) where T : SECTR_Door { string undoName = "Create " + newName; GameObject newGameObject; SECTR_Portal selectedPortal = Selection.activeGameObject ? Selection.activeGameObject.GetComponent <SECTR_Portal>() : null; if (selectedPortal) { newGameObject = CreateTriggerFromPortal(selectedPortal, newName); T newDoor = newGameObject.AddComponent <T>(); newDoor.Portal = selectedPortal; } else { newGameObject = CreateGameObject(newName); BoxCollider newCollider = newGameObject.AddComponent <BoxCollider>(); newCollider.isTrigger = true; newGameObject.AddComponent <T>(); } SECTR_Undo.Created(newGameObject, undoName); Selection.activeGameObject = newGameObject; return(newGameObject); }
void _CompleteHull(SECTR_Hull myHull) { int numNewVerts = newHullVerts.Count; if (numNewVerts >= 3) { Plane hullPlane = new Plane(newHullVerts[0], newHullVerts[1], newHullVerts[2]); Vector3 hullNormal = hullPlane.normal; // For new hulls, set their xform to match the hull geo. if (myHull.ForceEditHull && myHull.CenterOnEdit) { Vector3 newPos = Vector3.zero; for (int vertIndex = 0; vertIndex < numNewVerts; ++vertIndex) { newPos += newHullVerts[vertIndex]; } newPos /= newHullVerts.Count; myHull.transform.position = newPos; myHull.transform.forward = hullNormal; } // Constructu a new mesh. Mesh newMesh = new Mesh(); newMesh.name = myHull.name; Vector3[] newVerts = new Vector3[numNewVerts]; Vector3[] newNormals = new Vector3[numNewVerts]; Vector2[] newUVs = new Vector2[numNewVerts]; // Compute new positions and normals, which are always in hull local space. Vector3 localNormal = myHull.transform.worldToLocalMatrix.MultiplyVector(hullNormal); Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); for (int vertIndex = 0; vertIndex < numNewVerts; ++vertIndex) { Vector3 localPosition = myHull.transform.worldToLocalMatrix.MultiplyPoint3x4(newHullVerts[vertIndex]); newVerts[vertIndex] = localPosition; newNormals[vertIndex] = localNormal; min = Vector3.Min(min, localPosition); max = Vector3.Max(max, localPosition); } // Compute a planar projection for the UVs. Vector3 uvScalar = new Vector3(1f / max.x - min.x, 1f / max.y - min.y, 1); for (int vertIndex = 0; vertIndex < numNewVerts; ++vertIndex) { newUVs[vertIndex] = Vector3.Scale(newVerts[vertIndex], uvScalar); } // Triangle indices assume a CW sorting of the verts. int numTriangles = numNewVerts - 2; int[] triangles = new int[numTriangles * 3]; for (int triIndex = 0; triIndex < numTriangles; ++triIndex) { triangles[triIndex * 3] = 0; triangles[triIndex * 3 + 1] = triIndex + 1; triangles[triIndex * 3 + 2] = triIndex + 2; } // Fill out the mesh stuffs. newMesh.vertices = newVerts; newMesh.normals = newNormals; newMesh.uv = newUVs; newMesh.triangles = triangles; // Now create a new, unique mesh asset for the hull. // We use assets instead of storing geometry in the scene to ensure that everything serializes properly. string sceneDir = null; string sceneName = null; string exportDir = SECTR_Asset.MakeExportFolder("Portals", false, out sceneDir, out sceneName); string newAssetName = exportDir + newMesh.name + ".asset"; newAssetName = AssetDatabase.GenerateUniqueAssetPath(newAssetName); AssetDatabase.CreateAsset(newMesh, newAssetName); // Let the hull know that we've modified it in an undo friendly way. SECTR_Undo.Record(myHull, "Created Portal"); myHull.HullMesh = newMesh; } _EndNewHull(myHull, false); }
public override void OnInspectorGUI() { SECTR_LOD myLOD = (SECTR_LOD)target; float lodButtonHeight = 50; float lodGutterSize = 4f; if (lodButtonStyle == null) { lodButtonStyle = new GUIStyle(GUI.skin.button); lodButtonStyle.padding = new RectOffset(); lodButtonStyle.margin = new RectOffset(); } if (Event.current.type == EventType.Repaint) { lodRects.Clear(); } EditorGUILayout.BeginVertical(); Rect lodListRect = EditorGUILayout.BeginVertical(); GUI.enabled = false; GUILayout.Button(GUIContent.none, GUILayout.Height(lodButtonHeight)); GUI.enabled = true; EditorGUILayout.EndVertical(); float addButtonSize = lodListRect.width * 0.1f; int numLODs = myLOD.LODs.Count; float minThreshold = 1; float insertPos = lodListRect.xMin; for (int lodIndex = 0; lodIndex < numLODs; ++lodIndex) { SECTR_LOD.LODSet thisSet = myLOD.LODs[lodIndex]; float buttonScale = 1f - thisSet.Threshold; float percent = 1f; if (lodIndex > 0) { float prevThreshold = myLOD.LODs[lodIndex - 1].Threshold; buttonScale -= 1f - prevThreshold; percent = myLOD.LODs[lodIndex - 1].Threshold; } float buttonWidth = buttonScale * (lodListRect.width - addButtonSize); Rect buttonRect = new Rect(insertPos + lodGutterSize * 0.5f, lodListRect.y, buttonWidth - lodGutterSize, lodListRect.height); if (GUI.Button(buttonRect, "LOD" + lodIndex + "\n" + percent.ToString("P1"), lodButtonStyle)) { selectedLOD = lodIndex; } if (Event.current.type == EventType.Repaint) { lodRects.Add(buttonRect); } insertPos += buttonWidth; minThreshold = Mathf.Min(minThreshold, thisSet.Threshold); } if (minThreshold > 0f && myLOD.LODs.Count > 0) { Rect culledRect = new Rect(insertPos + lodGutterSize * 0.5f, lodListRect.y, lodListRect.xMax - insertPos - addButtonSize - lodGutterSize, lodListRect.height); if (GUI.Button(culledRect, "Culled\n" + minThreshold.ToString("P1"), lodButtonStyle)) { selectedLOD = -1; } if (Event.current.type == EventType.Repaint) { lodRects.Add(culledRect); } } GUI.enabled = minThreshold > 0; Rect addRect = myLOD.LODs.Count > 0 ? new Rect(lodListRect.xMax - addButtonSize, lodListRect.y, addButtonSize, lodListRect.height) : new Rect(lodListRect.xMin, lodListRect.y, lodListRect.width, lodListRect.height); if (GUI.Button(addRect, myLOD.LODs.Count > 0 ? "+" : "+\n(Add LOD)", lodButtonStyle)) { SECTR_Undo.Record(myLOD, "Added LOD"); SECTR_LOD.LODSet newSet = new SECTR_LOD.LODSet(); if (myLOD.LODs.Count == 0) { Transform[] children = myLOD.GetComponentsInChildren <Transform>(); int numChildren = children.Length; for (int childIndex = 0; childIndex < numChildren; ++childIndex) { if (children[childIndex] != myLOD.transform) { newSet.Add(children[childIndex].gameObject, null); } } } else { SECTR_LOD.LODSet prevSet = myLOD.LODs[myLOD.LODs.Count - 1]; int numEntries = prevSet.LODEntries.Count; for (int entryIndex = 0; entryIndex < numEntries; ++entryIndex) { SECTR_LOD.LODEntry prevEntry = prevSet.LODEntries[entryIndex]; newSet.Add(prevEntry.gameObject, prevEntry.lightmapSource); } } newSet.Threshold = minThreshold * 0.5f; selectedLOD = myLOD.LODs.Count; myLOD.LODs.Add(newSet); } GUI.enabled = true; int lodToRemove = -1; if (selectedLOD >= 0 && selectedLOD < myLOD.LODs.Count) { float sliderMin = 0; float sliderMax = 1; if (selectedLOD < myLOD.LODs.Count - 1) { sliderMin = myLOD.LODs[selectedLOD + 1].Threshold; } if (selectedLOD > 0) { sliderMax = myLOD.LODs[selectedLOD - 1].Threshold; } myLOD.LODs[selectedLOD].Threshold = EditorGUILayout.Slider("LOD" + selectedLOD + " Threshold", myLOD.LODs[selectedLOD].Threshold, sliderMin, sliderMax); detailsFoldout = EditorGUILayout.Foldout(detailsFoldout, "LOD" + selectedLOD + " Members"); if (detailsFoldout) { EditorGUILayout.BeginHorizontal(); GUILayout.Label("Name"); GUILayout.Label("Included"); GUILayout.Label("Lightmap Proxy"); EditorGUILayout.EndHorizontal(); _BuildChildControls(myLOD, myLOD.LODs[selectedLOD], myLOD.transform, true); } if (GUILayout.Button("Remove LOD" + selectedLOD)) { if (EditorUtility.DisplayDialog("Confirm LOD Removal", "Are you sure you wish to remove LOD " + selectedLOD + "?", "Yes", "No")) { lodToRemove = selectedLOD; } } } else if (selectedLOD == -1) { serializedObject.Update(); DrawProperty("CullSiblings"); serializedObject.ApplyModifiedProperties(); } EditorGUILayout.EndVertical(); if (lodToRemove >= 0) { SECTR_Undo.Record(myLOD, "Removed LOD"); myLOD.LODs.RemoveAt(lodToRemove); } if (Event.current.type == EventType.Repaint) { lodListRectPerm = lodListRect; } int dragLod = -1; if (lodListRectPerm.Contains(Event.current.mousePosition)) { for (int lodIndex = 0; lodIndex < lodRects.Count - 1; ++lodIndex) { if (Event.current.mousePosition.x > lodRects[lodIndex].xMax && Event.current.mousePosition.x < lodRects[lodIndex + 1].xMin) { dragLod = lodIndex; break; } } } if (dragLod >= 0 && Event.current.type == EventType.MouseDown) { dragging = true; selectedLOD = dragLod; } else if (Event.current.type == EventType.MouseUp) { dragging = false; } if (dragging && lodListRectPerm.Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDrag) { float newThreshold = 1f - ((Event.current.mousePosition.x - lodListRect.x) / lodListRect.width); newThreshold = Mathf.Clamp01(newThreshold); if (selectedLOD < myLOD.LODs.Count - 1) { newThreshold = Mathf.Max(newThreshold, myLOD.LODs[selectedLOD + 1].Threshold); } if (selectedLOD > 0) { newThreshold = Mathf.Min(newThreshold, myLOD.LODs[selectedLOD - 1].Threshold); } myLOD.LODs[selectedLOD].Threshold = newThreshold; } if (dragging || dragLod >= 0) { EditorGUIUtility.AddCursorRect(new Rect(Event.current.mousePosition.x - 16, Event.current.mousePosition.y - 16, 32, 32), MouseCursor.ResizeHorizontal); } }
private bool _DrawChildControl(SECTR_LOD myLOD, SECTR_LOD.LODSet lodSet, Transform transform, bool hasChildren) { string undoString = "Changed LOD"; bool expanded = false; float labelWidth = Screen.width * 0.3f; float checkWidth = 30; float buffer = 5; Rect propertyRect = EditorGUILayout.BeginHorizontal(GUILayout.Width(labelWidth)); if (hasChildren) { hierarchyFoldouts.TryGetValue(transform, out expanded); hierarchyFoldouts[transform] = EditorGUILayout.Foldout(expanded, transform.name); } else { EditorGUILayout.LabelField(transform.name, GUILayout.Width(labelWidth)); } EditorGUILayout.EndHorizontal(); SECTR_LOD.LODEntry entry = lodSet.GetEntry(transform.gameObject); bool isChecked = entry != null; bool newChecked = GUI.Toggle(new Rect(propertyRect.xMax + buffer, propertyRect.y, checkWidth, propertyRect.height), isChecked, GUIContent.none); if (newChecked != isChecked) { SECTR_Undo.Record(myLOD, undoString); if (newChecked) { entry = lodSet.Add(transform.gameObject, null); } else { lodSet.Remove(transform.gameObject); entry = null; } isChecked = newChecked; myLOD.Reset(); } if (entry != null && transform.GetComponent <Renderer>()) { Renderer newSource = (Renderer)EditorGUI.ObjectField(new Rect(propertyRect.xMax + checkWidth + buffer, propertyRect.y, Screen.width - (propertyRect.xMax + checkWidth + buffer * 2), propertyRect.height), GUIContent.none, entry.lightmapSource, typeof(Renderer), true); if (newSource != entry.lightmapSource) { SECTR_Undo.Record(myLOD, undoString); entry.lightmapSource = newSource; myLOD.Reset(); } } return(expanded); }
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); } } } }
public static void CreateSector() { List <GameObject> rootObjects = new List <GameObject>(8); List <GameObject> selectedObjects = new List <GameObject>(Selection.gameObjects); int numSelectedObjects = selectedObjects.Count; int selectedObjectIndex = 0; while (selectedObjectIndex < numSelectedObjects) { GameObject gameObject = selectedObjects[selectedObjectIndex]; // auto remove portals if selected. if (gameObject.GetComponent <SECTR_Portal>()) { selectedObjects.RemoveAt(selectedObjectIndex); --numSelectedObjects; } else { // Check to make sure nothing selected is already in a Sector. Transform parent = gameObject.transform; while (parent != null) { // Bail out if we encounter any classes that shouldn't be part of the list if (parent.GetComponent <SECTR_Sector>() != null) { EditorUtility.DisplayDialog("Sector Safety First", "Some of the selected objects are already in a Sector. Please unparent them from the current Sector before putting them into a new Sector.", "Ok"); return; } parent = parent.parent; } ++selectedObjectIndex; } } // Build a list of common parents from the selection to try to preserve any existing heirarchy // but without walking all the way up the tree. Complex scenes may have many Sectors parented // under a single transform and we do not want to walk higher than the current selection. for (selectedObjectIndex = 0; selectedObjectIndex < numSelectedObjects; ++selectedObjectIndex) { GameObject gameObject = selectedObjects[selectedObjectIndex]; Transform parent = gameObject.transform; while (parent != null) { if (parent.parent) { int numChildren = parent.parent.childCount; bool allChildrenSelected = true; for (int childIndex = 0; childIndex < numChildren; ++childIndex) { if (!selectedObjects.Contains(parent.parent.GetChild(childIndex).gameObject)) { allChildrenSelected = false; break; } } if (allChildrenSelected) { parent = parent.parent; } else { break; } } else { break; } } if (parent != null && !rootObjects.Contains(parent.gameObject)) { rootObjects.Add(parent.gameObject); } } int numRootObjects = rootObjects.Count; string newName = "SECTR Sector"; string undoName = "Create " + newName; SECTR_Sector newSector = null; // If there is just one root, give the open to make that into a Sector. This helps // with scenes that are already well organized. Transform commonParent = numRootObjects == 1 ? rootObjects[0].transform : null; if (commonParent && EditorUtility.DisplayDialog("Common Parent Detected", "Selected objects are all a child of " + commonParent.name + ". " + "Would you like to make " + commonParent.name + " into a Sector? If not, a new Sector will be created and " + commonParent.name + " will be parented to it.", "Yes", "No")) { newSector = commonParent.gameObject.AddComponent <SECTR_Sector>(); if (!commonParent.gameObject.isStatic && EditorUtility.DisplayDialog("Make Sector Static?", "SECTR can perform additional optimizations on Sectors that are marked as static, provided they " + "do not need to move or change bounds at runtime. Would you like " + commonParent.name + " to be marked as static?", "Yes", "No")) { commonParent.gameObject.isStatic = true; } SECTR_Undo.Created(newSector, undoName); } else { commonParent = numRootObjects > 0 ? rootObjects[0].transform.parent : null; for (int rootIndex = 0; rootIndex < numRootObjects; ++rootIndex) { if (rootObjects[rootIndex].transform.parent != commonParent) { commonParent = null; break; } } GameObject newGameObject = CreateGameObject(newName); newGameObject.transform.parent = commonParent; newGameObject.isStatic = true; SECTR_Undo.Created(newGameObject, undoName); List <Vector3> rootPositions = new List <Vector3>(numRootObjects); for (int rootObjectIndex = 0; rootObjectIndex < numRootObjects; ++rootObjectIndex) { GameObject gameObject = rootObjects[rootObjectIndex]; rootPositions.Add(gameObject.transform.position); SECTR_Undo.Parent(newGameObject, gameObject, undoName); } newSector = newGameObject.AddComponent <SECTR_Sector>(); SECTR_Undo.Created(newSector, undoName); newSector.transform.position = newSector.TotalBounds.center; for (int rootObjectIndex = 0; rootObjectIndex < numRootObjects; ++rootObjectIndex) { GameObject gameObject = rootObjects[rootObjectIndex]; gameObject.transform.position = rootPositions[rootObjectIndex]; } } List <SECTR_Member.Child> sharedChildren = newSector.GetSharedChildren(); if (sharedChildren.Count > 0 && EditorUtility.DisplayDialog("Overlap Warning", "Some objects in this Sector overlap other Sectors, which may cause unexpected behavior. Would you like to make them Members instead of children?", "Yes", "No")) { SECTR_SectorEditor.MakeSharedChildrenMembers(newSector, sharedChildren, undoName); } Selection.activeGameObject = newSector.gameObject; }
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); } }