2024-11-19 11:48:21 +01:00

379 lines
14 KiB
C#

#if UNITY_EDITOR
#region "Imports"
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
#endregion
namespace RoadArchitect
{
[CustomEditor(typeof(RoadSystem))]
public class RoadSystemEditor : Editor
{
#region "Vars"
//Main target for this editor file:
private RoadSystem roadSystem;
//Serialized properties:
private SerializedProperty isAllowingUpdates;
private SerializedProperty isTempMultithreading;
private SerializedProperty isTempSaveMeshAssets;
//Editor only variables:
private bool isInitialized;
// //Editor only camera variables:
private RoadIntersection[] intersections = null;
private int intersectionIndex = 0;
private SplineN[] bridges = null;
private int bridgesIndex = 0;
private bool isBridgeInitialized = false;
private bool isIntersectionInitialized = false;
private bool isEditorCameraFlipped = false;
private float cameraZoomFactor = 1f;
private float cameraHeightOffset = 1f;
private bool isCameraCustomRotated = false;
private Vector3 customCameraRotation = new Vector3(0.5f, 0f, -0.5f);
//Editor only graphic variables:
private Texture2D loadButtonBG = null;
private Texture2D loadButtonBGGlow = null;
private Texture2D warningLabelBG;
private GUIStyle warningLabelStyle;
private GUIStyle loadButton = null;
#endregion
private void OnEnable()
{
roadSystem = (RoadSystem)target;
isAllowingUpdates = serializedObject.FindProperty("isAllowingRoadUpdates");
isTempMultithreading = serializedObject.FindProperty("isMultithreaded");
isTempSaveMeshAssets = serializedObject.FindProperty("isSavingMeshes");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorStyles.label.wordWrap = true;
if(!isInitialized)
{
isInitialized = true;
InitChecks();
}
//Add road button:
RoadArchitect.EditorUtilities.DrawLine();
if (GUILayout.Button("Add road", loadButton, GUILayout.Width(128f)))
{
Selection.activeObject = roadSystem.AddRoad();
}
RoadArchitect.EditorUtilities.DrawLine();
//Update all roads button:
if (GUILayout.Button("Update all roads", EditorStyles.miniButton, GUILayout.Width(120f)))
{
roadSystem.UpdateAllRoads();
}
isAllowingUpdates.boolValue = EditorGUILayout.Toggle("Allow Roads to Update", roadSystem.isAllowingRoadUpdates);
//Multi-threading input:
isTempMultithreading.boolValue = EditorGUILayout.Toggle("Multi-threading enabled", roadSystem.isMultithreaded);
if (isTempMultithreading.boolValue != roadSystem.isMultithreaded)
{
roadSystem.UpdateAllRoadsMultiThreadedOption(isTempMultithreading.boolValue);
}
//Save mesh assets input:
isTempSaveMeshAssets.boolValue = EditorGUILayout.Toggle("Save mesh assets: ", roadSystem.isSavingMeshes);
if (isTempSaveMeshAssets.boolValue != roadSystem.isSavingMeshes)
{
roadSystem.UpdateAllRoadsSavingMeshesOption(isTempSaveMeshAssets.boolValue);
}
if (roadSystem.isSavingMeshes)
{
GUILayout.Label("WARNING: Saving meshes as assets is very slow and can increase road generation time by several minutes.", warningLabelStyle);
}
//Online manual button:
GUILayout.Space(4f);
if (GUILayout.Button("Online manual", EditorStyles.miniButton, GUILayout.Width(120f)))
{
Application.OpenURL("https://github.com/MicroGSD/RoadArchitect/wiki");
}
//Offline manual button:
GUILayout.Space(4f);
if (GUILayout.Button("Offline manual", EditorStyles.miniButton, GUILayout.Width(120f)))
{
RoadArchitect.EditorUtilities.OpenOfflineManual();
}
if (roadSystem.editorPlayCamera == null)
{
roadSystem.EditorCameraSetSingle();
}
RoadArchitect.EditorUtilities.DrawLine();
//View intersection
IntersectionView();
//View bridges
BridgeView();
if(bridges.Length > 0 || intersections.Length > 0)
{
EditorGUILayout.LabelField("* Hotkeys only work when this RoadArchitectSystem object is selected and the scene view has focus", EditorStyles.miniLabel);
}
//Hotkey check:
DoHotKeyCheck();
if (GUI.changed)
{
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(roadSystem);
}
}
private void InitChecks()
{
string basePath = RoadEditorUtility.GetBasePath();
RoadArchitect.EditorUtilities.LoadTexture(ref warningLabelBG, basePath + "/Editor/Icons/WarningLabelBG.png");
RoadArchitect.EditorUtilities.LoadTexture(ref loadButtonBG, basePath + "/Editor/Icons/otherbg.png");
RoadArchitect.EditorUtilities.LoadTexture(ref loadButtonBGGlow, basePath + "/Editor/Icons/otherbg2.png");
if (loadButton == null)
{
loadButton = new GUIStyle(GUI.skin.button);
loadButton.contentOffset = new Vector2(0f, 1f);
loadButton.normal.textColor = new Color(1f, 1f, 1f, 1f);
loadButton.normal.background = loadButtonBG;
loadButton.active.background = loadButtonBGGlow;
loadButton.focused.background = loadButtonBGGlow;
loadButton.hover.background = loadButtonBGGlow;
loadButton.fixedHeight = 16f;
loadButton.fixedWidth = 128f;
loadButton.padding = new RectOffset(0, 0, 0, 0);
}
if (warningLabelStyle == null)
{
warningLabelStyle = new GUIStyle(GUI.skin.textArea);
warningLabelStyle.normal.textColor = Color.red;
warningLabelStyle.active.textColor = Color.red;
warningLabelStyle.hover.textColor = Color.red;
warningLabelStyle.normal.background = warningLabelBG;
warningLabelStyle.active.background = warningLabelBG;
warningLabelStyle.hover.background = warningLabelBG;
warningLabelStyle.padding = new RectOffset(8, 8, 8, 8);
}
}
private void IntersectionView()
{
//View intersection
if (!isIntersectionInitialized)
{
isIntersectionInitialized = true;
intersections = roadSystem.GetComponentsInChildren<RoadIntersection>();
}
if (intersections.Length > 0)
{
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("View next intersection", GUILayout.Width(150f)))
{
IncrementIntersection();
}
EditorGUILayout.LabelField("Hotkey K");
EditorGUILayout.EndHorizontal();
}
}
private void IncrementIntersection()
{
if (intersections.Length > 0)
{
intersectionIndex += 1;
if (intersectionIndex >= intersections.Length)
{
intersectionIndex = 0;
}
ShowIntersection();
}
}
private void BridgeView()
{
//View bridges
if (!isBridgeInitialized)
{
isBridgeInitialized = true;
SplineN[] nodes = roadSystem.transform.GetComponentsInChildren<SplineN>();
List<SplineN> nodeList = new List<SplineN>();
foreach (SplineN node in nodes)
{
if (node.isBridgeStart && node.isBridgeMatched)
{
nodeList.Add(node);
}
}
bridges = nodeList.ToArray();
bridgesIndex = 0;
}
if (bridges.Length > 0)
{
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("View next bridge", GUILayout.Width(150f)))
{
IncrementBridge();
}
EditorGUILayout.LabelField("Hotkey L");
EditorGUILayout.EndHorizontal();
if (EditorApplication.isPlaying)
{
bool isCameraFlipped = EditorGUILayout.Toggle("Flip camera Y:", isEditorCameraFlipped);
if (isCameraFlipped != isEditorCameraFlipped)
{
isEditorCameraFlipped = isCameraFlipped;
ShowBridge();
}
float changeChecker = EditorGUILayout.Slider("Zoom factor:", cameraZoomFactor, 0.02f, 10f);
if (!RootUtils.IsApproximately(changeChecker, cameraZoomFactor, 0.001f))
{
cameraZoomFactor = changeChecker;
ShowBridge();
}
changeChecker = EditorGUILayout.Slider("Height offset:", cameraHeightOffset, 0f, 8f);
if (!RootUtils.IsApproximately(changeChecker, cameraHeightOffset, 0.001f))
{
cameraHeightOffset = changeChecker;
ShowBridge();
}
bool isCustomRotated = EditorGUILayout.Toggle("Custom camera rot:", isCameraCustomRotated);
if (isCustomRotated != isCameraCustomRotated)
{
isCameraCustomRotated = isCustomRotated;
ShowBridge();
}
if (isCameraCustomRotated)
{
Vector3 changedRotation = default(Vector3);
changedRotation.x = EditorGUILayout.Slider("Rotation X:", customCameraRotation.x, -1f, 1f);
changedRotation.z = EditorGUILayout.Slider("Rotation Z:", customCameraRotation.z, -1f, 1f);
if (changedRotation != customCameraRotation)
{
customCameraRotation = changedRotation;
ShowBridge();
}
}
}
}
}
private void IncrementBridge()
{
if (bridges.Length > 0)
{
bridgesIndex += 1;
if (bridgesIndex >= bridges.Length)
{
bridgesIndex = 0;
}
ShowBridge();
}
}
private void ShowIntersection()
{
if (EditorApplication.isPlaying && roadSystem.editorPlayCamera != null)
{
roadSystem.editorPlayCamera.transform.position = intersections[intersectionIndex].transform.position + new Vector3(-40f, 20f, -40f);
roadSystem.editorPlayCamera.transform.rotation = Quaternion.LookRotation(intersections[intersectionIndex].transform.position - (intersections[intersectionIndex].transform.position + new Vector3(-40f, 20f, -40f)));
}
else
{
SceneView.lastActiveSceneView.pivot = intersections[intersectionIndex].transform.position;
SceneView.lastActiveSceneView.Repaint();
}
}
private void ShowBridge()
{
if (EditorApplication.isPlaying && roadSystem.editorPlayCamera != null)
{
Vector3 bridgePosition = ((bridges[bridgesIndex].pos - bridges[bridgesIndex].bridgeCounterpartNode.pos) * 0.5f) + bridges[bridgesIndex].bridgeCounterpartNode.pos;
float bridgeLength = Vector3.Distance(bridges[bridgesIndex].pos, bridges[bridgesIndex].bridgeCounterpartNode.pos);
//Rotation:
Vector3 cameraRotation = Vector3.Cross((bridges[bridgesIndex].pos - bridges[bridgesIndex].bridgeCounterpartNode.pos), Vector3.up);
if (isCameraCustomRotated)
{
cameraRotation = customCameraRotation;
}
else
{
cameraRotation = cameraRotation.normalized;
}
//Calc offset:
Vector3 bridgeOffset = cameraRotation * (bridgeLength * 0.5f * cameraZoomFactor);
//Height offset:
bridgeOffset.y = Mathf.Lerp(20f, 120f, (bridgeLength * 0.001f)) * cameraZoomFactor * cameraHeightOffset;
roadSystem.editorPlayCamera.transform.position = bridgePosition + bridgeOffset;
roadSystem.editorPlayCamera.transform.rotation = Quaternion.LookRotation(bridgePosition - (bridgePosition + bridgeOffset));
}
else
{
SceneView.lastActiveSceneView.pivot = bridges[bridgesIndex].transform.position;
SceneView.lastActiveSceneView.Repaint();
}
}
public void OnSceneGUI()
{
DoHotKeyCheck();
}
private void DoHotKeyCheck()
{
Event current = Event.current;
if (current.type == EventType.KeyDown)
{
if (current.keyCode == KeyCode.K)
{
IncrementIntersection();
}
else if (current.keyCode == KeyCode.L)
{
IncrementBridge();
}
}
}
}
}
#endif