void OnEnable() { lastTool = Tools.current; Tools.current = Tool.None; for (int i = 0; i < targets.Length; i++) { LowPolyTerrain terrain = (LowPolyTerrain)targets[i]; if (terrain.transform.childCount == 0) { terrain.GenerateDefaultMesh(); } else { for (int j = 0; j < terrain.transform.childCount; j++) { if (terrain.transform.GetChild(j).GetComponent <MeshFilter>().sharedMesh != null) { terrain.transform.GetChild(j).GetComponent <LowPolyTerrainChunk>().vertices = new List <Vector3>(terrain.transform.GetChild(j).GetComponent <MeshFilter>().sharedMesh.vertices); } } } } }
public override void OnInspectorGUI() { for (int i = 0; i < targets.Length; i++) { LowPolyTerrain terrain = (LowPolyTerrain)targets[i]; EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Modify")) { terrain.lowPolyTerrainMode = LowPolyTerrain.LowPolyTerrainMode.ModifyHeight; } else if (GUILayout.Button("Set")) { terrain.lowPolyTerrainMode = LowPolyTerrain.LowPolyTerrainMode.SetHeight; } else if (GUILayout.Button("Smooth")) { terrain.lowPolyTerrainMode = LowPolyTerrain.LowPolyTerrainMode.SmoothHeight; } else if (GUILayout.Button("Paint")) { terrain.lowPolyTerrainMode = LowPolyTerrain.LowPolyTerrainMode.Paint; } else if (GUILayout.Button("Randomize")) { terrain.lowPolyTerrainMode = LowPolyTerrain.LowPolyTerrainMode.Randomize; } else if (GUILayout.Button("Options")) { terrain.lowPolyTerrainMode = LowPolyTerrain.LowPolyTerrainMode.Option; } EditorGUILayout.EndHorizontal(); DrawLine(); if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.ModifyHeight) { EditorGUI.BeginChangeCheck(); serializedObject.FindProperty("brushSize").floatValue = Mathf.Clamp(EditorGUILayout.FloatField("Brush Size", serializedObject.FindProperty("brushSize").floatValue), 0, Mathf.Max(serializedObject.FindProperty("chunkWidth").intValue *serializedObject.FindProperty("chunksWidth").intValue, serializedObject.FindProperty("chunksHeight").intValue *serializedObject.FindProperty("chunkHeight").intValue) * 0.75f); serializedObject.FindProperty("brushStrength").floatValue = Mathf.Max(EditorGUILayout.FloatField("Brush Strength", serializedObject.FindProperty("brushStrength").floatValue), 0); serializedObject.FindProperty("brushSmoothness").floatValue = Mathf.Clamp01(EditorGUILayout.FloatField("Brush Smoothness", serializedObject.FindProperty("brushSmoothness").floatValue)); if (EditorGUI.EndChangeCheck() == true) { serializedObject.ApplyModifiedProperties(); } } else if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.SetHeight) { EditorGUI.BeginChangeCheck(); serializedObject.FindProperty("brushSize").floatValue = Mathf.Clamp(EditorGUILayout.FloatField("Brush Size", serializedObject.FindProperty("brushSize").floatValue), 0, Mathf.Max(serializedObject.FindProperty("chunkWidth").intValue *serializedObject.FindProperty("chunksWidth").intValue, serializedObject.FindProperty("chunksHeight").intValue *serializedObject.FindProperty("chunkHeight").intValue) * 0.75f); serializedObject.FindProperty("terrainHeight").floatValue = Mathf.Clamp(EditorGUILayout.FloatField("Height", serializedObject.FindProperty("terrainHeight").floatValue), serializedObject.FindProperty("minHeight").floatValue, serializedObject.FindProperty("maxHeight").floatValue); GUILayout.Label("Pick height with shift left click"); if (EditorGUI.EndChangeCheck() == true) { serializedObject.ApplyModifiedProperties(); } if (GUILayout.Button("Flatten Terrain")) { terrain.FlattenTerrain(); } } else if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.SmoothHeight) { EditorGUI.BeginChangeCheck(); serializedObject.FindProperty("brushSize").floatValue = Mathf.Clamp(EditorGUILayout.FloatField("Brush Size", serializedObject.FindProperty("brushSize").floatValue), 0, Mathf.Max(serializedObject.FindProperty("chunkWidth").intValue *serializedObject.FindProperty("chunksWidth").intValue, serializedObject.FindProperty("chunksHeight").intValue *serializedObject.FindProperty("chunkHeight").intValue) * 0.75f); serializedObject.FindProperty("smoothFactor").floatValue = Mathf.Clamp(EditorGUILayout.FloatField("Smooth Factor", serializedObject.FindProperty("smoothFactor").floatValue), 1, 10); if (EditorGUI.EndChangeCheck() == true) { serializedObject.ApplyModifiedProperties(); } } else if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.Paint) { EditorGUI.BeginChangeCheck(); serializedObject.FindProperty("brushSize").floatValue = Mathf.Clamp(EditorGUILayout.FloatField("Brush Size", serializedObject.FindProperty("brushSize").floatValue), 0, Mathf.Max(serializedObject.FindProperty("chunkWidth").intValue *serializedObject.FindProperty("chunksWidth").intValue, serializedObject.FindProperty("chunksHeight").intValue *serializedObject.FindProperty("chunkHeight").intValue) * 0.75f); EditorGUILayout.PropertyField(serializedObject.FindProperty("materials"), true); serializedObject.FindProperty("materialIndex").intValue = Mathf.Clamp(EditorGUILayout.IntField("Material Index", serializedObject.FindProperty("materialIndex").intValue), 0, serializedObject.FindProperty("materials").arraySize - 1); serializedObject.FindProperty("materialMask").intValue = Mathf.Clamp(EditorGUILayout.IntField("Material Mask", serializedObject.FindProperty("materialMask").intValue), -1, serializedObject.FindProperty("materials").arraySize - 1); if (EditorGUI.EndChangeCheck() == true) { serializedObject.ApplyModifiedProperties(); } if (GUILayout.Button("Fill Terrain")) { terrain.FillTerrain(); } } else if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.Randomize) { EditorGUI.BeginChangeCheck(); GUILayout.Label("Randomize with perlin noise"); serializedObject.FindProperty("perlinScale").floatValue = Mathf.Min(8f, Mathf.Max(EditorGUILayout.FloatField("Perlin Noise Scale", serializedObject.FindProperty("perlinScale").floatValue), 0)); serializedObject.FindProperty("perlinStrength").floatValue = EditorGUILayout.FloatField("Perlin Strength", serializedObject.FindProperty("perlinStrength").floatValue); serializedObject.FindProperty("perlinPower2").boolValue = GUILayout.Toggle(serializedObject.FindProperty("perlinPower2").boolValue, "Exponential"); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } if (GUILayout.Button("Randomize with perlin noise")) { terrain.RandomizePerlinNoise(); } } else { GUILayout.Label("Warning: changing these properties will reset the terrain"); EditorGUI.BeginChangeCheck(); serializedObject.FindProperty("chunksWidth").intValue = Mathf.Clamp(EditorGUILayout.IntField("Chunks X", serializedObject.FindProperty("chunksWidth").intValue), 1, 50); serializedObject.FindProperty("chunksHeight").intValue = Mathf.Clamp(EditorGUILayout.IntField("Chunks Z", serializedObject.FindProperty("chunksHeight").intValue), 1, 50); serializedObject.FindProperty("chunkWidth").intValue = Mathf.Clamp(EditorGUILayout.IntField("Chunk Width", serializedObject.FindProperty("chunkWidth").intValue), 1, 20); serializedObject.FindProperty("chunkHeight").intValue = Mathf.Clamp(EditorGUILayout.IntField("Chunk Height", serializedObject.FindProperty("chunkHeight").intValue), 1, 20); DrawLine(); if (EditorGUI.EndChangeCheck() == true) { serializedObject.ApplyModifiedProperties(); terrain.GenerateDefaultMesh(); } EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(serializedObject.FindProperty("materials"), true); serializedObject.FindProperty("minHeight").floatValue = EditorGUILayout.FloatField("Minimum Height", serializedObject.FindProperty("minHeight").floatValue); serializedObject.FindProperty("maxHeight").floatValue = EditorGUILayout.FloatField("Maximum Height", serializedObject.FindProperty("maxHeight").floatValue); if (serializedObject.FindProperty("minHeight").floatValue >= serializedObject.FindProperty("maxHeight").floatValue) { serializedObject.FindProperty("minHeight").floatValue = serializedObject.FindProperty("maxHeight").floatValue; } terrain.ClampVertices(); if (EditorGUI.EndChangeCheck() == true) { serializedObject.ApplyModifiedProperties(); terrain.GenerateMesh(); } float quadSize = serializedObject.FindProperty("quadSize").floatValue; EditorGUI.BeginChangeCheck(); serializedObject.FindProperty("quadSize").floatValue = Mathf.Max(EditorGUILayout.FloatField("Quad Size", serializedObject.FindProperty("quadSize").floatValue), 0.0001f); if (EditorGUI.EndChangeCheck() == true) { if (quadSize != serializedObject.FindProperty("quadSize").floatValue) { serializedObject.ApplyModifiedPropertiesWithoutUndo(); terrain.ChangeQuadSize(quadSize); terrain.GenerateMesh(); } } EditorGUI.BeginChangeCheck(); serializedObject.FindProperty("colliders").boolValue = GUILayout.Toggle(serializedObject.FindProperty("colliders").boolValue, "Generate Colliders"); if (EditorGUI.EndChangeCheck() == true) { serializedObject.ApplyModifiedProperties(); for (int j = 0; j < terrain.transform.childCount; j++) { terrain.transform.GetChild(j).GetComponent <MeshCollider>().enabled = serializedObject.FindProperty("colliders").boolValue; } } EditorGUI.BeginChangeCheck(); serializedObject.FindProperty("flipNormals").boolValue = GUILayout.Toggle(serializedObject.FindProperty("flipNormals").boolValue, "Flip Normals"); if (EditorGUI.EndChangeCheck() == true) { serializedObject.ApplyModifiedProperties(); terrain.GenerateMesh(); } } GUILayout.Space(20); DrawLine(); if (GUILayout.Button("Generate Terrain")) { terrain.GenerateMesh(); } if (GUILayout.Button("Reset Terrain")) { terrain.GenerateDefaultMesh(); } } }
private void OnSceneGUI() { for (int i = 0; i < targets.Length; i++) { LowPolyTerrain terrain = (LowPolyTerrain)targets[i]; terrain.transform.rotation = Quaternion.identity; terrain.transform.localScale = Vector3.one; HandleUtility.nearestControl = GUIUtility.GetControlID(FocusType.Passive); Event currentEvent = Event.current; if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.ModifyHeight || terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.SetHeight || terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.SmoothHeight || terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.Paint) { Ray ray = HandleUtility.GUIPointToWorldRay(currentEvent.mousePosition); RaycastHit raycastHit; if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0) { leftButtonDown = true; } else if (currentEvent.type == EventType.MouseUp && currentEvent.button == 0) { leftButtonDown = false; } if (Physics.Raycast(ray, out raycastHit)) { if (raycastHit.transform.parent == ((LowPolyTerrain)target).transform) { Handles.color = Color.white; Handles.DrawWireDisc(raycastHit.point, Vector3.up, terrain.brushSize); if (leftButtonDown == true) { bool changed = false; Undo.RegisterFullObjectHierarchyUndo(terrain, "Modify terrain"); if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.Paint) { for (int j = 0; j < terrain.transform.childCount; j++) { LowPolyTerrainChunk terrainChunk = terrain.transform.GetChild(j).GetComponent <LowPolyTerrainChunk>(); // Chunk could be inside brush area if (terrainChunk.GetDistance(raycastHit.point) < terrainChunk.halfDiagonal + terrain.brushSize) { for (int k = 0; k < terrainChunk.triangles.Count; k += 3) { Vector3 centerPosition = GetCenter(terrainChunk.vertices[terrainChunk.triangles[k]], terrainChunk.vertices[terrainChunk.triangles[k + 1]], terrainChunk.vertices[terrainChunk.triangles[k + 2]]); float distance = Vector2.Distance(new Vector2(raycastHit.point.x, raycastHit.point.z) - new Vector2(terrainChunk.transform.localPosition.x, terrainChunk.transform.localPosition.z), new Vector2(centerPosition.x, centerPosition.z)); if (distance < terrain.brushSize) { if (terrain.materialMask == -1 || terrainChunk.materialIndexes[k / 3] == terrain.materialMask) { if (currentEvent.shift == true) { terrainChunk.materialIndexes[k / 3] = 0; } else { terrainChunk.materialIndexes[k / 3] = terrain.materialIndex; } } } } } } terrain.GenerateMesh(); } else if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.SmoothHeight) { // Add vertices in near chunks List <Vector3> vertices = new List <Vector3>(); List <int> indexes = new List <int>(); List <LowPolyTerrainChunk> terrainChunks = new List <LowPolyTerrainChunk>(); for (int j = 0; j < terrain.transform.childCount; j++) { LowPolyTerrainChunk terrainChunk = terrain.transform.GetChild(j).GetComponent <LowPolyTerrainChunk>(); // Chunk could be inside brush area if (terrainChunk.GetDistance(raycastHit.point) < terrainChunk.halfDiagonal + terrain.brushSize) { for (int k = 0; k < terrainChunk.vertices.Count; k += 4) { vertices.Add(terrainChunk.vertices[k] + terrainChunk.transform.position); indexes.Add(k); terrainChunks.Add(terrainChunk); } } } // Add vertices inside brush List <int> verticeIndexes = new List <int>(); List <float> verticeHeights = new List <float>(); List <LowPolyTerrainChunk> verticeChunks = new List <LowPolyTerrainChunk>(); for (int k = 0; k < vertices.Count; k++) { float distance = Vector2.Distance(new Vector2(raycastHit.point.x, raycastHit.point.z), new Vector2(vertices[k].x, vertices[k].z)); if (distance < terrain.brushSize) { float totalVertexHight = 0; float amount = 0; for (int l = 0; l < vertices.Count; l++) { if (Vector2.Distance(new Vector2(vertices[l].x, vertices[l].z), new Vector2(vertices[k].x, vertices[k].z)) < terrain.diagonalQuadSize) { totalVertexHight += vertices[l].y; amount += 1; } } verticeIndexes.Add(indexes[k]); verticeHeights.Add(totalVertexHight / amount); verticeChunks.Add(terrainChunks[k]); } } // Modify vertices for (int k = 0; k < verticeIndexes.Count; k++) { Vector3 vertex = verticeChunks[k].vertices[verticeIndexes[k]]; float height = Mathf.Lerp(vertex.y, verticeHeights[k], terrain.smoothFactor / 10f); Vector3 newPosition = new Vector3(vertex.x, height, vertex.z); verticeChunks[k].vertices[verticeIndexes[k]] = newPosition; verticeChunks[k].vertices[verticeIndexes[k] + 1] = newPosition; verticeChunks[k].vertices[verticeIndexes[k] + 2] = newPosition; verticeChunks[k].vertices[verticeIndexes[k] + 3] = newPosition; } changed = true; } else { for (int j = 0; j < terrain.transform.childCount; j++) { LowPolyTerrainChunk terrainChunk = terrain.transform.GetChild(j).GetComponent <LowPolyTerrainChunk>(); // Chunk could be inside brush area if (terrainChunk.GetDistance(raycastHit.point) < terrainChunk.halfDiagonal + terrain.brushSize) { for (int k = 0; k < terrainChunk.vertices.Count; k += 4) { float distance = Vector2.Distance(new Vector2(raycastHit.point.x, raycastHit.point.z) - new Vector2(terrainChunk.transform.localPosition.x, terrainChunk.transform.localPosition.z), new Vector2(terrainChunk.vertices[k].x, terrainChunk.vertices[k].z)); if (distance < terrain.brushSize) { distance = Mathf.Min(terrain.brushSize, terrain.brushSize - distance + ((1 - terrain.brushSmoothness) * terrain.brushSize)); if (terrain.lowPolyTerrainMode == LowPolyTerrain.LowPolyTerrainMode.ModifyHeight) { if (currentEvent.shift == true) { Vector3 position = new Vector3(terrainChunk.vertices[k].x, Mathf.Clamp(terrainChunk.vertices[k].y - (0.01f * terrain.brushStrength * distance), terrain.minHeight, terrain.maxHeight), terrainChunk.vertices[k].z); terrainChunk.vertices[k] = position; terrainChunk.vertices[k + 1] = position; terrainChunk.vertices[k + 2] = position; terrainChunk.vertices[k + 3] = position; changed = true; } else { Vector3 position = new Vector3(terrainChunk.vertices[k].x, Mathf.Clamp(terrainChunk.vertices[k].y + (0.01f * terrain.brushStrength * distance), terrain.minHeight, terrain.maxHeight), terrainChunk.vertices[k].z); terrainChunk.vertices[k] = position; terrainChunk.vertices[k + 1] = position; terrainChunk.vertices[k + 2] = position; terrainChunk.vertices[k + 3] = position; changed = true; } } else { if (currentEvent.shift == true) { terrain.terrainHeight = raycastHit.point.y - raycastHit.transform.position.y; } else { Vector3 position = new Vector3(terrainChunk.vertices[k].x, terrain.terrainHeight, terrainChunk.vertices[k].z); terrainChunk.vertices[k] = position; terrainChunk.vertices[k + 1] = position; terrainChunk.vertices[k + 2] = position; terrainChunk.vertices[k + 3] = position; changed = true; } } } } } } } if (changed == true) { terrain.GenerateMesh(); } } } } } } SceneView.RepaintAll(); }
public override void OnInspectorGUI() { Brush = new string[] { "Settings", "Raise", "Lower", "Flatten", "Paint", "Trees" }; myscript = (LowPolyTerrain)target; serializedObject.Update(); if (nResolution == 0) { nResolution = Mathf.RoundToInt(Mathf.Sqrt(myscript.GetComponent <MeshFilter>().sharedMesh.triangles.Length / 3 / 2)); } index = GUILayout.Toolbar(index, Brush, GUILayout.Height(25)); if (index == 0) { if (myscript.GetComponent <MeshFilter>().sharedMesh.triangles != null) { nResolution = EditorGUILayout.IntField("Resolution:", nResolution); } if (siz == 0) { siz = Mathf.RoundToInt(myscript.GetComponent <MeshFilter>().sharedMesh.bounds.size.x); } siz = EditorGUILayout.IntField("Size:", siz); vert = new Vector3[0]; tri = new int[0]; if (GUILayout.Button("Reset Terrain")) { Reset(); } if (GUILayout.Button("Smooth edges")) { Smooth(); } if (GUILayout.Button(new GUIContent("Create LOD", "Creates a 16x16 version of the terrain to use for LOD"))) { Lod(); } if (index == 0) { myscript.show = false; } else { myscript.show = true; } EditorGUILayout.HelpBox("High resolutions (=> 128) along with a big brush size can cause performance issues, when modifying the terrain! \nYou can, if neccesary, place several terrains alongside eachother. You will then have to remove this script from the terrains you don't edit, but you can always add it again", MessageType.Warning); myscript.show = false; } else { myscript.show = true; } if (index != 0 && index != 4) { myscript.brushSize = EditorGUILayout.IntSlider("Brush Size:", Mathf.CeilToInt(myscript.brushSize), 1, 200); myscript.opacity = EditorGUILayout.IntSlider("Brush Opacity:", Mathf.CeilToInt(myscript.opacity), 1, 500); } if (index == 3) { myscript.destHeight = EditorGUILayout.FloatField("Destination height", myscript.destHeight); } if (index == 4) { myscript.brushSize = EditorGUILayout.IntSlider("Brush Size:", Mathf.CeilToInt(myscript.brushSize), 0, 500); col = EditorGUILayout.ColorField("Brush Color:", col); } if (index == 5) { myscript.tree = EditorGUILayout.ObjectField("Tree:", myscript.tree, typeof(GameObject), false) as GameObject; treeRot = EditorGUILayout.Vector3Field("Tree rotation:", new Vector3(-90, 0, 0)); treeSiz.x = EditorGUILayout.IntSlider("Random tree x scale:", Mathf.CeilToInt(treeSiz.x), 0, 99); treeSiz.y = EditorGUILayout.IntSlider("Random tree y scale:", Mathf.CeilToInt(treeSiz.y), 0, 99); treeSiz.z = EditorGUILayout.IntSlider("Random tree z scale:", Mathf.CeilToInt(treeSiz.z), 0, 99); } }