void OnSceneGUI() { Foliage2D_Path path = (Foliage2D_Path)target; GUIStyle iconStyle = new GUIStyle(); iconStyle.alignment = TextAnchor.MiddleCenter; // draw the path line if (Event.current.type == EventType.Repaint) { DrawPath(path); if (handleSelected && !path.uniformValues && path.foliagePathType == Foliage2D_PathType.Smooth) { DrawTensionBiasLine(index, path); } } // draw and interact with all the path handles UpdateHandles(path, iconStyle); if (GUI.changed) { EditorUtility.SetDirty(target); path.RecreateFoliage(); } }
private void DrawTensionBiasLine(int index, Foliage2D_Path path) { Handles.color = Color.blue; Vector3 pos = path.transform.position + path.transform.rotation * Vector3.Scale(new Vector3(path.handlesPosition[index].x, path.handlesPosition[index].y, 0), path.transform.localScale); Vector3 pos2 = path.transform.position + path.transform.rotation * Vector3.Scale(new Vector3(path.handleControlsPos[index].x, path.handleControlsPos[index].y, 0), path.transform.localScale); Handles.DrawLine(pos, pos2); }
private void DrawPath(Foliage2D_Path path) { Handles.color = Color.white; List <Vector2> verts = path.handlesPosition; for (int i = 0; i < verts.Count - 1; i++) { Vector3 pos = path.transform.position + path.transform.rotation * Vector3.Scale(new Vector3(verts[i].x, verts[i].y, 0), path.transform.localScale); Vector3 pos2 = path.transform.position + path.transform.rotation * Vector3.Scale(new Vector3(verts[i + 1].x, verts[i + 1].y, 0), path.transform.localScale); Handles.DrawLine(pos, pos2); } }
static void AddPath() { GameObject obj = new GameObject("New Foliage2D_Path"); Foliage2D_Path path = obj.AddComponent <Foliage2D_Path>(); path.AddPathPoint(new Vector2(-3, 0)); path.AddPathPoint(new Vector2(3f, 0)); path.foliagePrefabs.Add(null); obj.transform.position = GetSpawnPos(); Selection.activeGameObject = obj; EditorGUIUtility.PingObject(obj); }
private void CustomInspector(Foliage2D_Path path2D) { Undo.RecordObject(target, "Modified Inspector"); showProperties = EditorGUILayout.Foldout(showProperties, "Path Properties"); if (showProperties) { EditorGUI.indentLevel = 1; InspectorBox(8, () => { path2D.foliagePattern = (Foliage2D_Pattern)EditorGUILayout.EnumPopup(new GUIContent("Foliage Pattern", "Describes how the foliage objects should be arranged on " + "the path lines. Consecutive option will place the foliage objects in a consecutive order while random, in a random order."), path2D.foliagePattern); path2D.foliageOverlapType = (Foliage2D_OverlappingType)EditorGUILayout.EnumPopup(new GUIContent("Overlapping Type", "This popup menu contains two options for how " + " the overlapping factor is calculated. The value of the overlapping factor determines which part of a foliage object mesh will be placed to the left of " + "the right edge of the previous foliage object mesh."), path2D.foliageOverlapType); if (path2D.foliageOverlapType == Foliage2D_OverlappingType.Fixed) { path2D.overlappingFactor = EditorGUILayout.Slider(new GUIContent("Overlapping Factor", "The value of the overlapping factor determines which part of a foliage " + "object mesh will be placed to the left of the right edge of the previous foliage object mesh."), path2D.overlappingFactor, -2, 0.99f); } else { path2D.minOverlappingFactor = EditorGUILayout.Slider(new GUIContent("Min Overlapping", "The min value for the overlaping factor."), path2D.minOverlappingFactor, -2, 0.99f); path2D.maxOverlappingFactor = EditorGUILayout.Slider(new GUIContent("Max Overlapping", "The max value for the overlaping factor."), path2D.maxOverlappingFactor, -2, 0.99f); } path2D.firstObjectOffset = EditorGUILayout.FloatField(new GUIContent("First Object Offset", "Offsets the distance from the start of the line, where the first object on the current line will be placed."), path2D.firstObjectOffset); path2D.lastObjectOffset = EditorGUILayout.FloatField(new GUIContent("Last Object Offset", "Offsets the distance from the start of the line, where the last object on the current line will be placed."), path2D.lastObjectOffset); path2D.foliagePathType = (Foliage2D_PathType)EditorGUILayout.EnumPopup(new GUIContent("Path Type", "Describes how the objects will be placed on the foliage path."), path2D.foliagePathType); if (path2D.foliagePathType == Foliage2D_PathType.Smooth) { path2D.uniformValues = EditorGUILayout.Toggle(new GUIContent("Uniform Values", "When set to true the tension and bias will be the same for all path nodes. " + "Disable this if you want to change the tension and bias of individual path nodes."), path2D.uniformValues); if (path2D.uniformValues) { path2D.bias = EditorGUILayout.FloatField(new GUIContent("Bias", "The bias of the path nodes."), path2D.bias); path2D.tension = EditorGUILayout.FloatField(new GUIContent("Tension", "The tension of the path nodes."), path2D.tension); } else { path2D.biasScale = EditorGUILayout.FloatField(new GUIContent("Bias Scale", "Bias scale"), path2D.biasScale); path2D.tensionScale = EditorGUILayout.FloatField(new GUIContent("Tension Scale", "Tension scale"), path2D.tensionScale); } } path2D.zOffset = EditorGUILayout.FloatField(new GUIContent("Z Offset", "The offset on the Z axis for a foliage object."), path2D.zOffset); }); } EditorGUI.indentLevel = 0; showFoliagePrefabList = EditorGUILayout.Foldout(showFoliagePrefabList, "Foliage Prefabs"); if (showProperties) { EditorGUI.indentLevel = 1; InspectorBox(10, () => { path2D.foliagePrefabListSize = Mathf.Clamp(EditorGUILayout.IntField(new GUIContent("Size", "The size of the list that contains the foliage prefabs."), path2D.foliagePrefabListSize), 1, 200); if (path2D.foliagePrefabListSize != path2D.foliagePrefabs.Count) { if (path2D.foliagePrefabListSize > path2D.foliagePrefabs.Count) { while (path2D.foliagePrefabListSize > path2D.foliagePrefabs.Count) { path2D.foliagePrefabs.Add(null); } } else { while (path2D.foliagePrefabListSize < path2D.foliagePrefabs.Count) { int last = path2D.foliagePrefabs.Count - 1; path2D.foliagePrefabs.RemoveAt(last); } } } for (int i = 0; i < path2D.foliagePrefabs.Count; i++) { path2D.foliagePrefabs[i] = EditorGUILayout.ObjectField(new GUIContent("Element " + i), path2D.foliagePrefabs[i], typeof(GameObject), true) as GameObject; } }); } EditorGUI.indentLevel = 0; if (GUILayout.Button("Clear and Fill")) { path2D.ClearList(); } if (GUILayout.Button("Center Position")) { path2D.ReCenterPivotPoint(); } if (GUI.changed) { EditorUtility.SetDirty(target); path2D.RecreateFoliage(); } if (Event.current.type == EventType.ValidateCommand) { switch (Event.current.commandName) { case "UndoRedoPerformed": path2D.RecreateFoliage(); break; } } }
// Inspector Fields public override void OnInspectorGUI() { Foliage2D_Path path = (Foliage2D_Path)target; CustomInspector(path); }
private void UpdateHandles(Foliage2D_Path path2D, GUIStyle iconStyle) { Quaternion inv = Quaternion.Inverse(path2D.transform.rotation); Handles.color = new Color(1, 1, 1, 0); Vector3 global, tGlobal = Vector3.zero; handleSelected = false; for (int i = 0; i < path2D.handlesPosition.Count; i++) { // global position of a path point Vector3 pos = path2D.transform.position + Vector3.Scale(new Vector3(path2D.handlesPosition[i].x, path2D.handlesPosition[i].y, 0), path2D.transform.localScale); Vector3 tPos = path2D.transform.position + Vector3.Scale(new Vector3(path2D.handleControlsPos[i].x, path2D.handleControlsPos[i].y, 0), path2D.transform.localScale); bool isSelected = selectedPoints.Contains(i); if (!handleSelected) { handleSelected = selectedPoints.Contains(i); } Texture2D tex = null; tex = isSelected ? texDotSelected : texDot; if (IsVisible(pos)) { SetScale(pos, tex, ref iconStyle); Handles.Label(pos, new GUIContent(tex), iconStyle); } if (!path2D.uniformValues && path2D.foliagePathType == Foliage2D_PathType.Smooth && IsVisible(tPos) && selectedPoints.Contains(i)) { SetScale(tPos, texBlueDot, ref iconStyle); Handles.Label(tPos, new GUIContent(texBlueDot), iconStyle); } global = Handles.FreeMoveHandle(pos, Quaternion.identity, HandleScale(pos), Vector3.zero, Handles.CircleHandleCap); if (!path2D.uniformValues && path2D.foliagePathType == Foliage2D_PathType.Smooth) { tGlobal = Handles.FreeMoveHandle(tPos, Quaternion.identity, HandleScale(tPos), Vector3.zero, Handles.CircleHandleCap); } if (global != pos) { selectedPoints.Clear(); selectedPoints.Add(i); isSelected = true; Vector3 local = inv * (global - path2D.transform.position); Vector2 relative = new Vector2( local.x / path2D.transform.localScale.x, local.y / path2D.transform.localScale.y) - path2D.handlesPosition[i]; path2D.handlesPosition[selectedPoints[0]] += relative; path2D.handleControlsPos[selectedPoints[0]] += relative; index = i; } if (tGlobal != tPos && !path2D.uniformValues && path2D.foliagePathType == Foliage2D_PathType.Smooth) { Vector3 local = inv * (tGlobal - path2D.transform.position); Vector2 relative = new Vector2( local.x / path2D.transform.localScale.x, local.y / path2D.transform.localScale.y) - path2D.handleControlsPos[i]; path2D.handleControlsPos[selectedPoints[0]] += relative; path2D.nodeTension[i] = -Vector2.Distance(path2D.handleControlsPos[i], path2D.handlesPosition[i]) + 2f; Vector2 line = path2D.handleControlsPos[i] - path2D.handlesPosition[i]; float angleInDeg = Mathf.Atan2(line.y, line.x) * Mathf.Rad2Deg; if (angleInDeg > 0) { path2D.nodeBias[i] = -4 * angleInDeg / 180f; } else { path2D.nodeBias[i] = -4 * angleInDeg / 180f; } } // make sure we can add new point at the midpoints! if (i + 1 < path2D.handlesPosition.Count) { int index = i + 1; Vector3 pos2 = path2D.transform.position + path2D.transform.rotation * Vector3.Scale(new Vector3(path2D.handlesPosition[index].x, path2D.handlesPosition[index].y, 0), path2D.transform.localScale); Vector3 mid = (pos + pos2) / 2; float handleScale = HandleScale(mid); if (Handles.Button(mid, SceneView.lastActiveSceneView.camera.transform.rotation, handleScale, handleScale, Handles.CircleHandleCap)) { Vector2 pt = inv * new Vector2((mid.x - path2D.transform.position.x) / path2D.transform.localScale.x, (mid.y - path2D.transform.position.y) / path2D.transform.localScale.y); path2D.handlesPosition.Insert(index, pt); path2D.handleControlsPos.Insert(index, new Vector2(pt.x + 2f, pt.y)); path2D.nodeTension.Insert(index, 0f); path2D.nodeBias.Insert(index, 0f); } if (IsVisible(mid)) { SetScale(mid, texDotPlus, ref iconStyle); Handles.Label(mid, new GUIContent(texDotPlus), iconStyle); } } // check if we want to remove points if (Event.current.alt && path2D.handlesPosition.Count > 2) { float handleScale = HandleScale(pos); if (IsVisible(pos)) { SetScale(pos, texMinus, ref iconStyle); Handles.Label(pos, new GUIContent(texMinus), iconStyle); } if (Handles.Button(pos, SceneView.lastActiveSceneView.camera.transform.rotation, handleScale, handleScale, Handles.CircleHandleCap)) { if (!isSelected) { selectedPoints.Clear(); selectedPoints.Add(i); } for (int s = 0; s < selectedPoints.Count; s++) { path2D.handlesPosition.RemoveAt(selectedPoints[s]); path2D.handleControlsPos.RemoveAt(selectedPoints[s]); path2D.nodeTension.RemoveAt(selectedPoints[s]); path2D.nodeBias.RemoveAt(selectedPoints[s]); if (selectedPoints[s] <= i) { i--; } for (int u = 0; u < selectedPoints.Count; u++) { if (selectedPoints[u] > selectedPoints[s]) { selectedPoints[u] -= 1; } } } selectedPoints.Clear(); GUI.changed = true; } } } }